diff --git a/vehicle-data-platform/apps/web/src/App.tsx b/vehicle-data-platform/apps/web/src/App.tsx index 07013f1b..f841ecc9 100644 --- a/vehicle-data-platform/apps/web/src/App.tsx +++ b/vehicle-data-platform/apps/web/src/App.tsx @@ -26,7 +26,7 @@ export default function App() { initialRoute.page === 'vehicles' ? vehicleFiltersFromRoute(initialRoute) : {} ); const [realtimeFilters, setRealtimeFilters] = useState>( - initialRoute.page === 'realtime' ? realtimeFiltersFromRoute(initialRoute) : {} + initialRoute.page === 'realtime' || initialRoute.page === 'map' ? realtimeFiltersFromRoute(initialRoute) : {} ); const [historyFilters, setHistoryFilters] = useState>( initialRoute.page === 'history' ? historyFiltersFromRoute(initialRoute) : {} @@ -111,7 +111,7 @@ export default function App() { if (route.page === 'vehicles') { setVehicleFilters(vehicleFiltersFromRoute(route)); } - if (route.page === 'realtime') { + if (route.page === 'realtime' || route.page === 'map') { setRealtimeFilters(realtimeFiltersFromRoute(route)); } if (route.page === 'history') { @@ -167,6 +167,10 @@ export default function App() { replaceRealtimeHash(realtimeFilters); return; } + if (page === 'map') { + replaceMapHash(realtimeFilters); + return; + } replaceHash(page); }; @@ -191,11 +195,21 @@ export default function App() { replaceRealtimeHash(filters); }; + const updateMapFilters = (filters: Record = {}) => { + setRealtimeFilters(filters); + replaceMapHash(filters); + }; + const replaceRealtimeHash = (filters: Record = {}) => { const { keyword, protocol, ...restFilters } = filters; replaceHash('realtime', keyword, protocol, restFilters); }; + const replaceMapHash = (filters: Record = {}) => { + const { keyword, protocol, ...restFilters } = filters; + replaceHash('map', keyword, protocol, restFilters); + }; + const updateHistoryFilters = (filters: Record = {}, tab = historyTab) => { const hasKeywordInput = Object.prototype.hasOwnProperty.call(filters, 'keyword'); const nextFilters = normalizeHistoryFilterValues(filters); @@ -408,6 +422,7 @@ export default function App() { const pages: Record = { dashboard: , vehicles: , + map: , realtime: , detail: , history: , diff --git a/vehicle-data-platform/apps/web/src/domain/appRoute.test.ts b/vehicle-data-platform/apps/web/src/domain/appRoute.test.ts index 891b18e7..b6e334fa 100644 --- a/vehicle-data-platform/apps/web/src/domain/appRoute.test.ts +++ b/vehicle-data-platform/apps/web/src/domain/appRoute.test.ts @@ -55,6 +55,17 @@ describe('parseAppHash', () => { }); }); + test('parses map situation page', () => { + expect(parseAppHash('#/map?keyword=VIN001&protocol=GB32960&online=online')).toEqual({ + page: 'map', + keyword: 'VIN001', + protocol: 'GB32960', + filters: { + online: 'online' + } + }); + }); + test('parses ops quality page', () => { expect(parseAppHash('#/ops-quality')).toEqual({ page: 'ops-quality', @@ -88,6 +99,10 @@ describe('buildAppHash', () => { expect(buildAppHash({ page: 'notification-rules' })).toBe('#/notification-rules'); }); + test('builds map situation page hash', () => { + expect(buildAppHash({ page: 'map', keyword: 'VIN001', protocol: 'GB32960', filters: { online: 'online' } })).toBe('#/map?keyword=VIN001&protocol=GB32960&online=online'); + }); + test('builds ops quality page hash', () => { expect(buildAppHash({ page: 'ops-quality' })).toBe('#/ops-quality'); }); diff --git a/vehicle-data-platform/apps/web/src/domain/appRoute.ts b/vehicle-data-platform/apps/web/src/domain/appRoute.ts index 3181c0fe..f4128cab 100644 --- a/vehicle-data-platform/apps/web/src/domain/appRoute.ts +++ b/vehicle-data-platform/apps/web/src/domain/appRoute.ts @@ -1,6 +1,6 @@ import type { PageKey } from '../layout/AppShell'; -const pageKeys = new Set(['dashboard', 'vehicles', 'realtime', 'detail', 'history', 'mileage', 'quality', 'notification-rules', 'ops-quality']); +const pageKeys = new Set(['dashboard', 'vehicles', 'map', 'realtime', 'detail', 'history', 'mileage', 'quality', 'notification-rules', 'ops-quality']); export type AppRoute = { page?: PageKey; diff --git a/vehicle-data-platform/apps/web/src/layout/AppShell.tsx b/vehicle-data-platform/apps/web/src/layout/AppShell.tsx index ab3c286f..92aaf977 100644 --- a/vehicle-data-platform/apps/web/src/layout/AppShell.tsx +++ b/vehicle-data-platform/apps/web/src/layout/AppShell.tsx @@ -16,11 +16,12 @@ import { useState } from 'react'; import type { VehicleSourceConsistency, VehicleServiceStatus } from '../api/types'; import { isAMapConfigured } from '../config/appConfig'; -export type PageKey = 'dashboard' | 'vehicles' | 'realtime' | 'detail' | 'history' | 'mileage' | 'quality' | 'notification-rules' | 'ops-quality'; +export type PageKey = 'dashboard' | 'vehicles' | 'map' | 'realtime' | 'detail' | 'history' | 'mileage' | 'quality' | 'notification-rules' | 'ops-quality'; const navItems = [ { itemKey: 'dashboard', text: '运营驾驶舱', icon: }, { itemKey: 'vehicles', text: '车辆中心', icon: }, + { itemKey: 'map', text: '地图态势', icon: }, { itemKey: 'realtime', text: '实时监控', icon: }, { itemKey: 'detail', text: '车辆档案', icon: }, { itemKey: 'history', text: '轨迹回放', icon: }, @@ -116,6 +117,7 @@ export function AppShell({ {platformRelease ? 版本 {platformRelease} : null} 多源接入 / 一个车辆服务 {amapConfigured ? '地图就绪' : '地图待配置'} + diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 9f308b32..bead242d 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -178,12 +178,16 @@ async function copyText(value: string, label: string) { } export function Realtime({ + title = '实时监控', + description = '以车辆为主对象查看最新位置、在线来源、核心实时数据和地图作业状态', onOpenVehicle, onOpenHistory, onOpenQuality, onFiltersChange, initialFilters = {} }: { + title?: string; + description?: string; onOpenVehicle: (vin: string, protocol?: string) => void; onOpenHistory?: (vin: string, protocol?: string) => void; onOpenQuality?: (filters: Record) => void; @@ -334,7 +338,7 @@ export function Realtime({ return (
- +
{ const nextFilters = values as Record; 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 d9899822..5376533b 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -80,6 +80,10 @@ test('exposes AMap operations shortcuts when map key is configured', async () => expect(await screen.findByText('地图就绪')).toBeInTheDocument(); expect(screen.getByText('版本 platform-20260704153357')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '顶部地图态势' })); + expect(window.location.hash).toBe('#/map'); + expect(await screen.findByRole('heading', { name: '地图态势' })).toBeInTheDocument(); + expect(screen.getByText('地图车辆服务队列')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '顶部实时监控' })); expect(window.location.hash).toBe('#/realtime'); fireEvent.click(screen.getByRole('button', { name: '顶部轨迹回放' }));