feat(web): add role-aware React dashboards
Add student and teacher dashboard variants backed by existing APIs, retain the administrative overview for management roles, and provide responsive navigation back to legacy routes that have not migrated yet.
This commit is contained in:
1 parent
93ab6a7b9e
commit
850eb6b05c
7 files changed
+269
-5
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