diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index 4412664d..859e8c15 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -477,13 +477,14 @@ export function Quality({ { title: '说明', dataIndex: 'detail' }, { title: '操作', - width: 290, + width: 360, render: (_: unknown, row: PriorityIssueRow) => { const lookup = qualityIssueVehicleLookup(row); 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 0ff1bd37..b8f5aba6 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -2608,6 +2608,100 @@ test('opens same-day trajectory evidence from quality priority queue', async () expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument(); }); +test('opens same-day raw evidence 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-RAW', + plate: '粤ARAW告警', + phone: '13307795425', + sourceEndpoint: '115.231.168.135:43625', + protocol: 'JT808', + issueType: 'LINK_GAP', + severity: 'error', + lastSeen: '2026-07-03 20:12:10', + detail: 'JT808 来源间断,需要核对 RAW' + }], + total: 1, + limit: 20, + offset: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/history/locations') || path.includes('/api/history/raw-frames')) { + return { + ok: true, + json: async () => ({ + data: { items: [], total: 0, limit: 10, 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: 'RAW证据' })); + + await waitFor(() => { + expect(window.location.hash.startsWith('#/history?')).toBe(true); + }); + const params = new URLSearchParams(window.location.hash.slice('#/history?'.length)); + expect(params.get('keyword')).toBe('VIN-ALERT-RAW'); + expect(params.get('protocol')).toBe('JT808'); + expect(params.get('dateFrom')).toBe('2026-07-03'); + expect(params.get('dateTo')).toBe('2026-07-04'); + expect(params.get('includeFields')).toBe('true'); + expect(params.get('tab')).toBe('raw'); + 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) => {