feat(platform): show realtime coverage metrics
This commit is contained in:
@@ -36,6 +36,10 @@ function sourceEvidenceText(row: VehicleRealtimeRow) {
|
||||
return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`;
|
||||
}
|
||||
|
||||
function formatPercent(value: number) {
|
||||
return Number.isFinite(value) ? `${value.toLocaleString(undefined, { maximumFractionDigits: 1 })}%` : '0%';
|
||||
}
|
||||
|
||||
function serviceStatusWeight(row: VehicleRealtimeRow) {
|
||||
const severity = row.serviceStatus?.severity;
|
||||
if (severity === 'error') return 4;
|
||||
@@ -233,6 +237,10 @@ export function Realtime({
|
||||
const onlineCount = rows.filter((row) => row.online).length;
|
||||
const locatedCount = rows.filter(isValidCoordinate).length;
|
||||
const degradedCount = rows.filter((row) => row.onlineSourceCount < row.sourceCount).length;
|
||||
const onlineRate = rows.length > 0 ? (onlineCount / rows.length) * 100 : 0;
|
||||
const locatedRate = rows.length > 0 ? (locatedCount / rows.length) * 100 : 0;
|
||||
const degradedRate = rows.length > 0 ? (degradedCount / rows.length) * 100 : 0;
|
||||
const pageCoverageRate = pagination.total > 0 ? (rows.length / pagination.total) * 100 : 0;
|
||||
const primaryProtocols = new Set(rows.map((row) => row.primaryProtocol).filter(Boolean));
|
||||
const sourceIssueRows = rows
|
||||
.filter((row) => canOpenVehicle(row.vin) && hasSourceIssue(row))
|
||||
@@ -417,6 +425,22 @@ export function Realtime({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card bordered title="实时覆盖判读" style={{ marginBottom: 16 }}>
|
||||
<div className="vp-stat-insight-grid">
|
||||
{[
|
||||
{ label: '在线率', value: formatPercent(onlineRate), color: onlineRate >= 80 ? 'green' as const : 'orange' as const, detail: `${onlineCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆在线。` },
|
||||
{ label: '定位有效率', value: formatPercent(locatedRate), color: locatedRate >= 80 ? 'green' as const : 'orange' as const, detail: `${locatedCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆坐标有效。` },
|
||||
{ label: '降级率', value: formatPercent(degradedRate), color: degradedCount > 0 ? 'orange' as const : 'green' as const, detail: `${degradedCount.toLocaleString()} 辆存在来源不完整或离线。` },
|
||||
{ label: '分页覆盖率', value: formatPercent(pageCoverageRate), color: pageCoverageRate >= 100 ? 'green' as const : 'grey' as const, detail: '当前页车辆数 / 当前筛选总车辆数。' }
|
||||
].map((item) => (
|
||||
<Card key={item.label} bordered>
|
||||
<Tag color={item.color}>{item.label}</Tag>
|
||||
<div className="vp-monitor-metric-value">{item.value}</div>
|
||||
<Typography.Text type="secondary">{item.detail}</Typography.Text>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Tabs type="line">
|
||||
<Tabs.TabPane tab="表格视图" itemKey="table">
|
||||
<div className="vp-table-toolbar">
|
||||
|
||||
@@ -7056,6 +7056,13 @@ test('copies realtime operations summary from realtime page', async () => {
|
||||
render(<App />);
|
||||
|
||||
expect((await screen.findAllByText('VIN-RT-SUMMARY-001')).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText('实时覆盖判读')).toBeInTheDocument();
|
||||
expect(screen.getByText('在线率')).toBeInTheDocument();
|
||||
expect(screen.getAllByText('66.7%').length).toBeGreaterThanOrEqual(3);
|
||||
expect(screen.getByText('定位有效率')).toBeInTheDocument();
|
||||
expect(screen.getByText('降级率')).toBeInTheDocument();
|
||||
expect(screen.getByText('分页覆盖率')).toBeInTheDocument();
|
||||
expect(screen.getByText('100%')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: '复制实时摘要' }));
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【实时监控摘要】'));
|
||||
|
||||
Reference in New Issue
Block a user