import { useCallback, useEffect, useState } from 'react'; import { Toast } from '@douyinfe/semi-ui'; import { api } from './api/client'; import type { VehicleServiceStatus } from './api/types'; import { buildAppHash, parseAppHash } from './domain/appRoute'; import { AppShell, type PageKey } from './layout/AppShell'; import { Dashboard } from './pages/Dashboard'; import { History } from './pages/History'; import { Mileage } from './pages/Mileage'; import { Quality } from './pages/Quality'; import { Realtime } from './pages/Realtime'; import { VehicleDetail } from './pages/VehicleDetail'; import { Vehicles } from './pages/Vehicles'; export default function App() { const initialRoute = parseAppHash(window.location.hash); const initialVehicleKey = initialRoute.keyword || 'LB9A32A24R0LS1426'; const [activePage, setActivePage] = useState(initialRoute.page ?? 'dashboard'); const [activeVin, setActiveVin] = useState(initialVehicleKey); const [analysisVin, setAnalysisVin] = useState(initialVehicleKey); const [activeProtocol, setActiveProtocol] = useState(initialRoute.protocol ?? ''); const [linkIssueCount, setLinkIssueCount] = useState(null); const [currentVehicleStatus, setCurrentVehicleStatus] = useState(); const [currentVehicleLabel, setCurrentVehicleLabel] = useState(''); const refreshOpsHealth = useCallback((showError = true) => { return api.opsHealth() .then((health) => { setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length); return health; }) .catch((error: Error) => { if (showError) { Toast.error(error.message); } }); }, []); useEffect(() => { refreshOpsHealth(); const timer = window.setInterval(() => { refreshOpsHealth(false); }, 60000); return () => window.clearInterval(timer); }, [refreshOpsHealth]); useEffect(() => { const applyHashRoute = () => { const route = parseAppHash(window.location.hash); if (!route.page) { return; } setActivePage(route.page); if (route.keyword) { if (route.page === 'detail') { setActiveVin(route.keyword); } if (route.page === 'history' || route.page === 'mileage') { setAnalysisVin(route.keyword); } } setActiveProtocol(route.protocol ?? ''); }; window.addEventListener('hashchange', applyHashRoute); return () => window.removeEventListener('hashchange', applyHashRoute); }, []); const replaceHash = (page: PageKey, keyword?: string, protocol?: string) => { const nextHash = buildAppHash({ page, keyword, protocol }); if (window.location.hash !== nextHash) { window.history.replaceState(null, '', nextHash); } }; const navigatePage = (page: PageKey) => { setActivePage(page); if (page === 'detail') { replaceHash(page, activeVin, activeProtocol); return; } if (page === 'history' || page === 'mileage') { replaceHash(page, analysisVin, activeProtocol); return; } replaceHash(page); }; const openVehicle = async (keyword: string, protocol?: string) => { const lookupKey = keyword.trim(); const nextProtocol = protocol?.trim() ?? ''; if (!lookupKey) { return; } try { const resolved = await api.vehicleResolve(new URLSearchParams({ keyword: lookupKey })); const nextKey = resolved.resolved && resolved.vin ? resolved.vin : lookupKey; setCurrentVehicleStatus(resolved.serviceStatus); setCurrentVehicleLabel(resolved.resolved ? [resolved.plate, resolved.vin || nextKey].filter(Boolean).join(' / ') : lookupKey); setActiveVin(nextKey); setActiveProtocol(nextProtocol); setActivePage('detail'); replaceHash('detail', nextKey, nextProtocol); if (!resolved.resolved) { Toast.warning('未匹配到车辆身份,已打开问题排查视图'); } } catch (error) { Toast.error(error instanceof Error ? error.message : '车辆查询失败'); } }; const openHistoryForVehicle = (vin: string, protocol?: string) => { const nextVin = vin.trim(); const nextProtocol = protocol?.trim() ?? activeProtocol; if (!nextVin) { return; } setAnalysisVin(nextVin); setActiveProtocol(nextProtocol); setActivePage('history'); replaceHash('history', nextVin, nextProtocol); }; const openMileageForVehicle = (vin: string, protocol?: string) => { const nextVin = vin.trim(); const nextProtocol = protocol?.trim() ?? activeProtocol; if (!nextVin) { return; } setAnalysisVin(nextVin); setActiveProtocol(nextProtocol); setActivePage('mileage'); replaceHash('mileage', nextVin, nextProtocol); }; const updateVehicleDetailQuery = (keyword: string, protocol?: string) => { const nextKeyword = keyword.trim(); const nextProtocol = protocol?.trim() ?? ''; if (!nextKeyword) { return; } setActiveVin(nextKeyword); setActiveProtocol(nextProtocol); replaceHash('detail', nextKeyword, nextProtocol); }; const pages: Record = { dashboard: navigatePage('quality')} />, vehicles: , realtime: , detail: , history: , mileage: , quality: setLinkIssueCount(health.linkHealth.filter((item) => item.status !== 'ok').length)} /> }; return ( {pages[activePage]} ); }