feat(platform): add vehicle scenario navigation

This commit is contained in:
lingniu
2026-07-04 20:47:29 +08:00
parent e8f0ebff35
commit 431f4a4505
3 changed files with 159 additions and 0 deletions

View File

@@ -504,6 +504,58 @@ export function Dashboard({
]
}
];
const scenarioNavigation = [
{
title: '实时监控',
objective: '确认车辆是否在线、位置是否可信、三类来源是否齐全。',
evidence: `Redis 在线态 / 最新定位 / 有效坐标 ${commandLocatedCount.toLocaleString()}`,
sla: '目标 0-1 秒内进入实时视图',
primaryAction: '实时监控',
secondaryAction: '地图态势',
onPrimary: () => onOpenRealtime({ online: 'online' }),
onSecondary: () => onOpenMap({ online: 'online' })
},
{
title: '轨迹回放',
objective: '按车辆复盘位置、速度、里程和断点,定位平台转发或车辆异常。',
evidence: `高德轨迹底座 / 今日活跃 ${formatCount(summary?.activeToday)}`,
sla: '支持按车辆和时间窗快速回放',
primaryAction: '轨迹回放',
secondaryAction: 'RAW证据',
onPrimary: () => onOpenHistory(),
onSecondary: () => onOpenHistory({ tab: 'raw', includeFields: 'true' })
},
{
title: '历史数据查询',
objective: '围绕车辆查询位置、RAW、解析字段给 BI、运维和业务复盘提供证据。',
evidence: `TDengine / 今日帧 ${formatCount(summary?.frameToday)} / Kafka Lag ${formatLag(summary?.kafkaLag)}`,
sla: '优先返回必要字段,避免大 JSON 拖慢查询',
primaryAction: '历史查询',
secondaryAction: '字段证据',
onPrimary: () => onOpenHistory({ tab: 'location' }),
onSecondary: () => onOpenHistory({ tab: 'raw', includeFields: 'true' })
},
{
title: '告警事件触发和通知',
objective: '把断链、无来源、VIN 缺失、字段缺失转换为可通知、可闭环事件。',
evidence: `告警车辆 ${formatCount(summary?.issueVehicles)} / 最高优先级 ${highPriorityIssue ? qualityIssueLabel(highPriorityIssue.issueType) : '暂无'}`,
sla: 'P0 进入通知队列,超时升级',
primaryAction: '告警事件',
secondaryAction: '通知规则',
onPrimary: () => onOpenQuality(),
onSecondary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {})
},
{
title: '统计查询',
objective: '按车辆口径查询里程等指标,保证区间统计和日统计可闭合。',
evidence: `车辆口径 ${formatCount(serviceSummary?.totalVehicles)} / 多源覆盖 ${formatCount(serviceSummary?.multiSourceVehicles)}`,
sla: '统计值必须能追溯轨迹和 RAW 证据',
primaryAction: '统计查询',
secondaryAction: '车辆中心',
onPrimary: () => onOpenMileage(),
onSecondary: () => onOpenVehicles({})
}
];
const copyOperationsHandoff = () => {
const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok');
const priorityAction = serviceActionQueue[0];
@@ -532,6 +584,24 @@ export function Dashboard({
].filter(Boolean);
copyText(lines.join('\n'), '运营交接摘要');
};
const copyScenarioBlueprint = () => {
const lines = [
'【车辆数据中台功能蓝图】',
'定位32960 / 808 / 宇通 MQTT 都只是车辆服务证据源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。',
`高德地图:${amapConfigured ? '已配置,前端只使用运行时配置和安全代理' : '待配置'}`,
`车辆规模:${formatCount(serviceSummary?.totalVehicles)};在线:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)};告警:${formatCount(summary?.issueVehicles)}`,
'',
...scenarioNavigation.map((item, index) => [
`${index + 1}. ${item.title}`,
` 目标:${item.objective}`,
` 证据:${item.evidence}`,
` SLA${item.sla}`
].join('\n')),
'',
`入口:${appURL(buildAppHash({ page: 'dashboard' }))}`
];
copyText(lines.join('\n'), '功能蓝图');
};
const buildDashboardSnapshotRows = () => {
const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok');
const priorityAction = serviceActionQueue[0];
@@ -707,6 +777,32 @@ export function Dashboard({
))}
</div>
</Card>
<Card
bordered
title={<Space><span></span><Button size="small" onClick={copyScenarioBlueprint}></Button></Space>}
style={{ marginBottom: 16 }}
>
<div className="vp-scenario-grid">
{scenarioNavigation.map((item) => (
<div key={item.title} className="vp-scenario-item">
<div className="vp-scenario-main">
<Typography.Title heading={6} style={{ margin: 0 }}>{item.title}</Typography.Title>
<Typography.Text type="secondary">{item.objective}</Typography.Text>
</div>
<div className="vp-scenario-evidence">{item.evidence}</div>
<Tag color="blue">{item.sla}</Tag>
<Space wrap>
<Button size="small" theme="solid" type="primary" aria-label={`场景导航 ${item.title} ${item.primaryAction}`} onClick={item.onPrimary}>
{item.primaryAction}
</Button>
<Button size="small" theme="light" aria-label={`场景导航 ${item.title} ${item.secondaryAction}`} onClick={item.onSecondary}>
{item.secondaryAction}
</Button>
</Space>
</div>
))}
</div>
</Card>
<Card bordered title="全链路值班闭环" style={{ marginBottom: 16 }}>
<div className="vp-operation-flow">
{workflowSteps.map((item, index) => (