@@ -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 < string | null > ( null ) ;
const [ addItemBillName , setAddItemBillName ] = useState < string > ( '加项单' ) ;
const [ isAddItemBillSigned , setIsAddItemBillSigned ] = useState ( false ) ; // 加项单是否已签名
const [ showAddItemBillPreview , setShowAddItemBillPreview ] = useState ( false ) ;
const [ showAddItemBillSignature , setShowAddItemBillSignature ] = useState ( false ) ;
const addItemBillSignaturePadRef = useRef < SignaturePadHandle | null > ( null ) ;
const [ addItemBillSubmitLoading , setAddItemBillSubmitLoading ] = useState ( false ) ;
const [ addItemBillSubmitMessage , setAddItemBillSubmitMessage ] = useState < string | null > ( 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,14 +1348,19 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
< div className = 'flex items-center justify-between gap-3 p-2 rounded-xl border bg-white shadow-sm' >
< div className = 'text-sm text-gray-800 truncate flex items-center gap-2 relative pr-20' >
< span className = 'truncate' > { addItemBillName . length > 12 ? addItemBillName . slice ( 0 , 12 ) + "..." : addItemBillName } < / span >
{ isAddItemBillSigned && (
< img
src = '/sign.png'
alt = '已生成 '
alt = '已签名 '
className = 'w-16 h-16 absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none select-none'
loading = 'lazy'
/ >
) }
< / div >
< div className = 'flex items-center gap-2' >
{ isAddItemBillSigned ? (
// 已签名:显示打印和查看按钮
< >
< Button
className = 'py-1.5 px-3 bg-blue-600 hover:bg-blue-700 text-white'
onClick = { ( ) = > {
@@ -1293,6 +1381,32 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
>
查 看
< / Button >
< / >
) : (
// 未签名:显示查看和签名按钮
< >
< Button
className = 'py-1.5 px-3'
onClick = { ( ) = > {
if ( busy ) return ;
setShowAddItemBillPreview ( true ) ;
} }
disabled = { busy }
>
查 看
< / Button >
< Button
className = 'py-1.5 px-3'
onClick = { ( ) = > {
if ( busy ) return ;
setShowAddItemBillSignature ( true ) ;
} }
disabled = { busy }
>
签 名
< / Button >
< / >
) }
< / div >
< / div >
) }
@@ -1471,12 +1585,64 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
< / div >
)
}
{
showAddItemBillSignature && (
< 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 = { ( ) = > setShowAddItemBillSignature ( false ) } disabled = { busy } >
关 闭
< / Button >
< / div >
< / div >
< div className = 'ui7-signature-wrapper border rounded-xl overflow-hidden bg-gray-50' >
< SignaturePad
ref = { addItemBillSignaturePadRef }
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 = { ( ) = > addItemBillSignaturePadRef . current ? . clear ( ) } disabled = { addItemBillSubmitLoading } >
清 除
< / Button >
< / div >
< / div >
{ addItemBillSubmitMessage && (
< div className = { ` text-sm text-center ${ addItemBillSubmitMessage . includes ( '成功' ) ? 'text-green-600' : 'text-amber-600' } ` } > { addItemBillSubmitMessage } < / div >
) }
< div className = 'flex items-center justify-end gap-3' >
< Button
className = 'py-2 px-6'
onClick = { ( ) = > {
setShowAddItemBillSignature ( false ) ;
setAddItemBillSubmitMessage ( null ) ;
addItemBillSignaturePadRef . current ? . clear ( ) ;
} }
disabled = { addItemBillSubmitLoading }
>
取 消
< / Button >
< Button
className = 'py-2 px-6 bg-blue-600 text-white hover:bg-blue-700'
onClick = { handleSubmitAddItemBillSign }
disabled = { addItemBillSubmitLoading }
>
{ addItemBillSubmitLoading ? '提交中...' : '提交签名' }
< / Button >
< / div >
< / div >
< / div >
)
}
{
showAddItemBillPreview && addItemBillUrl && (
< 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' > { addItemBillName } < / div >
< div className = 'flex items-center gap-2' >
{ isAddItemBillSigned && (
< Button
className = 'py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-50 disabled:cursor-not-allowed'
onClick = { handleAddItemBillDirectPrint }
@@ -1484,6 +1650,20 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
>
打 印
< / Button >
) }
{ ! isAddItemBillSigned && (
< Button
className = 'py-1 px-3 bg-blue-600 hover:bg-blue-700 text-white'
onClick = { ( ) = > {
if ( busy ) return ;
setShowAddItemBillPreview ( false ) ;
setShowAddItemBillSignature ( true ) ;
} }
disabled = { busy }
>
签 名
< / Button >
) }
< Button className = 'py-1 px-3' onClick = { ( ) = > ! busy && setShowAddItemBillPreview ( false ) } disabled = { busy } >
关 闭
< / Button >