From 8765ac002a222a35367c858eb5656cb068ff8602 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 16:59:01 +0800 Subject: [PATCH] feat(platform): add customer delivery cockpit --- .../apps/web/src/pages/Dashboard.tsx | 67 +++++++++++++++++ .../apps/web/src/styles/global.css | 74 +++++++++++++++++++ .../apps/web/src/test/App.test.tsx | 7 ++ 3 files changed, 148 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 0ee8f30d..c447f081 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -1584,6 +1584,40 @@ export function Dashboard({ onClick: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}) } ]; + const customerDeliveryItems = [ + { + title: '在线车辆交付', + value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, + detail: `地图有效定位 ${commandLocatedCount.toLocaleString()} 辆,先交付客户最关心的车辆当前位置和在线态势。`, + action: '实时地图', + color: 'green' as const, + onClick: () => onOpenMap({ online: 'online' }) + }, + { + title: '时间窗复盘', + value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo), + detail: '同一时间范围同步到轨迹、统计、历史证据和告警复盘,避免客户反复给条件。', + action: '轨迹回放', + color: 'blue' as const, + onClick: openTimeMonitorHistory + }, + { + title: '数据证据交付', + value: `${formatCount(summary?.frameToday)} 今日数据`, + detail: '按车辆、时间和字段裁剪导出位置、原始记录、解析字段和统计依据。', + action: '历史导出', + color: 'blue' as const, + onClick: openTimeMonitorRaw + }, + { + title: '告警通知交付', + 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 } : {}) + } + ]; return (
@@ -1742,6 +1776,39 @@ export function Dashboard({
+ +
+ + 客户交付驾驶舱 + 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警车辆 + {amapConfigured ? '地图可交付' : '坐标可交付'} + + 把车辆位置、时间窗、里程统计、历史证据和告警通知组织成客户可以直接使用的服务包。 + + 客户打开首页后,先知道今天能交付什么,再选择地图、轨迹、统计、导出或通知。 + + + + + +
+
+ {customerDeliveryItems.map((item) => ( + + ))} +
+
自定义时间窗复盘{timeMonitorSummary}} diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index 09da25a8..7c139004 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -819,6 +819,78 @@ body { line-height: 17px; } +.vp-customer-delivery-board { + margin-bottom: 16px; + overflow: hidden; + border-color: rgba(22, 100, 255, 0.16); +} + +.vp-customer-delivery-board .semi-card-body { + display: grid; + grid-template-columns: minmax(280px, 0.58fr) minmax(0, 1.42fr); + background: #ffffff; +} + +.vp-customer-delivery-summary { + padding: 18px; + border-right: 1px solid var(--vp-border); + background: #f8fbff; + display: grid; + gap: 12px; + align-content: start; +} + +.vp-customer-delivery-grid { + padding: 16px; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.vp-customer-delivery-item { + min-height: 150px; + padding: 12px; + border: 1px solid var(--vp-border); + border-radius: var(--vp-radius); + background: #fbfcff; + color: inherit; + cursor: pointer; + font: inherit; + text-align: left; + display: grid; + gap: 8px; + align-content: start; +} + +.vp-customer-delivery-item:hover, +.vp-customer-delivery-item:focus-visible { + border-color: rgba(22, 100, 255, 0.42); + background: #f5f9ff; + outline: none; +} + +.vp-customer-delivery-item strong { + color: var(--vp-text); + font-size: 17px; + line-height: 23px; + font-weight: 700; + word-break: break-word; +} + +.vp-customer-delivery-item span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; +} + +.vp-customer-delivery-item em { + color: var(--vp-primary); + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: 18px; +} + .vp-customer-cockpit { margin-bottom: 16px; overflow: hidden; @@ -7277,6 +7349,8 @@ button.vp-realtime-command-item:focus-visible { .vp-fleet-monitor-stats, .vp-fleet-task-grid, .vp-fleet-monitor-trust, + .vp-customer-delivery-board .semi-card-body, + .vp-customer-delivery-grid, .vp-vehicle-live-view .semi-card-body, .vp-vehicle-live-main, .vp-vehicle-live-facts, diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index d1f99794..3badb75e 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -832,6 +832,13 @@ test('dashboard exposes vehicle data center capability matrix', async () => { expect(screen.getByRole('button', { name: '客户常问 哪些车需要马上处理? 告警事件' })).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();