docs(plan): 切片 1 按钮级权限控制实施计划
18 个 Task,2 个 Chunk: - Chunk 1: 权限基础设施(JWT 解码 + store + hook + 组件) - Chunk 2: 健康模块 15 页面按钮改造 + 集成验证
This commit is contained in:
657
docs/superpowers/plans/2026-04-25-slice1-button-permissions.md
Normal file
657
docs/superpowers/plans/2026-04-25-slice1-button-permissions.md
Normal file
@@ -0,0 +1,657 @@
|
||||
# 切片 1: 按钮级权限控制 实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 实现前端按钮级权限控制,让无权限用户看不到操作按钮(hidden 模式)。
|
||||
|
||||
**Architecture:** JWT claims 已包含 `permissions: Vec<String>`(后端登录时写入),前端复用 `client.ts` 的 `decodeJwtPayload` 提取权限码列表,存入 Zustand auth store。新增 `usePermission` hook + `AuthButton` / `AuthGuard` 声明式组件,包裹健康模块 15 个页面的操作按钮。
|
||||
|
||||
**Tech Stack:** React 19 + TypeScript + Zustand 5 + Ant Design 6
|
||||
|
||||
**设计规格:** `docs/superpowers/specs/2026-04-25-feature-completion-design.md` §2
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1: 权限基础设施
|
||||
|
||||
### Task 1: 从 JWT 提取 permissions 并存入 auth store
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/stores/auth.ts`
|
||||
|
||||
**背景:** JWT payload 已包含 `permissions` 字段(string 数组)。`client.ts` 已有 `decodeJwtPayload` 函数。auth store 登录时已存 `access_token` 到 localStorage,可从中解码权限。
|
||||
|
||||
- [ ] **Step 1: 在 auth store 中添加 permissions 状态和提取逻辑**
|
||||
|
||||
在 `apps/web/src/stores/auth.ts` 中:
|
||||
|
||||
1. 新增辅助函数(文件顶部,import 之后):
|
||||
|
||||
```typescript
|
||||
function extractPermissions(): string[] {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (!token) return [];
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) return [];
|
||||
const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
|
||||
return Array.isArray(payload.permissions) ? payload.permissions : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 修改 `restoreInitialState` 返回值,增加 `permissions`:
|
||||
|
||||
```typescript
|
||||
function restoreInitialState(): { user: UserInfo | null; isAuthenticated: boolean; permissions: string[] } {
|
||||
const token = localStorage.getItem('access_token');
|
||||
const userStr = localStorage.getItem('user');
|
||||
if (token && userStr) {
|
||||
try {
|
||||
const user = JSON.parse(userStr) as UserInfo;
|
||||
return { user, isAuthenticated: true, permissions: extractPermissions() };
|
||||
} catch {
|
||||
localStorage.removeItem('user');
|
||||
}
|
||||
}
|
||||
return { user: null, isAuthenticated: false, permissions: [] };
|
||||
}
|
||||
```
|
||||
|
||||
3. 修改 `AuthState` 接口,增加 `permissions`:
|
||||
|
||||
```typescript
|
||||
interface AuthState {
|
||||
user: UserInfo | null;
|
||||
isAuthenticated: boolean;
|
||||
loading: boolean;
|
||||
permissions: string[];
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
loadFromStorage: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
4. 修改 store 创建,初始化 `permissions`,在 login/logout 中同步更新:
|
||||
|
||||
```typescript
|
||||
export const useAuthStore = create<AuthState>((set) => ({
|
||||
user: initial.user,
|
||||
isAuthenticated: initial.isAuthenticated,
|
||||
loading: false,
|
||||
permissions: initial.permissions,
|
||||
|
||||
login: async (username, password) => {
|
||||
set({ loading: true });
|
||||
try {
|
||||
const resp = await apiLogin({ username, password });
|
||||
localStorage.setItem('access_token', resp.access_token);
|
||||
localStorage.setItem('refresh_token', resp.refresh_token);
|
||||
localStorage.setItem('user', JSON.stringify(resp.user));
|
||||
set({ user: resp.user, isAuthenticated: true, loading: false, permissions: extractPermissions() });
|
||||
} catch (error) {
|
||||
set({ loading: false });
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
logout: async () => {
|
||||
try {
|
||||
await apiLogout();
|
||||
} catch {
|
||||
// Ignore logout API errors
|
||||
}
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
localStorage.removeItem('user');
|
||||
set({ user: null, isAuthenticated: false, permissions: [] });
|
||||
},
|
||||
|
||||
loadFromStorage: () => {
|
||||
const state = restoreInitialState();
|
||||
set({ user: state.user, isAuthenticated: state.isAuthenticated, permissions: state.permissions });
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证编译通过**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
Expected: 无类型错误
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/stores/auth.ts
|
||||
git commit -m "feat(web): auth store 添加 permissions 状态,从 JWT 解码提取"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 创建 usePermission hook
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/web/src/hooks/usePermission.ts`
|
||||
|
||||
- [ ] **Step 1: 创建 usePermission hook**
|
||||
|
||||
```typescript
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
|
||||
export function usePermission(code: string): { hasPermission: boolean } {
|
||||
const permissions = useAuthStore((s) => s.permissions);
|
||||
return { hasPermission: permissions.includes(code) };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证编译通过**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
Expected: 无类型错误
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/hooks/usePermission.ts
|
||||
git commit -m "feat(web): 添加 usePermission hook"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 创建 AuthButton + AuthGuard 组件
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/web/src/components/AuthButton.tsx`
|
||||
- Create: `apps/web/src/components/AuthGuard.tsx`
|
||||
|
||||
- [ ] **Step 1: 创建 AuthButton 组件**
|
||||
|
||||
`apps/web/src/components/AuthButton.tsx`:
|
||||
|
||||
```typescript
|
||||
import type { ReactNode } from 'react';
|
||||
import { usePermission } from '../hooks/usePermission';
|
||||
|
||||
interface AuthButtonProps {
|
||||
code: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AuthButton({ code, children }: AuthButtonProps) {
|
||||
const { hasPermission } = usePermission(code);
|
||||
if (!hasPermission) return null;
|
||||
return <>{children}</>;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 创建 AuthGuard 组件**
|
||||
|
||||
`apps/web/src/components/AuthGuard.tsx`:
|
||||
|
||||
```typescript
|
||||
import type { ReactNode } from 'react';
|
||||
import { usePermission } from '../hooks/usePermission';
|
||||
|
||||
interface AuthGuardProps {
|
||||
code: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function AuthGuard({ code, children }: AuthGuardProps) {
|
||||
const { hasPermission } = usePermission(code);
|
||||
if (!hasPermission) return null;
|
||||
return <>{children}</>;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 验证编译通过**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
Expected: 无类型错误
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/components/AuthButton.tsx apps/web/src/components/AuthGuard.tsx
|
||||
git commit -m "feat(web): 添加 AuthButton/AuthGuard 声明式权限组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2: 健康模块页面按钮权限改造
|
||||
|
||||
### Task 4: PatientList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PatientList.tsx`
|
||||
|
||||
**改造目标:**
|
||||
- 第 304 行 `新建患者` 按钮 → `<AuthButton code="health.patient.manage">`
|
||||
- 第 242-267 行 操作列的编辑/删除按钮 → `<AuthButton code="health.patient.manage">`
|
||||
|
||||
- [ ] **Step 1: 添加 import**
|
||||
|
||||
在 PatientList.tsx 顶部 import 区域添加:
|
||||
|
||||
```typescript
|
||||
import { AuthButton } from '../../components/AuthButton';
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 包裹新建患者按钮**
|
||||
|
||||
将第 304 行:
|
||||
```tsx
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openCreateModal}>
|
||||
新建患者
|
||||
</Button>
|
||||
```
|
||||
|
||||
改为:
|
||||
```tsx
|
||||
<AuthButton code="health.patient.manage">
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openCreateModal}>
|
||||
新建患者
|
||||
</Button>
|
||||
</AuthButton>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 包裹操作列按钮**
|
||||
|
||||
将 columns 操作列的 render(第 241-270 行):
|
||||
```tsx
|
||||
render: (_: unknown, record: PatientListItem) => (
|
||||
<Space size={4}>
|
||||
<Button ... />
|
||||
<Popconfirm ...><Button ... /></Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
```
|
||||
|
||||
改为:
|
||||
```tsx
|
||||
render: (_: unknown, record: PatientListItem) => (
|
||||
<AuthButton code="health.patient.manage">
|
||||
<Space size={4}>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openEditModal(record);
|
||||
}}
|
||||
style={{ color: isDark ? '#94a3b8' : '#475569' }}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除此患者?"
|
||||
onConfirm={(e) => {
|
||||
e?.stopPropagation();
|
||||
handleDelete(record.id);
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
danger
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
</AuthButton>
|
||||
),
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 验证编译通过**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
Expected: 无类型错误
|
||||
|
||||
- [ ] **Step 5: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/pages/health/PatientList.tsx
|
||||
git commit -m "feat(web): PatientList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: AppointmentList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/AppointmentList.tsx`
|
||||
|
||||
**改造模式同 Task 4:**
|
||||
- 新建预约按钮 → `<AuthButton code="health.appointment.manage">`
|
||||
- 操作列(编辑/取消/状态变更) → `<AuthButton code="health.appointment.manage">`
|
||||
|
||||
- [ ] **Step 1: 读取文件,识别所有操作按钮位置**
|
||||
|
||||
Run: `grep -n "Button\|onClick\|Popconfirm" apps/web/src/pages/health/AppointmentList.tsx`
|
||||
|
||||
- [ ] **Step 2: 添加 import + 包裹所有操作按钮**
|
||||
|
||||
添加 `import { AuthButton } from '../../components/AuthButton';`
|
||||
用 `<AuthButton code="health.appointment.manage">` 包裹:
|
||||
- 顶部新建按钮
|
||||
- 表格操作列中的所有按钮
|
||||
|
||||
- [ ] **Step 3: 验证编译通过**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add apps/web/src/pages/health/AppointmentList.tsx
|
||||
git commit -m "feat(web): AppointmentList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: DoctorList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/DoctorList.tsx`
|
||||
|
||||
**权限码:** `health.doctor.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
模式同 Task 4-5。新建按钮 + 操作列用 `<AuthButton code="health.doctor.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/DoctorList.tsx
|
||||
git commit -m "feat(web): DoctorList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: DoctorSchedule 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/DoctorSchedule.tsx`
|
||||
|
||||
**权限码:** `health.doctor.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建排班 + 操作列用 `<AuthButton code="health.doctor.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/DoctorSchedule.tsx
|
||||
git commit -m "feat(web): DoctorSchedule 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: FollowUpTaskList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/FollowUpTaskList.tsx`
|
||||
|
||||
**权限码:** `health.follow-up.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建随访 + 操作列(编辑/完成/取消)用 `<AuthButton code="health.follow-up.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/FollowUpTaskList.tsx
|
||||
git commit -m "feat(web): FollowUpTaskList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 9: FollowUpRecordList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/FollowUpRecordList.tsx`
|
||||
|
||||
**权限码:** `health.follow-up.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
添加记录 + 操作列用 `<AuthButton code="health.follow-up.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/FollowUpRecordList.tsx
|
||||
git commit -m "feat(web): FollowUpRecordList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10: ConsultationList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/ConsultationList.tsx`
|
||||
|
||||
**权限码:** `health.consultation.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建会话 + 操作列(关闭/导出)用 `<AuthButton code="health.consultation.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/ConsultationList.tsx
|
||||
git commit -m "feat(web): ConsultationList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 11: ConsultationDetail 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/ConsultationDetail.tsx`
|
||||
|
||||
**权限码:** `health.consultation.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
发送消息 + 关闭会话 + 导出按钮用 `<AuthButton code="health.consultation.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/ConsultationDetail.tsx
|
||||
git commit -m "feat(web): ConsultationDetail 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 12: OfflineEventList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/OfflineEventList.tsx`
|
||||
|
||||
**权限码:** `health.articles.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建活动 + 操作列用 `<AuthButton code="health.articles.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/OfflineEventList.tsx
|
||||
git commit -m "feat(web): OfflineEventList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 13: PatientDetail 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PatientDetail.tsx`
|
||||
|
||||
**权限码:** `health.patient.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
编辑患者信息按钮 + 标签管理 + 新增健康数据按钮用 `<AuthButton code="health.patient.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/PatientDetail.tsx
|
||||
git commit -m "feat(web): PatientDetail 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 14: PatientTagManage 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PatientTagManage.tsx`
|
||||
|
||||
**权限码:** `health.patient.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建标签 + 编辑/删除标签用 `<AuthButton code="health.patient.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/PatientTagManage.tsx
|
||||
git commit -m "feat(web): PatientTagManage 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 15: PointsProductList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PointsProductList.tsx`
|
||||
|
||||
**权限码:** `health.points.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建商品 + 编辑/删除/上下架用 `<AuthButton code="health.points.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/PointsProductList.tsx
|
||||
git commit -m "feat(web): PointsProductList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 16: PointsOrderList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PointsOrderList.tsx`
|
||||
|
||||
**权限码:** `health.points.list`(只读列表,如有核销操作用 `health.points.manage`)
|
||||
|
||||
- [ ] **Step 1: 读取文件,识别是否有写操作按钮**
|
||||
|
||||
Run: `grep -n "Button\|onClick" apps/web/src/pages/health/PointsOrderList.tsx`
|
||||
|
||||
- [ ] **Step 2: 如有核销/管理按钮,用 `<AuthButton code="health.points.manage">` 包裹**
|
||||
|
||||
- [ ] **Step 3: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/PointsOrderList.tsx
|
||||
git commit -m "feat(web): PointsOrderList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 17: PointsRuleList 按钮权限
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/src/pages/health/PointsRuleList.tsx`
|
||||
|
||||
**权限码:** `health.points.manage`
|
||||
|
||||
- [ ] **Step 1: 添加 import + 包裹操作按钮**
|
||||
|
||||
新建规则 + 编辑/删除用 `<AuthButton code="health.points.manage">` 包裹。
|
||||
|
||||
- [ ] **Step 2: 验证 + 提交**
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit
|
||||
git add apps/web/src/pages/health/PointsRuleList.tsx
|
||||
git commit -m "feat(web): PointsRuleList 添加按钮级权限控制"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 18: 集成验证
|
||||
|
||||
- [ ] **Step 1: 全量 TypeScript 编译检查**
|
||||
|
||||
Run: `cd apps/web && npx tsc --noEmit`
|
||||
Expected: 0 errors
|
||||
|
||||
- [ ] **Step 2: 启动前端开发服务器**
|
||||
|
||||
Run: `cd apps/web && pnpm dev`
|
||||
|
||||
- [ ] **Step 3: 功能验证**
|
||||
|
||||
1. 用管理员账号登录 → 所有按钮可见
|
||||
2. 创建一个无权限的测试角色(仅 `health.patient.list`)→ 分配给测试用户
|
||||
3. 用测试用户登录 → 仅患者列表可见,新建/编辑/删除按钮隐藏
|
||||
4. 确认表格行点击导航(如患者详情页)仍然正常
|
||||
|
||||
- [ ] **Step 4: 生产构建验证**
|
||||
|
||||
Run: `cd apps/web && pnpm build`
|
||||
Expected: 构建成功
|
||||
|
||||
- [ ] **Step 5: 推送所有提交**
|
||||
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 权限码速查表
|
||||
|
||||
| 页面 | 文件 | 权限码 |
|
||||
|------|------|--------|
|
||||
| PatientList | PatientList.tsx | health.patient.manage |
|
||||
| PatientDetail | PatientDetail.tsx | health.patient.manage |
|
||||
| PatientTagManage | PatientTagManage.tsx | health.patient.manage |
|
||||
| AppointmentList | AppointmentList.tsx | health.appointment.manage |
|
||||
| DoctorList | DoctorList.tsx | health.doctor.manage |
|
||||
| DoctorSchedule | DoctorSchedule.tsx | health.doctor.manage |
|
||||
| FollowUpTaskList | FollowUpTaskList.tsx | health.follow-up.manage |
|
||||
| FollowUpRecordList | FollowUpRecordList.tsx | health.follow-up.manage |
|
||||
| ConsultationList | ConsultationList.tsx | health.consultation.manage |
|
||||
| ConsultationDetail | ConsultationDetail.tsx | health.consultation.manage |
|
||||
| OfflineEventList | OfflineEventList.tsx | health.articles.manage |
|
||||
| PointsProductList | PointsProductList.tsx | health.points.manage |
|
||||
| PointsOrderList | PointsOrderList.tsx | health.points.manage |
|
||||
| PointsRuleList | PointsRuleList.tsx | health.points.manage |
|
||||
| StatisticsDashboard | StatisticsDashboard.tsx | health.health-data.list (只读,无操作按钮) |
|
||||
Reference in New Issue
Block a user