From 1c75f1d5f158604ea7370bbb6d82253d253fa78d Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 08:23:12 +0800 Subject: [PATCH] feat(platform): show active realtime filters --- .../apps/web/src/pages/Realtime.tsx | 28 +++++++++++++ .../apps/web/src/test/App.test.tsx | 39 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 88f364c4..9834f194 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -32,6 +32,18 @@ function sourceEvidenceText(row: VehicleRealtimeRow) { return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; } +const onlineLabel: Record = { + online: '在线', + offline: '离线' +}; + +const serviceStatusLabel: Record = { + healthy: '服务正常', + degraded: '来源不完整', + offline: '车辆离线', + identity_required: '身份未绑定' +}; + export function Realtime({ onOpenVehicle, onFiltersChange, @@ -72,6 +84,12 @@ export function Realtime({ onFiltersChange?.(nextFilters); load(nextFilters, 1, pagination.pageSize); }; + const filterSummary = [ + filters.keyword ? `关键词:${filters.keyword}` : '', + filters.protocol ? `数据来源:${filters.protocol}` : '', + filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '', + filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : '' + ].filter(Boolean); return (
@@ -104,6 +122,16 @@ export function Realtime({ }}>重置 + {filterSummary.length > 0 ? ( + + + {filterSummary.map((item) => ( + {item} + ))} + + + + ) : null} {rows.length === 0 && !loading ? ( 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 34ab08cc..1a752160 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -3196,6 +3196,45 @@ test('loads realtime vehicles from shareable source filter hash', async () => { expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/realtime/vehicles?limit=50&offset=0&keyword=%E7%B2%A4ART002&protocol=JT808&online=online&serviceStatus=degraded'), undefined); }); +test('shows and clears current realtime service filters', async () => { + window.history.replaceState(null, '', '/#/realtime?keyword=%E7%B2%A4ART002&protocol=JT808&online=online&serviceStatus=degraded'); + const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const path = String(input); + if (path.includes('/api/realtime/vehicles')) { + return { + ok: true, + json: async () => ({ + data: { items: [], total: 1, limit: 50, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + return { + ok: true, + json: async () => ({ + data: { items: [], total: 0, limit: 10, offset: 0 }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + }); + + render(); + + expect(await screen.findByText('当前实时筛选')).toBeInTheDocument(); + expect(screen.getByText('关键词:粤ART002')).toBeInTheDocument(); + expect(screen.getByText('数据来源:JT808')).toBeInTheDocument(); + expect(screen.getByText('在线:在线')).toBeInTheDocument(); + expect(screen.getByText('服务状态:来源不完整')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '清空筛选' })); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/realtime/vehicles?limit=50&offset=0'), undefined); + }); + expect(window.location.hash).toBe('#/realtime'); +}); + test('updates realtime hash when source filters are submitted', async () => { window.history.replaceState(null, '', '/#/realtime'); const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {