feat(platform): add mileage evidence package
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user