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, RocketOutlined, HeartOutlined, SafetyCertificateOutlined as DegreeOutlined, CheckCircleOutlined, ExperimentOutlined, FundProjectionScreenOutlined, BankOutlined, ApartmentOutlined, TeamOutlined, CarryOutOutlined, SettingOutlined, } 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: '通知中心' }, { key: '/courses', 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: '/academic-planning', icon: , label: '学业规划' }, { key: '/physical-fitness', icon: , label: '体质健康' }, { key: '/degree-awards', icon: , label: '学位授予' }, { key: '/graduation-clearance', icon: , label: '毕业离校' }, { key: '/experiments', icon: , label: '我的实验' }, { key: '/experiment-grades', icon: , label: '实验成绩' }, { key: '/campus-registrations', icon: , label: '校园报名' }, { key: '/approvals', icon: , label: '审批申请' }, { key: '/warnings', icon: , label: '学业预警' }, ] : []), ...(roles.includes('Teacher') ? [ { key: '/teacher-roster', icon: , label: '选课名单' }, ] : []), ...(roles.some((role) => ['Teacher', 'SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'].includes(role)) ? [ { key: '/teacher-attendance', icon: , label: '教学点名' }, ] : []), ...(roles.some((role) => ['Teacher', 'SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)) ? [ { key: '/teaching-preferences', icon: , label: roles.includes('Teacher') && !roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)) ? '授课科目申报' : '授课资格审核' }, ] : []), ...(roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin'].includes(role)) ? [ { key: '/course-equivalency-rules', icon: , label: '等价课程规则' }, { key: '/course-enrollments', icon: , label: '修读课程管理' }, { key: '/campus-wallets', icon: , label: '学生余额管理' }, ] : []), ...(roles.some((role) => ['SuperAdmin', 'AcademicAdmin'].includes(role)) ? [ { key: '/base-data/organization', icon: , label: '组织机构' }, { key: '/base-data/terms', icon: , label: '学年学期' }, { key: '/base-data/facilities', icon: , label: '教学场所' }, { key: '/base-data/course-categories', 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) }} />
) }