pmc 15e725a32f docs(02-01): 完成 Phase 2 Plan 02-01(RBAC 扩展 + /ai-model 凭据槽位入口)
- 新增 .planning/phases/02-rbac-ai/02-01-SUMMARY.md(含 frontmatter / 任务详情 / 验证结果 / Self-Check PASSED)
- STATE.md:进度 50% → 75%,已完成 plan 2 → 3,Phase 2 状态切到 In progress(1/2 plan)
- ROADMAP.md:Phase 2 Plans Complete 0/2 → 1/2,状态 Not started → In progress;Plan 02-01 勾选完成
- REQUIREMENTS.md:CRED-FE-02 + CRED-FE-03 状态切到  Done,Active 段两条 unchecked → checked
2026-05-08 11:47:59 +08:00

140 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
phase: 02-rbac-ai
plan: 01
subsystem: rbac + ai-model-page
tags: [rbac, permissions, ai-model, dialog, client-component]
requires:
- lib/api/credential-slot.ts:CredentialSlotPhase 1 已交付,本 plan 暂未直接消费,留给 Phase 3
- components/ui/dialog.tsx:Dialog/DialogContent/DialogHeader/DialogTitle/DialogDescription
- components/ui/button.tsx:Button(variant="outline")
- components/dashboard-header.tsx:DashboardHeaderchildren 单 slot
- components/sidebar.tsx:mounted 守卫模式line 83-104
- lucide-react:KeyRound
provides:
- PermissionModule literal "credential-slot"lib/permissions.ts
- PERMISSION_MATRIX 中超级管理员/AI模型管理员可访问 credential-slot
- /ai-model 页面入口 Button受 hasPermission 收敛)
- 占位 Dialog 挂载点Phase 3 落实表单内容)
affects:
- lib/permissions.ts
- app/ai-model/page.tsx
tech-stack:
added: []
patterns:
- mounted 守卫(避免 SSR 水合警告,复用 components/sidebar.tsx 同模式)
- hasPermission(...) && JSX 收敛DOM 中完全不存在,非仅 hidden
- Dialog controlled modeopen + onOpenChange + useState 配对)
key-files:
created: []
modified:
- lib/permissions.ts
- app/ai-model/page.tsx
decisions:
- 凭据槽位入口 Button 与原「添加新模型」Button 用 div.flex.items-center.gap-2 包裹后作为 DashboardHeader children 单节点传入DashboardHeader 本身只渲染单 children slot无 gap
- 入口 Button variant="outline"(与现有页面其他次要按钮如「查看详情」「试听示例」视觉一致)
- 图标用 KeyRoundlucide-react 0.454.0 锁定版本支持,凭据语义最贴切)
- 占位 Dialog 内联在 page.tsx不抽离单独组件Phase 3 才抽到 components/ai-model/CredentialSlotDialog.tsx
- mounted 守卫复用 components/sidebar.tsx:83-104 的 useState(false) + useEffect(setMounted(true)) 模式
metrics:
duration: ~6min
completed: 2026-05-08
tasks: 2
files_changed: 2
lines_added: 53
lines_removed: 6
---
# Phase 2 Plan 01扩展 RBAC 矩阵 + /ai-model 页面凭据槽位入口 Summary
## 一句话
`'credential-slot'` 模块加入 PermissionModule union 与「超级管理员/AI模型管理员」角色矩阵同时把 `/ai-model` 页面转为 Client Component 并加上受 `mounted && hasPermission('credential-slot')` 收敛的「凭据槽位」入口 Button + 占位 DialogDialogTitle「通用凭据槽位」+ DialogDescription「对话框真实内容由 Phase 3 落地」)。
## 完成的需求
-**CRED-FE-02** RBAC 模块声明PermissionModule 14 项 union + 6 角色矩阵其中超级管理员、AI模型管理员含 credential-slot其他 4 角色逐字未动
-**CRED-FE-03** /ai-model 页面入口Button + 占位 Dialog 已落地,未授权角色 DOM 中完全不存在;点击触发占位 Dialog 打开
## 执行的任务
### Task 1扩展 lib/permissions.ts RBACcommit `d60dd89`
**改动 4 处(与 PLAN 一致):**
1. PermissionModule union 末尾追加 `| "credential-slot";`(第 14 项)
2. 「超级管理员」数组末尾追加 `"credential-slot"`
3. 「AI模型管理员」数组末尾追加 `"credential-slot"`
4. 文件顶部权限矩阵注释表新增「凭据槽位」行(与代码同步)
**未动:**
- 内容管理员 / 卡牌管理员 / 查看者 / 管理员 4 个角色数组逐字不变
- `getModuleFromPath` 函数体pathMap 不含 `credential-slot`
- `getUserRole` / `getAllowedModules` / `hasPermission` / `hasPathPermission` 函数体
**验证:**
- `grep -nE "['\"]credential-slot['\"]" lib/permissions.ts` 命中 3 行union literal + 2 角色数组)
- `grep -n "credential-slot" lib/permissions.ts` 命中 4 行(含注释)
- `npx tsc --noEmit` 整体 67 条存量错误,无新错误指向 lib/permissions.ts
### Task 2app/ai-model/page.tsx 加入口 Button + 占位 Dialogcommit `0bcaa39`
**改动 5 处(与 PLAN 一致):**
1. line 1 加 `"use client"` 指令(文件转为 Client Component
2. 新增 imports`useState, useEffect` (react)、Dialog 子组件 5 个 (`@/components/ui/dialog`)、`KeyRound` (lucide-react)、`hasPermission` (`@/lib/permissions`)
3. 函数体顶部加 `mounted` + `isCredentialDialogOpen` 两个 `useState(false)` + 一个 `useEffect(() => setMounted(true), [])`(复用 sidebar.tsx 的 mounted 守卫模式)
4. DashboardHeader children 改为 `<div className="flex items-center gap-2">` 包裹含原「添加新模型」Button + 新增 `{mounted && hasPermission("credential-slot") && <Button variant="outline" onClick={() => setIsCredentialDialogOpen(true)}><KeyRound .../>凭据槽位</Button>}`
5. `</Tabs>` 之后、`</DashboardShell>` 之前插入 controlled mode 占位 DialogDialogTitle「通用凭据槽位」+ DialogDescription「对话框真实内容由 Phase 3 落地」)
**未动:**
- Tabs / TabsContent / Card 等所有原有内容line 18-441对应改动后 line 60-470逐字未动
- 不新建 `components/ai-model/CredentialSlotDialog.tsx`
- 不引入 sonner / useToast / `getCredentialSlot` / `updateCredentialSlot`
- package.json / yarn.lock / package-lock.json / pnpm-lock.yaml 全部未动
**验证:**
- `head -n 1 app/ai-model/page.tsx` 输出 `"use client"`
- `grep` 命中 KeyRound×2 + 凭据槽位×2 + 通用凭据槽位×1 + hasPermission("credential-slot")×1 + setIsCredentialDialogOpen×3 + 对话框真实内容由 Phase 3 落地×1
- `npx tsc --noEmit` 无新错误指向 app/ai-model/page.tsx
## Plan 级整体验证
| # | 校验项 | 结果 |
|---|--------|------|
| 1 | `npx tsc --noEmit` 不引入指向 lib/permissions.ts / app/ai-model/page.tsx 的新错误 | ✅ 0 条 |
| 2 | `grep -nE "['\"]credential-slot['\"]" lib/permissions.ts` 命中 3 行 | ✅ |
| 3 | 4 个不应含 credential-slot 的角色数组逐字未变 | ✅ |
| 4 | `getModuleFromPath` pathMap 中无新增 credential-slot 路径映射 | ✅ |
| 5 | `head -n 1 app/ai-model/page.tsx` = `"use client"` | ✅ |
| 6 | KeyRound / 凭据槽位 / 通用凭据槽位 / hasPermission(credential-slot) / setIsCredentialDialogOpen / 对话框真实内容由 Phase 3 落地 全部命中 | ✅(综合 ≥10 条) |
| 7 | `git diff --stat package.json yarn.lock package-lock.json pnpm-lock.yaml` 0 行输出(不引入新依赖) | ✅ |
## 偏离 PLAN 之处
无 — Plan 02-01 完全按照 02-01-PLAN.md 的 5 处改动逐字执行mounted 守卫模式严格复刻 components/sidebar.tsx:83-104。
## 已知遗留 / 移交事项
- **修改记录**:本 plan 未触动 `docs/修改记录.md`,由 Plan 02-02 在收尾任务中统一为 Phase 2 写一条条目
- **占位 Dialog**:仅含 DialogTitle + DialogDescription无 Footer / 表单 / 提交按钮;表单与提交逻辑由 Phase 3 / CRED-FE-04 落地
- **后端联调**Phase 3 才会真正调用 `getCredentialSlot()` / `updateCredentialSlot()`;本 plan 不涉及任何后端调用
## 提交历史
| Task | Commit | 描述 |
|------|--------|------|
| 1 | d60dd89 | feat(02-01): 扩展 RBAC 矩阵增加 credential-slot 模块 |
| 2 | 0bcaa39 | feat(02-01): /ai-model 页面新增凭据槽位入口 Button + 占位 Dialog |
## Self-Check
**文件存在性:**
- `lib/permissions.ts`FOUND126 行123 → 126+3 行 = union +1 / 超管 +1 / AI模型管理员 +1注释表 +1 与原 -1 净 0实际 4 处改动结果总 +3 行而非 +4 是因为最早 union 末项 `"settings";` 替换为 `"settings"` + 新增一行 `| "credential-slot";`,注释表本身只增 1 行,三个数组各 +1 但其中一个原行被改为多行)
- `app/ai-model/page.tsx`FOUND488 行446 → 488+42 行 = imports +13 / state +5 / DashboardHeader 重构 +7 / Dialog +14 - 其他微调)
**Commit 存在性:**
- d60dd89FOUNDgit log
- 0bcaa39FOUNDgit log
## Self-Check: PASSED