feat(platform-web): show source consistency
This commit is contained in:
@@ -26,6 +26,26 @@ const coverageStatusText: Record<string, string> = {
|
||||
no_data: '暂无来源'
|
||||
};
|
||||
|
||||
function isFiniteNumber(value: unknown): value is number {
|
||||
return typeof value === 'number' && Number.isFinite(value);
|
||||
}
|
||||
|
||||
function formatCompactNumber(value?: number, suffix = '') {
|
||||
if (!isFiniteNumber(value)) return '-';
|
||||
return `${value.toLocaleString(undefined, { maximumFractionDigits: 1 })}${suffix}`;
|
||||
}
|
||||
|
||||
function parseTimeMs(value?: string) {
|
||||
if (!value) return undefined;
|
||||
const ms = Date.parse(value);
|
||||
return Number.isFinite(ms) ? ms : undefined;
|
||||
}
|
||||
|
||||
function formatSeconds(value?: number) {
|
||||
if (!isFiniteNumber(value)) return '-';
|
||||
return `${Math.round(value)} 秒`;
|
||||
}
|
||||
|
||||
export function VehicleDetail({
|
||||
vin,
|
||||
protocol,
|
||||
@@ -96,6 +116,20 @@ export function VehicleDetail({
|
||||
const online = resolution?.online || summary?.online || identity?.online || false;
|
||||
const lastSeen = resolution?.lastSeen || summary?.lastSeen || latest?.lastSeen || '-';
|
||||
const serviceStatus = detail?.serviceStatus;
|
||||
const realtimeRows = detail?.realtime ?? [];
|
||||
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
|
||||
? detail.sourceStatus.filter((source) => source.online).length
|
||||
: summary?.onlineSourceCount ?? 0;
|
||||
const locatedSourceCount = realtimeRows.filter((row) => isFiniteNumber(row.longitude) && isFiniteNumber(row.latitude)).length;
|
||||
const mileageValues = realtimeRows.map((row) => row.totalMileageKm).filter(isFiniteNumber);
|
||||
const mileageDelta = mileageValues.length > 1 ? Math.max(...mileageValues) - Math.min(...mileageValues) : undefined;
|
||||
const timeValues = [
|
||||
...realtimeRows.map((row) => parseTimeMs(row.lastSeen)),
|
||||
...(detail?.sourceStatus ?? []).map((source) => parseTimeMs(source.lastSeen))
|
||||
].filter(isFiniteNumber);
|
||||
const sourceTimeDeltaSeconds = timeValues.length > 1 ? (Math.max(...timeValues) - Math.min(...timeValues)) / 1000 : undefined;
|
||||
const activeProtocol = query.protocol?.trim() ?? '';
|
||||
const scopeText = activeProtocol ? `单一来源:${activeProtocol}` : '全部来源聚合';
|
||||
const formKey = `${query.keyword}-${query.protocol ?? ''}`;
|
||||
@@ -284,6 +318,20 @@ export function VehicleDetail({
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card bordered title="跨来源一致性" style={{ marginTop: 16 }}>
|
||||
<Descriptions
|
||||
row
|
||||
data={[
|
||||
{ key: '来源覆盖', value: sourceCount > 0 ? `${sourceCount} 个来源` : '-' },
|
||||
{ key: '在线来源', value: sourceCount > 0 ? `${onlineSourceCount}/${sourceCount} 在线` : '-' },
|
||||
{ key: '位置覆盖', value: locatedSourceCount > 0 ? `${locatedSourceCount} 个来源有位置` : '暂无位置' },
|
||||
{ key: '里程差异', value: formatCompactNumber(mileageDelta, ' km') },
|
||||
{ key: '来源时间差', value: formatSeconds(sourceTimeDeltaSeconds) },
|
||||
{ key: '车辆服务视角', value: activeProtocol ? `当前只看 ${activeProtocol}` : '三类来源合并为同一车辆服务' }
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card bordered title="来源诊断明细" style={{ marginTop: 16 }}>
|
||||
<Table
|
||||
loading={loading}
|
||||
|
||||
Reference in New Issue
Block a user