import { useEffect, useMemo, useState } from 'react' import { AppstoreOutlined, BellOutlined, ExportOutlined, IdcardOutlined, MenuFoldOutlined, MenuUnfoldOutlined, SafetyCertificateOutlined, CalendarOutlined, FileDoneOutlined, ReadOutlined, WarningOutlined, AuditOutlined, CheckSquareOutlined, FormOutlined, BookOutlined, LineChartOutlined, EnvironmentOutlined, SwapOutlined, FileProtectOutlined, SafetyOutlined, WalletOutlined, ScheduleOutlined, TrophyOutlined, SolutionOutlined, CommentOutlined, } from '@ant-design/icons' import { Avatar, Badge, Button, Layout, Menu, Tag } from 'antd' import { useQuery } from '@tanstack/react-query' import { Outlet, useLocation, useNavigate } from 'react-router-dom' import http from '../api/http' import { branding } from '../config/branding' import { useAuthStore } from '../stores/auth' import BrandMark from '../components/BrandMark' const { Header, Sider, Content } = Layout export default function AppShell() { const location = useLocation() const navigate = useNavigate() const user = useAuthStore((state) => state.user) const logout = useAuthStore((state) => state.logout) const refreshUser = useAuthStore((state) => state.refreshUser) const [collapsed, setCollapsed] = useState(false) const roles = user?.roles ?? [] const isStudentHome = roles.includes('Student') && !roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'].includes(role)) const isTeacherHome = roles.includes('Teacher') && !roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student'].includes(role)) const unreadQuery = useQuery({ queryKey: ['notification-unread-count'], queryFn: async () => (await http.get<{ count: number }>('/notifications/unread-count')).data.count, refetchInterval: 60_000, }) useEffect(() => { void refreshUser().catch(() => undefined) }, [refreshUser]) const items = useMemo(() => [ { key: '/dashboard', icon: , label: isStudentHome ? '学习首页' : isTeacherHome ? '教学工作台' : '教务总览' }, { key: '/account', icon: , label: '个人账户' }, { key: '/notifications', icon: , label: '通知中心' }, ...((roles.includes('Student') || roles.includes('Teacher')) ? [{ key: '/my-timetable', icon: , label: '我的课表' }] : []), ...((roles.includes('Student') || roles.includes('Teacher')) ? [{ key: '/exams', icon: , label: roles.includes('Teacher') ? '监考安排' : '考试安排' }] : []), ...(isStudentHome ? [ { key: '/my-profile', icon: , label: '个人档案' }, { key: '/grades', icon: , label: '我的成绩' }, { key: '/my-attendance', icon: , label: '我的考勤' }, { key: '/course-selections', icon: , label: '学生选课' }, { key: '/my-curriculum', icon: , label: '培养方案' }, { key: '/academic-overview', icon: , label: '学业总览' }, { key: '/free-classrooms', icon: , label: '空闲教室' }, { key: '/academic-calendar', icon: , label: '学术校历' }, { key: '/student-status-changes', icon: , label: '学籍异动' }, { key: '/official-documents', icon: , label: '电子凭证' }, { key: '/graduation-audits', icon: , label: '毕业审核' }, { key: '/tuition', icon: , label: '我的学费' }, { key: '/other-exams', icon: , label: '其他考试' }, { key: '/makeup-exams', icon: , label: '补考安排' }, { key: '/evaluations', icon: , label: '教学评价' }, { key: '/warnings', icon: , label: '学业预警' }, ] : []), ...(user?.roles.includes('SuperAdmin') ? [{ key: '/users', icon: , label: '用户与权限' }] : []), { key: 'legacy', icon: , label: '返回现有系统' }, ], [isStudentHome, isTeacherHome, user?.roles]) const pageTitle = items.find((item) => location.pathname.startsWith(item.key))?.label ?? '迁移预览' function signOut() { logout() navigate('/login', { replace: true }) } return (
{!collapsed &&
{branding.shortName}REACT MIGRATION
}
item.key.startsWith('/') && location.pathname.startsWith(item.key))?.key ?? '/dashboard']} items={items} onClick={({ key }) => { if (key === 'legacy') { window.location.assign(import.meta.env.VITE_LEGACY_APP_URL ?? 'http://localhost:5255') return } navigate(key) }} />
) }