feat(platform): add mileage evidence package

This commit is contained in:
lingniu
2026-07-04 20:20:24 +08:00
parent 09e9cc937d
commit fb4bcbaa55
2 changed files with 47 additions and 1 deletions

View File

@@ -177,6 +177,31 @@ function mileageSummaryText({
].join('\n');
}
function mileageEvidencePackageText(row: DailyMileageRow) {
const dateFrom = row.date?.trim() ?? '';
const dateTo = nextDate(dateFrom);
const evidenceFilters = {
...(dateFrom ? { dateFrom } : {}),
...(dateTo ? { dateTo } : {})
};
const vehicle = row.plate?.trim() ? `${row.plate.trim()} / ${row.vin}` : row.vin;
return [
'【里程异常证据包】',
`车辆:${vehicle}`,
`数据来源:${row.source || '未知来源'}`,
`统计日期:${row.date || '-'}`,
`起始里程:${formatKm(row.startMileageKm)} km`,
`结束里程:${formatKm(row.endMileageKm)} km`,
`日里程:${formatKm(row.dailyMileageKm)} km`,
`异常等级:${row.anomalySeverity || '正常'}`,
`处置建议:${mileageActionRecommendation(row).detail}`,
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: row.vin, protocol: row.source }))}`,
`轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: row.vin, protocol: row.source, filters: evidenceFilters }))}`,
`RAW证据${appURL(buildAppHash({ page: 'history-query', keyword: row.vin, protocol: row.source, filters: { ...evidenceFilters, tab: 'raw', includeFields: 'true' } }))}`,
`统计查询:${appURL(buildAppHash({ page: 'mileage', keyword: row.vin, protocol: row.source, filters: evidenceFilters }))}`
].join('\n');
}
async function copyText(value: string, label: string) {
const text = value.trim();
if (!text) {
@@ -326,6 +351,9 @@ export function Mileage({
...(dateTo ? { dateTo } : {})
});
};
const copyMileageEvidence = (row: DailyMileageRow) => {
copyText(mileageEvidencePackageText(row), '里程证据包');
};
const exportMileage = () => {
if (rows.length === 0) {
Toast.warning('当前没有可导出的里程明细');
@@ -683,9 +711,10 @@ export function Mileage({
},
{
title: '操作',
width: 270,
width: 360,
render: (_: unknown, row: DailyMileageRow) => (
<Space spacing={4} wrap>
<Button disabled={!canOpenVehicle(row.vin)} onClick={() => copyMileageEvidence(row)}></Button>
<Button disabled={!canOpenVehicle(row.vin) || !onOpenHistory} onClick={() => openMileageEvidence(row)}></Button>
<Button disabled={!canOpenVehicle(row.vin) || !onOpenRaw} onClick={() => openMileageRawEvidence(row)}> RAW</Button>
<Button disabled={!canOpenVehicle(row.vin)} onClick={() => onOpenVehicle(row.vin, row.source)}></Button>

View File

@@ -6808,6 +6808,11 @@ test('opens same-day raw frame evidence from mileage row', async () => {
test('shows mileage anomaly action guidance', async () => {
window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960');
const writeText = vi.fn(() => Promise.resolve());
Object.defineProperty(navigator, 'clipboard', {
configurable: true,
value: { writeText }
});
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
const path = String(input);
if (path.includes('/api/ops/health')) {
@@ -6869,6 +6874,18 @@ test('shows mileage anomaly action guidance', async () => {
expect(await screen.findByText('VIN-MILEAGE-ANOMALY')).toBeInTheDocument();
expect(screen.getByText('核对里程来源')).toBeInTheDocument();
expect(screen.getByText('日里程异常,优先核对当天首末总里程、来源断链和补传数据。')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '复制证据' }));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【里程异常证据包】'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆粤A异常1 / VIN-MILEAGE-ANOMALY'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据来源GB32960'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统计日期2026-07-03'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('日里程:-3.2 km'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常等级warning'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务http://localhost:3000/#/detail?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据http://localhost:3000/#/history?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据http://localhost:3000/#/history-query?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统计查询http://localhost:3000/#/mileage?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
});
test('shows mileage statistics workspace with trend source and definition', async () => {