feat(web): refine Semi UI history workspace
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery, useQueryClient, type UseQueryResult } from '@tanstack/react-query';
|
||||
import { IconCalendar, IconChevronRight, IconClose, IconDownload, IconMapPin, IconMore, IconRefresh, IconSearch, IconSetting } from '@douyinfe/semi-icons';
|
||||
import { IconCalendar, IconChevronRight, IconClose, IconDownload, IconFilter, IconMapPin, IconMore, IconRefresh, IconSearch, IconSetting } from '@douyinfe/semi-icons';
|
||||
import { Button, Card, CardGroup, Checkbox, Descriptions, Dropdown, Empty, Input, Progress, Select, Spin, Table, Tag, Typography } from '@douyinfe/semi-ui';
|
||||
import { FormEvent, KeyboardEvent, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
@@ -369,6 +369,7 @@ export default function HistoryPage() {
|
||||
const [columnSettingsOpen, setColumnSettingsOpen] = useState(false);
|
||||
const [trendExpanded, setTrendExpanded] = useState(false);
|
||||
const [filtersCollapsed, setFiltersCollapsed] = useState(true);
|
||||
const [desktopQueryCollapsed, setDesktopQueryCollapsed] = useState(false);
|
||||
const [exportJobsOpen, setExportJobsOpen] = useState(false);
|
||||
const [toolsOpen, setToolsOpen] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -445,6 +446,7 @@ export default function HistoryPage() {
|
||||
const next = { ...draft, keywords: parsed.join(',') };
|
||||
setCriteria(next); setOffset(0);
|
||||
setFiltersCollapsed(true);
|
||||
setDesktopQueryCollapsed(true);
|
||||
const url = new URLSearchParams({ keywords: next.keywords, category: next.category });
|
||||
if (next.dateFrom) url.set('dateFrom', next.dateFrom);
|
||||
if (next.dateTo) url.set('dateTo', next.dateTo);
|
||||
@@ -453,7 +455,7 @@ export default function HistoryPage() {
|
||||
};
|
||||
const submit = (event: FormEvent) => { event.preventDefault(); applyDraft(); };
|
||||
const resetDraft = () => setDraft({ keywords: '', ...currentHistoryWindow(), category: 'location', protocol: '' });
|
||||
const reset = () => { const next = { keywords: '', ...currentHistoryWindow(), category: 'location', protocol: '' }; setDraft(next); setCriteria(next); setOffset(0); setFiltersCollapsed(true); setSearchParams(preserveMonitorReturn(new URLSearchParams(), monitorReturn), { replace: true }); };
|
||||
const reset = () => { const next = { keywords: '', ...currentHistoryWindow(), category: 'location', protocol: '' }; setDraft(next); setCriteria(next); setOffset(0); setFiltersCollapsed(true); setDesktopQueryCollapsed(false); setSearchParams(preserveMonitorReturn(new URLSearchParams(), monitorReturn), { replace: true }); };
|
||||
const toggleMetric = (key: string) => setVisibleByCategory((current) => {
|
||||
const baseline = current[criteria.category] ?? allMetrics.filter((metric) => metric.defaultVisible).map((metric) => metric.key);
|
||||
const next = baseline.includes(key) ? baseline.filter((item) => item !== key) : [...baseline, key];
|
||||
@@ -471,6 +473,11 @@ export default function HistoryPage() {
|
||||
const page = Math.floor(offset / limit) + 1;
|
||||
const summarySources = resultSummary?.sources?.join('、') || '—';
|
||||
const summaryLoading = keywords.length > 0 && dataQuery.isFetching && !result;
|
||||
const appliedCategoryLabel = catalogQuery.data?.categories.find((item) => item.key === criteria.category)?.label
|
||||
?? (criteria.category === 'raw' ? '原始报文' : criteria.category === 'mileage' ? '日里程' : '位置数据');
|
||||
const appliedWindowLabel = criteria.dateFrom && criteria.dateTo
|
||||
? `${criteria.dateFrom.replace('T', ' ').slice(5, 16)} – ${criteria.dateTo.replace('T', ' ').slice(5, 16)}`
|
||||
: '时间待选择';
|
||||
const historyMetrics: WorkspaceQueueMetricRailItem[] = [
|
||||
{
|
||||
label: '结果行数',
|
||||
@@ -513,7 +520,7 @@ export default function HistoryPage() {
|
||||
|
||||
return <div className="v2-history-page">
|
||||
<MonitorReturnBar />
|
||||
<div className="v2-history-discovery-shell">
|
||||
<div className="v2-history-discovery-shell" data-query-collapsed={!mobileLayout && desktopQueryCollapsed}>
|
||||
<WorkspaceCommandBar
|
||||
className="v2-history-command-bar"
|
||||
ariaLabel="历史数据查询操作"
|
||||
@@ -521,7 +528,21 @@ export default function HistoryPage() {
|
||||
description="按车辆、时间和协议核验可追溯的历史数据"
|
||||
status={keywords.length ? `${keywords.length} 辆 · ${(result?.total ?? 0).toLocaleString('zh-CN')} 条` : '等待查询'}
|
||||
statusColor={keywords.length ? 'blue' : 'grey'}
|
||||
meta={<Typography.Text type="tertiary">上海时区 · 最多 5 台车辆</Typography.Text>}
|
||||
meta={keywords.length ? <span className="v2-history-applied-scope" aria-label="已应用查询范围">
|
||||
<span><IconCalendar />{appliedWindowLabel}</span>
|
||||
<span>{appliedCategoryLabel}</span>
|
||||
<span>{criteria.protocol || '全部来源'}</span>
|
||||
</span> : <Typography.Text type="tertiary">上海时区 · 最多 5 台车辆</Typography.Text>}
|
||||
actions={!mobileLayout ? <Button
|
||||
className="v2-history-query-toggle"
|
||||
theme="light"
|
||||
type="tertiary"
|
||||
icon={<IconFilter />}
|
||||
aria-label={desktopQueryCollapsed ? '修改查询条件' : '收起查询条件'}
|
||||
aria-expanded={!desktopQueryCollapsed}
|
||||
aria-controls="v2-history-query-panel"
|
||||
onClick={() => setDesktopQueryCollapsed((value) => !value)}
|
||||
>{desktopQueryCollapsed ? '修改条件' : '收起条件'}</Button> : undefined}
|
||||
/>
|
||||
{mobileLayout ? <div className="v2-history-mobile-discovery">
|
||||
<MobileFilterToggle
|
||||
@@ -582,7 +603,7 @@ export default function HistoryPage() {
|
||||
statusColor={keywords.length ? 'blue' : 'grey'}
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
>
|
||||
<form className={`v2-history-toolbar${filtersCollapsed ? ' is-mobile-collapsed' : ''}`} onSubmit={submit}>
|
||||
<form id="v2-history-query-panel" className={`v2-history-toolbar${filtersCollapsed ? ' is-mobile-collapsed' : ''}`} onSubmit={submit}>
|
||||
{historyFilterFields}
|
||||
<div className="v2-history-toolbar-actions"><Button className="v2-primary-button" theme="solid" htmlType="submit" disabled={!parseHistoryKeywords(draft.keywords).length}>查询</Button><Button className="v2-secondary-button" theme="light" onClick={reset}>重置</Button>{!exportAllowed ? <Tag className="v2-history-permission-tag" color="grey" type="light" size="large">只读</Tag> : null}</div>
|
||||
</form>
|
||||
@@ -648,7 +669,7 @@ export default function HistoryPage() {
|
||||
onKeyDown={scrollHistoryTable}
|
||||
>
|
||||
{mobileLayout
|
||||
? <div className="v2-history-mobile-list">{resultRows.map((row) => <Card key={row.id} className={`v2-history-mobile-card${selectedRow?.id === row.id ? ' is-selected' : ''}`} bodyStyle={{ padding: 0 }}><Button theme="borderless" type="tertiary" className="v2-history-mobile-action" aria-pressed={selectedRow?.id === row.id} aria-expanded={selectedRow?.id === row.id} aria-label={`查看 ${row.plate || row.vin} ${row.deviceTime} 数据详情`} onClick={() => selectRow(row)}><span className="v2-history-mobile-card-content"><header><span><strong>{row.plate || '未绑定车牌'}</strong><small>{row.vin}</small></span><span className={`v2-quality is-${row.quality}`}><i />{historyQualityLabel(row.quality)}</span></header><span className="v2-history-mobile-meta"><PlatformTime className="v2-history-time" value={row.deviceTime} /><ProtocolTag className="v2-history-protocol-tag" protocol={row.protocol} compact /></span>{isHealthyHistoryQuality(row.quality) ? null : <small className="v2-history-mobile-quality">{row.qualityReason || '平台未返回质量说明'}</small>}<dl>{visibleMetrics.slice(0, 4).map((metric) => <div key={metric.key}><dt>{metric.label}</dt><dd>{formatHistoryValue(row.values[metric.key], metric)}</dd></div>)}</dl><footer>查看完整详情<IconChevronRight /></footer></span></Button></Card>)}</div>
|
||||
? <div className="v2-history-mobile-list">{resultRows.map((row) => <Card key={row.id} className={`v2-history-mobile-card is-quality-${row.quality}${selectedRow?.id === row.id ? ' is-selected' : ''}`} bodyStyle={{ padding: 0 }}><Button theme="borderless" type="tertiary" className="v2-history-mobile-action" aria-pressed={selectedRow?.id === row.id} aria-expanded={selectedRow?.id === row.id} aria-label={`查看 ${row.plate || row.vin} ${row.deviceTime} 数据详情`} onClick={() => selectRow(row)}><span className="v2-history-mobile-card-content"><header><span><strong>{row.plate || '未绑定车牌'}</strong><small>{row.vin}</small></span><span className={`v2-quality is-${row.quality}`}><i />{historyQualityLabel(row.quality)}</span></header><span className="v2-history-mobile-meta"><PlatformTime className="v2-history-time" value={row.deviceTime} /><ProtocolTag className="v2-history-protocol-tag" protocol={row.protocol} compact /></span>{isHealthyHistoryQuality(row.quality) ? null : <small className="v2-history-mobile-quality">{row.qualityReason || '平台未返回质量说明'}</small>}<dl>{visibleMetrics.slice(0, 4).map((metric) => <div key={metric.key}><dt>{metric.label}</dt><dd>{formatHistoryValue(row.values[metric.key], metric)}</dd></div>)}</dl><footer>查看完整详情<IconChevronRight /></footer></span></Button></Card>)}</div>
|
||||
: resultRows.length ? <HistoryDataTable rows={resultRows} metrics={visibleMetrics} selectedRowID={selectedRow?.id} onSelect={selectRow} /> : null}
|
||||
{dataQuery.isPending && keywords.length
|
||||
? <PanelLoading className="v2-history-loading" title="正在加载当前筛选范围的历史数据" description="按车辆、时间和协议整理可追溯证据。" />
|
||||
|
||||
Reference in New Issue
Block a user