feat(platform): preserve customer time window in tasks

This commit is contained in:
lingniu
2026-07-05 19:16:33 +08:00
parent 30c15ec8fd
commit 4085268971
3 changed files with 58 additions and 7 deletions

View File

@@ -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<typeof parseAppHash>): 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 } : {})
};
}

View File

@@ -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<MileageSummary> | 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<string, string> = 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))

View File

@@ -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 () => {