From 2cfb9e551f352a3afa2eb142be8fcaa2538aa1f4 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 07:25:13 +0800 Subject: [PATCH] feat(platform): open vehicle list from dashboard coverage --- .../apps/web/src/pages/Dashboard.tsx | 27 ++-- .../apps/web/src/test/App.test.tsx | 116 ++++++++++++++++++ 2 files changed, 133 insertions(+), 10 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 1f147216..95974e10 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -94,21 +94,24 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on const [loading, setLoading] = useState(true); const [coverageLoading, setCoverageLoading] = useState(false); const [coverageServiceStatusTitle, setCoverageServiceStatusTitle] = useState(''); + const [coverageFilters, setCoverageFilters] = useState>({}); const loadCoverage = (values?: Record) => { + const nextValues = values ?? {}; setCoverageLoading(true); + setCoverageFilters(nextValues); const scopeTitle = [ - values?.serviceStatus ? serviceStatusTitle[values.serviceStatus] ?? values.serviceStatus : '', - values?.missingProtocol ? missingProtocolTitle[values.missingProtocol] ?? `缺 ${values.missingProtocol}` : '' + nextValues.serviceStatus ? serviceStatusTitle[nextValues.serviceStatus] ?? nextValues.serviceStatus : '', + nextValues.missingProtocol ? missingProtocolTitle[nextValues.missingProtocol] ?? `缺 ${nextValues.missingProtocol}` : '' ].filter(Boolean).join(' / '); setCoverageServiceStatusTitle(scopeTitle); const params = new URLSearchParams({ limit: '8' }); - if (values?.keyword) params.set('keyword', values.keyword); - if (values?.coverage) params.set('coverage', values.coverage); - if (values?.missingProtocol) params.set('missingProtocol', values.missingProtocol); - if (values?.online) params.set('online', values.online); - if (values?.bindingStatus) params.set('bindingStatus', values.bindingStatus); - if (values?.serviceStatus) params.set('serviceStatus', values.serviceStatus); + if (nextValues.keyword) params.set('keyword', nextValues.keyword); + if (nextValues.coverage) params.set('coverage', nextValues.coverage); + if (nextValues.missingProtocol) params.set('missingProtocol', nextValues.missingProtocol); + if (nextValues.online) params.set('online', nextValues.online); + if (nextValues.bindingStatus) params.set('bindingStatus', nextValues.bindingStatus); + if (nextValues.serviceStatus) params.set('serviceStatus', nextValues.serviceStatus); api.vehicleCoverage(params) .then((page) => setCoverage(page.items)) .catch((error: Error) => Toast.error(error.message)) @@ -270,7 +273,11 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on ]} /> - + 车辆服务覆盖} + bordered + style={{ marginTop: 16 }} + > {coverageServiceStatusTitle ? (
当前筛选:{coverageServiceStatusTitle} @@ -304,7 +311,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on - + expect(screen.getByText('当前筛选:来源异常 / 缺 JT808')).toBeInTheDocument(); }); +test('opens full vehicle service list from dashboard coverage filters', async () => { + window.history.replaceState(null, '', '/#/dashboard'); + const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/dashboard/summary')) { + return { + ok: true, + json: async () => ({ + data: { + onlineVehicles: 3, + activeToday: 4, + frameToday: 1286320, + issueVehicles: 7, + kafkaLag: 0, + protocols: [], + serviceStatuses: [], + linkHealth: [] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicle-service/summary')) { + return { + ok: true, + json: async () => ({ + data: { + totalVehicles: 1, + boundVehicles: 1, + onlineVehicles: 1, + singleSourceVehicles: 1, + multiSourceVehicles: 0, + noDataVehicles: 0, + identityRequiredVehicles: 0, + serviceStatuses: [], + protocols: [], + missingSources: [] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicles/coverage')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: 'VIN-DASH-FULL-LIST', + plate: '粤A全量', + phone: '', + oem: '', + protocols: ['GB32960'], + missingProtocols: ['JT808', 'YUTONG_MQTT'], + sourceCount: 1, + onlineSourceCount: 1, + online: true, + lastSeen: '2026-07-04 07:20:14', + bindingStatus: 'bound', + sourceConsistency: { + sourceCount: 1, + onlineSourceCount: 1, + locatedSourceCount: 0, + missingProtocols: ['JT808', 'YUTONG_MQTT'], + mileageDeltaKm: 0, + sourceTimeDeltaSeconds: 0, + scope: 'coverage', + status: 'single_source', + severity: 'warning', + title: '单一来源', + detail: '当前车辆只有一个数据来源,无法做跨来源一致性校验。' + } + }], + total: 1, + limit: path.includes('limit=20') ? 20 : 8, + 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: 8, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('VIN-DASH-FULL-LIST')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '单一来源' })); + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=JT808'), undefined); + }); + + fireEvent.click(screen.getByRole('button', { name: '查看全部车辆' })); + + await waitFor(() => { + expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=JT808'); + }); + const vehicleCoverageCall = fetchMock.mock.calls.find(([input]) => String(input).startsWith('/api/vehicles/coverage?limit=20&offset=0')); + expect(vehicleCoverageCall).toBeTruthy(); + const vehicleCoverageParams = new URLSearchParams(String(vehicleCoverageCall?.[0]).split('?')[1]); + expect(vehicleCoverageParams.get('serviceStatus')).toBe('degraded'); + expect(vehicleCoverageParams.get('missingProtocol')).toBe('JT808'); +}); + test('opens vehicle service from dashboard realtime preview with source evidence', async () => { window.history.replaceState(null, '', '/#/dashboard'); vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {