feat(platform): enrich vehicle source evidence matrix
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user