feat(platform): streamline dashboard for vehicle service

This commit is contained in:
lingniu
2026-07-05 00:24:07 +08:00
parent 2ca656a3f3
commit 0613fe9f7b
3 changed files with 116 additions and 266 deletions

View File

@@ -424,9 +424,9 @@ test('shows vehicle service status distribution on dashboard', async () => {
render(<App />);
expect(await screen.findByText('车辆服务状态')).toBeInTheDocument();
expect(screen.getAllByText('来源不完整').length).toBeGreaterThan(0);
expect(screen.getByText('39')).toBeInTheDocument();
expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
expect(screen.getAllByText('服务状态').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('质量问题预览')).toBeInTheDocument();
});
test('dashboard renders vehicle service summary metrics', async () => {
@@ -495,18 +495,12 @@ test('dashboard renders vehicle service summary metrics', async () => {
expect(await screen.findByText('总车辆')).toBeInTheDocument();
expect(screen.getAllByText('1,033').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('单源车辆')).toBeInTheDocument();
expect(screen.getByText('391')).toBeInTheDocument();
expect(screen.getByText('多源车辆')).toBeInTheDocument();
expect(screen.getByText('181')).toBeInTheDocument();
expect(screen.getByText('暂无来源车辆')).toBeInTheDocument();
expect(screen.getByText('461')).toBeInTheDocument();
expect(screen.getByText('身份未绑定')).toBeInTheDocument();
expect(screen.getByText('今日活跃')).toBeInTheDocument();
expect(screen.getAllByText('有效定位').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('告警车辆')).toBeInTheDocument();
expect(screen.getByText('今日数据')).toBeInTheDocument();
expect(screen.getByText('档案不完整')).toBeInTheDocument();
expect(screen.getByText('366')).toBeInTheDocument();
expect(screen.getByText('来源证据在线分布')).toBeInTheDocument();
expect(screen.getByText('YUTONG_MQTT')).toBeInTheDocument();
expect(screen.getByText('0%')).toBeInTheDocument();
expect(screen.queryByText('NaN%')).not.toBeInTheDocument();
expect(fetchMock).toHaveBeenCalledWith('/api/vehicle-service/summary', undefined);
});
@@ -593,9 +587,9 @@ test('dashboard presents one vehicle service operating posture', async () => {
render(<App />);
expect(await screen.findByText('统一车辆服务入口')).toBeInTheDocument();
expect(await screen.findByText('车辆服务入口')).toBeInTheDocument();
expect(screen.getAllByText('208 / 1,033 在线').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('181 多源覆盖')).toBeInTheDocument();
expect(screen.getByText('今日活跃 4')).toBeInTheDocument();
expect(screen.getByText('7 告警事件')).toBeInTheDocument();
});
@@ -792,24 +786,24 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
await renderDashboard();
expect(screen.getByText('车辆运营监控台')).toBeInTheDocument();
expect(screen.getByText('先看车,再看数据来源')).toBeInTheDocument();
expect(screen.getByText('先看车辆服务')).toBeInTheDocument();
expect(screen.getByText('客户常用工作流')).toBeInTheDocument();
expect(screen.getByText('实时车辆地图')).toBeInTheDocument();
expect(screen.getByText('历史数据导出')).toBeInTheDocument();
expect(screen.getByText('车辆服务入口')).toBeInTheDocument();
expect(screen.getByText('自定义时间监控')).toBeInTheDocument();
expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument();
expect(screen.getByText('历史时间窗')).toBeInTheDocument();
expect(screen.getByText('区间里程')).toBeInTheDocument();
expect(screen.getByText('告警复盘')).toBeInTheDocument();
expect(screen.getByText('车辆服务作业台')).toBeInTheDocument();
expect(screen.getByText('地图运营能力')).toBeInTheDocument();
expect(screen.getByText('数据流转作业链')).toBeInTheDocument();
expect(screen.getByText('车联网场景导航')).toBeInTheDocument();
expect(screen.getByText('高德 Web JS')).toBeInTheDocument();
expect(screen.getByText('服务端 API')).toBeInTheDocument();
expect(screen.getByText('安全代理')).toBeInTheDocument();
expect(screen.getByText('安全码暴露')).toBeInTheDocument();
expect(screen.getByText('协议接入')).toBeInTheDocument();
expect(screen.getByText('统一解析')).toBeInTheDocument();
expect(screen.getByText('实时投影')).toBeInTheDocument();
expect(screen.getByText('车辆归并')).toBeInTheDocument();
expect(screen.getAllByText('历史证据').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('运营闭环')).toBeInTheDocument();
expect(screen.getAllByText('已配置').length).toBeGreaterThanOrEqual(2);
expect(screen.getByText('已启用')).toBeInTheDocument();
expect(screen.getByText('未暴露')).toBeInTheDocument();
@@ -822,7 +816,6 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
expect(screen.getByText('告警事件与通知')).toBeInTheDocument();
expect(screen.getByText('区间闭合复核')).toBeInTheDocument();
expect(screen.getByText('高德轨迹证据')).toBeInTheDocument();
expect(screen.getAllByText('Kafka Lag 0').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('1,033 车辆口径')).toBeInTheDocument();
expect(screen.getAllByText('在线 208').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('在线态势 208').length).toBeGreaterThanOrEqual(1);
@@ -840,9 +833,6 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('高德地图Web JS 已配置;服务端 API 已配置;安全代理 已启用;安全码未暴露'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 实时监控'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA目标 0-1 秒内进入实时视图'));
fireEvent.click(screen.getByRole('button', { name: '复制流转图' }));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台数据流转图】'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('03. 实时投影 / Redis KV'));
fireEvent.click(screen.getByRole('button', { name: '客户工作流 实时车辆地图' }));
expect(window.location.hash).toBe('#/map?online=online');
@@ -867,14 +857,14 @@ test('dashboard exposes vehicle data center capability matrix', async () => {
cleanup();
await renderDashboard();
fireEvent.click(screen.getByRole('button', { name: '数据流转 实时投影' }));
expect(window.location.hash).toBe('#/realtime?online=online');
fireEvent.click(screen.getByRole('button', { name: '时间监控 历史时间窗' }));
expect(window.location.hash.startsWith('#/history-query')).toBe(true);
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
cleanup();
await renderDashboard();
fireEvent.click(screen.getByRole('button', { name: '数据流转 统一解析' }));
expect(window.location.hash.startsWith('#/history-query')).toBe(true);
expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
fireEvent.click(screen.getByRole('button', { name: '时间监控 区间里程' }));
expect(window.location.hash.startsWith('#/mileage')).toBe(true);
cleanup();
await renderDashboard();
@@ -1172,7 +1162,7 @@ test('dashboard shows vehicle service action queue', async () => {
test.each([
{ buttonName: '查看在线车辆', expectedHash: '#/vehicles?online=online' },
{ buttonName: '查看多源车辆', expectedHash: '#/vehicles?coverage=multi' },
{ buttonName: '实时地图', expectedHash: '#/map?online=online' },
{ buttonName: '查看告警事件', expectedHash: '#/alert-events' }
])('dashboard posture opens %s', async ({ buttonName, expectedHash }) => {
window.history.replaceState(null, '', '/#/dashboard');
@@ -1335,18 +1325,16 @@ test('dashboard exposes end-to-end operations workflow entries', async () => {
render(<App />);
expect(await screen.findByText('全链路值班闭环')).toBeInTheDocument();
expect(screen.getByText('接入巡检')).toBeInTheDocument();
expect(screen.getByText('实时态势')).toBeInTheDocument();
expect(screen.getByText('轨迹复盘')).toBeInTheDocument();
expect(screen.getAllByText('历史证据').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('告警事件').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('统计复核')).toBeInTheDocument();
expect(await screen.findByText('自定义时间监控')).toBeInTheDocument();
expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument();
expect(screen.getByText('历史时间窗')).toBeInTheDocument();
expect(screen.getByText('区间里程')).toBeInTheDocument();
expect(screen.getByText('告警复盘')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '闭环入口 轨迹复盘' }));
fireEvent.click(screen.getByRole('button', { name: '时间监控 今天车辆轨迹' }));
await waitFor(() => {
expect(window.location.hash).toBe('#/history?keyword=LB9A32A24R0LS1426');
expect(window.location.hash.startsWith('#/history')).toBe(true);
});
});
@@ -1459,7 +1447,7 @@ test('dashboard surfaces highest priority quality issue with evidence shortcuts'
render(<App />);
expect(await screen.findByText('重点车辆服务')).toBeInTheDocument();
expect(screen.getByText('把协议来源作为证据把实时、轨迹、RAW、统计和告警集中到同一辆车处理。')).toBeInTheDocument();
expect(screen.getByText('把数据来源作为证据把实时、轨迹、RAW、统计和告警集中到同一辆车处理。')).toBeInTheDocument();
expect(screen.getByText('实时证据')).toBeInTheDocument();
expect(screen.getAllByText('轨迹证据').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('告警证据')).toBeInTheDocument();
@@ -1611,7 +1599,6 @@ test('dashboard copies highest priority quality issue notification text', async
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆88 / 1,033'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃96'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日帧量1,286,320'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('Kafka Lag0'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最高告警P0 VIN 缺失 / 粤A告警1 / 13307795425 / 2026-07-03 20:12:10'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控http://localhost:3000/#/realtime?online=online'));
expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时地图http://localhost:3000/#/map?online=online'));
@@ -1695,11 +1682,11 @@ test('dashboard keeps core vehicle service metrics when preview requests fail',
expect(await screen.findByText('总车辆')).toBeInTheDocument();
expect(screen.getAllByText('1,033').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('单源车辆')).toBeInTheDocument();
expect(screen.getByText('391')).toBeInTheDocument();
expect(screen.getByText('今日活跃')).toBeInTheDocument();
expect(screen.getByText('告警车辆')).toBeInTheDocument();
});
test('opens dashboard coverage from missing source distribution', async () => {
test('opens dashboard coverage from service action queue', async () => {
window.history.replaceState(null, '', '/#/dashboard');
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
const path = String(input);
@@ -1755,14 +1742,11 @@ test('opens dashboard coverage from missing source distribution', async () => {
render(<App />);
expect(await screen.findByText('缺失车辆')).toBeInTheDocument();
expect(screen.getByText('983')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '查看缺 YUTONG_MQTT' }));
fireEvent.click(await screen.findByRole('button', { name: '补齐 YUTONG_MQTT 来源 983' }));
await waitFor(() => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
});
expect(screen.getByText('当前筛选:缺 YUTONG_MQTT')).toBeInTheDocument();
});
test('opens vehicle list filtered by service summary KPI', async () => {
@@ -1811,12 +1795,12 @@ test('opens vehicle list filtered by service summary KPI', async () => {
render(<App />);
fireEvent.click(await screen.findByRole('button', { name: /暂无来源车辆/ }));
fireEvent.click(await screen.findByRole('button', { name: /告警车辆/ }));
await waitFor(() => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&serviceStatus=no_data'), undefined);
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&serviceStatus=degraded'), undefined);
});
expect(window.location.hash).toBe('#/vehicles?serviceStatus=no_data');
expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded');
});
test('shows vehicle service result summary on vehicle list filters', async () => {
@@ -3362,7 +3346,10 @@ test('opens dashboard coverage from service status distribution', async () => {
render(<App />);
fireEvent.click(await screen.findByRole('button', { name: '查看来源不完整' }));
expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('dashboard-service-status-filter'));
fireEvent.click(await screen.findByText('来源不完整'));
fireEvent.click(screen.getByRole('button', { name: '筛选' }));
await waitFor(() => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);