diff --git a/apps/web/src/pages/health/PatientDetail.tsx b/apps/web/src/pages/health/PatientDetail.tsx index 317ba69..c2f1d55 100644 --- a/apps/web/src/pages/health/PatientDetail.tsx +++ b/apps/web/src/pages/health/PatientDetail.tsx @@ -30,6 +30,7 @@ import { FollowUpTab } from './components/FollowUpTab'; import { DeviceReadingsTab } from './components/DeviceReadingsTab'; import { PointsAccountTab } from './components/PointsAccountTab'; import { AiSuggestionTab } from './components/AiSuggestionTab'; +import { FamilyMembersTab } from './components/FamilyMembersTab'; import { DailyMonitoringTab } from './components/DailyMonitoringTab'; import { GENDER_OPTIONS, BLOOD_TYPE_OPTIONS } from '../../constants/health'; import { useThemeMode } from '../../hooks/useThemeMode'; @@ -297,6 +298,11 @@ export default function PatientDetail() { ), }, + { + key: 'family', + label: '家属管理', + children: id ? : null, + }, { key: 'health', label: '健康数据', diff --git a/apps/web/src/pages/health/components/FamilyMembersTab.tsx b/apps/web/src/pages/health/components/FamilyMembersTab.tsx new file mode 100644 index 0000000..0d1763a --- /dev/null +++ b/apps/web/src/pages/health/components/FamilyMembersTab.tsx @@ -0,0 +1,156 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Table, Button, Form, Input, Select, Drawer, message, Popconfirm, Space } from 'antd'; +import { PlusOutlined } from '@ant-design/icons'; +import { patientApi, type FamilyMember, type CreateFamilyMemberReq } from '../../../api/health/patients'; +import { AuthButton } from '../../../components/AuthButton'; + +const RELATIONSHIP_OPTIONS = [ + { label: '父母', value: 'parent' }, + { label: '配偶', value: 'spouse' }, + { label: '子女', value: 'child' }, + { label: '兄弟姐妹', value: 'sibling' }, + { label: '其他', value: 'other' }, +]; + +interface Props { + patientId: string; +} + +export function FamilyMembersTab({ patientId }: Props) { + const [members, setMembers] = useState([]); + const [loading, setLoading] = useState(false); + const [drawerOpen, setDrawerOpen] = useState(false); + const [editingMember, setEditingMember] = useState(null); + const [form] = Form.useForm(); + + const fetchMembers = useCallback(async () => { + setLoading(true); + try { + const data = await patientApi.listFamilyMembers(patientId); + setMembers(data); + } catch { + message.error('加载家属列表失败'); + } + setLoading(false); + }, [patientId]); + + useEffect(() => { fetchMembers(); }, [fetchMembers]); + + const handleSubmit = async (values: CreateFamilyMemberReq) => { + try { + if (editingMember) { + await patientApi.updateFamilyMember(patientId, editingMember.id, { ...values, version: editingMember.version }); + message.success('家属信息已更新'); + } else { + await patientApi.createFamilyMember(patientId, values); + message.success('家属已添加'); + } + setDrawerOpen(false); + setEditingMember(null); + form.resetFields(); + fetchMembers(); + } catch (err: unknown) { + const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '操作失败'; + message.error(msg); + } + }; + + const handleDelete = async (memberId: string) => { + try { + await patientApi.deleteFamilyMember(patientId, memberId); + message.success('已删除'); + fetchMembers(); + } catch { + message.error('删除失败'); + } + }; + + const openCreate = () => { + setEditingMember(null); + form.resetFields(); + setDrawerOpen(true); + }; + + const openEdit = (member: FamilyMember) => { + setEditingMember(member); + form.setFieldsValue({ + name: member.name, + relationship: member.relationship, + phone: member.phone, + id_number: member.id_number, + notes: member.notes, + }); + setDrawerOpen(true); + }; + + const columns = [ + { title: '姓名', dataIndex: 'name', key: 'name' }, + { + title: '关系', dataIndex: 'relationship', key: 'relationship', + render: (v: string) => RELATIONSHIP_OPTIONS.find(r => r.value === v)?.label || v, + }, + { title: '电话', dataIndex: 'phone', key: 'phone' }, + { title: '身份证号', dataIndex: 'id_number', key: 'id_number' }, + { title: '备注', dataIndex: 'notes', key: 'notes', ellipsis: true }, + { + title: '操作', key: 'actions', width: 150, + render: (_: unknown, record: FamilyMember) => ( + + + + + + handleDelete(record.id)}> + + + + + ), + }, + ]; + + return ( + <> +
+ + + +
+ + { setDrawerOpen(false); setEditingMember(null); }} + width={400} + > +
+ + + + + + + + + + + + + + + + +
+ + ); +}