fix(web): 链路测试修复 — 健康记录枚举 + 预约名称解析
- HealthRecordsTab: record_type 从自由文本 Input 改为 Select 枚举 (checkup/outpatient/inpatient → 体检/门诊/住院),匹配后端校验规则 - AppointmentList: 添加批量患者/医生名称解析,列表不再显示截断 UUID Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,8 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import { appointmentApi, type Appointment, type CreateAppointmentReq } from '../../api/health/appointments';
|
import { appointmentApi, type Appointment, type CreateAppointmentReq } from '../../api/health/appointments';
|
||||||
|
import { patientApi } from '../../api/health/patients';
|
||||||
|
import { doctorApi } from '../../api/health/doctors';
|
||||||
import { StatusTag } from './components/StatusTag';
|
import { StatusTag } from './components/StatusTag';
|
||||||
import { PatientSelect } from './components/PatientSelect';
|
import { PatientSelect } from './components/PatientSelect';
|
||||||
import { DoctorSelect } from './components/DoctorSelect';
|
import { DoctorSelect } from './components/DoctorSelect';
|
||||||
@@ -83,6 +85,7 @@ export default function AppointmentList() {
|
|||||||
// 患者选择状态(受控组件,不挂在 Form.Item 上)
|
// 患者选择状态(受控组件,不挂在 Form.Item 上)
|
||||||
const [selectedPatientId, setSelectedPatientId] = useState<string | undefined>(undefined);
|
const [selectedPatientId, setSelectedPatientId] = useState<string | undefined>(undefined);
|
||||||
const [selectedDoctorId, setSelectedDoctorId] = useState<string | undefined>(undefined);
|
const [selectedDoctorId, setSelectedDoctorId] = useState<string | undefined>(undefined);
|
||||||
|
const [nameCache, setNameCache] = useState<Record<string, string>>({});
|
||||||
|
|
||||||
// ---- 数据获取 ----
|
// ---- 数据获取 ----
|
||||||
const fetchData = useCallback(async (p = page, ps = pageSize) => {
|
const fetchData = useCallback(async (p = page, ps = pageSize) => {
|
||||||
@@ -94,7 +97,31 @@ export default function AppointmentList() {
|
|||||||
status: statusFilter || undefined,
|
status: statusFilter || undefined,
|
||||||
date: dateFilter ? dateFilter.format('YYYY-MM-DD') : undefined,
|
date: dateFilter ? dateFilter.format('YYYY-MM-DD') : undefined,
|
||||||
});
|
});
|
||||||
setData(result.data);
|
const items = result.data;
|
||||||
|
// 批量解析患者和医生名称
|
||||||
|
const missingIds = new Set<string>();
|
||||||
|
items.forEach((a) => {
|
||||||
|
if (a.patient_id && !nameCache[a.patient_id]) missingIds.add(a.patient_id);
|
||||||
|
if (a.doctor_id && !nameCache[a.doctor_id]) missingIds.add(a.doctor_id);
|
||||||
|
});
|
||||||
|
const newCache: Record<string, string> = {};
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(missingIds).map(async (id) => {
|
||||||
|
try {
|
||||||
|
const p = await patientApi.get(id);
|
||||||
|
newCache[id] = p.name;
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const d = await doctorApi.get(id);
|
||||||
|
newCache[id] = d.name;
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
if (Object.keys(newCache).length > 0) {
|
||||||
|
setNameCache((prev) => ({ ...prev, ...newCache }));
|
||||||
|
}
|
||||||
|
setData(items);
|
||||||
setTotal(result.total);
|
setTotal(result.total);
|
||||||
} catch {
|
} catch {
|
||||||
message.error('加载预约列表失败');
|
message.error('加载预约列表失败');
|
||||||
@@ -218,7 +245,7 @@ export default function AppointmentList() {
|
|||||||
key: 'patient_name',
|
key: 'patient_name',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (_: unknown, record: Appointment) =>
|
render: (_: unknown, record: Appointment) =>
|
||||||
record.patient_name ?? record.patient_id.slice(0, 8),
|
record.patient_name ?? nameCache[record.patient_id] ?? record.patient_id.slice(0, 8),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '医护',
|
title: '医护',
|
||||||
@@ -226,7 +253,7 @@ export default function AppointmentList() {
|
|||||||
key: 'doctor_name',
|
key: 'doctor_name',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (_: unknown, record: Appointment) => {
|
render: (_: unknown, record: Appointment) => {
|
||||||
return record.doctor_name || record.doctor_id?.slice(0, 8) || '-';
|
return record.doctor_name || nameCache[record.doctor_id ?? ''] || record.doctor_id?.slice(0, 8) || '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Table, Tag, Button, Modal, Form, Input, DatePicker, message } from 'antd';
|
import { Table, Tag, Button, Modal, Form, Select, DatePicker, Input, message } from 'antd';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import { healthDataApi } from '../../../api/health/healthData';
|
import { healthDataApi } from '../../../api/health/healthData';
|
||||||
@@ -10,8 +10,20 @@ interface Props {
|
|||||||
patientId: string;
|
patientId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RECORD_TYPE_OPTIONS = [
|
||||||
|
{ value: 'checkup', label: '体检' },
|
||||||
|
{ value: 'outpatient', label: '门诊' },
|
||||||
|
{ value: 'inpatient', label: '住院' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const RECORD_TYPE_MAP: Record<string, string> = {
|
||||||
|
checkup: '体检',
|
||||||
|
outpatient: '门诊',
|
||||||
|
inpatient: '住院',
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '记录类型', dataIndex: 'record_type', key: 'record_type', width: 120, render: (v: string) => <Tag>{v}</Tag> },
|
{ title: '记录类型', dataIndex: 'record_type', key: 'record_type', width: 120, render: (v: string) => <Tag>{RECORD_TYPE_MAP[v] || v}</Tag> },
|
||||||
{ title: '记录日期', dataIndex: 'record_date', key: 'record_date', width: 120 },
|
{ title: '记录日期', dataIndex: 'record_date', key: 'record_date', width: 120 },
|
||||||
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||||
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 170, render: (v: string) => new Date(v).toLocaleString('zh-CN') },
|
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 170, render: (v: string) => new Date(v).toLocaleString('zh-CN') },
|
||||||
@@ -32,7 +44,7 @@ export function HealthRecordsTab({ patientId }: Props) {
|
|||||||
const { data, total, page, loading, refresh } = usePaginatedData<HealthRecord>(fetcher, 10);
|
const { data, total, page, loading, refresh } = usePaginatedData<HealthRecord>(fetcher, 10);
|
||||||
|
|
||||||
const handleCreate = async (values: {
|
const handleCreate = async (values: {
|
||||||
record_type: string;
|
record_type: 'checkup' | 'outpatient' | 'inpatient';
|
||||||
record_date: Dayjs;
|
record_date: Dayjs;
|
||||||
content?: string;
|
content?: string;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -84,8 +96,8 @@ export function HealthRecordsTab({ patientId }: Props) {
|
|||||||
width={520}
|
width={520}
|
||||||
>
|
>
|
||||||
<Form form={form} layout="vertical" onFinish={handleCreate}>
|
<Form form={form} layout="vertical" onFinish={handleCreate}>
|
||||||
<Form.Item name="record_type" label="记录类型" rules={[{ required: true, message: '请输入类型' }]}>
|
<Form.Item name="record_type" label="记录类型" rules={[{ required: true, message: '请选择类型' }]}>
|
||||||
<Input placeholder="如:门诊记录、出院小结、体检报告" />
|
<Select placeholder="请选择记录类型" options={RECORD_TYPE_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="record_date" label="记录日期" rules={[{ required: true, message: '请选择日期' }]}>
|
<Form.Item name="record_date" label="记录日期" rules={[{ required: true, message: '请选择日期' }]}>
|
||||||
<DatePicker style={{ width: '100%' }} />
|
<DatePicker style={{ width: '100%' }} />
|
||||||
|
|||||||
Reference in New Issue
Block a user