From 10c598e013d3333abac333b88f3add292ed845df Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 14:48:58 +0800 Subject: [PATCH] feat(platform): show mileage confidence metrics --- vehicle-data-platform/apps/web/src/pages/Mileage.tsx | 10 +++++++++- vehicle-data-platform/apps/web/src/test/App.test.tsx | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx index 4e4d8a94..5041b21c 100644 --- a/vehicle-data-platform/apps/web/src/pages/Mileage.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Mileage.tsx @@ -202,6 +202,11 @@ export function Mileage({ const anomalyRows = rows.filter((row) => row.anomalySeverity); const peakMileage = maxMileageRow(rows); const pageMileageTotal = rows.reduce((total, row) => total + row.dailyMileageKm, 0); + const anomalyRate = rows.length > 0 ? (anomalyRows.length / rows.length) * 100 : 0; + const peakMileageShare = pageMileageTotal > 0 && peakMileage ? (peakMileage.dailyMileageKm / pageMileageTotal) * 100 : 0; + const pageCoverageRate = summary.recordCount > 0 ? (rows.length / summary.recordCount) * 100 : 0; + const confidenceStatus = anomalyRows.length > 0 || pageCoverageRate < 100 ? '需复核' : '可用于 BI'; + const confidenceColor = confidenceStatus === '可用于 BI' ? 'green' as const : 'orange' as const; const maxDateMileage = Math.max(...dateSeries.map((item) => item.value), 0); const maxSourceMileage = Math.max(...sourceSeries.map((item) => item.value), 0); const filterSummary = [ @@ -389,10 +394,13 @@ export function Mileage({
{[ + { label: '统计可信度', value: confidenceStatus, color: confidenceColor, detail: anomalyRows.length > 0 ? '存在异常里程记录,建议先核对来源证据。' : '当前分页未发现异常记录。' }, + { label: '异常率', value: formatPercent(anomalyRate), color: anomalyRows.length > 0 ? 'orange' as const : 'green' as const, detail: `${anomalyRows.length.toLocaleString()} / ${rows.length.toLocaleString()} 条明细命中异常标记。` }, + { label: '最大单日占比', value: formatPercent(peakMileageShare), color: peakMileageShare > 50 ? 'orange' as const : 'blue' as const, detail: peakMileage ? `${peakMileage.plate || peakMileage.vin} 贡献最高单日里程。` : '暂无明细。' }, { label: '当前页里程', value: `${formatKm(pageMileageTotal)} km`, color: 'blue' as const, detail: '用于快速核对当前分页明细合计。' }, { label: '异常记录', value: anomalyRows.length.toLocaleString(), color: anomalyRows.length > 0 ? 'orange' as const : 'green' as const, detail: '来自异常标记,优先核对首末总里程和断链。' }, { label: '最大单日', value: peakMileage ? `${formatKm(peakMileage.dailyMileageKm)} km` : '-', color: 'blue' as const, detail: peakMileage ? `${peakMileage.plate || peakMileage.vin} / ${peakMileage.date}` : '暂无明细。' }, - { label: '统计覆盖率', value: summary.recordCount > 0 ? formatPercent((rows.length / summary.recordCount) * 100) : '0%', color: 'grey' as const, detail: '当前分页记录数 / 全量统计记录数。' } + { label: '分页覆盖率', value: formatPercent(pageCoverageRate), color: pageCoverageRate >= 100 ? 'green' as const : 'grey' as const, detail: '当前分页记录数 / 全量统计记录数。' } ].map((item) => ( {item.label} 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 2e0145d2..827de352 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -5884,6 +5884,13 @@ test('shows mileage statistics workspace with trend source and definition', asyn expect(screen.getAllByText('1').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('最大单日')).toBeInTheDocument(); expect(screen.getByText('60 km')).toBeInTheDocument(); + expect(screen.getByText('统计可信度')).toBeInTheDocument(); + expect(screen.getByText('异常率')).toBeInTheDocument(); + expect(screen.getByText('25%')).toBeInTheDocument(); + expect(screen.getByText('最大单日占比')).toBeInTheDocument(); + expect(screen.getByText('42.9%')).toBeInTheDocument(); + expect(screen.getByText('分页覆盖率')).toBeInTheDocument(); + expect(screen.getByText('100%')).toBeInTheDocument(); expect(screen.getByText('统计口径')).toBeInTheDocument(); expect(screen.getByText((content) => content.includes('区间总值应等于各日里程之和'))).toBeInTheDocument(); });