feat(web): 家属管理 Tab — 列表+添加/编辑/删除家属
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

This commit is contained in:
iven
2026-05-01 18:24:57 +08:00
parent 7764f7f8a6
commit 8e177ca705
2 changed files with 162 additions and 0 deletions

View File

@@ -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() {
</Descriptions>
),
},
{
key: 'family',
label: '家属管理',
children: id ? <FamilyMembersTab patientId={id} /> : null,
},
{
key: 'health',
label: '健康数据',

View File

@@ -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<FamilyMember[]>([]);
const [loading, setLoading] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);
const [editingMember, setEditingMember] = useState<FamilyMember | null>(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) => (
<Space>
<AuthButton code="health.patient.manage">
<Button type="link" size="small" onClick={() => openEdit(record)}></Button>
</AuthButton>
<AuthButton code="health.patient.manage">
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.id)}>
<Button type="link" size="small" danger></Button>
</Popconfirm>
</AuthButton>
</Space>
),
},
];
return (
<>
<div style={{ marginBottom: 16 }}>
<AuthButton code="health.patient.manage">
<Button type="primary" icon={<PlusOutlined />} onClick={openCreate}></Button>
</AuthButton>
</div>
<Table
columns={columns}
dataSource={members}
rowKey="id"
loading={loading}
pagination={false}
size="small"
/>
<Drawer
title={editingMember ? '编辑家属' : '添加家属'}
open={drawerOpen}
onClose={() => { setDrawerOpen(false); setEditingMember(null); }}
width={400}
>
<Form form={form} onFinish={handleSubmit} layout="vertical">
<Form.Item name="name" label="姓名" rules={[{ required: true, message: '请输入姓名' }]}>
<Input />
</Form.Item>
<Form.Item name="relationship" label="关系" rules={[{ required: true, message: '请选择关系' }]}>
<Select options={RELATIONSHIP_OPTIONS} placeholder="选择关系" />
</Form.Item>
<Form.Item name="phone" label="电话">
<Input />
</Form.Item>
<Form.Item name="id_number" label="身份证号">
<Input />
</Form.Item>
<Form.Item name="notes" label="备注">
<Input.TextArea rows={2} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">{editingMember ? '更新' : '添加'}</Button>
</Form.Item>
</Form>
</Drawer>
</>
);
}