From 5234eb5542776c46567b61d6c2bc8a889160071d Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 06:34:23 +0800 Subject: [PATCH] fix(platform): keep dashboard source when opening vehicle --- .../apps/web/src/pages/Dashboard.tsx | 6 +- .../apps/web/src/test/App.test.tsx | 182 ++++++++++++++++++ 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 4165959c..9901bdcc 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -63,7 +63,7 @@ function sourceEvidenceText(row: { onlineSourceCount: number; sourceCount: numbe return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; } -export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { onOpenVehicle: (vin: string) => void; onOpenQuality: () => void; onOpenVehicles: (filters?: Record) => void }) { +export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { onOpenVehicle: (vin: string, protocol?: string) => void; onOpenQuality: () => void; onOpenVehicles: (filters?: Record) => void }) { const [summary, setSummary] = useState(null); const [serviceSummary, setServiceSummary] = useState(null); const [coverage, setCoverage] = useState([]); @@ -242,7 +242,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on width: 130, render: (_: unknown, row: QualityIssueRow) => { const lookup = qualityIssueVehicleLookup(row); - return ; + return ; } } ]} @@ -362,7 +362,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on } }, { title: '最后时间', dataIndex: 'lastSeen' }, - { title: '操作', width: 110, render: (_: unknown, row: VehicleRealtimeRow) => } + { title: '操作', width: 110, render: (_: unknown, row: VehicleRealtimeRow) => } ]} /> 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 abf37326..acd5004e 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -788,6 +788,188 @@ test('shows row service status in dashboard vehicle previews', async () => { expect(screen.getByText('实时车辆离线')).toBeInTheDocument(); }); +test('opens vehicle service from dashboard realtime preview with source evidence', async () => { + window.history.replaceState(null, '', '/#/dashboard'); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/dashboard/summary')) { + return { + ok: true, + json: async () => ({ + data: { onlineVehicles: 1, activeToday: 1, frameToday: 10, issueVehicles: 0, 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: [] }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/realtime/vehicles')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: 'VIN-DASH-RT', + plate: '粤A实时1', + phone: '', + oem: 'G7s', + protocols: ['JT808'], + primaryProtocol: 'JT808', + sourceCount: 1, + onlineSourceCount: 1, + online: true, + bindingStatus: 'bound', + longitude: 113.2, + latitude: 23.1, + speedKmh: 12, + socPercent: 80, + totalMileageKm: 100, + lastSeen: '2026-07-03 20:12:10' + }], + total: 1, + limit: 8, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicle-service/overview')) { + return { + ok: true, + json: async () => ({ + data: { + vin: 'VIN-DASH-RT', + plate: '粤A实时1', + sourceCount: 1, + onlineSourceCount: 1, + coverageStatus: 'online', + primaryProtocol: 'JT808', + lastSeen: '2026-07-03 20:12:10', + historyCount: 0, + rawCount: 0, + mileageCount: 0, + qualityIssueCount: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: { items: [], total: 0, limit: 8, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('VIN-DASH-RT')).toBeInTheDocument(); + const vehicleButtons = screen.getAllByRole('button', { name: '车辆服务' }); + fireEvent.click(vehicleButtons[vehicleButtons.length - 1]); + + await waitFor(() => { + expect(window.location.hash).toBe('#/detail?keyword=VIN-DASH-RT&protocol=JT808'); + }); +}); + +test('opens vehicle service from dashboard quality preview with issue source evidence', async () => { + window.history.replaceState(null, '', '/#/dashboard'); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/dashboard/summary')) { + return { + ok: true, + json: async () => ({ + data: { onlineVehicles: 1, activeToday: 1, frameToday: 10, issueVehicles: 1, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/quality/issues')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: '', + plate: '粤A质量1', + phone: '13307795425', + sourceEndpoint: '115.231.168.135:43625', + protocol: 'JT808', + issueType: 'VIN_MISSING', + severity: 'error', + lastSeen: '2026-07-03 20:12:10', + detail: 'JT808 数据缺少 VIN 映射' + }], + total: 1, + limit: 5, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicle-service/overview')) { + return { + ok: true, + json: async () => ({ + data: { + vin: 'VIN-DASH-QUALITY', + plate: '粤A质量1', + sourceCount: 1, + onlineSourceCount: 1, + coverageStatus: 'online', + primaryProtocol: 'JT808', + lastSeen: '2026-07-03 20:12:10', + historyCount: 0, + rawCount: 0, + mileageCount: 0, + qualityIssueCount: 1 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: path.includes('/api/vehicle-service/summary') + ? { totalVehicles: 1, boundVehicles: 1, onlineVehicles: 1, singleSourceVehicles: 1, multiSourceVehicles: 0, noDataVehicles: 0, identityRequiredVehicles: 0, serviceStatuses: [], protocols: [] } + : { items: [], total: 0, limit: 8, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('13307795425')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '按车牌查车' })); + + await waitFor(() => { + expect(window.location.hash).toBe('#/detail?keyword=VIN-DASH-QUALITY&protocol=JT808'); + }); +}); + test('filters dashboard coverage by service status', async () => { window.history.replaceState(null, '', '/#/dashboard'); const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {