feat(platform): surface source readiness on dashboard
This commit is contained in:
@@ -2,7 +2,7 @@ import { Button, Card, Col, Form, Row, Select, Space, Spin, Table, Tag, Toast, T
|
||||
import { IconSearch } from '@douyinfe/semi-icons';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { api } from '../api/client';
|
||||
import type { DashboardSummary, LinkHealth, OpsHealth, ProtocolStat, QualityIssueRow, ServiceStatusStat, VehicleCoverageRow, VehicleRealtimeRow, VehicleServiceSummary } from '../api/types';
|
||||
import type { DashboardSummary, LinkHealth, OpsHealth, ProtocolStat, QualityIssueRow, ServiceStatusStat, SourceReadinessPlan, SourceReadinessRow, VehicleCoverageRow, VehicleRealtimeRow, VehicleServiceSummary } from '../api/types';
|
||||
import { PageHeader } from '../components/PageHeader';
|
||||
import { SourceStatusTags } from '../components/SourceStatusTags';
|
||||
import { StatusTag } from '../components/StatusTag';
|
||||
@@ -93,6 +93,15 @@ function formatProtocolRate(row: ProtocolStat) {
|
||||
return `${Math.round((row.online / row.total) * 100)}%`;
|
||||
}
|
||||
|
||||
function normalizeSourceReadiness(plan: SourceReadinessPlan | null): SourceReadinessPlan | null {
|
||||
return plan && Array.isArray(plan.sources) ? plan : null;
|
||||
}
|
||||
|
||||
function sourceReadinessColor(row: SourceReadinessRow): 'green' | 'orange' | 'red' {
|
||||
if (row.severity === 'error') return 'red';
|
||||
return row.severity === 'warning' ? 'orange' : 'green';
|
||||
}
|
||||
|
||||
function rowServiceStatus(row: { serviceStatus?: { title: string; severity: string }; onlineSourceCount: number; sourceCount: number }) {
|
||||
if (row.serviceStatus) {
|
||||
return {
|
||||
@@ -259,6 +268,7 @@ export function Dashboard({
|
||||
const [locations, setLocations] = useState<VehicleRealtimeRow[]>([]);
|
||||
const [qualityIssues, setQualityIssues] = useState<QualityIssueRow[]>([]);
|
||||
const [opsHealth, setOpsHealth] = useState<OpsHealth | null>(null);
|
||||
const [sourceReadiness, setSourceReadiness] = useState<SourceReadinessPlan | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [coverageLoading, setCoverageLoading] = useState(false);
|
||||
const [coverageServiceStatusTitle, setCoverageServiceStatusTitle] = useState('');
|
||||
@@ -298,7 +308,8 @@ export function Dashboard({
|
||||
api.vehicleCoverage(new URLSearchParams({ limit: '8' })).then((page) => setCoverage(page.items)),
|
||||
api.vehicleRealtime(new URLSearchParams({ limit: '8' })).then((page) => setLocations(page.items)),
|
||||
api.alertEvents(new URLSearchParams({ limit: '5' })).then((page) => setQualityIssues(page.items)),
|
||||
api.opsHealth().then(setOpsHealth)
|
||||
api.opsHealth().then(setOpsHealth),
|
||||
api.sourceReadiness().then((plan) => setSourceReadiness(normalizeSourceReadiness(plan)))
|
||||
];
|
||||
Promise.allSettled(tasks)
|
||||
.then((results) => {
|
||||
@@ -954,6 +965,48 @@ export function Dashboard({
|
||||
<Button size="small" theme="light" type="primary" onClick={exportDashboardSnapshot}>导出驾驶舱 CSV</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
<Card bordered title="数据源到车辆服务就绪度" style={{ marginBottom: 16 }}>
|
||||
<div className="vp-source-readiness-grid">
|
||||
{(sourceReadiness?.sources ?? []).map((item) => (
|
||||
<div key={item.protocol} className="vp-source-readiness-item">
|
||||
<div className="vp-source-readiness-head">
|
||||
<Space wrap>
|
||||
<Tag color={sourceReadinessColor(item)}>{item.protocol}</Tag>
|
||||
<Tag color={sourceReadinessColor(item)}>{item.status}</Tag>
|
||||
</Space>
|
||||
<Typography.Text type="secondary">{item.role}</Typography.Text>
|
||||
</div>
|
||||
<div className="vp-source-readiness-metrics">
|
||||
<div>
|
||||
<span>在线</span>
|
||||
<strong>{formatCount(item.online)} / {formatCount(item.total)}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>在线率</span>
|
||||
<strong>{item.onlineRate.toLocaleString(undefined, { maximumFractionDigits: 1 })}%</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>缺失车辆</span>
|
||||
<strong>{formatCount(item.missingVehicles)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<Typography.Text type="secondary">{item.evidence}</Typography.Text>
|
||||
<div className="vp-source-readiness-actions">
|
||||
<Button size="small" theme="light" onClick={() => { window.location.hash = item.realtimeHash; }}>实时</Button>
|
||||
<Button size="small" theme="light" onClick={() => { window.location.hash = item.vehiclesHash; }}>车辆</Button>
|
||||
<Button size="small" theme="light" onClick={() => { window.location.hash = item.historyHash; }}>RAW</Button>
|
||||
<Button size="small" theme="light" type={item.severity === 'ok' ? 'tertiary' : 'warning'} onClick={() => { window.location.hash = item.alertHash; }}>告警</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{sourceReadiness?.sources?.length ? null : (
|
||||
<div className="vp-source-readiness-empty">
|
||||
<Tag color="grey">待接入</Tag>
|
||||
<Typography.Text type="secondary">数据源就绪度加载后会展示 32960、808 和宇通 MQTT 对车辆服务的支撑状态。</Typography.Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
<Card bordered title="地图运营能力" style={{ marginBottom: 16 }}>
|
||||
<div className="vp-map-ops-board">
|
||||
<div className="vp-map-ops-readiness">
|
||||
|
||||
Reference in New Issue
Block a user