From b465d1ff7a83bd7dfb7004ef80c1a726816ca7ed Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 18 Jul 2026 22:28:00 +0800 Subject: [PATCH] refine Semi UI telemetry details --- .../apps/web/src/v2/domain/telemetry.ts | 19 +++++ .../web/src/v2/pages/VehiclePage.test.tsx | 11 ++- .../apps/web/src/v2/pages/VehiclePage.tsx | 28 ++++--- .../apps/web/src/v2/productionEntry.test.ts | 2 + .../apps/web/src/v2/styles/v2.css | 82 +++++++++++++++++-- 5 files changed, 126 insertions(+), 16 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/v2/domain/telemetry.ts b/vehicle-data-platform/apps/web/src/v2/domain/telemetry.ts index 4504329f..5840e986 100644 --- a/vehicle-data-platform/apps/web/src/v2/domain/telemetry.ts +++ b/vehicle-data-platform/apps/web/src/v2/domain/telemetry.ts @@ -16,6 +16,25 @@ export function telemetryQualityLabel(quality: string) { return '异常'; } +export function telemetryQualityColor(quality: string) { + return quality === 'good' ? 'green' : quality === 'stale' ? 'orange' : 'red'; +} + +export function telemetryValueTypeLabel(value?: string) { + const normalized = value?.trim().toLowerCase(); + if (!normalized) return '—'; + const labels: Record = { + array: '列表', + boolean: '布尔值', + dynamic: '动态值', + number: '数值', + numeric: '数值', + object: '对象', + string: '文本' + }; + return labels[normalized] ?? value!; +} + export function formatTelemetryTime(value?: string) { if (!value) return '—'; const local = formatShanghaiDateTime(value); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx index bb6ed4b9..2084ebd6 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx @@ -484,7 +484,9 @@ test('keeps protocol telemetry independent and explains mileage semantics', asyn expect(complexValue).toHaveTextContent('4 个值'); expect(complexValue).toHaveTextContent('3.21,3.22,3.23'); fireEvent.click(complexValue); - expect(await screen.findByRole('dialog', { name: '遥测字段完整数据' })).toBeInTheDocument(); + expect(await screen.findByRole('dialog', { name: '遥测字段详情' })).toBeInTheDocument(); + expect(document.querySelector('.v2-telemetry-detail-protocol')).toHaveClass('semi-tag-blue-light'); + expect(document.querySelector('.v2-telemetry-value-meta')).toHaveTextContent('数据类型列表'); expect(document.querySelector('.v2-telemetry-value-detail pre')).toHaveTextContent('3.24'); expect(document.querySelector('.v2-telemetry-value-detail > footer strong')).toHaveTextContent('10.0.0.1:8080'); fireEvent.click(screen.getByRole('tab', { name: /JT\/T 808/ })); @@ -515,6 +517,13 @@ test('uses compact Semi telemetry evidence cards on mobile', async () => { expect(document.querySelector('.v2-telemetry-table')).not.toBeInTheDocument(); expect(document.querySelector('.v2-telemetry-mobile-list.semi-list')).toBeInTheDocument(); expect(document.querySelector('.v2-telemetry-mobile-item.semi-list-item')).toHaveTextContent('36km/h正常'); + expect(screen.getByRole('button', { name: '查看 GPS 速度 字段详情' })).toHaveClass('v2-telemetry-mobile-action'); + fireEvent.click(screen.getByRole('button', { name: '查看 GPS 速度 字段详情' })); + expect(await screen.findByRole('dialog', { name: '遥测字段详情' })).toBeInTheDocument(); + expect(document.querySelector('.v2-telemetry-value-sidesheet')).toHaveClass('semi-sidesheet-bottom'); + expect(document.querySelector('.v2-telemetry-value-meta')).toHaveTextContent('数据类型数值'); + expect(document.querySelector('.v2-telemetry-detail-protocol')).toHaveClass('semi-tag-cyan-light'); + expect(screen.getByRole('button', { name: '关闭遥测字段详情' })).toBeInTheDocument(); expect(document.querySelector('.v2-vehicle-record-page')).toHaveClass('v2-vehicle-record-v3', 'is-mobile-layout'); expect(screen.getByRole('group', { name: '车辆快捷操作' }).querySelectorAll('.semi-button')).toHaveLength(5); expect(screen.getByRole('group', { name: '车辆快捷操作' })).toHaveTextContent('切换车辆轨迹回放历史数据里程查询告警事件'); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.tsx index 6fb9ba15..cdc11d06 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.tsx @@ -11,7 +11,7 @@ import type { LatestTelemetryResponse, LatestTelemetryValue, QualityIssueRow, Ve import { usePlatformSession } from '../auth/AuthGate'; import { canAdminister, hasMenu } from '../auth/session'; import { LIVE_QUERY_POLICY, QUERY_MEMORY } from '../queryPolicy'; -import { formatTelemetryTime, formatTelemetryValue, telemetryQualityLabel } from '../domain/telemetry'; +import { formatTelemetryTime, formatTelemetryValue, telemetryQualityColor, telemetryQualityLabel, telemetryValueTypeLabel } from '../domain/telemetry'; import { formatZhNumber } from '../domain/formatters'; import { protocolDisplayLabel, protocolSourceLabel } from '../domain/protocols'; import { isValidAMapCoordinate } from '../../integrations/amap'; @@ -201,6 +201,9 @@ function telemetryValueDetail(item: LatestTelemetryValue) { function TelemetryValueCell({ item, onInspect }: { item: LatestTelemetryValue; onInspect?: (item: LatestTelemetryValue) => void }) { const presentation = telemetryValuePresentation(item); if (presentation.complex) { + if (!onInspect) { + return
{presentation.summary}{presentation.preview}
; + } return
; + return
{telemetryQualityLabel(item.quality)}{item.qualityReason || '未提供质量说明'} · {formatZhNumber(item.freshnessSeconds, 0)}s
; } function TelemetryTimeCell({ item }: { item: LatestTelemetryValue }) { @@ -239,7 +241,7 @@ 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(Boolean(inspectedValue), '.v2-telemetry-value-sidesheet', 'v2-telemetry-value-detail', '遥测字段完整数据', '关闭遥测字段完整数据'); + useSideSheetA11y(Boolean(inspectedValue), '.v2-telemetry-value-sidesheet', 'v2-telemetry-value-detail', '遥测字段详情', '关闭遥测字段详情'); const indexed = useMemo(() => { const valuesByProtocolCategory = new Map(); const sources = new Map(); @@ -277,9 +279,12 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon ? : mobileLayout ? -
+
- +
+ + +
} /> :
{inspectedValue.label}{inspectedValue.sourceField} : null} onCancel={() => { setInspectedValue(undefined); setCopiedValue(false); }} footer={
完整值仅按需渲染,不影响实时遥测刷新。
} @@ -316,10 +324,10 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon size="small" row data={[ - { key: '协议', value: protocolDisplayLabel(inspectedValue.protocol) }, - { key: '数据类型', value: inspectedValue.valueType || '—' }, + { key: '协议', value: }, + { key: '数据类型', value: telemetryValueTypeLabel(inspectedValue.valueType) }, { key: '单位', value: inspectedValue.unit || '—' }, - { key: '质量', value: telemetryQualityLabel(inspectedValue.quality) }, + { key: '质量', value: {telemetryQualityLabel(inspectedValue.quality)} }, { key: '设备时间', value: formatTelemetryTime(inspectedValue.deviceTime) }, { key: '接收时间', value: formatTelemetryTime(inspectedValue.serverTime) } ]} diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts index 34b8fd20..42846d72 100644 --- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts +++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts @@ -90,6 +90,8 @@ describe('V2 production entry', () => { expect(corePageSources.VehiclePage).toContain('className="v2-telemetry-table"'); expect(corePageSources.VehiclePage).toContain(' section { overflow: hidden; border: 1px solid #dfe6ef; @@ -8791,13 +8815,61 @@ button, a { -webkit-tap-highlight-color: transparent; } border-top: 1px solid #edf1f5; padding-top: 9px; } - .v2-telemetry-mobile-item > .v2-telemetry-source { display: flex; align-items: center; gap: 8px; } - .v2-telemetry-mobile-item > .v2-telemetry-source small { min-width: 0; } + .v2-telemetry-mobile-source { + display: flex; + min-width: 0; + align-items: center; + justify-content: space-between; + gap: 8px; + border-top: 1px solid #edf1f5; + padding-top: 8px; + } + .v2-telemetry-mobile-source > .v2-telemetry-source { + display: flex; + min-width: 0; + align-items: center; + gap: 7px; + } + .v2-telemetry-mobile-source > .v2-telemetry-source small { + min-width: 0; + } + .v2-telemetry-mobile-action.semi-button { + min-width: 76px; + min-height: 30px; + flex: 0 0 auto; + justify-content: flex-end; + border-radius: 7px; + padding-inline: 7px; + font-size: 9px; + font-weight: 700; + } .v2-telemetry-card > .semi-card-body > footer { flex-wrap: wrap; gap: 5px; margin: 3px 9px 9px; } .v2-telemetry-card > .semi-card-body > footer small { width: 100%; margin: 3px 0 0; } - .v2-telemetry-value-sidesheet .semi-sidesheet-inner { width: 100vw !important; } - .v2-telemetry-value-sidesheet .semi-sidesheet-header { padding: 15px 16px 13px; } - .v2-telemetry-value-sidesheet .semi-sidesheet-body { padding: 12px; } + .v2-telemetry-value-sidesheet .semi-sidesheet-inner { + width: 100vw !important; + overflow: hidden; + border-radius: 16px 16px 0 0; + background: #f4f7fb; + box-shadow: 0 -18px 48px rgba(25,45,72,.18); + } + .v2-telemetry-value-sidesheet .semi-sidesheet-header { + min-height: 68px; + border-bottom-color: #dfe7f0; + background: rgba(255,255,255,.98); + padding: 11px 14px; + } + .v2-telemetry-value-sidesheet .semi-sidesheet-body { + overflow: auto; + background: #f4f7fb; + padding: 10px; + overscroll-behavior: contain; + } + .v2-telemetry-value-sidesheet .semi-sidesheet-footer { + border-top: 1px solid #dfe7f0; + background: rgba(255,255,255,.98); + padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); + } + .v2-telemetry-value-title strong { font-size: 15px; } .v2-telemetry-value-meta .semi-descriptions-item { min-width: 108px; } .v2-telemetry-value-detail > section > header { align-items: flex-start; flex-direction: column; } .v2-telemetry-value-detail > section > header .semi-button { width: 100%; justify-content: center; }