diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index c0eb4e3e..4a086745 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -2053,6 +2053,40 @@ export function Dashboard({ onClick: openTimeMonitorRaw } ]; + const vehicleHealthEnergyItems = [ + { + label: '实时工况', + value: `${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线`, + detail: '查看车辆最新速度、SOC、总里程、在线状态和多来源实时字段。', + action: '实时监控', + color: 'green' as const, + onClick: () => onOpenRealtime({ online: 'online' }) + }, + { + label: 'SOC/氢能字段', + value: `${formatCount(summary?.frameToday)} 今日数据`, + detail: '从历史字段中回查 SOC、燃料电池、氢耗和关键工况字段。', + action: '字段证据', + color: 'blue' as const, + onClick: () => onOpenHistory({ tab: 'fields', includeFields: 'true' }) + }, + { + label: '异常维护', + 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 } : {}) + }, + { + label: '里程能耗', + value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo), + detail: '用同一时间窗核对里程、运行状态和能耗相关统计口径。', + action: '统计查询', + color: 'blue' as const, + onClick: openTimeMonitorMileage + } + ]; const dispatchOperationsHighlights = [ { label: '实时地图', @@ -2575,6 +2609,35 @@ export function Dashboard({ ))} +
+
+ + 车辆健康与能耗工作台 + {formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} 在线 + 0 ? 'orange' : 'green'}>{formatCount(summary?.issueVehicles)} 告警 + + 把实时工况、SOC/氢能字段、异常维护和里程能耗放到同一辆车的服务视角里。 + + 客户不需要先理解字段来自 32960、808 还是 MQTT;先按车辆查看健康、能耗、异常和统计证据。 + +
+
+ {vehicleHealthEnergyItems.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 0cf845db..b808d337 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -2223,6 +2223,74 @@ body { font-weight: 700; } +.vp-vehicle-health-energy { + margin-bottom: 16px; + padding: 18px; + border: 1px solid rgba(18, 183, 106, 0.22); + border-radius: 8px; + background: #ffffff; + display: grid; + grid-template-columns: minmax(280px, 0.82fr) minmax(0, 1.5fr); + gap: 16px; +} + +.vp-vehicle-health-energy-copy { + min-width: 0; + display: grid; + align-content: start; + gap: 10px; +} + +.vp-vehicle-health-energy-grid { + min-width: 0; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; +} + +.vp-vehicle-health-energy-item { + min-height: 136px; + padding: 13px; + border: 1px solid var(--vp-border); + border-radius: 8px; + background: linear-gradient(180deg, #ffffff 0%, #f5fff9 100%); + color: inherit; + cursor: pointer; + text-align: left; + font: inherit; + display: grid; + align-content: space-between; + gap: 8px; +} + +.vp-vehicle-health-energy-item:hover, +.vp-vehicle-health-energy-item:focus-visible { + border-color: rgba(18, 183, 106, 0.42); + box-shadow: var(--vp-shadow-sm); + outline: none; +} + +.vp-vehicle-health-energy-item strong { + color: var(--vp-text); + font-size: 20px; + line-height: 25px; + word-break: break-word; +} + +.vp-vehicle-health-energy-item span { + color: var(--vp-text-muted); + font-size: 12px; + line-height: 18px; + word-break: break-word; +} + +.vp-vehicle-health-energy-item em { + color: #1664ff; + font-style: normal; + font-size: 12px; + font-weight: 700; +} + .vp-customer-cockpit-summary { padding: 18px; border-right: 1px solid var(--vp-border); @@ -11676,6 +11744,8 @@ button.vp-realtime-command-item:focus-visible { .vp-customer-fleet-groups-grid, .vp-customer-service-sla, .vp-customer-service-sla-grid, + .vp-vehicle-health-energy, + .vp-vehicle-health-energy-grid, .vp-customer-cockpit, .vp-customer-cockpit-actions, .vp-amap-service-foundation, 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 01e04661..8f7f4e6a 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -772,6 +772,12 @@ test('dashboard prioritizes customer vehicle service command over data sources', expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 异常通知 7 告警 通知闭环' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 恢复验收 轨迹/统计可查 验收证据' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 数据交付 1,286,320 今日数据 导出证据' })).toBeInTheDocument(); + expect(screen.getByText('车辆健康与能耗工作台')).toBeInTheDocument(); + expect(screen.getByText('把实时工况、SOC/氢能字段、异常维护和里程能耗放到同一辆车的服务视角里。')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 实时工况 208 在线 实时监控' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 SOC/氢能字段 1,286,320 今日数据 字段证据' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 异常维护 7 告警 告警事件' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 里程能耗 1 天窗口 统计查询' })).toBeInTheDocument(); }); test('dashboard presents a customer dispatch operations home', async () => {