From 2e9d3fa5ebe6f3c88dc9e2130e59fd085fae81c4 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 12:36:48 +0800 Subject: [PATCH] feat(platform): add alert realtime map action --- .../apps/web/src/pages/Quality.tsx | 9 +- .../apps/web/src/test/App.test.tsx | 102 ++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index f1007c35..cc0ada55 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -477,10 +477,15 @@ export function Quality({ { title: '说明', dataIndex: 'detail' }, { title: '操作', - width: 130, + width: 210, render: (_: unknown, row: PriorityIssueRow) => { const lookup = qualityIssueVehicleLookup(row); - return ; + return ( + + + + + ); } } ]} 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 29550903..443358d9 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -2413,6 +2413,108 @@ test('shows actionable priority queue on quality page', async () => { }); }); +test('opens realtime map context from quality priority queue', async () => { + window.history.replaceState(null, '', '/#/quality'); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/ops/health')) { + return { + ok: true, + json: async () => ({ + data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/quality/summary')) { + return { + ok: true, + json: async () => ({ + data: { + issueVehicleCount: 1, + issueRecordCount: 1, + errorCount: 1, + warningCount: 0, + protocols: [{ name: 'JT808', count: 1 }], + issueTypes: [{ name: 'LINK_GAP', count: 1 }] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/quality/issues')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: 'VIN-ALERT-MAP', + plate: '粤A地图告警', + phone: '13307795425', + sourceEndpoint: '115.231.168.135:43625', + protocol: 'JT808', + issueType: 'LINK_GAP', + severity: 'error', + lastSeen: '2026-07-03 20:12:10', + detail: 'JT808 来源间断,需要定位当前车辆' + }], + total: 1, + limit: 20, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/realtime/locations')) { + return { + ok: true, + json: async () => ({ + data: { + items: [{ + vin: 'VIN-ALERT-MAP', + plate: '粤A地图告警', + primaryProtocol: 'JT808', + longitude: 113.24567, + latitude: 23.12345, + online: true, + sourceCount: 1, + onlineSourceCount: 1, + lastSeen: '2026-07-03 20:12:11' + }], + total: 1, + limit: 20, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: { items: [], total: 0, limit: 20, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('处置优先队列')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '实时定位' })); + + await waitFor(() => { + expect(window.location.hash).toBe('#/realtime?keyword=VIN-ALERT-MAP&protocol=JT808'); + }); + expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument(); +}); + test('opens vehicle service from quality issue with issue source evidence', async () => { window.history.replaceState(null, '', '/#/quality'); vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {