From 08382a0794b4e6afbd7a8df698bff1a902ca2a88 Mon Sep 17 00:00:00 2001 From: xianyi Date: Fri, 12 Dec 2025 17:12:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=AD=BE=E5=90=8D=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/sign.png | Bin 0 -> 9301 bytes src/components/exam/ExamModal.tsx | 116 +++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 public/sign.png diff --git a/public/sign.png b/public/sign.png new file mode 100644 index 0000000000000000000000000000000000000000..13afac6047b9313576a70f5027f5194ae744d75c GIT binary patch literal 9301 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 literal 0 HcmV?d00001 diff --git a/src/components/exam/ExamModal.tsx b/src/components/exam/ExamModal.tsx index 256c34e..a8489e4 100644 --- a/src/components/exam/ExamModal.tsx +++ b/src/components/exam/ExamModal.tsx @@ -8,7 +8,7 @@ import type { OutputTongyishuFileInfo, PhysicalExamProgressItem, } from '../../api'; -import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf } from '../../api'; +import { getCustomerDetail, getPhysicalExamProgressDetail, signInMedicalExamCenter, getTongyishuPdf, submitTongyishuSign } from '../../api'; import type { SignaturePadHandle } from '../ui'; import { Button, Input, SignaturePad } from '../ui'; @@ -189,11 +189,31 @@ const ExamSignPanel = ({ examId }: { examId?: number }) => { const [previewPdf, setPreviewPdf] = useState(null); const [showSignature, setShowSignature] = useState(false); const signaturePadRef = useRef(null); + const [submitLoading, setSubmitLoading] = useState(false); + const [submitMessage, setSubmitMessage] = useState(null); + const [signedCombinationCodes, setSignedCombinationCodes] = useState([]); + + const SIGN_STORAGE_KEY = 'yh_signed_consents'; const handlePickFile = () => { fileInputRef.current?.click(); }; + useEffect(() => { + if (typeof window === 'undefined') return; + const raw = localStorage.getItem(SIGN_STORAGE_KEY); + if (raw) { + try { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + setSignedCombinationCodes(parsed.filter((x) => typeof x === 'number')); + } + } catch (err) { + console.warn('签名记录解析失败', err); + } + } + }, []); + const mockOcr = async (file: File) => { // 简单模拟 OCR:提取文件名中的数字,或返回示例身份证 const match = file.name.match(/\d{6,18}/); @@ -238,6 +258,61 @@ const ExamSignPanel = ({ examId }: { examId?: number }) => { } }; + const handleSubmitSign = async () => { + if (!examId || !previewPdf?.combination_code) { + setSubmitMessage('缺少必要信息,无法提交签名'); + return; + } + + const dataUrl = signaturePadRef.current?.toDataURL('image/png'); + if (!dataUrl) { + setSubmitMessage('请先完成签名'); + return; + } + + setSubmitLoading(true); + setSubmitMessage(null); + try { + // 将 base64 转换为 Blob + const blob = await fetch(dataUrl).then(r => r.blob()); + + // 提交签名 + const res = await submitTongyishuSign({ + exam_id: examId, + combination_code: previewPdf.combination_code, + sign_file: blob, + }); + + if (res.Status === 200) { + setSubmitMessage('签名提交成功'); + // 记录已签名的组合代码 + setSignedCombinationCodes((prev) => { + const code = Number(previewPdf.combination_code); + if (!Number.isFinite(code)) return prev || []; + const next = Array.from(new Set([...(prev || []), code])); + if (typeof window !== 'undefined') { + localStorage.setItem(SIGN_STORAGE_KEY, JSON.stringify(next)); + } + return next; + }); + // 2秒后关闭弹窗 + setTimeout(() => { + setShowSignature(false); + setPreviewPdf(null); + setSubmitMessage(null); + signaturePadRef.current?.clear(); + }, 2000); + } else { + setSubmitMessage(res.Message || '签名提交失败'); + } + } catch (err) { + console.error('提交签名失败', err); + setSubmitMessage('签名提交失败,请稍后重试'); + } finally { + setSubmitLoading(false); + } + }; + useEffect(() => { if (!examId) { setConsentList([]); @@ -311,7 +386,18 @@ const ExamSignPanel = ({ examId }: { examId?: number }) => { key={item.pdf_url || item.pdf_name} className='flex items-center justify-between gap-3 p-3 rounded-xl border bg-white shadow-sm' > -

{item.pdf_name}
+
+ {item.pdf_name} + {item.combination_code !== undefined && + signedCombinationCodes.includes(Number(item.combination_code)) && ( + 已签名 + )} +
@@ -372,6 +458,32 @@ const ExamSignPanel = ({ examId }: { examId?: number }) => { + {submitMessage && ( +
+ {submitMessage} +
+ )} +
+ + +
)}