From a68db5e0eb0b0aec0a323cfc7751fa47f25f7fa4 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 21:51:44 +0800 Subject: [PATCH] feat(platform): add dispatch operations home --- .../apps/web/src/pages/Dashboard.tsx | 111 +++++++++++++++ .../apps/web/src/styles/global.css | 129 ++++++++++++++++++ .../apps/web/src/test/App.test.tsx | 93 ++++++++++++- 3 files changed, 332 insertions(+), 1 deletion(-) 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');