feat(portal-shell): wire AppFrame + navigation + permission filter (P1-1)

实现 ARCHITECTURE.md §10 P1-1:
- 新增 src/shared/lib/navigation.ts:27 项静态导航注册表,
  按 teacher/student/parent/admin 四角色分区,group 字段用于角色过滤
- 新增 src/app/shell/layout.tsx:RSC AppFrame,从 headers() 读取
  middleware 注入的身份头,batchCheckRoutePermission 按位图二次过滤
- 新增 src/shared/components/layout/user-menu.tsx:顶部用户菜单,
  显示 userId + role,登出 POST /api/auth/logout
- 新增 src/shared/lib/__tests__/navigation.test.ts:P1-1 验收单测
  (7 用例:href 登记一致性 + 4 角色隔离 + 权限检查)
- 修改 src/shared/lib/route-permissions.ts:补全 7 个列表页根路由
  的 EXACT 登记(/shell/admin/announcements、/shell/admin/classes、
  /shell/teacher/exams 等),与 PREFIX 表互补避免 catch-all 拒绝

验收:
- vitest run navigation → 7/7 passed
- tsc --noEmit 通过;eslint(5 文件)通过

回填 ARCHITECTURE.md §10 P1-1 状态为 ,附验收证据。

关联:ARCHITECTURE.md §7.2 AppFrame / §10 P1-1
This commit is contained in:
SpecialX
2026-07-22 12:17:16 +08:00
parent dc13a2afb4
commit f92fdf8efe
6 changed files with 602 additions and 12 deletions

View File

