完善签名功能

This commit is contained in:
xianyi
2025-12-12 17:12:12 +08:00
parent 227e89b32b
commit 08382a0794
2 changed files with 114 additions and 2 deletions

BIN
public/sign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -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<OutputTongyishuFileInfo | null>(null);
const [showSignature, setShowSignature] = useState(false);
const signaturePadRef = useRef<SignaturePadHandle | null>(null);
const [submitLoading, setSubmitLoading] = useState(false);
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
const [signedCombinationCodes, setSignedCombinationCodes] = useState<number[]>([]);
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'
>
<div className='text-sm text-gray-800 truncate'>{item.pdf_name}</div>
<div className='text-sm text-gray-800 truncate flex items-center gap-2 relative pr-20'>
<span className='truncate'>{item.pdf_name}</span>
{item.combination_code !== undefined &&
signedCombinationCodes.includes(Number(item.combination_code)) && (
<img
src='/sign.png'
alt='已签名'
className='w-16 h-16 absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none select-none'
loading='lazy'
/>
)}
</div>
<Button className='py-1.5 px-3' onClick={() => setPreviewPdf(item)}>
</Button>
@@ -372,6 +458,32 @@ const ExamSignPanel = ({ examId }: { examId?: number }) => {
</Button>
</div>
</div>
{submitMessage && (
<div className={`text-sm text-center ${submitMessage.includes('成功') ? 'text-green-600' : 'text-amber-600'
}`}>
{submitMessage}
</div>
)}
<div className='flex items-center justify-end gap-3'>
<Button
className='py-2 px-6'
onClick={() => {
setShowSignature(false);
setSubmitMessage(null);
signaturePadRef.current?.clear();
}}
disabled={submitLoading}
>
</Button>
<Button
className='py-2 px-6 bg-blue-600 text-white hover:bg-blue-700'
onClick={handleSubmitSign}
disabled={submitLoading}
>
{submitLoading ? '提交中...' : '提交签名'}
</Button>
</div>
</div>
</div>
)}