diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
index f3b113f3..3b5497c9 100644
--- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
+++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx
@@ -1950,6 +1950,58 @@ export function Dashboard({
onClick: openTimeMonitorRaw
}
];
+ const dispatchOperationsHighlights = [
+ {
+ label: '实时地图',
+ value: `${commandLocatedCount.toLocaleString()} 有定位`,
+ detail: '先看车辆空间分布、在线态势和最新位置。',
+ action: '打开地图',
+ color: commandLocatedCount > 0 ? 'blue' as const : 'orange' as const,
+ onClick: () => onOpenMap({ online: 'online' })
+ },
+ {
+ label: '今日重点',
+ value: `${formatCount(summary?.issueVehicles)} 告警`,
+ detail: highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '没有高优先级告警,保持巡检。',
+ action: '处理告警',
+ color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const,
+ onClick: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {})
+ }
+ ];
+ const dispatchOperationsTasks = [
+ {
+ title: '看全域位置',
+ value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`,
+ detail: '打开实时地图,确认在线车辆、有效坐标和重点区域。',
+ action: '实时地图',
+ color: 'green' as const,
+ onClick: () => onOpenMap({ online: 'online' })
+ },
+ {
+ title: '回放重点轨迹',
+ value: `${formatCount(summary?.activeToday)} 活跃`,
+ detail: '按车辆和时间窗回放路线,核对停驶、偏航和里程断点。',
+ action: '轨迹回放',
+ color: 'blue' as const,
+ onClick: openTimeMonitorHistory
+ },
+ {
+ title: '核对时间窗里程',
+ value: `${formatCount(serviceSummary?.totalVehicles)} 车辆`,
+ detail: '按同一时间范围核对区间里程、每日统计和 BI 口径。',
+ action: '统计查询',
+ color: 'blue' as const,
+ onClick: openTimeMonitorMileage
+ },
+ {
+ title: '导出客户证据',
+ value: `${formatCount(summary?.frameToday)} 帧`,
+ detail: '导出历史位置、原始记录、解析字段和告警说明。',
+ action: '历史导出',
+ color: 'blue' as const,
+ onClick: openTimeMonitorRaw
+ }
+ ];
return (
@@ -1980,6 +2032,65 @@ export function Dashboard({
))}
+
+
+
+
+ 客户调度运营首页
+ {amapConfigured ? '地图可用' : '坐标预览'}
+ 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警
+
+ 值班人员进来先看地图态势和异常队列,再进入轨迹回放、时间窗统计、历史导出和告警通知。
+
+ 首页把车辆监控组织成一个调度工作台:地图负责看车,任务队列负责处理今天要交付和要解释的问题。
+
+
+
+
+ {dispatchOperationsHighlights.map((item) => (
+
+ ))}
+
+
+
+
+ 今日车辆任务
+ 按客户最常用动作排序,不按协议来源排序。
+
+
+ {dispatchOperationsTasks.map((item) => (
+
+ ))}
+
+
+
{
+ window.history.replaceState(null, '', '/#/dashboard');
+ vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
+ const path = String(input);
+ if (path.includes('/api/ops/health')) {
+ return {
+ ok: true,
+ json: async () => ({
+ data: {
+ linkHealth: [],
+ kafkaLag: 0,
+ activeConnections: 0,
+ capacityFindings: [],
+ redisOnlineKeys: 0,
+ tdengineWritable: true,
+ mysqlWritable: true,
+ runtime: {
+ requestTimeoutMs: 5000,
+ amapWebJsConfigured: true,
+ amapApiConfigured: true,
+ amapSecurityProxyEnabled: true,
+ amapSecurityCodeExposed: false
+ }
+ },
+ traceId: 'trace-test',
+ timestamp: 1783094400000
+ })
+ } as Response;
+ }
+ if (path.includes('/api/dashboard/summary')) {
+ return {
+ ok: true,
+ json: async () => ({
+ data: {
+ onlineVehicles: 3,
+ activeToday: 4,
+ frameToday: 1286320,
+ issueVehicles: 7,
+ kafkaLag: 0,
+ protocols: [],
+ serviceStatuses: [],
+ linkHealth: []
+ },
+ traceId: 'trace-test',
+ timestamp: 1783094400000
+ })
+ } as Response;
+ }
+ if (path.includes('/api/vehicle-service/summary')) {
+ return {
+ ok: true,
+ json: async () => ({
+ data: {
+ totalVehicles: 1033,
+ boundVehicles: 1024,
+ onlineVehicles: 208,
+ singleSourceVehicles: 391,
+ multiSourceVehicles: 181,
+ noDataVehicles: 461,
+ identityRequiredVehicles: 9,
+ serviceStatuses: [],
+ protocols: [],
+ missingSources: []
+ },
+ traceId: 'trace-test',
+ timestamp: 1783094400000
+ })
+ } as Response;
+ }
+ return {
+ ok: true,
+ json: async () => ({
+ data: { items: [], total: 0, limit: 8, offset: 0 },
+ traceId: 'trace-test',
+ timestamp: 1783094400000
+ })
+ } as Response;
+ });
+
+ render();
+
+ expect(await screen.findByText('客户调度运营首页')).toBeInTheDocument();
+ expect(screen.getByText('值班人员进来先看地图态势和异常队列,再进入轨迹回放、时间窗统计、历史导出和告警通知。')).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '客户调度运营首页 实时地图 0 有定位 打开地图' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '客户调度运营首页 今日重点 7 告警 处理告警' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '调度运营任务 看全域位置 208 在线 实时地图' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '调度运营任务 回放重点轨迹 4 活跃 轨迹回放' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '调度运营任务 核对时间窗里程 1,033 车辆 统计查询' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: '调度运营任务 导出客户证据 1,286,320 帧 历史导出' })).toBeInTheDocument();
+});
+
test('dashboard guides customer custom time window service path', async () => {
window.history.replaceState(null, '', '/#/dashboard');
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
@@ -1318,7 +1409,7 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
await renderDashboard();
fireEvent.click(screen.getByRole('button', { name: '能力入口 统计查询' }));
expect(window.location.hash.startsWith('#/mileage')).toBe(true);
-});
+}, 10000);
test('dashboard exposes vehicle command center map actions', async () => {
window.history.replaceState(null, '', '/#/dashboard');