完善签名功能
This commit is contained in:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user