From 63ead0c4424f64d3b04ff22c51e4099894a0b0af Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 2 May 2026 11:27:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=E6=96=B0=E5=A2=9E=20useDictio?= =?UTF-8?q?nary=20hook=20+=204=20=E4=B8=AA=E9=A1=B5=E9=9D=A2=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=E9=80=89=E9=A1=B9=E6=94=B9=E7=94=A8=E5=AD=97=E5=85=B8?= =?UTF-8?q?=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 useDictionary hook 支持字典 API 获取 + fallback 降级 - DoctorList 科室/职称改用 useDictionary (health_department/health_title) - FollowUpTaskList 随访类型改用 useDictionary (health_follow_up_type) - ConsultationList 咨询类型改用 useDictionary (health_consultation_type) - FamilyMembersTab 家庭关系改用 useDictionary (health_relationship) --- apps/web/src/hooks/useDictionary.ts | 31 +++++++++++++++++++ .../web/src/pages/health/ConsultationList.tsx | 4 ++- apps/web/src/pages/health/DoctorList.tsx | 7 +++-- .../web/src/pages/health/FollowUpTaskList.tsx | 4 ++- .../health/components/FamilyMembersTab.tsx | 4 ++- 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/hooks/useDictionary.ts diff --git a/apps/web/src/hooks/useDictionary.ts b/apps/web/src/hooks/useDictionary.ts new file mode 100644 index 0000000..66d9dd0 --- /dev/null +++ b/apps/web/src/hooks/useDictionary.ts @@ -0,0 +1,31 @@ +import { useEffect, useState, useMemo } from 'react'; +import { listItemsByCode, type DictionaryItemInfo } from '../api/dictionaries'; + +export interface DictOption { + value: string; + label: string; +} + +export function useDictionary(code: string, fallback?: DictOption[]) { + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(true); + listItemsByCode(code) + .then((data) => setItems(data)) + .catch(() => setItems([])) + .finally(() => setLoading(false)); + }, [code]); + + const options = useMemo(() => { + if (items.length > 0) { + return items + .sort((a, b) => a.sort_order - b.sort_order) + .map((item) => ({ value: item.value, label: item.label })); + } + return fallback ?? []; + }, [items, fallback]); + + return { items, options, loading }; +} diff --git a/apps/web/src/pages/health/ConsultationList.tsx b/apps/web/src/pages/health/ConsultationList.tsx index 542f5c6..d8d485b 100644 --- a/apps/web/src/pages/health/ConsultationList.tsx +++ b/apps/web/src/pages/health/ConsultationList.tsx @@ -23,6 +23,7 @@ import { PageContainer } from '../../components/PageContainer'; import { EntityName } from '../../components/EntityName'; import { formatDateTime } from '../../utils/format'; import { usePaginatedData } from '../../hooks/usePaginatedData'; +import { useDictionary } from '../../hooks/useDictionary'; const STATUS_OPTIONS = [ { value: 'waiting', label: '等待中' }, @@ -30,7 +31,7 @@ const STATUS_OPTIONS = [ { value: 'closed', label: '已关闭' }, ]; -const CONSULTATION_TYPE_OPTIONS = [ +const CONSULTATION_TYPE_FALLBACK = [ { value: 'customer_service', label: '客服咨询' }, { value: 'medical', label: '医疗咨询' }, { value: 'health_consultation', label: '健康咨询' }, @@ -48,6 +49,7 @@ interface ConsultationFilters { } export default function ConsultationList() { + const { options: CONSULTATION_TYPE_OPTIONS } = useDictionary('health_consultation_type', CONSULTATION_TYPE_FALLBACK); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const urlPatientId = searchParams.get('patient_id'); diff --git a/apps/web/src/pages/health/DoctorList.tsx b/apps/web/src/pages/health/DoctorList.tsx index 16c1d97..a26b07a 100644 --- a/apps/web/src/pages/health/DoctorList.tsx +++ b/apps/web/src/pages/health/DoctorList.tsx @@ -20,6 +20,7 @@ import { DeleteOutlined, } from '@ant-design/icons'; import { doctorApi, type Doctor, type CreateDoctorReq, type UpdateDoctorReq } from '../../api/health/doctors'; +import { useDictionary } from '../../hooks/useDictionary'; import { AuthButton } from '../../components/AuthButton'; import { PageContainer } from '../../components/PageContainer'; import { EntityName } from '../../components/EntityName'; @@ -27,7 +28,7 @@ import { formatDateTime } from '../../utils/format'; import { usePaginatedData } from '../../hooks/usePaginatedData'; /** 科室选项 — 可后续改为从字典接口获取 */ -const DEPARTMENT_OPTIONS = [ +const DEPARTMENT_FALLBACK = [ { value: '全科', label: '全科' }, { value: '内科', label: '内科' }, { value: '外科', label: '外科' }, @@ -41,7 +42,7 @@ const DEPARTMENT_OPTIONS = [ { value: '体检中心', label: '体检中心' }, ]; -const TITLE_OPTIONS = [ +const TITLE_FALLBACK = [ { value: '住院医师', label: '住院医师' }, { value: '主治医师', label: '主治医师' }, { value: '副主任医师', label: '副主任医师' }, @@ -74,6 +75,8 @@ interface DoctorFilters { } export default function DoctorList() { + const { options: DEPARTMENT_OPTIONS } = useDictionary('health_department', DEPARTMENT_FALLBACK); + const { options: TITLE_OPTIONS } = useDictionary('health_title', TITLE_FALLBACK); const [modalOpen, setModalOpen] = useState(false); const [editing, setEditing] = useState(null); const [form] = Form.useForm(); diff --git a/apps/web/src/pages/health/FollowUpTaskList.tsx b/apps/web/src/pages/health/FollowUpTaskList.tsx index f4da370..a2fed68 100644 --- a/apps/web/src/pages/health/FollowUpTaskList.tsx +++ b/apps/web/src/pages/health/FollowUpTaskList.tsx @@ -25,6 +25,7 @@ import { DrawerForm } from '../../components/DrawerForm'; import { formatDate, formatDateTime } from '../../utils/format'; import { usePaginatedData } from '../../hooks/usePaginatedData'; import { useApiRequest } from '../../hooks/useApiRequest'; +import { useDictionary } from '../../hooks/useDictionary'; const STATUS_OPTIONS = [ { value: 'pending', label: '待处理' }, @@ -34,7 +35,7 @@ const STATUS_OPTIONS = [ { value: 'cancelled', label: '已取消' }, ]; -const FOLLOW_UP_TYPE_OPTIONS = [ +const FOLLOW_UP_TYPE_FALLBACK = [ { value: 'phone', label: '电话' }, { value: 'outpatient', label: '门诊' }, { value: 'home_visit', label: '家访' }, @@ -62,6 +63,7 @@ interface AssignFormValues { } export default function FollowUpTaskList() { + const { options: FOLLOW_UP_TYPE_OPTIONS } = useDictionary('health_follow_up_type', FOLLOW_UP_TYPE_FALLBACK); const [searchParams] = useSearchParams(); const urlPatientId = searchParams.get('patient_id'); diff --git a/apps/web/src/pages/health/components/FamilyMembersTab.tsx b/apps/web/src/pages/health/components/FamilyMembersTab.tsx index 0d1763a..6042b43 100644 --- a/apps/web/src/pages/health/components/FamilyMembersTab.tsx +++ b/apps/web/src/pages/health/components/FamilyMembersTab.tsx @@ -3,8 +3,9 @@ import { Table, Button, Form, Input, Select, Drawer, message, Popconfirm, Space import { PlusOutlined } from '@ant-design/icons'; import { patientApi, type FamilyMember, type CreateFamilyMemberReq } from '../../../api/health/patients'; import { AuthButton } from '../../../components/AuthButton'; +import { useDictionary } from '../../../hooks/useDictionary'; -const RELATIONSHIP_OPTIONS = [ +const RELATIONSHIP_FALLBACK = [ { label: '父母', value: 'parent' }, { label: '配偶', value: 'spouse' }, { label: '子女', value: 'child' }, @@ -17,6 +18,7 @@ interface Props { } export function FamilyMembersTab({ patientId }: Props) { + const { options: RELATIONSHIP_OPTIONS } = useDictionary('health_relationship', RELATIONSHIP_FALLBACK); const [members, setMembers] = useState([]); const [loading, setLoading] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false);