diff --git a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx index 524803ec..283f8b5c 100644 --- a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx @@ -34,6 +34,29 @@ function sourceEvidenceText(row: VehicleCoverageRow) { return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; } +const coverageLabel: Record = { + single: '单源', + multi: '多源' +}; + +const serviceStatusLabel: Record = { + healthy: '服务正常', + degraded: '来源不完整', + offline: '车辆离线', + no_data: '暂无数据来源', + identity_required: '身份未绑定' +}; + +const onlineLabel: Record = { + online: '在线', + offline: '离线' +}; + +const bindingStatusLabel: Record = { + bound: '已绑定', + unbound: '未绑定' +}; + function sourceDiagnosisText(row: VehicleCoverageRow) { const parts = [sourceEvidenceText(row)]; if ((row.missingProtocols ?? []).length > 0) { @@ -110,6 +133,15 @@ export function Vehicles({ } return items; }, [pagination.total, summary]); + const filterSummary = [ + filters.keyword ? `关键词:${filters.keyword}` : '', + filters.protocol ? `数据来源:${filters.protocol}` : '', + filters.coverage ? `来源覆盖:${coverageLabel[filters.coverage] ?? filters.coverage}` : '', + filters.missingProtocol ? `缺失来源:${filters.missingProtocol}` : '', + filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : '', + filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '', + filters.bindingStatus ? `绑定:${bindingStatusLabel[filters.bindingStatus] ?? filters.bindingStatus}` : '' + ].filter(Boolean); const load = (values: Record = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => { setLoading(true); @@ -249,6 +281,16 @@ export function Vehicles({ + {filterSummary.length > 0 ? ( + + + {filterSummary.map((item) => ( + {item} + ))} + + + + ) : null}
{resultSummary.map((item) => ( 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 cc0b583b..34ab08cc 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -354,6 +354,75 @@ test('shows vehicle service result summary on vehicle list filters', async () => expect(screen.getByText('1/2 来源在线,缺 YUTONG_MQTT')).toBeInTheDocument(); }); +test('shows and clears current vehicle service filters', async () => { + window.history.replaceState(null, '', '/#/vehicles?keyword=%E7%B2%A4A&protocol=JT808&coverage=multi&missingProtocol=YUTONG_MQTT&serviceStatus=degraded&online=online&bindingStatus=bound'); + const fetchMock = 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, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicles/coverage/summary')) { + return { + ok: true, + json: async () => ({ + data: { + totalVehicles: 1, + onlineVehicles: 1, + singleSourceVehicles: 0, + multiSourceVehicles: 1, + noDataVehicles: 0, + unboundVehicles: 0 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicles/coverage')) { + return { + ok: true, + json: async () => ({ + data: { items: [], 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(); + expect(screen.getByText('关键词:粤A')).toBeInTheDocument(); + expect(screen.getByText('数据来源:JT808')).toBeInTheDocument(); + expect(screen.getByText('来源覆盖:多源')).toBeInTheDocument(); + expect(screen.getByText('缺失来源:YUTONG_MQTT')).toBeInTheDocument(); + expect(screen.getByText('服务状态:来源不完整')).toBeInTheDocument(); + expect(screen.getByText('在线:在线')).toBeInTheDocument(); + expect(screen.getByText('绑定:已绑定')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '清空筛选' })); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0'), undefined); + }); + expect(window.location.hash).toBe('#/vehicles'); +}); + test('filters vehicle list from result summary actions', async () => { window.history.replaceState(null, '', '/#/vehicles'); const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {