- 新增 .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
140 lines
7.8 KiB
Markdown
140 lines
7.8 KiB
Markdown
---
|
||
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:CredentialSlot(Phase 1 已交付,本 plan 暂未直接消费,留给 Phase 3)
|
||
- components/ui/dialog.tsx:Dialog/DialogContent/DialogHeader/DialogTitle/DialogDescription
|
||
- components/ui/button.tsx:Button(variant="outline")
|
||
- components/dashboard-header.tsx:DashboardHeader(children 单 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 mode(open + 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"(与现有页面其他次要按钮如「查看详情」「试听示例」视觉一致)
|
||
- 图标用 KeyRound(lucide-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 + 占位 Dialog(DialogTitle「通用凭据槽位」+ 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 RBAC(commit `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 2:app/ai-model/page.tsx 加入口 Button + 占位 Dialog(commit `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 占位 Dialog(DialogTitle「通用凭据槽位」+ 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`:FOUND(126 行;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`:FOUND(488 行;446 → 488,+42 行 = imports +13 / state +5 / DashboardHeader 重构 +7 / Dialog +14 - 其他微调)
|
||
|
||
**Commit 存在性:**
|
||
- d60dd89:FOUND(git log)
|
||
- 0bcaa39:FOUND(git log)
|
||
|
||
## Self-Check: PASSED
|