feat(platform): add vehicle-first statistics overview

This commit is contained in:
lingniu
2026-07-04 16:44:55 +08:00
parent bdb2ebc723
commit d7f1a9279f
4 changed files with 104 additions and 2 deletions

View File

@@ -210,6 +210,8 @@ export function Mileage({
const closurePassed = closureDeltaKm != null && Math.abs(closureDeltaKm) < 0.01;
const confidenceStatus = anomalyRows.length > 0 || pageCoverageRate < 100 ? '需复核' : '可用于 BI';
const confidenceColor = confidenceStatus === '可用于 BI' ? 'green' as const : 'orange' as const;
const currentEvidenceText = `${summary.vehicleCount.toLocaleString()} 车 / ${summary.sourceCount.toLocaleString()} 来源 / ${rows.length.toLocaleString()} 条明细`;
const sourceConsistencyText = summary.sourceCount > 1 ? '可做跨来源核对' : summary.sourceCount === 1 ? '单来源车辆需关注' : '等待来源证据';
const maxDateMileage = Math.max(...dateSeries.map((item) => item.value), 0);
const maxSourceMileage = Math.max(...sourceSeries.map((item) => item.value), 0);
const filterSummary = [
@@ -318,6 +320,49 @@ export function Mileage({
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部来源'}</Tag>
<Typography.Text type="tertiary"></Typography.Text>
</div>
<Card bordered title="车辆统计服务总览" style={{ marginTop: 16 }}>
<div className="vp-table-toolbar">
<Space wrap>
<Tag color="blue"></Tag>
<Typography.Text strong>{currentEvidenceText}</Typography.Text>
<Typography.Text type="secondary"></Typography.Text>
</Space>
</div>
<div className="vp-stat-insight-grid">
{[
{
title: '里程统计',
value: `${formatKm(summary.totalMileageKm)} km`,
color: 'blue' as const,
detail: '每日里程、区间里程、异常差值和可审计证据。'
},
{
title: '在线率与离线时长',
value: currentVehicleKeyword ? '车辆维度' : '车队维度',
color: 'green' as const,
detail: '后续接入在线区间聚合,按 VIN 统计在线率、离线时间和无更新时长。'
},
{
title: '数据完整性',
value: confidenceStatus,
color: confidenceColor,
detail: '围绕位置、里程、SOC、速度和来源新鲜度判断统计可信度。'
},
{
title: '来源一致性',
value: sourceConsistencyText,
color: summary.sourceCount > 1 ? 'green' as const : 'orange' as const,
detail: 'GB32960、JT808、Yutong MQTT 只作为同一车辆服务的来源证据。'
}
].map((item) => (
<div key={item.title} className="vp-monitor-metric">
<Tag color={item.color}>{item.title}</Tag>
<div className="vp-monitor-metric-value">{item.value}</div>
<Typography.Text type="secondary">{item.detail}</Typography.Text>
</div>
))}
</div>
</Card>
<Card bordered>
<Form key={JSON.stringify(filters)} initValues={filters} layout="horizontal" onSubmit={(values) => {
const nextFilters = values as Record<string, string>;