feat(web): 护理计划 Web UI — Phase 2a-1
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

新增护理计划管理前端页面,接入后端 8 条孤立路由:
- API 模块: carePlans.ts(计划 + 干预项目 + 预后测量 CRUD)
- 列表页: CarePlanList.tsx(筛选/新建/编辑/删除/跳转详情)
- 详情页: CarePlanDetail.tsx(计划信息 + Items/Outcomes 双 Tab CRUD)
- 路由注册: /health/care-plans + /health/care-plans/:id
- 菜单标题: routeTitleFallback 映射

权限: health.care-plan.list / health.care-plan.manage
This commit is contained in:
iven
2026-05-04 23:26:28 +08:00
parent 2b90db4028
commit 3aa436f872
5 changed files with 885 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
import client from '../client';
import type { PaginatedResponse } from '../types';
// --- Types ---
export interface CarePlan {
id: string;
patient_id: string;
plan_type: string;
status: string;
title: string;
goals?: Record<string, unknown>;
start_date?: string;
end_date?: string;
notes?: string;
created_at: string;
updated_at: string;
version: number;
}
export interface CarePlanItem {
id: string;
plan_id: string;
item_type: string;
title: string;
description?: string;
status: string;
schedule?: string;
sort_order?: number;
created_at: string;
updated_at: string;
version: number;
}
export interface CarePlanOutcome {
id: string;
plan_id: string;
item_id?: string;
metric: string;
baseline_value: string;
target_value: string;
current_value?: string;
measured_at?: string;
notes?: string;
created_at: string;
updated_at: string;
version: number;
}
export interface CreateCarePlanReq {
patient_id: string;
plan_type: string;
title: string;
goals?: Record<string, unknown>;
start_date?: string;
end_date?: string;
notes?: string;
}
export interface UpdateCarePlanReq {
plan_type?: string;
title?: string;
status?: string;
goals?: Record<string, unknown>;
start_date?: string;
end_date?: string;
notes?: string;
}
export interface CreateCarePlanItemReq {
item_type: string;
title: string;
description?: string;
schedule?: string;
sort_order?: number;
}
export interface UpdateCarePlanItemReq {
item_type?: string;
title?: string;
description?: string;
status?: string;
schedule?: string;
sort_order?: number;
}
export interface CreateCarePlanOutcomeReq {
item_id?: string;
metric: string;
baseline_value: string;
target_value: string;
current_value?: string;
measured_at?: string;
notes?: string;
}
export interface UpdateCarePlanOutcomeReq {
item_id?: string;
metric?: string;
baseline_value?: string;
target_value?: string;
current_value?: string;
measured_at?: string;
notes?: string;
}
export interface ListCarePlansParams {
page?: number;
page_size?: number;
patient_id?: string;
plan_type?: string;
status?: string;
}
// --- Constants ---
export const PLAN_TYPE_OPTIONS = [
{ label: '血液透析', value: 'hemodialysis' },
{ label: '腹膜透析', value: 'peritoneal' },
{ label: '慢性病管理', value: 'chronic_disease' },
{ label: '康复计划', value: 'rehabilitation' },
];
export const PLAN_STATUS_OPTIONS = [
{ label: '草稿', value: 'draft' },
{ label: '进行中', value: 'active' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' },
];
export const ITEM_TYPE_OPTIONS = [
{ label: '药物干预', value: 'medication' },
{ label: '饮食管理', value: 'diet' },
{ label: '运动计划', value: 'exercise' },
{ label: '监测项目', value: 'monitoring' },
{ label: '教育指导', value: 'education' },
{ label: '其他', value: 'other' },
];
export const PLAN_STATUS_COLOR: Record<string, string> = {
draft: 'default',
active: 'processing',
completed: 'success',
cancelled: 'error',
};
// --- API ---
export const carePlanApi = {
list: async (params: ListCarePlansParams) => {
const { data } = await client.get<{
success: boolean;
data: PaginatedResponse<CarePlan>;
}>('/health/care-plans', { params });
return data.data;
},
get: async (id: string) => {
const { data } = await client.get<{
success: boolean;
data: CarePlan;
}>(`/health/care-plans/${id}`);
return data.data;
},
create: async (req: CreateCarePlanReq) => {
const { data } = await client.post<{
success: boolean;
data: CarePlan;
}>('/health/care-plans', req);
return data.data;
},
update: async (id: string, req: UpdateCarePlanReq & { version: number }) => {
const { data } = await client.put<{
success: boolean;
data: CarePlan;
}>(`/health/care-plans/${id}`, req);
return data.data;
},
delete: async (id: string, version: number) => {
await client.delete(`/health/care-plans/${id}`, { data: { version } });
},
// --- Items ---
listItems: async (planId: string, params?: { page?: number; page_size?: number }) => {
const { data } = await client.get<{
success: boolean;
data: PaginatedResponse<CarePlanItem>;
}>(`/health/care-plans/${planId}/items`, { params });
return data.data;
},
createItem: async (planId: string, req: CreateCarePlanItemReq) => {
const { data } = await client.post<{
success: boolean;
data: CarePlanItem;
}>(`/health/care-plans/${planId}/items`, req);
return data.data;
},
updateItem: async (planId: string, itemId: string, req: UpdateCarePlanItemReq & { version: number }) => {
const { data } = await client.put<{
success: boolean;
data: CarePlanItem;
}>(`/health/care-plans/${planId}/items/${itemId}`, req);
return data.data;
},
deleteItem: async (planId: string, itemId: string, version: number) => {
await client.delete(`/health/care-plans/${planId}/items/${itemId}`, { data: { version } });
},
// --- Outcomes ---
listOutcomes: async (planId: string, params?: { page?: number; page_size?: number }) => {
const { data } = await client.get<{
success: boolean;
data: PaginatedResponse<CarePlanOutcome>;
}>(`/health/care-plans/${planId}/outcomes`, { params });
return data.data;
},
createOutcome: async (planId: string, req: CreateCarePlanOutcomeReq) => {
const { data } = await client.post<{
success: boolean;
data: CarePlanOutcome;
}>(`/health/care-plans/${planId}/outcomes`, req);
return data.data;
},
updateOutcome: async (planId: string, outcomeId: string, req: UpdateCarePlanOutcomeReq & { version: number }) => {
const { data } = await client.put<{
success: boolean;
data: CarePlanOutcome;
}>(`/health/care-plans/${planId}/outcomes/${outcomeId}`, req);
return data.data;
},
deleteOutcome: async (planId: string, outcomeId: string, version: number) => {
await client.delete(`/health/care-plans/${planId}/outcomes/${outcomeId}`, { data: { version } });
},
};