diff --git a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx index 5dd900c1..fdc384ea 100644 --- a/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx +++ b/vehicle-data-platform/apps/web/src/pages/VehicleDetail.tsx @@ -196,6 +196,12 @@ export function VehicleDetail({ const evidenceTimeDeltaSeconds = consistencyTimeDeltaSeconds; const activeProtocol = query.protocol?.trim() ?? ''; const scopeText = activeProtocol ? `单一来源:${activeProtocol}` : '全部来源聚合'; + const archivePlate = resolution?.plate || summary?.plate || identity?.plate || latest?.plate || ''; + const archivePhone = resolution?.phone || summary?.phone || identity?.phone || ''; + const archiveOEM = resolution?.oem || summary?.oem || identity?.oem || ''; + const archiveFields = [hasResolvedVIN && displayVIN !== '-', archivePlate, archivePhone, archiveOEM]; + const archiveCompleteness = `${archiveFields.filter(Boolean).length}/${archiveFields.length}`; + const archiveSourceCount = Math.max(protocols.length, sourceCount); const formKey = `${query.keyword}-${query.protocol ?? ''}`; const switchSource = (nextProtocol = '') => { const nextQuery = { keyword: resolvedVIN || query.keyword, protocol: nextProtocol }; @@ -389,17 +395,20 @@ export function VehicleDetail({ - +
{hasResolvedVIN ? '已解析 VIN' : '未解析到 VIN'} }, - { key: '车牌', value: resolution?.plate || summary?.plate || identity?.plate || latest?.plate || '-' }, - { key: '手机号', value: resolution?.phone || summary?.phone || identity?.phone || '-' }, - { key: 'OEM', value: resolution?.oem || summary?.oem || identity?.oem || '-' }, + { key: '车牌', value: archivePlate || '-' }, + { key: '手机号', value: archivePhone || '-' }, + { key: 'OEM', value: archiveOEM || '-' }, + { key: '档案完整度', value: {archiveCompleteness} }, + { key: '归并来源数', value: archiveSourceCount }, { key: '在线', value: }, { key: '数据来源', diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 45423cae..b5c70dec 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -5402,3 +5402,80 @@ test('shows actionable vehicle service recommendations on detail', async () => { fireEvent.click(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 来源' })); expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=YUTONG_MQTT'); }); + +test('shows canonical vehicle archive on detail', async () => { + window.history.replaceState(null, '', '/#/detail?keyword=VIN-ARCHIVE-001'); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/vehicle-service')) { + return { + ok: true, + json: async () => ({ + data: { + vin: 'VIN-ARCHIVE-001', + lookupKey: 'VIN-ARCHIVE-001', + lookupResolved: true, + resolution: { + lookupKey: 'VIN-ARCHIVE-001', + resolved: true, + vin: 'VIN-ARCHIVE-001', + plate: '粤A档案1', + phone: '13307795425', + oem: 'G7s', + protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'], + online: true, + lastSeen: '2026-07-03T20:12:10+08:00' + }, + realtimeSummary: { + vin: 'VIN-ARCHIVE-001', + plate: '粤A档案1', + phone: '13307795425', + oem: 'G7s', + protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'], + sourceCount: 3, + onlineSourceCount: 2, + online: true, + bindingStatus: 'bound', + primaryProtocol: 'GB32960', + longitude: 113.2, + latitude: 23.1, + speedKmh: 32, + socPercent: 78, + totalMileageKm: 1000.5, + lastSeen: '2026-07-03T20:12:10+08:00' + }, + sources: ['GB32960', 'JT808', 'YUTONG_MQTT'], + sourceStatus: [], + realtime: [], + history: { items: [], total: 0, limit: 20, offset: 0 }, + raw: { items: [], total: 0, limit: 10, offset: 0 }, + mileage: { items: [], total: 0, limit: 20, offset: 0 }, + quality: { items: [], total: 0, limit: 20, offset: 0 } + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: path.includes('/api/ops/health') + ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } } + : { items: [], total: 0, limit: 10, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('车辆档案')).toBeInTheDocument(); + expect(screen.getByText('车辆主键')).toBeInTheDocument(); + expect(screen.getAllByText('VIN-ARCHIVE-001').length).toBeGreaterThan(0); + expect(screen.getByText('档案完整度')).toBeInTheDocument(); + expect(screen.getByText('4/4')).toBeInTheDocument(); + expect(screen.getByText('归并来源数')).toBeInTheDocument(); + expect(screen.getByText('3')).toBeInTheDocument(); +});