From 4085268971dad306e848bb7eba216dea95460e09 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 19:16:33 +0800 Subject: [PATCH] feat(platform): preserve customer time window in tasks --- vehicle-data-platform/apps/web/src/App.tsx | 35 ++++++++++++++++--- .../apps/web/src/pages/Mileage.tsx | 19 +++++++++- .../apps/web/src/test/App.test.tsx | 11 +++++- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/App.tsx b/vehicle-data-platform/apps/web/src/App.tsx index 726d832a..33f9653d 100644 --- a/vehicle-data-platform/apps/web/src/App.tsx +++ b/vehicle-data-platform/apps/web/src/App.tsx @@ -445,13 +445,36 @@ export default function App() { replaceHash('mileage', keyword ?? nextVin, protocol ?? nextProtocol, restFilters); }; + const currentCustomerTaskTimeWindow = () => { + const candidates = activePage === 'mileage' + ? [mileageFilters, historyFilters, qualityFilters] + : activePage === 'history' || activePage === 'history-query' + ? [historyFilters, mileageFilters, qualityFilters] + : activePage === 'alert-events' || activePage === 'quality' + ? [qualityFilters, historyFilters, mileageFilters] + : [historyFilters, mileageFilters, qualityFilters]; + for (const filters of candidates) { + const dateFrom = filters.dateFrom?.trim(); + const dateTo = filters.dateTo?.trim(); + if (dateFrom || dateTo) { + return { + ...(dateFrom ? { dateFrom } : {}), + ...(dateTo ? { dateTo } : {}) + }; + } + } + return {}; + }; + const openCustomerTask = (page: PageKey) => { const keyword = (activeVin || analysisVin).trim(); const protocol = activeProtocol.trim(); + const timeWindow = currentCustomerTaskTimeWindow(); if (!keyword) { navigatePage(page); return; } + const scopedFilters = { keyword, protocol, ...timeWindow }; if (page === 'map') { openMap({ keyword, protocol }); return; @@ -461,19 +484,19 @@ export default function App() { return; } if (page === 'history') { - openHistoryWithFilters({ keyword, protocol }); + openHistoryWithFilters(scopedFilters); return; } if (page === 'history-query') { - openRawWithFilters({ keyword, protocol, tab: 'raw' }); + openRawWithFilters({ ...scopedFilters, tab: 'raw' }); return; } if (page === 'mileage') { - openMileageWithFilters({ keyword, protocol }); + openMileageWithFilters(scopedFilters); return; } if (page === 'alert-events' || page === 'quality') { - openQuality({ keyword, protocol }); + openQuality(scopedFilters); return; } navigatePage(page); @@ -522,7 +545,9 @@ function qualityFiltersFromRoute(route: ReturnType): Record return { ...(route.keyword ? { keyword: route.keyword } : {}), ...(route.protocol ? { protocol: route.protocol } : {}), - ...(route.filters?.issueType ? { issueType: route.filters.issueType } : {}) + ...(route.filters?.issueType ? { issueType: route.filters.issueType } : {}), + ...(route.filters?.dateFrom ? { dateFrom: route.filters.dateFrom } : {}), + ...(route.filters?.dateTo ? { dateTo: route.filters.dateTo } : {}) }; } diff --git a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx index 4fa74d58..3f405273 100644 --- a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx @@ -14,6 +14,23 @@ const emptySummary: MileageSummary = { averageMileagePerVin: 0 }; +function numberOrZero(value: unknown) { + const numeric = Number(value); + return Number.isFinite(numeric) ? numeric : 0; +} + +function normalizeMileageSummary(summary?: Partial | null): MileageSummary { + return { + ...emptySummary, + ...(summary ?? {}), + vehicleCount: numberOrZero(summary?.vehicleCount), + recordCount: numberOrZero(summary?.recordCount), + sourceCount: numberOrZero(summary?.sourceCount), + totalMileageKm: numberOrZero(summary?.totalMileageKm), + averageMileagePerVin: numberOrZero(summary?.averageMileagePerVin) + }; +} + const emptyOnlineSummary: OnlineStatisticsSummary = { vehicleCount: 0, onlineVehicleCount: 0, @@ -940,7 +957,7 @@ export function Mileage({ const loadSummary = (values: Record = filters) => { setSummaryLoading(true); api.mileageSummary(mileageParams(values)) - .then((nextSummary) => setSummary(nextSummary ?? emptySummary)) + .then((nextSummary) => setSummary(normalizeMileageSummary(nextSummary))) .catch((error: Error) => Toast.error(error.message)) .finally(() => setSummaryLoading(false)); api.onlineStatisticsSummary(mileageParams(values)) 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 2fa71129..dbf037a7 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -6488,7 +6488,7 @@ test('shows vehicle context when opening detail from sidebar navigation', async }); test('applies protocol from shareable history hash to API requests', async () => { - window.history.replaceState(null, '', '/#/history?keyword=%E7%B2%A4AG18312&protocol=JT808'); + window.history.replaceState(null, '', '/#/history?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03'); const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => { const path = String(input); if (path.includes('/api/ops/health')) { @@ -6517,12 +6517,21 @@ test('applies protocol from shareable history hash to API requests', async () => expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/history/locations?'), undefined); }); expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('protocol=JT808'), undefined); + expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateFrom=2026-07-01'), undefined); + expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateTo=2026-07-03'), undefined); expect(fetchMock).toHaveBeenCalledWith('/api/history/raw-frames/query', expect.objectContaining({ method: 'POST', body: expect.stringContaining('"protocol":"JT808"') })); expect(screen.getByText('当前车辆:粤AG18312')).toBeInTheDocument(); expect(screen.getAllByText('数据通道:JT808').length).toBeGreaterThan(0); + + fireEvent.click(screen.getByRole('button', { name: '顶部统计查询' })); + expect(window.location.hash).toBe('#/mileage?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03'); + fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' })); + expect(window.location.hash).toBe('#/history-query?keyword=%E7%B2%A4AG18312&protocol=JT808&tab=raw&dateFrom=2026-07-01&dateTo=2026-07-03'); + fireEvent.click(await screen.findByRole('button', { name: '顶部告警事件正常' })); + expect(window.location.hash).toBe('#/alert-events?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03'); }); test('shows and clears current history filters while keeping vehicle scope', async () => {