18 个 Task,2 个 Chunk: - Chunk 1: 权限基础设施(JWT 解码 + store + hook + 组件) - Chunk 2: 健康模块 15 页面按钮改造 + 集成验证
658 lines
17 KiB
Markdown
658 lines
17 KiB
Markdown
# 切片 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 (只读,无操作按钮) |
|