添加canvas签名

This commit is contained in:
xianyi
2025-12-12 16:40:44 +08:00
parent ad696d2fcb
commit 2e62ce4a1d
3 changed files with 246 additions and 4 deletions

View File

@@ -5,10 +5,12 @@ import type {
CustomerAppointmentInfo,
CustomerExamAddItem,
CustomerInfo,
OutputTongyishuFileInfo,
PhysicalExamProgressItem,
} from '../../api';
import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf } from '../../api';
import { Button, Input } from '../ui';
import type { SignaturePadHandle } from '../ui';
import { Button, Input, SignaturePad } from '../ui';
interface ExamModalProps {
client: ExamClient;
@@ -164,7 +166,7 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
loading={detailLoading}
/>
)}
{tab === 'sign' && <ExamSignPanel />}
{tab === 'sign' && <ExamSignPanel examId={Number(client.id)} />}
{tab === 'addon' && <ExamAddonPanel client={client} />}
{tab === 'print' && <ExamPrintPanel client={client} />}
{tab === 'delivery' && <ExamDeliveryPanel client={client} />}
@@ -175,12 +177,18 @@ export const ExamModal = ({ client, tab, onTabChange, onClose }: ExamModalProps)
);
};
const ExamSignPanel = () => {
const ExamSignPanel = ({ examId }: { examId?: number }) => {
const [idNo, setIdNo] = useState('');
const [ocrLoading, setOcrLoading] = useState(false);
const [signLoading, setSignLoading] = useState(false);
const [message, setMessage] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [consentList, setConsentList] = useState<OutputTongyishuFileInfo[]>([]);
const [consentLoading, setConsentLoading] = useState(false);
const [consentMessage, setConsentMessage] = useState<string | null>(null);
const [previewPdf, setPreviewPdf] = useState<OutputTongyishuFileInfo | null>(null);
const [showSignature, setShowSignature] = useState(false);
const signaturePadRef = useRef<SignaturePadHandle | null>(null);
const handlePickFile = () => {
fileInputRef.current?.click();
@@ -230,6 +238,29 @@ const ExamSignPanel = () => {
}
};
useEffect(() => {
if (!examId) {
setConsentList([]);
setConsentMessage('缺少体检ID无法获取知情同意书');
return;
}
setConsentLoading(true);
setConsentMessage(null);
getTongyishuPdf({ exam_id: examId })
.then((res) => {
const list = res.Data?.list_pdf_url || [];
setConsentList(list);
if (!list.length) {
setConsentMessage(res.Data?.message || '暂无知情同意书');
}
})
.catch((err) => {
console.error('获取知情同意书失败', err);
setConsentMessage('知情同意书加载失败,请稍后重试');
})
.finally(() => setConsentLoading(false));
}, [examId]);
return (
<div className='grid grid-cols-2 gap-4 text-sm'>
<div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'>
@@ -268,8 +299,82 @@ const ExamSignPanel = () => {
<div className='p-4 rounded-2xl border bg-gray-50/60 flex flex-col gap-3'>
<div className='font-medium'></div>
<div className='text-xs text-gray-500'></div>
<Button className='py-1.5 px-3'></Button>
<div className='flex flex-col gap-2'>
{consentLoading && <div className='text-xs text-gray-500'>...</div>}
{!consentLoading && consentMessage && (
<div className='text-xs text-amber-600'>{consentMessage}</div>
)}
{!consentLoading && consentList.length > 0 && (
<div className='space-y-2'>
{consentList.map((item) => (
<div
key={item.pdf_url || item.pdf_name}
className='flex items-center justify-between gap-3 p-3 rounded-xl border bg-white shadow-sm'
>
<div className='text-sm text-gray-800 truncate'>{item.pdf_name}</div>
<Button className='py-1.5 px-3' onClick={() => setPreviewPdf(item)}>
</Button>
</div>
))}
</div>
)}
</div>
</div>
{previewPdf && (
<div className='fixed inset-0 z-[60] bg-black/75 flex flex-col'>
<div className='flex items-center justify-between p-4 text-white bg-gray-900/80'>
<div className='text-sm font-medium truncate pr-3'>{previewPdf.pdf_name}</div>
<div className='flex items-center gap-2'>
<Button
className='py-1 px-3'
onClick={() => setShowSignature(true)}
>
</Button>
<Button className='py-1 px-3' onClick={() => setPreviewPdf(null)}>
</Button>
</div>
</div>
<div className='flex-1 bg-gray-100'>
<iframe
src={previewPdf.pdf_url}
title={previewPdf.pdf_name}
className='w-full h-full'
/>
</div>
</div>
)}
{showSignature && (
<div className='fixed inset-0 z-[70] bg-black/80 flex items-center justify-center px-6'>
<div className='bg-white rounded-2xl w-full max-w-3xl shadow-2xl p-4 flex flex-col gap-4'>
<div className='flex items-center justify-between'>
<div className='text-base font-semibold text-gray-900'></div>
<div className='flex items-center gap-2'>
<Button className='py-1 px-3' onClick={() => setShowSignature(false)}>
</Button>
</div>
</div>
<div className='ui7-signature-wrapper border rounded-xl overflow-hidden bg-gray-50'>
<SignaturePad
ref={signaturePadRef}
className='ui7-signature-canvas w-full h-72 bg-white touch-none'
/>
<div className='flex items-center justify-between px-3 py-2 bg-gray-50 border-t'>
<div className='text-xs text-gray-500'></div>
<Button
className='ui7-clear-button py-1 px-3'
onClick={() => signaturePadRef.current?.clear()}
>
</Button>
</div>
</div>
</div>
</div>
)}
</div>
);
};