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 (
+ <>
+
+
+ } onClick={openCreate}>添加家属
+
+
+
+ { setDrawerOpen(false); setEditingMember(null); }}
+ width={400}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}