import { useState, useEffect, lazy, Suspense } from 'react' import { Routes, Route, useNavigate, useLocation } from 'react-router-dom' import { LoadingBar } from './components/ui' const Dashboard = lazy(() => import('./pages/Dashboard').then(m => ({ default: m.Dashboard }))) const WorkList = lazy(() => import('./pages/WorkList').then(m => ({ default: m.WorkList }))) const ReviewCenter = lazy(() => import('./pages/ReviewCenter').then(m => ({ default: m.ReviewCenter }))) const PeoplePage = lazy(() => import('./pages/People').then(m => ({ default: m.PeoplePage }))) const StationsPage = lazy(() => import('./pages/Stations').then(m => ({ default: m.StationsPage }))) const ArchivePage = lazy(() => import('./pages/Archive').then(m => ({ default: m.ArchivePage }))) const NoticesPage = lazy(() => import('./pages/Notices').then(m => ({ default: m.NoticesPage }))) const SettingsPage = lazy(() => import('./pages/Settings').then(m => ({ default: m.SettingsPage }))) const RulesPage = lazy(() => import('./pages/Rules').then(m => ({ default: m.RulesPage }))) const ScoresPage = lazy(() => import('./pages/Scores').then(m => ({ default: m.ScoresPage }))) const LeaderboardPage = lazy(() => import('./pages/Leaderboard').then(m => ({ default: m.LeaderboardPage }))) const AppealsPage = lazy(() => import('./pages/Appeals').then(m => ({ default: m.AppealsPage }))) const SystemLogsPage = lazy(() => import('./pages/SystemLogs').then(m => ({ default: m.SystemLogsPage }))) const StationMapPage = lazy(() => import('./pages/StationMap').then(m => ({ default: m.StationMapPage }))) const CockpitPage = lazy(() => import('./pages/Cockpit').then(m => ({ default: m.CockpitPage }))) const ProfilePage = lazy(() => import('./pages/Profile').then(m => ({ default: m.ProfilePage }))) const LoginPage = lazy(() => import('./pages/Login').then(m => ({ default: m.LoginPage }))) import { Sidebar, TopBar } from './components/layout' const CreateRecordModal = lazy(() => import('./modals/CreateRecordModal').then(m => ({ default: m.CreateRecordModal }))) const RecordDrawer = lazy(() => import('./modals/RecordDrawer').then(m => ({ default: m.RecordDrawer }))) import { RoleProvider, ToastProvider, useRole, useToast } from './context' import { api } from './api' import { navLabels, pathMap, pathToPage } from './routes' import type { WorkRecord } from './types' import type { PageKey } from './routes' function AppShell() { const { role, isAuthenticated } = useRole() const navigate = useNavigate() const location = useLocation() const { showToast: showToastCtx } = useToast() const [sidebarOpen, setSidebarOpen] = useState(false) const [createOpen, setCreateOpen] = useState(false) const [selected, setSelected] = useState(null) const [records, setRecords] = useState([]) const [loading, setLoading] = useState(true) const [loadError, setLoadError] = useState('') const [toast, setToast] = useState('') const page: PageKey = pathToPage(location.pathname) useEffect(() => { let active = true setLoading(true) setLoadError('') api.records(role) .then(data => { if (active) setRecords(data) }) .catch(err => { if (active) setLoadError(err.message) }) .finally(() => { if (active) setLoading(false) }) return () => { active = false } }, [role]) // 角色受限页面自动跳转 useEffect(() => { const restricted: Record = { reporter: ['review', 'people', 'stations', 'stationmap', 'cockpit'], station: ['stations', 'stationmap', 'cockpit'], } if (restricted[role]?.includes(page)) { navigate('/') } }, [role, page, navigate]) const navigateTo = (p: PageKey) => { navigate(pathMap[p]) setSidebarOpen(false) } const pendingCount = records.filter(r => role === 'station' ? r.status === 'station_review' : r.status === 'headquarters_review' ).length const handleCreate = async (record: WorkRecord) => { setRecords(items => [record, ...items]) setCreateOpen(false) showToast('工作记录已提交,进入分站审核') } const handleUpdate = async (id: string, patch: Partial) => { try { const updated = await api.reviewRecord(role, id, { decision: patch.status === 'returned' ? 'return' : 'pass', score: patch.score ?? 0, note: patch.reviewNote ?? '', }) setRecords(items => items.map(item => item.id === id ? updated : item)) setSelected(null) return true } catch (error) { showToast(error instanceof Error ? error.message : '审核操作失败') return false } } const showToast = (msg: string) => { setToast(msg) showToastCtx?.(msg) setTimeout(() => setToast(''), 2400) } // 未认证时显示登录页 if (!isAuthenticated && !localStorage.getItem('auth_token')) { // 演示模式:允许直接进入 const isDemoMode = !localStorage.getItem('auth_token') && localStorage.getItem('auth_role') if (!isDemoMode) { return ( }> ) } } return (
setSidebarOpen(false)} badge={pendingCount} />
setSidebarOpen(true)} />
{loadError && (
{loadError}
)} {loading && } }> setCreateOpen(true)} onSelect={setSelected} /> } /> setCreateOpen(true)} onSelect={setSelected} /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } />
{createOpen && ( setCreateOpen(false)} onSubmit={handleCreate} /> )} {selected && ( setSelected(null)} onUpdate={handleUpdate} onNotify={showToast} /> )} {toast &&
{toast}
} {sidebarOpen && (
setSidebarOpen(false)} /> )}
) } export default function App() { return ( ) }