diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 6370953a..d03fc311 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -479,6 +479,55 @@ export function Realtime({ ) : null} +
+
+
实时作业入口
+
+ {[ + { label: '在线车辆', value: onlineCount, color: 'green' as const, action: () => applyFilters({ ...filters, online: 'online' }) }, + { label: '离线车辆', value: rows.length - onlineCount, color: rows.length - onlineCount > 0 ? 'orange' as const : 'green' as const, action: () => applyFilters({ ...filters, online: 'offline' }) }, + { label: '定位有效', value: locatedCount, color: locatedCount > 0 ? 'blue' as const : 'orange' as const }, + { label: '降级服务', value: degradedCount, color: degradedCount > 0 ? 'orange' as const : 'green' as const, action: () => applyFilters({ ...filters, serviceStatus: 'degraded' }) }, + { label: '更新超时', value: staleCount, color: staleCount > 0 ? 'red' as const : 'green' as const }, + { label: '来源类型', value: primaryProtocols.size, color: 'blue' as const } + ].map((item) => ( + item.action ? ( + + ) : ( +
+ {item.label} + {item.value.toLocaleString()} +
+ ) + ))} +
+
+
+
建议处置
+
+ {degradedCount > 0 ? ( + + ) : 服务状态稳定} + {staleCount > 0 ? 核对超时车辆 {staleCount.toLocaleString()} : 实时新鲜} + {sourceIssueRows.length > 0 && onOpenQuality ? ( + + ) : null} +
+
+
diff --git a/vehicle-data-platform/apps/web/src/styles/global.css b/vehicle-data-platform/apps/web/src/styles/global.css index e8989aaf..a6b8976d 100644 --- a/vehicle-data-platform/apps/web/src/styles/global.css +++ b/vehicle-data-platform/apps/web/src/styles/global.css @@ -296,6 +296,54 @@ body { gap: 8px; } +.vp-realtime-command-board { + margin-bottom: 16px; + padding: 14px; + border: 1px solid var(--vp-border); + border-radius: var(--vp-radius); + background: #fbfcff; + display: grid; + grid-template-columns: minmax(0, 1fr) 280px; + gap: 16px; +} + +.vp-realtime-command-grid { + display: grid; + grid-template-columns: repeat(6, minmax(0, 1fr)); + gap: 10px; +} + +.vp-realtime-command-item { + min-height: 74px; + padding: 12px; + border: 1px solid var(--vp-border); + border-radius: var(--vp-radius); + background: var(--vp-surface); + display: grid; + align-content: space-between; + gap: 8px; + text-align: left; + font: inherit; +} + +button.vp-realtime-command-item { + cursor: pointer; +} + +button.vp-realtime-command-item:hover, +button.vp-realtime-command-item:focus-visible { + border-color: rgba(22, 100, 255, 0.42); + background: #f5f9ff; + outline: none; +} + +.vp-realtime-command-item span:last-child { + color: var(--vp-text); + font-size: 22px; + font-weight: 700; + line-height: 28px; +} + .vp-result-summary-item { min-height: 72px; padding: 12px; @@ -1128,6 +1176,8 @@ body { .vp-action-grid, .vp-current-service-board, .vp-current-service-grid, + .vp-realtime-command-board, + .vp-realtime-command-grid, .vp-operation-flow, .vp-service-model-grid, .vp-capability-grid, 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 790717f8..7973cf69 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -7817,7 +7817,7 @@ test('frames realtime page as one vehicle service with source evidence', async ( expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument(); expect(screen.getByText('高德地图待配置')).toBeInTheDocument(); - expect(screen.getByText('定位有效')).toBeInTheDocument(); + expect(screen.getAllByText('定位有效').length).toBeGreaterThan(0); expect(screen.getByText('车辆核心数据')).toBeInTheDocument(); expect(screen.getByText('来源证据')).toBeInTheDocument(); expect(screen.getByText('GB32960 在线')).toBeInTheDocument(); @@ -8168,6 +8168,12 @@ test('copies realtime operations summary from realtime page', async () => { render(); expect((await screen.findAllByText('VIN-RT-SUMMARY-001')).length).toBeGreaterThan(0); + expect(screen.getByText('实时作业入口')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '实时筛选 在线车辆 2' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '实时筛选 离线车辆 1' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '实时筛选 降级服务 2' })).toBeInTheDocument(); + expect(screen.getByText('建议处置')).toBeInTheDocument(); + expect(screen.getByText('排查降级服务 2')).toBeInTheDocument(); expect(screen.getByText('实时覆盖判读')).toBeInTheDocument(); expect(screen.getByText('在线率')).toBeInTheDocument(); expect(screen.getAllByText('66.7%').length).toBeGreaterThanOrEqual(3); @@ -8186,6 +8192,8 @@ test('copies realtime operations summary from realtime page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('重点车辆:')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A摘要3 / YUTONG_MQTT / 车辆离线')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时页面:http://localhost:3000/#/realtime?protocol=JT808&online=online')); + fireEvent.click(screen.getByRole('button', { name: '实时筛选 降级服务 2' })); + expect(window.location.hash).toBe('#/realtime?protocol=JT808&serviceStatus=degraded&online=online'); }); test('shows production AMap integration status on realtime page', async () => {