refactor(web): 新增 useDictionary hook + 4 个页面下拉选项改用字典 API

- 新增 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)
This commit is contained in:
iven
2026-05-02 11:27:11 +08:00
parent b6e780e649
commit 63ead0c442
5 changed files with 45 additions and 5 deletions

View File

@@ -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<DictionaryItemInfo[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
listItemsByCode(code)
.then((data) => setItems(data))
.catch(() => setItems([]))
.finally(() => setLoading(false));
}, [code]);
const options = useMemo<DictOption[]>(() => {
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 };
}

View File

@@ -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');

View File

@@ -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<Doctor | null>(null);
const [form] = Form.useForm();

View File

@@ -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');

View File

@@ -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<FamilyMember[]>([]);
const [loading, setLoading] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);