179 lines
6.8 KiB
TypeScript
179 lines
6.8 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
|
||
import type { ExamClient, ExamModalTab } from '../../data/mockData';
|
||
import type { CustomerAppointmentInfo, CustomerExamAddItem, CustomerInfo, PhysicalExamProgressItem } from '../../api';
|
||
import { getCustomerDetail, getPhysicalExamProgress } from '../../api';
|
||
import { ExamDetailPanel } from './ExamDetailPanel';
|
||
import { ExamAddonPanel } from './ExamAddonPanel';
|
||
import { ExamPrintPanel } from './ExamPrintPanel';
|
||
import { ExamDeliveryPanel } from './ExamDeliveryPanel';
|
||
import { ExamSignPanel } from './ExamSignPanel';
|
||
|
||
interface ExamModalProps {
|
||
client: ExamClient;
|
||
tab: ExamModalTab;
|
||
onTabChange: (key: ExamModalTab) => void;
|
||
onClose: () => void;
|
||
}
|
||
|
||
export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps) => {
|
||
const tabs: { key: ExamModalTab; label: string }[] = [
|
||
{ key: 'detail', label: '详情' },
|
||
{ key: 'sign', label: '签到' },
|
||
{ key: 'addon', label: '加项' },
|
||
// { key: 'print', label: '打印导检单' },
|
||
{ key: 'delivery', label: '报告寄送' },
|
||
];
|
||
|
||
const signDone = ((client as any).is_sign_in === 1) || client.signStatus === '已签到' || client.checkedItems.includes('签到');
|
||
const addonDone = (client.addonCount || 0) > 0;
|
||
const printDone = !!client.guidePrinted;
|
||
const deliveryDone = !!client.deliveryDone;
|
||
|
||
const tabDone: Record<ExamModalTab, boolean> = {
|
||
detail: false,
|
||
sign: signDone,
|
||
addon: addonDone,
|
||
print: printDone,
|
||
delivery: deliveryDone,
|
||
};
|
||
|
||
const handleDoubleClick = (e: React.MouseEvent) => {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
};
|
||
|
||
const handleTouchStart = (e: React.TouchEvent) => {
|
||
if (e.touches.length > 1) {
|
||
e.preventDefault();
|
||
}
|
||
};
|
||
|
||
const [detailLoading, setDetailLoading] = useState(false);
|
||
const [customerInfo, setCustomerInfo] = useState<CustomerInfo | null>(null);
|
||
const [appointmentInfo, setAppointmentInfo] = useState<CustomerAppointmentInfo | null>(null);
|
||
const [addItemInfoList, setAddItemInfoList] = useState<CustomerExamAddItem[] | null>(null);
|
||
const [progressList, setProgressList] = useState<PhysicalExamProgressItem[] | null>(null);
|
||
const [signBusy, setSignBusy] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const physical_exam_id = Number(client.id);
|
||
if (!physical_exam_id) return;
|
||
setDetailLoading(true);
|
||
Promise.all([
|
||
getCustomerDetail({ physical_exam_id }),
|
||
getPhysicalExamProgress({ physical_exam_id }),
|
||
])
|
||
.then(([detailRes, progressRes]) => {
|
||
setCustomerInfo(detailRes.Data?.customerInfo ?? null);
|
||
setAppointmentInfo(detailRes.Data?.appointmentInfo ?? null);
|
||
setAddItemInfoList(detailRes.Data?.addItemInfoList ?? null);
|
||
setProgressList(progressRes.Data ?? null);
|
||
})
|
||
.catch((err) => {
|
||
console.error('获取客户详情/进度失败', err);
|
||
})
|
||
.finally(() => setDetailLoading(false));
|
||
}, [client.id]);
|
||
|
||
const refetchDetail = () => {
|
||
const physical_exam_id = Number(client.id);
|
||
if (!physical_exam_id) return;
|
||
getCustomerDetail({ physical_exam_id }).then((detailRes) => {
|
||
setCustomerInfo(detailRes.Data?.customerInfo ?? null);
|
||
setAppointmentInfo(detailRes.Data?.appointmentInfo ?? null);
|
||
setAddItemInfoList(detailRes.Data?.addItemInfoList ?? null);
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div
|
||
className='fixed inset-0 z-40 flex items-center justify-center bg-black/50'
|
||
style={{ touchAction: 'none' }}
|
||
onDoubleClick={handleDoubleClick}
|
||
onTouchStart={handleTouchStart}
|
||
>
|
||
<div
|
||
className='w-[960px] max-w-[95vw] max-h-[95vh] bg-white rounded-2xl shadow-xl overflow-hidden text-sm'
|
||
style={{ touchAction: 'none' }}
|
||
onDoubleClick={handleDoubleClick}
|
||
onTouchStart={handleTouchStart}
|
||
>
|
||
<div className='px-8 pt-6 pb-2'>
|
||
<div className='flex items-center justify-between'>
|
||
<div className='flex items-end gap-3'>
|
||
<span className='text-2xl font-bold text-gray-900 leading-none'>
|
||
{client.name}
|
||
</span>
|
||
<span className='inline-flex items-center justify-center bg-indigo-100 text-indigo-600 text-[11px] font-bold px-2 py-0.5 rounded h-5 align-bottom'>
|
||
VIP
|
||
</span>
|
||
<span className='text-sm text-gray-400 ml-1 leading-none mb-0.5'>
|
||
体检号:{client.id}
|
||
</span>
|
||
</div>
|
||
<button
|
||
className={`text-sm transition-colors ${signBusy ? 'text-gray-300 cursor-not-allowed' : 'text-gray-400 hover:text-gray-600'}`}
|
||
onClick={!signBusy ? onClose : undefined}
|
||
disabled={signBusy}
|
||
>
|
||
关闭
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className='px-8 py-4 flex items-center justify-between'>
|
||
<div className='flex items-center gap-3'>
|
||
{tabs.map((t) => {
|
||
const isActive = tab === t.key;
|
||
const isDone = tabDone[t.key];
|
||
return (
|
||
<button
|
||
key={t.key}
|
||
onClick={() => onTabChange(t.key)}
|
||
className={`px-5 py-2 rounded-2xl border text-sm transition-all duration-200 ${isActive
|
||
? 'bg-blue-600 text-white border-blue-600 shadow-md shadow-blue-200'
|
||
: isDone
|
||
? 'bg-gray-50 text-gray-400 border-gray-100'
|
||
: 'bg-white text-gray-600 border-gray-200 hover:border-gray-300'
|
||
}`}
|
||
disabled={signBusy}
|
||
>
|
||
<span className='flex items-center gap-1'>
|
||
{t.label}
|
||
{isDone && <span>✅</span>}
|
||
{t.key === 'addon' && (client.addonCount || 0) > 0 && (
|
||
<span className={`text-xs ${isActive ? 'text-blue-100' : 'text-gray-400'}`}>
|
||
({client.addonCount})
|
||
</span>
|
||
)}
|
||
</span>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
<div className='px-4 py-4 bg-gray-50/60'>
|
||
{tab === 'detail' && (
|
||
<ExamDetailPanel
|
||
client={client}
|
||
customerInfo={customerInfo}
|
||
appointmentInfo={appointmentInfo}
|
||
addItemInfoList={addItemInfoList}
|
||
progressList={progressList}
|
||
loading={detailLoading}
|
||
onCustomerUpdated={refetchDetail}
|
||
/>
|
||
)}
|
||
{tab === 'sign' && <ExamSignPanel examId={Number(client.id)} onBusyChange={setSignBusy} />}
|
||
{tab === 'addon' && <ExamAddonPanel client={client} onGoToSign={() => onTabChange('sign')} />}
|
||
{tab === 'print' && <ExamPrintPanel client={client} />}
|
||
{tab === 'delivery' && <ExamDeliveryPanel client={client} />}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|