import { useEffect, useState, useCallback } from 'react'; import { Table, Button, Space, Modal, Form, Select, DatePicker, TimePicker, Input, Dropdown, message, Card, Row, Col, } from 'antd'; import { PlusOutlined, DownOutlined, } from '@ant-design/icons'; import type { Dayjs } from 'dayjs'; 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 { PatientSelect } from './components/PatientSelect'; import { DoctorSelect } from './components/DoctorSelect'; import { AuthButton } from '../../components/AuthButton'; /** 预约类型选项 */ const APPOINTMENT_TYPE_OPTIONS = [ { value: 'outpatient', label: '门诊' }, { value: 'recheck', label: '复诊' }, { value: 'health_checkup', label: '体检' }, { value: 'consultation', label: '咨询' }, { value: 'dialysis', label: '透析' }, ]; const APPOINTMENT_TYPE_MAP: Record = { outpatient: '门诊', recheck: '复诊', health_checkup: '体检', consultation: '咨询', dialysis: '透析', }; /** 状态筛选选项 */ const STATUS_OPTIONS = [ { value: 'pending', label: '待确认' }, { value: 'confirmed', label: '已确认' }, { value: 'completed', label: '已完成' }, { value: 'cancelled', label: '已取消' }, { value: 'no_show', label: '未到诊' }, ]; /** 状态流转规则 */ const STATUS_TRANSITIONS: Record = { pending: [ { value: 'confirmed', label: '确认' }, { value: 'cancelled', label: '取消' }, ], confirmed: [ { value: 'completed', label: '完成' }, { value: 'no_show', label: '未到诊' }, { value: 'cancelled', label: '取消' }, ], completed: [], cancelled: [], no_show: [ { value: 'confirmed', label: '重新确认' }, ], }; export default function AppointmentList() { const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [loading, setLoading] = useState(false); const [statusFilter, setStatusFilter] = useState(undefined); const [dateFilter, setDateFilter] = useState(null); const [modalOpen, setModalOpen] = useState(false); const [form] = Form.useForm(); // 患者选择状态(受控组件,不挂在 Form.Item 上) const [selectedPatientId, setSelectedPatientId] = useState(undefined); const [selectedDoctorId, setSelectedDoctorId] = useState(undefined); const [nameCache, setNameCache] = useState>({}); // ---- 数据获取 ---- const fetchData = useCallback(async (p = page, ps = pageSize) => { setLoading(true); try { const result = await appointmentApi.list({ page: p, page_size: ps, status: statusFilter || undefined, date: dateFilter ? dateFilter.format('YYYY-MM-DD') : undefined, }); const items = result.data; // 批量解析患者和医生名称(分别调用对应 API) const missingPatientIds = new Set(); const missingDoctorIds = new Set(); items.forEach((a) => { if (a.patient_id && !nameCache[a.patient_id]) missingPatientIds.add(a.patient_id); if (a.doctor_id && !nameCache[a.doctor_id]) missingDoctorIds.add(a.doctor_id); }); const newCache: Record = {}; await Promise.allSettled([ ...Array.from(missingPatientIds).map(async (id) => { try { const p = await patientApi.get(id); newCache[id] = p.name; } catch { /* skip */ } }), ...Array.from(missingDoctorIds).map(async (id) => { try { const d = await doctorApi.get(id); newCache[id] = d.name; } catch { /* skip */ } }), ]); if (Object.keys(newCache).length > 0) { setNameCache((prev) => ({ ...prev, ...newCache })); } setData(items); setTotal(result.total); } catch { message.error('加载预约列表失败'); } finally { setLoading(false); } }, [page, pageSize, statusFilter, dateFilter]); useEffect(() => { fetchData(); }, [fetchData]); // ---- 状态变更 ---- const DESTRUCTIVE_STATUSES = new Set(['cancelled', 'no_show']); const handleStatusChange = (record: Appointment, newStatus: string) => { const transition = STATUS_TRANSITIONS[record.status]?.find((t) => t.value === newStatus); if (!transition) return; if (DESTRUCTIVE_STATUSES.has(newStatus)) { let cancelReason = ''; Modal.confirm({ title: `确认${transition.label}`, content: newStatus === 'cancelled' ? ( { cancelReason = e.target.value; }} /> ) : ( 确定将此预约标记为"{transition.label}"? ), okText: '确认', cancelText: '取消', onOk: async () => { try { await appointmentApi.updateStatus(record.id, { status: newStatus, version: record.version, ...(newStatus === 'cancelled' && { cancel_reason: cancelReason }), }); message.success('状态更新成功'); fetchData(page, pageSize); } catch { message.error('状态更新失败'); } }, }); } else { Modal.confirm({ title: `确认${transition.label}`, content: `确定将此预约状态变更为"${transition.label}"?`, okText: '确认', cancelText: '取消', onOk: async () => { try { await appointmentApi.updateStatus(record.id, { status: newStatus, version: record.version, }); message.success('状态更新成功'); fetchData(page, pageSize); } catch { message.error('状态更新失败'); } }, }); } }; // ---- 新建预约 ---- const openCreate = () => { form.resetFields(); setSelectedPatientId(undefined); setSelectedDoctorId(undefined); setModalOpen(true); }; const handleSubmit = async (values: { appointment_date: Dayjs; start_time: Dayjs; end_time: Dayjs; appointment_type?: string; notes?: string; }) => { if (!selectedPatientId) { message.warning('请选择患者'); return; } if (!selectedDoctorId) { message.warning('请选择医护'); return; } try { const req: CreateAppointmentReq = { patient_id: selectedPatientId, doctor_id: selectedDoctorId || undefined, appointment_date: values.appointment_date.format('YYYY-MM-DD'), start_time: values.start_time.format('HH:mm'), end_time: values.end_time.format('HH:mm'), appointment_type: values.appointment_type || 'outpatient', notes: values.notes || undefined, }; await appointmentApi.create(req); message.success('预约创建成功'); setModalOpen(false); form.resetFields(); setSelectedPatientId(undefined); setSelectedDoctorId(undefined); fetchData(page, pageSize); } catch { message.error('创建预约失败'); } }; // ---- 列定义 ---- const columns = [ { title: '患者', dataIndex: 'patient_name', key: 'patient_name', width: 100, render: (_: unknown, record: Appointment) => record.patient_name ?? nameCache[record.patient_id] ?? record.patient_id.slice(0, 8), }, { title: '医护', dataIndex: 'doctor_name', key: 'doctor_name', width: 100, render: (_: unknown, record: Appointment) => { return record.doctor_name || nameCache[record.doctor_id ?? ''] || record.doctor_id?.slice(0, 8) || '-'; }, }, { title: '预约类型', dataIndex: 'appointment_type', key: 'appointment_type', width: 90, render: (val: string) => APPOINTMENT_TYPE_MAP[val] || val, }, { title: '预约日期', dataIndex: 'appointment_date', key: 'appointment_date', width: 120, render: (val: string) => val || '-', }, { title: '时段', key: 'time_range', width: 120, render: (_: unknown, record: Appointment) => record.start_time && record.end_time ? `${record.start_time} - ${record.end_time}` : '-', }, { title: '状态', dataIndex: 'status', key: 'status', width: 100, render: (val: string) => , }, { title: '备注', dataIndex: 'notes', key: 'notes', width: 180, ellipsis: true, render: (val: string) => val || '-', }, { title: '操作', key: 'action', width: 100, fixed: 'right' as const, render: (_: unknown, record: Appointment) => { const transitions = STATUS_TRANSITIONS[record.status] || []; if (transitions.length === 0) { return 无可用操作; } return ( ({ key: t.value, label: t.label, onClick: () => handleStatusChange(record, t.value), })), }} > ); }, }, ]; return ( {/* 筛选栏 */} ); }