From ad932565ed63b722b2fb05832efaa6c7f85a3a0e Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 09:01:34 +0800 Subject: [PATCH] feat(platform): highlight unified vehicle service posture --- .../apps/web/src/pages/Dashboard.tsx | 19 ++++++ .../apps/web/src/test/App.test.tsx | 62 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 8b30f7d2..da669e11 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -44,6 +44,15 @@ function formatCount(value?: number | null) { return value == null ? '0' : value.toLocaleString(); } +function vehicleServiceOnlineText(serviceSummary: VehicleServiceSummary | null, summary: DashboardSummary | null) { + const onlineVehicles = serviceSummary?.onlineVehicles ?? summary?.onlineVehicles; + const totalVehicles = serviceSummary?.totalVehicles; + if (onlineVehicles == null || totalVehicles == null) { + return '未接入'; + } + return `${onlineVehicles.toLocaleString()} / ${totalVehicles.toLocaleString()} 在线`; +} + function formatProtocolRate(row: ProtocolStat) { if (!Number.isFinite(row.total) || row.total <= 0) { return '0%'; @@ -169,6 +178,16 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on ))} + + + {vehicleServiceOnlineText(serviceSummary, summary)} + {formatCount(serviceSummary?.multiSourceVehicles)} 多源覆盖 + 0 ? 'orange' : 'green'}> + {formatCount(summary?.issueVehicles)} 质量关注 + + 0 ? 'orange' : 'green'}>Kafka Lag {formatLag(summary?.kafkaLag)} + + 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 1835c28e..e6c3daea 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -135,6 +135,68 @@ test('dashboard renders vehicle service summary metrics', async () => { expect(fetchMock).toHaveBeenCalledWith('/api/vehicle-service/summary', undefined); }); +test('dashboard presents one vehicle service operating posture', 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: 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: 1033, + boundVehicles: 1024, + onlineVehicles: 208, + singleSourceVehicles: 391, + multiSourceVehicles: 181, + noDataVehicles: 461, + identityRequiredVehicles: 9, + serviceStatuses: [], + protocols: [], + missingSources: [] + }, + 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('统一车辆服务')).toBeInTheDocument(); + expect(screen.getByText('208 / 1,033 在线')).toBeInTheDocument(); + expect(screen.getByText('181 多源覆盖')).toBeInTheDocument(); + expect(screen.getByText('7 质量关注')).toBeInTheDocument(); +}); + test('dashboard keeps core vehicle service metrics when preview requests fail', async () => { window.history.replaceState(null, '', '/#/dashboard'); vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {