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 () => {