feat(platform): add vehicle monitoring command center

This commit is contained in:
lingniu
2026-07-05 02:10:10 +08:00
parent 7657412b8c
commit 13ef1779ac
3 changed files with 120 additions and 0 deletions

View File

@@ -1044,6 +1044,48 @@ export function Dashboard({
downloadCsv('dashboard-snapshot.csv', buildCsv(dashboardSnapshotColumns, rows));
Toast.success(`已导出 ${rows.length.toLocaleString()} 条驾驶舱摘要`);
};
const commandCenterItems = [
{
title: '全域车辆监控',
value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`,
detail: `有效定位 ${commandLocatedCount.toLocaleString()} 辆,先从地图确认车辆分布、在线状态和最新位置。`,
color: 'green' as const,
primaryAction: '打开车辆地图',
secondaryAction: '在线车辆',
onPrimary: () => onOpenMap({ online: 'online' }),
onSecondary: () => onOpenVehicles({ online: 'online' })
},
{
title: '异常车辆闭环',
value: `${formatCount(summary?.issueVehicles)} 告警`,
detail: highPriorityIssue ? `最高优先级:${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '当前没有高优先级告警,保持实时巡检。',
color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const,
primaryAction: '告警事件',
secondaryAction: '通知规则',
onPrimary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}),
onSecondary: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {})
},
{
title: '自定义时间复盘',
value: taskTimeMonitorSummary,
detail: '一个时间窗贯穿轨迹回放、里程统计、历史查询和告警复盘,适合客户问询与 BI 核对。',
color: 'blue' as const,
primaryAction: '轨迹复盘',
secondaryAction: '里程统计',
onPrimary: () => openTimeMonitorHistory(),
onSecondary: () => openTimeMonitorMileage()
},
{
title: '数据交付与导出',
value: `${formatCount(summary?.frameToday)} 今日数据`,
detail: '按车辆、时间、字段裁剪导出位置、原始记录和字段证据,交付给业务或客户复盘。',
color: 'blue' as const,
primaryAction: '历史导出',
secondaryAction: '导出概览',
onPrimary: () => openTimeMonitorRaw(),
onSecondary: () => exportDashboardSnapshot()
}
];
return (
<div className="vp-page">
@@ -1086,6 +1128,39 @@ export function Dashboard({
</div>
</div>
</Card>
<Card bordered title="车辆监控指挥台" style={{ marginBottom: 16 }}>
<div className="vp-command-center-grid">
{commandCenterItems.map((item) => (
<div key={item.title} className="vp-command-center-item">
<div className="vp-command-center-head">
<Tag color={item.color}>{item.title}</Tag>
<strong>{item.value}</strong>
</div>
<Typography.Text type="secondary">{item.detail}</Typography.Text>
<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"
type="primary"
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-customer-workflow-grid">
{customerHeroActions.map((item) => (

View File

@@ -410,6 +410,41 @@ body {
line-height: 26px;
}
.vp-command-center-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.vp-command-center-item {
min-height: 166px;
padding: 16px;
border: 1px solid var(--vp-border);
border-radius: 8px;
background: #ffffff;
display: grid;
align-content: space-between;
gap: 12px;
}
.vp-command-center-head {
min-width: 0;
display: grid;
gap: 8px;
}
.vp-command-center-head strong {
color: var(--vp-text);
font-size: 20px;
line-height: 26px;
word-break: break-word;
}
.vp-command-center-item .semi-typography {
font-size: 13px;
line-height: 20px;
}
.vp-customer-task-grid {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
@@ -3176,6 +3211,7 @@ button.vp-realtime-command-item:focus-visible {
.vp-map-ops-readiness,
.vp-map-ops-work,
.vp-time-monitor-grid,
.vp-command-center-grid,
.vp-customer-task-grid,
.vp-current-service-board,
.vp-current-service-grid,

View File

@@ -788,6 +788,15 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
await renderDashboard();
expect(screen.getByText('车辆运营监控台')).toBeInTheDocument();
expect(screen.getByText('先看车辆服务')).toBeInTheDocument();
expect(screen.getByText('车辆监控指挥台')).toBeInTheDocument();
expect(screen.getByText('全域车辆监控')).toBeInTheDocument();
expect(screen.getByText('异常车辆闭环')).toBeInTheDocument();
expect(screen.getByText('自定义时间复盘')).toBeInTheDocument();
expect(screen.getByText('数据交付与导出')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆监控指挥台 全域车辆监控 打开车辆地图' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆监控指挥台 异常车辆闭环 告警事件' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆监控指挥台 自定义时间复盘 轨迹复盘' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '车辆监控指挥台 数据交付与导出 历史导出' })).toBeInTheDocument();
expect(screen.getByText('客户常用工作流')).toBeInTheDocument();
expect(screen.getByText('实时车辆地图')).toBeInTheDocument();
expect(screen.getByText('历史数据导出')).toBeInTheDocument();