From 403a52ce2929130dea6f5b8466e4ea12e1935f51 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 16:22:01 +0800 Subject: [PATCH] feat(platform): add customer task navigation --- .../apps/web/src/pages/Dashboard.tsx | 56 ++++++++++++++ .../apps/web/src/styles/global.css | 73 +++++++++++++++++++ .../apps/web/src/test/App.test.tsx | 7 +- .../docs/fleet-platform-product-research.md | 14 ++++ 4 files changed, 149 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 2d4a5e8e..154edfdc 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -1508,6 +1508,40 @@ export function Dashboard({ detail: '同一时间范围贯穿轨迹、统计、历史和告警。' } ]; + const customerTaskNavigation = [ + { + 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) : '当前无高优先级', + action: '告警事件', + color: (summary?.issueVehicles ?? 0) > 0 ? 'orange' as const : 'green' as const, + onClick: () => onOpenQuality(highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {}) + } + ]; return (
@@ -1543,6 +1577,28 @@ export function Dashboard({
+
+
+ 客户任务导航 + 先按客户问题进入业务页面 +
+
+ {customerTaskNavigation.map((item) => ( + + ))} +
+
今日先处理 diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index c27eb7c7..def63be5 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -460,6 +460,78 @@ body { gap: 12px; } +.vp-fleet-task-router { + padding: 12px; + border: 1px solid rgba(22, 100, 255, 0.18); + border-radius: var(--vp-radius); + background: #ffffff; + display: grid; + gap: 10px; +} + +.vp-fleet-task-router-head { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; +} + +.vp-fleet-task-router-head .semi-typography-secondary { + font-size: 12px; + line-height: 18px; +} + +.vp-fleet-task-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; +} + +.vp-fleet-task-item { + min-height: 122px; + padding: 10px; + border: 1px solid var(--vp-border); + border-radius: 8px; + background: #fbfcff; + color: inherit; + cursor: pointer; + font: inherit; + text-align: left; + display: grid; + gap: 7px; + align-content: start; +} + +.vp-fleet-task-item:hover, +.vp-fleet-task-item:focus-visible { + border-color: rgba(22, 100, 255, 0.45); + background: #f5f9ff; + box-shadow: 0 0 0 3px rgba(22, 100, 255, 0.06); + outline: none; +} + +.vp-fleet-task-item strong { + color: var(--vp-text); + font-size: 15px; + line-height: 21px; + font-weight: 700; + word-break: break-word; +} + +.vp-fleet-task-item span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 17px; +} + +.vp-fleet-task-item em { + color: var(--vp-primary); + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: 18px; +} + .vp-fleet-next-actions { display: grid; gap: 9px; @@ -6827,6 +6899,7 @@ button.vp-realtime-command-item:focus-visible { .vp-action-grid, .vp-fleet-monitor .semi-card-body, .vp-fleet-monitor-stats, + .vp-fleet-task-grid, .vp-fleet-monitor-trust, .vp-vehicle-live-view .semi-card-body, .vp-vehicle-live-main, 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 d9086877..46bf47bc 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -26,7 +26,7 @@ test('renders vehicle platform shell', () => { expect(screen.getByText('地图看车、在线监控、异常优先')).toBeInTheDocument(); expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('按 VIN、车牌、手机号管理车辆')).toBeInTheDocument(); - expect(screen.getByText('轨迹统计')).toBeInTheDocument(); + expect(screen.getAllByText('轨迹统计').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('回放轨迹、统计查询、导出数据')).toBeInTheDocument(); expect(screen.getAllByText('告警通知').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('接入运维')).toBeInTheDocument(); @@ -802,6 +802,11 @@ test('dashboard exposes vehicle data center capability matrix', async () => { expect(screen.getByText('客户主流程围绕车辆本身:地图看车、实时状态、时间窗复盘、统计查询、历史数据导出和告警通知。接入来源只作为追溯证据。')).toBeInTheDocument(); expect(screen.getByRole('button', { name: '车辆监控指标 车辆总数 1,033' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '车辆监控指标 在线车辆 208' })).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.getByRole('button', { name: '车辆运营建议 先处理告警车辆 进入告警' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '车辆运营建议 选择一辆车复盘 车辆中心' })).toBeInTheDocument(); diff --git a/vehicle-data-platform/docs/fleet-platform-product-research.md b/vehicle-data-platform/docs/fleet-platform-product-research.md index 438c34f4..696fb340 100644 --- a/vehicle-data-platform/docs/fleet-platform-product-research.md +++ b/vehicle-data-platform/docs/fleet-platform-product-research.md @@ -8,6 +8,20 @@ - 告警和通知需要从“发现异常”延伸到“责任人、升级、闭环”,而不是只展示技术链路状态。 - 运维链路、消息队列、存储状态适合放在内部视图,作为客户问题排查的支撑层。 +## 2026-07-05 外部产品对标补充 + +- Geotab 的公开资料强调一体化车队平台:GPS 车辆位置可近实时查看,并保留完整 trip history。这说明首页主任务应是“车在哪里”和“历史怎么回放”,原始协议只能是证据层。参考:https://www.geotab.com/ +- Samsara 的车队远程信息产品强调实时 GPS、车辆诊断、路线导航、燃料/能源监控和风险告警。这对应本项目的客户主路径:实时地图、车辆状态、轨迹、统计、告警。参考:https://www.samsara.com/products/telematics +- Verizon Connect 的车队管理公开页面把 dashboards、reports、alerts、near real-time location、driver behavior、routing visibility 放在核心能力中。这说明客户视角需要“监控、报表、告警、路线/位置”并列,而不是把 Kafka、Redis、TDengine 放在第一层。参考:https://www.verizonconnect.com/ +- Geotab 关于远程车队管理的说明把数据流描述为:车辆采集位置和健康信息,经网络到云端,再在 Web/Mobile dashboard 展示实时报告和告警。这与本项目架构一致,但产品表达应从 dashboard 和 alerts 开始,而不是从接入来源开始。参考:https://www.geotab.com/blog/remote-fleet-management/ + +## 对标后的产品改造规则 + +- 首页第一屏只回答客户任务:看车在哪、查某段时间、导出证据、处理告警。 +- “32960 / 808 / MQTT”不作为主任务名称,只在车辆档案、历史证据、运维质量中作为来源可信度出现。 +- 自定义时间窗必须成为跨页面共享的查询意图:轨迹、里程、历史数据、告警复盘使用同一组参数。 +- 内部可观测性继续保留,但默认折叠在运维/依据层,避免客户把系统理解为“协议接入平台”。 + ## 本项目采用的产品原则 - 三个接入来源只是数据通道,最终服务对象是车辆。