diff --git a/src/components/exam/ExamAddonPanel.tsx b/src/components/exam/ExamAddonPanel.tsx index 2ad487c..8a3268e 100644 --- a/src/components/exam/ExamAddonPanel.tsx +++ b/src/components/exam/ExamAddonPanel.tsx @@ -367,10 +367,11 @@ export const ExamAddonPanel = ({ client }: ExamAddonPanelProps) => { }); if (res.Status === 200 && res.Data?.pdf_url) { - // 保存加项PDF信息到localStorage + // 保存加项PDF信息到localStorage(未签名状态) setAddItemBillPdf(examId, { pdf_name: res.Data.pdf_name || '加项单', pdf_url: res.Data.pdf_url, + is_signed: false, }); return true; } else { diff --git a/src/components/exam/ExamPrintPanel.tsx b/src/components/exam/ExamPrintPanel.tsx index 7d324c0..00d911a 100644 --- a/src/components/exam/ExamPrintPanel.tsx +++ b/src/components/exam/ExamPrintPanel.tsx @@ -75,6 +75,7 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => { setDaojiandanPdf(examId, { pdf_name: pdfNameValue, pdf_url: pdfUrlValue, + is_signed: true, }); // 记录打印导检单是否签名操作 setExamActionRecord(examId, 'printSign', true); @@ -134,13 +135,8 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => { const res = await getDaojiandanPdfApi({ exam_id: examId }); if (res.Status === 200 && res.Data?.pdf_url) { const pdfUrlValue = res.Data.pdf_url; - const pdfNameValue = res.Data.pdf_name || '导检单'; setPdfUrl(pdfUrlValue); - // 保存到localStorage - setDaojiandanPdf(examId, { - pdf_name: pdfNameValue, - pdf_url: pdfUrlValue, - }); + // 获取到的导检单是未签名的,不保存到localStorage,只用于显示 setShowPreview(true); } else { setError(res.Message || '获取导检单失败'); diff --git a/src/components/exam/ExamSignPanel.tsx b/src/components/exam/ExamSignPanel.tsx index 6df3117..65541ba 100644 --- a/src/components/exam/ExamSignPanel.tsx +++ b/src/components/exam/ExamSignPanel.tsx @@ -3,13 +3,14 @@ import * as pdfjsLib from 'pdfjs-dist'; import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url'; import type { OutputTongyishuFileInfo } from '../../api'; -import { getTongyishuPdf, signInMedicalExamCenter, submitTongyishuSign, submitDaojiandanSign, editDaojiandanPrintStatus, getDaojiandanPdf as getDaojiandanPdfApi } from '../../api'; +import { getTongyishuPdf, signInMedicalExamCenter, submitTongyishuSign, submitDaojiandanSign, editDaojiandanPrintStatus, getDaojiandanPdf as getDaojiandanPdfApi, getAddItemBillPdf as getAddItemBillPdfApi, submitAddItemBillSign } from '../../api'; import { setExamActionRecord, setTongyishuPdfList, getTongyishuPdfList, setDaojiandanPdf, getDaojiandanPdf as getDaojiandanPdfFromStorage, + setAddItemBillPdf, getAddItemBillPdf as getAddItemBillPdfFromStorage, type TongyishuPdfInfo, } from '../../utils/examActions'; @@ -70,9 +71,14 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { // 加项单相关状态 const [addItemBillUrl, setAddItemBillUrl] = useState(null); const [addItemBillName, setAddItemBillName] = useState('加项单'); + const [isAddItemBillSigned, setIsAddItemBillSigned] = useState(false); // 加项单是否已签名 const [showAddItemBillPreview, setShowAddItemBillPreview] = useState(false); + const [showAddItemBillSignature, setShowAddItemBillSignature] = useState(false); + const addItemBillSignaturePadRef = useRef(null); + const [addItemBillSubmitLoading, setAddItemBillSubmitLoading] = useState(false); + const [addItemBillSubmitMessage, setAddItemBillSubmitMessage] = useState(null); - const busy = signLoading || submitLoading || consentLoading || pdfLoading || daojiandanSubmitLoading; + const busy = signLoading || submitLoading || consentLoading || pdfLoading || daojiandanSubmitLoading || addItemBillSubmitLoading; useEffect(() => { onBusyChange?.(busy); @@ -303,11 +309,12 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { useEffect(() => { if (!examId) return; - // 先检查 localStorage 中的导检单(已签名的) + // 先检查 localStorage 中的导检单 const storedPdf = getDaojiandanPdfFromStorage(examId); if (storedPdf && storedPdf.pdf_url) { setDaojiandanUrl(storedPdf.pdf_url); - setIsDaojiandanSigned(true); // localStorage 中的是已签名的 + // 使用 localStorage 中保存的 is_signed 字段判断是否已签名 + setIsDaojiandanSigned(storedPdf.is_signed === true); } else { // 如果 localStorage 中没有导检单,调用接口获取未签名的导检单用于查看和签名 const fetchDaojiandan = async () => { @@ -326,11 +333,31 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { fetchDaojiandan(); } - // 检查加项单PDF + // 检查加项单PDF(逻辑和导检单一样) const storedAddItemBill = getAddItemBillPdfFromStorage(examId); if (storedAddItemBill && storedAddItemBill.pdf_url) { setAddItemBillUrl(storedAddItemBill.pdf_url); setAddItemBillName(storedAddItemBill.pdf_name || '加项单'); + // 使用 localStorage 中保存的 is_signed 字段判断是否已签名 + setIsAddItemBillSigned(storedAddItemBill.is_signed === true); + } else { + // 如果 localStorage 中没有加项单,调用接口获取未签名的加项单用于查看和签名 + const fetchAddItemBill = async () => { + try { + const res = await getAddItemBillPdfApi({ exam_id: examId }); + if (res.Status === 200 && res.Data?.pdf_url) { + const pdfUrlValue = res.Data.pdf_url; + const pdfNameValue = res.Data.pdf_name || '加项单'; + // 不保存到 localStorage,只用于显示和签名 + setAddItemBillUrl(pdfUrlValue); + setAddItemBillName(pdfNameValue); + setIsAddItemBillSigned(false); // 接口获取的是未签名的 + } + } catch (err) { + console.error('获取加项单失败', err); + } + }; + fetchAddItemBill(); } }, [examId]); @@ -620,10 +647,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { setDaojiandanUrl(pdfUrlValue); setIsDaojiandanSigned(true); // 签名成功后标记为已签名 - // 保存导检单PDF信息到localStorage + // 保存导检单PDF信息到localStorage(标记为已签名) setDaojiandanPdf(examId, { pdf_name: pdfNameValue, pdf_url: pdfUrlValue, + is_signed: true, }); // 记录打印导检单是否签名操作 @@ -653,6 +681,61 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { } }; + // 加项单签名提交 + const handleSubmitAddItemBillSign = async () => { + if (!examId) { + setAddItemBillSubmitMessage('缺少必要信息,无法提交签名'); + return; + } + + const dataUrl = addItemBillSignaturePadRef.current?.toDataURL('image/png'); + if (!dataUrl) { + setAddItemBillSubmitMessage('请先完成签名'); + return; + } + + setAddItemBillSubmitLoading(true); + setAddItemBillSubmitMessage(null); + try { + const blob = await fetch(dataUrl).then((r) => r.blob()); + + const res = await submitAddItemBillSign({ + exam_id: examId, + sign_file: blob, + }); + + if (res.Status === 200 && res.Data?.pdf_url) { + setAddItemBillSubmitMessage('签名提交成功'); + const pdfUrlValue = res.Data.pdf_url; + const pdfNameValue = res.Data.pdf_name || '加项单'; + setAddItemBillUrl(pdfUrlValue); + setAddItemBillName(pdfNameValue); + setIsAddItemBillSigned(true); // 签名成功后标记为已签名 + + // 保存加项单PDF信息到localStorage(标记为已签名) + setAddItemBillPdf(examId, { + pdf_name: pdfNameValue, + pdf_url: pdfUrlValue, + is_signed: true, + }); + + setTimeout(() => { + setShowAddItemBillSignature(false); + setAddItemBillSubmitMessage(null); + addItemBillSignaturePadRef.current?.clear(); + setShowAddItemBillPreview(true); + }, 2000); + } else { + setAddItemBillSubmitMessage(res.Message || '签名提交失败'); + } + } catch (err) { + console.error('提交签名失败', err); + setAddItemBillSubmitMessage('签名提交失败,请稍后重试'); + } finally { + setAddItemBillSubmitLoading(false); + } + }; + // 加项单直接打印 const handleAddItemBillDirectPrint = async () => { if (!addItemBillUrl) return; @@ -1265,34 +1348,65 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
{addItemBillName.length > 12 ? addItemBillName.slice(0, 12) + "..." : addItemBillName} - 已生成 + {isAddItemBillSigned && ( + 已签名 + )}
- - + {isAddItemBillSigned ? ( + // 已签名:显示打印和查看按钮 + <> + + + + ) : ( + // 未签名:显示查看和签名按钮 + <> + + + + )}
)} @@ -1471,19 +1585,85 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => { ) } + { + showAddItemBillSignature && ( +
+
+
+
签署加项单
+
+ +
+
+
+ +
+
请在上方区域签名
+ +
+
+ {addItemBillSubmitMessage && ( +
{addItemBillSubmitMessage}
+ )} +
+ + +
+
+
+ ) + } { showAddItemBillPreview && addItemBillUrl && (
{addItemBillName}
- + {isAddItemBillSigned && ( + + )} + {!isAddItemBillSigned && ( + + )} diff --git a/src/utils/examActions.ts b/src/utils/examActions.ts index 0e7507e..f11eef5 100644 --- a/src/utils/examActions.ts +++ b/src/utils/examActions.ts @@ -146,6 +146,8 @@ export interface DaojiandanPdfInfo { pdf_name: string; /** PDF文件地址 */ pdf_url: string; + /** 是否已签名 */ + is_signed?: boolean; } /** @@ -196,6 +198,8 @@ export interface AddItemBillPdfInfo { pdf_name: string; /** PDF文件地址 */ pdf_url: string; + /** 是否已签名 */ + is_signed?: boolean; } /**