@@ -2,7 +2,7 @@
> 版本3.0
> 日期2026-07-20
> 状态:**P0 已完成2026-07-22 验收)+ P1 进行中;架构审计完成 + 重设计方案定稿**
> 状态:**P0 已完成 + P1-1 已完成2026-07-22 验收)+ P1 进行中;架构审计完成 + 重设计方案定稿**
> 本文档地位:**portal-shell 前端工作的唯一权威指导文档**。所有后续 AI/人工在此模块的工作必须先读本文件,以其为准。
>
> 关联文档(按效力排序):
@@ -757,20 +757,34 @@ export default async function ExamsPage(): Promise<React.ReactElement> {
- **P0-7**`eslint .` → 0 errors, 2 warnings均在 `__generated__/types.ts` 生成文件);[eslint.config.js](file:///e:/Desktop/Edu/apps/portal-shell/eslint.config.js) 含 `no-restricted-imports`
- **P0-8**`git status` 干净commit `cfb7b00``.env.local``tsconfig.tsbuildinfo` 已在 [.gitignore](file:///e:/Desktop/Edu/apps/portal-shell/.gitignore)
### P1 · 框架与数据源接通12 周)
### P1 · 框架与数据源接通12 周)— 进行中P1-1 ✅ 2026-07-22 验收)
**目标**AppFrame + 导航 + 真实数据仪表盘 + 页面模板 + MSW + i18n页面迁移的"流水线"建成。
| # | 任务 | 验收 |
| ---- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| P1-1 | `src/app/shell/layout.tsx` AppFrameTopBar 静态挂载 + Sidebar 导航菜单 `navigation.ts` + 权限过滤) | 4 角色各见各菜单;导航项与 route-permissions 表一致(单测) |
| P1-2 | 仪表盘改接真实查询:`teacherDashboard`/`studentDashboard`/`parentDashboard`/`adminDashboard` + `warnings`/`errorBookStats` | 起全栈4 角色仪表盘显示真实聚合数据截图widget 假契约查询grades/homeworks/schedule/attendance/exams/announcements全部下线或改造 |
| P1-3 | 页面模板四件套list/detail/form/workbench+ 三态规范 | Storybook 或示例页 4 张(/shell/_dev/templates/*,仅 dev 可见) |
| P1-4 | next-intl 接入 + messages 合并迁移 | 切换 locale 页面文案切换(截图);`useT` 自造函数删除 |
| P1-5 | MSW 兜底层(迁移旧 handlers覆盖 dashboard/users/exams/grades 四域起步) | `NEXT_PUBLIC_MSW=1` 无后端启动,仪表盘 + users 页有数据(截图);生产构建 bundle 无 mocks |
| P1-6 | 31 widget 令牌清债271 处机械替换)+ `border border` 去重 + 未知类检测进 arch:scan | `grep -c "text-heading-\|mt-sm\|py-xs\|p-md" src/widgets` = 0`pnpm lint:tokens` 通过 |
| P1-7 | codegen 恢复 typescript-operationsconfig + data-ana 两域先行关闭 skipDocumentsValidation | 生成操作级类型lib/api 对应域删除手写 interfacetypecheck 通过 |
| P1-8 | CI 增补:路由表一致性脚本 + 页面计数 + codegen diff 检查 | CI 对预埋违规报红(附 pipeline 链接) |
| # | 任务 | 验收 | 状态 |
| ---- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| P1-1 | `src/app/shell/layout.tsx` AppFrameTopBar 静态挂载 + Sidebar 导航菜单 `navigation.ts` + 权限过滤) | 4 角色各见各菜单;导航项与 route-permissions 表一致(单测) | ✅ |
| P1-2 | 仪表盘改接真实查询:`teacherDashboard`/`studentDashboard`/`parentDashboard`/`adminDashboard` + `warnings`/`errorBookStats` | 起全栈4 角色仪表盘显示真实聚合数据截图widget 假契约查询grades/homeworks/schedule/attendance/exams/announcements全部下线或改造 | ⏳ |
| P1-3 | 页面模板四件套list/detail/form/workbench+ 三态规范 | Storybook 或示例页 4 张(/shell/_dev/templates/*,仅 dev 可见) | ⏳ |
| P1-4 | next-intl 接入 + messages 合并迁移 | 切换 locale 页面文案切换(截图);`useT` 自造函数删除 | ⏳ |
| P1-5 | MSW 兜底层(迁移旧 handlers覆盖 dashboard/users/exams/grades 四域起步) | `NEXT_PUBLIC_MSW=1` 无后端启动,仪表盘 + users 页有数据(截图);生产构建 bundle 无 mocks | ⏳ |
| P1-6 | 31 widget 令牌清债271 处机械替换)+ `border border` 去重 + 未知类检测进 arch:scan | `grep -c "text-heading-\|mt-sm\|py-xs\|p-md" src/widgets` = 0`pnpm lint:tokens` 通过 | ⏳ |
| P1-7 | codegen 恢复 typescript-operationsconfig + data-ana 两域先行关闭 skipDocumentsValidation | 生成操作级类型lib/api 对应域删除手写 interfacetypecheck 通过 | ⏳ |
| P1-8 | CI 增补:路由表一致性脚本 + 页面计数 + codegen diff 检查 | CI 对预埋违规报红(附 pipeline 链接) | ⏳ |
**P1-1 验收证据2026-07-22**
- **单测**`vitest run --reporter=verbose navigation` → 7/7 passed
- 每个导航项 href 在 EXACT/PREFIX/DASHBOARD 表登记
- teacher/student/parent/admin 各仅见本组导航项
- 4 角色导航项两两不交叉(角色隔离)
- admin 无权限位图时 `checkRoutePermission` 拒绝
- **实现文件**
- [src/shared/lib/navigation.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/shared/lib/navigation.ts)27 项扁平数组,按 4 角色分区,`group` 字段用于角色过滤
- [src/app/shell/layout.tsx](file:///e:/Desktop/Edu/apps/portal-shell/src/app/shell/layout.tsx)RSC AppFrame`headers()` 读取身份头,`batchCheckRoutePermission` 按位图二次过滤导航项
- [src/shared/components/layout/user-menu.tsx](file:///e:/Desktop/Edu/apps/portal-shell/src/shared/components/layout/user-menu.tsx):顶部用户菜单,显示 userId + role登出 POST `/api/auth/logout`
- [src/shared/lib/route-permissions.ts](file:///e:/Desktop/Edu/apps/portal-shell/src/shared/lib/route-permissions.ts):补全 7 个列表页根路由的 EXACT 登记(`/shell/admin/announcements``/shell/admin/classes``/shell/teacher/exams` 等),与 PREFIX 表互补
- **质量校验**`tsc --noEmit` 通过;`eslint`5 文件)通过
### P2 · 教师域页面23 周,可与 P3 部分并行)