WIP: Switch to React #6
No files matched your search
@@ -5,7 +5,7 @@
|
||||
| 路由 | 页面 | 角色/公开范围 | 关键合同 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/login` | 登录 | 公开 | 验证码、密码、TOTP、SSO 设置、会话落盘 | building |
|
||||
| `/dashboard` | 教务总览 | 已登录用户 | `GET /api/dashboard`、角色视图、待办和学期 | building |
|
||||
| `/dashboard` | 角色化首页 | 已登录用户 | 管理总览;学生课表/消息/考试/预警;教师教学班/成绩登记进度 | verification |
|
||||
| `/account` | 个人账户 | 已登录用户 | TOTP、恢复码、SSO 绑定;Passkey 浏览器仪式尚未开放 | verification |
|
||||
| `/users` | 用户与权限 | SuperAdmin | 列表、筛选、分页、创建、启停、重置密码、角色范围、Excel | verification |
|
||||
|
||||
|
||||
@@ -23,17 +23,21 @@ export default function AppShell() {
|
||||
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 legacyBase = (import.meta.env.VITE_LEGACY_APP_URL ?? 'http://localhost:5255').replace(/\/$/, '')
|
||||
|
||||
useEffect(() => { void refreshUser().catch(() => undefined) }, [refreshUser])
|
||||
|
||||
const items = useMemo(() => [
|
||||
{ key: '/dashboard', icon: <AppstoreOutlined />, label: '教务总览' },
|
||||
{ key: '/dashboard', icon: <AppstoreOutlined />, label: isStudentHome ? '学习首页' : isTeacherHome ? '教学工作台' : '教务总览' },
|
||||
{ key: '/account', icon: <IdcardOutlined />, label: '个人账户' },
|
||||
...(user?.roles.includes('SuperAdmin')
|
||||
? [{ key: '/users', icon: <SafetyCertificateOutlined />, label: '用户与权限' }]
|
||||
: []),
|
||||
{ key: 'legacy', icon: <ExportOutlined />, label: '返回现有系统' },
|
||||
], [user?.roles])
|
||||
], [isStudentHome, isTeacherHome, user?.roles])
|
||||
|
||||
const pageTitle = items.find((item) => location.pathname.startsWith(item.key))?.label ?? '迁移预览'
|
||||
|
||||
@@ -83,7 +87,7 @@ export default function AppShell() {
|
||||
<div className="shell-context"><span>当前工作区</span><strong>{pageTitle}</strong></div>
|
||||
</div>
|
||||
<div className="shell-user">
|
||||
<Button type="text" aria-label="通知中心" icon={<BellOutlined />} />
|
||||
<Button type="text" aria-label="通知中心" icon={<BellOutlined />} onClick={() => window.location.assign(`${legacyBase}/notifications`)} />
|
||||
<Tag color="geekblue">预览</Tag>
|
||||
<div className="shell-user__copy"><strong>{user?.displayName ?? '用户'}</strong><small>{user?.roles.join(' · ') || '正在读取权限'}</small></div>
|
||||
<Avatar style={{ background: '#54cfbd', color: '#163f43' }}>{user?.displayName?.slice(0, 1) ?? '明'}</Avatar>
|
||||
|
||||
@@ -2,6 +2,10 @@ import { Alert, Button, Progress, Skeleton } from 'antd'
|
||||
import { ArrowRightOutlined, ReloadOutlined } from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import http from '../api/http'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import StudentDashboardPage from './dashboard/StudentDashboardPage'
|
||||
import TeacherDashboardPage from './dashboard/TeacherDashboardPage'
|
||||
import './dashboard/role-dashboard.css'
|
||||
|
||||
interface DashboardData {
|
||||
audience: {
|
||||
@@ -49,7 +53,7 @@ function formatDate(value?: string) {
|
||||
return new Intl.DateTimeFormat('zh-CN', { month: 'long', day: 'numeric' }).format(date)
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
function AdminDashboardPage() {
|
||||
const query = useQuery({
|
||||
queryKey: ['dashboard'],
|
||||
queryFn: async () => (await http.get<DashboardData>('/dashboard')).data,
|
||||
@@ -131,3 +135,15 @@ export default function DashboardPage() {
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const roles = useAuthStore((state) => state.user?.roles ?? [])
|
||||
const isStudent = roles.includes('Student') &&
|
||||
!roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'].includes(role))
|
||||
const isTeacher = roles.includes('Teacher') &&
|
||||
!roles.some((role) => ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Student'].includes(role))
|
||||
|
||||
if (isStudent) return <StudentDashboardPage />
|
||||
if (isTeacher) return <TeacherDashboardPage />
|
||||
return <AdminDashboardPage />
|
||||
}
|
||||
Loaded 3 of 7 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user