feat(health): Phase 4 跨模块集成与架构优化 — 通知/标签/待办/数据录入
后端: - erp-message: 添加 appointment.created/confirmed/cancelled 事件监听,自动发送站内通知 - erp-health: 新增 GET /health/patient-tags 标签列表端点 + list_tags service - wechat-templates: 添加 isTemplateConfigured 运行时校验 前端: - 新增 Zustand useHealthStore 共享患者/医生名称缓存 - PatientTagManage: UUID 输入替换为 Checkbox 标签选择器 - VitalSignsTab: 添加体征数据录入 Modal (血压/心率/体重/血糖) - LabReportsTab: 添加化验报告创建 Modal - HealthRecordsTab: 添加健康记录创建 Modal - patients API: 添加 TagItem 类型 + listTags 方法 小程序: - 首页待办事项接入预约和随访 API,替换硬编码 EmptyState
This commit is contained in:
75
apps/web/src/stores/health.ts
Normal file
75
apps/web/src/stores/health.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { create } from 'zustand';
|
||||
import { patientApi } from '../api/health/patients';
|
||||
import { doctorApi } from '../api/health/doctors';
|
||||
|
||||
interface HealthState {
|
||||
patientNames: Record<string, string>;
|
||||
doctorNames: Record<string, string>;
|
||||
loadingIds: Set<string>;
|
||||
|
||||
resolvePatientName: (id: string) => Promise<string>;
|
||||
resolveDoctorName: (id: string) => Promise<string>;
|
||||
getPatientName: (id: string) => string;
|
||||
getDoctorName: (id: string) => string;
|
||||
}
|
||||
|
||||
export const useHealthStore = create<HealthState>((set, get) => ({
|
||||
patientNames: {},
|
||||
doctorNames: {},
|
||||
loadingIds: new Set(),
|
||||
|
||||
resolvePatientName: async (id: string) => {
|
||||
const { patientNames, loadingIds } = get();
|
||||
if (patientNames[id]) return patientNames[id];
|
||||
if (loadingIds.has(`p:${id}`)) return id.slice(0, 8);
|
||||
|
||||
const newLoading = new Set(loadingIds);
|
||||
newLoading.add(`p:${id}`);
|
||||
set({ loadingIds: newLoading });
|
||||
|
||||
try {
|
||||
const detail = await patientApi.get(id);
|
||||
const name = detail.name;
|
||||
set((s) => ({
|
||||
patientNames: { ...s.patientNames, [id]: name },
|
||||
loadingIds: new Set([...s.loadingIds].filter((k) => k !== `p:${id}`)),
|
||||
}));
|
||||
return name;
|
||||
} catch {
|
||||
set((s) => ({
|
||||
patientNames: { ...s.patientNames, [id]: id.slice(0, 8) },
|
||||
loadingIds: new Set([...s.loadingIds].filter((k) => k !== `p:${id}`)),
|
||||
}));
|
||||
return id.slice(0, 8);
|
||||
}
|
||||
},
|
||||
|
||||
resolveDoctorName: async (id: string) => {
|
||||
const { doctorNames, loadingIds } = get();
|
||||
if (doctorNames[id]) return doctorNames[id];
|
||||
if (loadingIds.has(`d:${id}`)) return id.slice(0, 8);
|
||||
|
||||
const newLoading = new Set(loadingIds);
|
||||
newLoading.add(`d:${id}`);
|
||||
set({ loadingIds: newLoading });
|
||||
|
||||
try {
|
||||
const detail = await doctorApi.get(id);
|
||||
const name = detail.name;
|
||||
set((s) => ({
|
||||
doctorNames: { ...s.doctorNames, [id]: name },
|
||||
loadingIds: new Set([...s.loadingIds].filter((k) => k !== `d:${id}`)),
|
||||
}));
|
||||
return name;
|
||||
} catch {
|
||||
set((s) => ({
|
||||
doctorNames: { ...s.doctorNames, [id]: id.slice(0, 8) },
|
||||
loadingIds: new Set([...s.loadingIds].filter((k) => k !== `d:${id}`)),
|
||||
}));
|
||||
return id.slice(0, 8);
|
||||
}
|
||||
},
|
||||
|
||||
getPatientName: (id: string) => get().patientNames[id] || id.slice(0, 8),
|
||||
getDoctorName: (id: string) => get().doctorNames[id] || id.slice(0, 8),
|
||||
}));
|
||||
Reference in New Issue
Block a user