feat: unify track and telemetry workbenches
This commit is contained in:
@@ -3,7 +3,7 @@ import {
|
||||
IconAlarm, IconBox, IconCalendar, IconClock, IconCopy,
|
||||
IconChevronRight, IconMapPin, IconSearch, IconTickCircle
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, SideSheet, Table, Tag } from '@douyinfe/semi-ui';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, Table, Tag } from '@douyinfe/semi-ui';
|
||||
import { FormEvent, lazy, Suspense, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { api } from '../../api/client';
|
||||
@@ -24,9 +24,9 @@ import { SegmentedTabs } from '../shared/SegmentedTabs';
|
||||
import { VehicleSourceEvidencePanel } from '../shared/VehicleSourceEvidencePanel';
|
||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { WorkspaceSideSheet, type WorkspaceSideSheetBadgeColor } from '../shared/WorkspaceSideSheet';
|
||||
import { monitorReturnFromParams, withMonitorReturn } from '../routing/monitorContext';
|
||||
import { useMobileLayout } from '../hooks/useMobileLayout';
|
||||
import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
|
||||
|
||||
function fmt(value?: string) { return value?.trim() || '—'; }
|
||||
function metric(value: number | undefined) { return Number.isFinite(value as number) ? formatZhNumber(value!, 1) : '—'; }
|
||||
@@ -245,7 +245,10 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
const [copiedValue, setCopiedValue] = useState(false);
|
||||
const inspectedPresentation = useMemo(() => inspectedValue ? telemetryValuePresentation(inspectedValue) : undefined, [inspectedValue]);
|
||||
const inspectedDetail = useMemo(() => inspectedValue ? telemetryValueDetail(inspectedValue) : '', [inspectedValue]);
|
||||
useSideSheetA11y(!!inspectedValue, '.v2-telemetry-value-sidesheet', 'v2-telemetry-value-detail', '遥测字段详情', '关闭遥测字段详情');
|
||||
const closeInspection = () => {
|
||||
setInspectedValue(undefined);
|
||||
setCopiedValue(false);
|
||||
};
|
||||
const indexed = useMemo(() => {
|
||||
const valuesByProtocolCategory = new Map<string, LatestTelemetryResponse['values']>();
|
||||
const sources = new Map<string, { protocol: string; endpoint?: string }>();
|
||||
@@ -311,16 +314,40 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
</div>
|
||||
{state}
|
||||
<footer><b>来源</b>{indexed.sources.map((source) => <span key={`${source.protocol}-${source.endpoint ?? ''}`} title={source.endpoint}>{protocolDisplayLabel(source.protocol)}</span>)}<small>扫描 {data?.scannedFrames ?? 0} 帧 · 上海时间 {formatTelemetryTime(data?.asOf)}</small></footer>
|
||||
<SideSheet
|
||||
className="v2-telemetry-value-sidesheet"
|
||||
<WorkspaceSideSheet
|
||||
className="v2-telemetry-evidence-sidesheet"
|
||||
variant="detail"
|
||||
visible={!!inspectedValue}
|
||||
ariaLabel="遥测字段详情"
|
||||
placement={mobileLayout ? 'bottom' : 'right'}
|
||||
width={mobileLayout ? undefined : 520}
|
||||
height={mobileLayout ? 'min(88dvh, 780px)' : undefined}
|
||||
aria-label="遥测字段详情"
|
||||
title={inspectedValue ? <div className="v2-telemetry-value-title"><strong>{inspectedValue.label}</strong><span>{inspectedValue.sourceField}</span></div> : null}
|
||||
onCancel={() => { setInspectedValue(undefined); setCopiedValue(false); }}
|
||||
footer={<div className="v2-telemetry-value-footer"><span>完整值仅按需渲染,不影响实时遥测刷新。</span><Button theme="solid" onClick={() => { setInspectedValue(undefined); setCopiedValue(false); }}>完成</Button></div>}
|
||||
title={inspectedValue?.label}
|
||||
description={inspectedValue?.sourceField}
|
||||
badge={inspectedValue ? telemetryQualityLabel(inspectedValue.quality) : undefined}
|
||||
badgeColor={inspectedValue ? telemetryQualityColor(inspectedValue.quality) as WorkspaceSideSheetBadgeColor : 'grey'}
|
||||
summaryItems={inspectedValue ? [
|
||||
{
|
||||
label: '协议来源',
|
||||
value: <ProtocolTag className="v2-telemetry-detail-protocol" protocol={inspectedValue.protocol} compact />,
|
||||
detail: inspectedValue.sourceEndpoint || '协议默认来源',
|
||||
tone: 'primary'
|
||||
},
|
||||
{
|
||||
label: '完整值',
|
||||
value: inspectedPresentation?.summary || '—',
|
||||
detail: inspectedValue.unit ? `单位 ${inspectedValue.unit}` : '无单位'
|
||||
},
|
||||
{
|
||||
label: '数据质量',
|
||||
value: telemetryQualityLabel(inspectedValue.quality),
|
||||
detail: `${formatZhNumber(inspectedValue.freshnessSeconds, 0)}s · ${inspectedValue.qualityReason || '未提供说明'}`,
|
||||
tone: inspectedValue.quality === 'good' ? 'success' : inspectedValue.quality === 'stale' ? 'warning' : 'danger'
|
||||
}
|
||||
] : []}
|
||||
footerNote="完整值按需渲染,不影响实时遥测刷新。"
|
||||
primaryAction={{ label: '完成', onClick: closeInspection }}
|
||||
onCancel={closeInspection}
|
||||
>
|
||||
{inspectedValue ? <div className="v2-telemetry-value-detail">
|
||||
<Descriptions
|
||||
@@ -328,12 +355,11 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
size="small"
|
||||
row
|
||||
data={[
|
||||
{ key: '协议', value: <ProtocolTag className="v2-telemetry-detail-protocol" protocol={inspectedValue.protocol} compact /> },
|
||||
{ key: '数据类型', value: telemetryValueTypeLabel(inspectedValue.valueType) },
|
||||
{ key: '单位', value: inspectedValue.unit || '—' },
|
||||
{ key: '质量', value: <Tag className="v2-telemetry-detail-quality" color={telemetryQualityColor(inspectedValue.quality)} type="light" size="small">{telemetryQualityLabel(inspectedValue.quality)}</Tag> },
|
||||
{ key: '设备时间', value: formatTelemetryTime(inspectedValue.deviceTime) },
|
||||
{ key: '接收时间', value: formatTelemetryTime(inspectedValue.serverTime) }
|
||||
{ key: '接收时间', value: formatTelemetryTime(inspectedValue.serverTime) },
|
||||
{ key: '质量说明', value: inspectedValue.qualityReason || '未提供质量说明' }
|
||||
]}
|
||||
/>
|
||||
<section>
|
||||
@@ -347,9 +373,8 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
>{copiedValue ? '已复制' : '复制完整值'}</Button></header>
|
||||
<pre>{inspectedDetail}</pre>
|
||||
</section>
|
||||
<footer><span>来源端点</span><strong>{inspectedValue.sourceEndpoint || '协议默认来源'}</strong></footer>
|
||||
</div> : null}
|
||||
</SideSheet>
|
||||
</WorkspaceSideSheet>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user