feat(platform): enrich vehicle source evidence matrix

This commit is contained in:
lingniu
2026-07-04 10:06:43 +08:00
parent 7925be676b
commit e59e5f7c11
2 changed files with 62 additions and 2 deletions

View File

@@ -2,7 +2,7 @@ import { Banner, Button, Card, Descriptions, Form, Select, Space, Table, Tabs, T
import { IconCopy, IconRefresh, IconSearch } from '@douyinfe/semi-icons';
import { useEffect, useMemo, useRef, useState } from 'react';
import { api } from '../api/client';
import type { QualityIssueRow, VehicleDetail as VehicleDetailData, VehicleSourceStatus } from '../api/types';
import type { QualityIssueRow, RealtimeLocationRow, VehicleDetail as VehicleDetailData, VehicleSourceStatus } from '../api/types';
import { PageHeader } from '../components/PageHeader';
import { SourceStatusTags } from '../components/SourceStatusTags';
import { StatusTag } from '../components/StatusTag';
@@ -36,6 +36,11 @@ function formatCompactNumber(value?: number, suffix = '') {
return `${value.toLocaleString(undefined, { maximumFractionDigits: 1 })}${suffix}`;
}
function formatLocation(row?: RealtimeLocationRow) {
if (!row || !isFiniteNumber(row.longitude) || !isFiniteNumber(row.latitude)) return '-';
return `${row.longitude}, ${row.latitude}`;
}
function parseTimeMs(value?: string) {
if (!value) return undefined;
const ms = Date.parse(value);
@@ -152,6 +157,14 @@ export function VehicleDetail({
const lastSeen = resolution?.lastSeen || summary?.lastSeen || latest?.lastSeen || '-';
const serviceStatus = detail?.serviceStatus;
const realtimeRows = detail?.realtime ?? [];
const latestRealtimeByProtocol = useMemo(() => {
const next = new Map<string, RealtimeLocationRow>();
for (const row of realtimeRows) {
if (!row.protocol || next.has(row.protocol)) continue;
next.set(row.protocol, row);
}
return next;
}, [realtimeRows]);
const realtimeSources = new Set(realtimeRows.map((row) => row.protocol).filter(Boolean));
const sourceCount = Math.max(detail?.sourceStatus?.length ?? 0, realtimeSources.size, summary?.sourceCount ?? 0, protocols.length);
const onlineSourceCount = detail?.sourceStatus?.length
@@ -488,6 +501,26 @@ export function VehicleDetail({
},
{ title: '在线', width: 100, render: (_: unknown, row: VehicleSourceStatus) => <StatusTag status={row.online ? 'ok' : 'offline'} /> },
{ title: '最后时间', dataIndex: 'lastSeen', width: 190, render: (value?: string) => value || '无上报' },
{
title: '最新位置',
width: 170,
render: (_: unknown, row: VehicleSourceStatus) => formatLocation(latestRealtimeByProtocol.get(row.protocol))
},
{
title: '里程',
width: 110,
render: (_: unknown, row: VehicleSourceStatus) => formatCompactNumber(latestRealtimeByProtocol.get(row.protocol)?.totalMileageKm, ' km')
},
{
title: '速度',
width: 110,
render: (_: unknown, row: VehicleSourceStatus) => formatCompactNumber(latestRealtimeByProtocol.get(row.protocol)?.speedKmh, ' km/h')
},
{
title: 'SOC',
width: 90,
render: (_: unknown, row: VehicleSourceStatus) => formatCompactNumber(latestRealtimeByProtocol.get(row.protocol)?.socPercent, '%')
},
...sourceCapabilities.map((item) => ({
title: item.label,
width: 90,