diff --git a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx index 79b93a4b..b3f83ca7 100644 --- a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx @@ -40,6 +40,15 @@ function vehicleArchiveCompleteness(row: VehicleCoverageRow) { return { completed, total: fields.length, label: `${completed}/${fields.length}` }; } +function vehicleArchiveMissingLabels(row: VehicleCoverageRow) { + return [ + { value: row.vin, label: '缺VIN' }, + { value: row.plate, label: '缺车牌' }, + { value: row.phone, label: '缺手机号' }, + { value: row.oem, label: '缺OEM' } + ].filter((item) => !String(item.value ?? '').trim()).map((item) => item.label); +} + const coverageLabel: Record = { single: '单源', multi: '多源' @@ -265,10 +274,18 @@ export function Vehicles({ { title: 'OEM', dataIndex: 'oem', width: 120 }, { title: '档案完整度', - width: 120, + width: 190, render: (_: unknown, row: VehicleCoverageRow) => { const archive = vehicleArchiveCompleteness(row); - return {archive.label}; + const missingLabels = vehicleArchiveMissingLabels(row); + return ( + + {archive.label} + {missingLabels.map((label) => ( + {label} + ))} + + ); } }, { 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 fa7b5051..b44e503a 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -611,32 +611,48 @@ test('shows vehicle service result summary on vehicle list filters', async () => ok: true, json: async () => ({ data: { - items: [{ - vin: 'VIN-MULTI-001', - plate: '粤A服务1', - phone: '13307795425', - oem: 'G7s', - protocols: ['GB32960', 'JT808'], - missingProtocols: ['YUTONG_MQTT'], - sourceStatus: [ - { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false }, - { protocol: 'JT808', online: false, lastSeen: '2026-07-03 20:10:00', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false }, - { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false } - ], - sourceCount: 2, - onlineSourceCount: 1, - online: true, - lastSeen: '2026-07-03 20:12:10', - bindingStatus: 'bound', - serviceStatus: { - status: 'degraded', - severity: 'warning', - title: '来源不完整', - detail: '1/2 个来源在线', + items: [ + { + vin: 'VIN-MULTI-001', + plate: '粤A服务1', + phone: '13307795425', + oem: 'G7s', + protocols: ['GB32960', 'JT808'], + missingProtocols: ['YUTONG_MQTT'], + sourceStatus: [ + { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false }, + { protocol: 'JT808', online: false, lastSeen: '2026-07-03 20:10:00', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false }, + { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false } + ], sourceCount: 2, - onlineSourceCount: 1 + onlineSourceCount: 1, + online: true, + lastSeen: '2026-07-03 20:12:10', + bindingStatus: 'bound', + serviceStatus: { + status: 'degraded', + severity: 'warning', + title: '来源不完整', + detail: '1/2 个来源在线', + sourceCount: 2, + onlineSourceCount: 1 + } + }, + { + vin: 'VIN-MISSING-ARCHIVE', + plate: '粤A缺项', + phone: '', + oem: '', + protocols: ['JT808'], + missingProtocols: ['GB32960', 'YUTONG_MQTT'], + sourceStatus: [], + sourceCount: 1, + onlineSourceCount: 1, + online: true, + lastSeen: '2026-07-03 20:12:09', + bindingStatus: 'bound' } - }], + ], total: 181, limit: 20, offset: 0 @@ -667,7 +683,7 @@ test('shows vehicle service result summary on vehicle list filters', async () => expect(screen.getByText('暂无来源车辆')).toBeInTheDocument(); expect(screen.getByText('待绑定')).toBeInTheDocument(); expect(screen.getByText('档案不完整')).toBeInTheDocument(); - expect(screen.getByText('缺手机号')).toBeInTheDocument(); + expect(screen.getAllByText('缺手机号').length).toBeGreaterThanOrEqual(2); expect(screen.getByText('处置队列')).toBeInTheDocument(); expect(screen.getByRole('button', { name: '维护身份绑定 9' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '确认平台转发 4' })).toBeInTheDocument(); @@ -675,10 +691,13 @@ test('shows vehicle service result summary on vehicle list filters', async () => expect(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 来源 181' })).toBeInTheDocument(); expect(screen.getByText('档案完整度')).toBeInTheDocument(); expect(screen.getByText('4/4')).toBeInTheDocument(); + expect(screen.getByText('2/4')).toBeInTheDocument(); + expect(screen.getByText('缺OEM')).toBeInTheDocument(); expect(screen.getByText('VIN-MULTI-001')).toBeInTheDocument(); + expect(screen.getByText('VIN-MISSING-ARCHIVE')).toBeInTheDocument(); expect(screen.getAllByText('来源证据').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('GB32960 在线')).toBeInTheDocument(); - expect(screen.getByText('JT808 离线')).toBeInTheDocument(); + expect(screen.getAllByText('JT808 离线').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument(); expect(screen.getByText('1/2 来源在线')).toBeInTheDocument(); expect(screen.getByText('1/2 来源在线,缺 YUTONG_MQTT')).toBeInTheDocument();