导检单和加项单PDF加载状态
This commit is contained in:
@@ -68,6 +68,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
||||
const [daojiandanSubmitLoading, setDaojiandanSubmitLoading] = useState(false);
|
||||
const [daojiandanSubmitMessage, setDaojiandanSubmitMessage] = useState<string | null>(null);
|
||||
const [showDaojiandanPreview, setShowDaojiandanPreview] = useState(false);
|
||||
const [daojiandanPdfData, setDaojiandanPdfData] = useState<ArrayBuffer | null>(null);
|
||||
const [daojiandanPdfLoading, setDaojiandanPdfLoading] = useState(false);
|
||||
const [daojiandanPdfReady, setDaojiandanPdfReady] = useState(false);
|
||||
const [daojiandanPdfBlobUrl, setDaojiandanPdfBlobUrl] = useState<string | null>(null);
|
||||
const daojiandanCanvasContainerRef = useRef<HTMLDivElement>(null);
|
||||
// 加项单相关状态
|
||||
const [addItemBillUrl, setAddItemBillUrl] = useState<string | null>(null);
|
||||
const [addItemBillName, setAddItemBillName] = useState<string>('加项单');
|
||||
@@ -77,6 +82,11 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
||||
const addItemBillSignaturePadRef = useRef<SignaturePadHandle | null>(null);
|
||||
const [addItemBillSubmitLoading, setAddItemBillSubmitLoading] = useState(false);
|
||||
const [addItemBillSubmitMessage, setAddItemBillSubmitMessage] = useState<string | null>(null);
|
||||
const [addItemBillPdfData, setAddItemBillPdfData] = useState<ArrayBuffer | null>(null);
|
||||
const [addItemBillPdfLoading, setAddItemBillPdfLoading] = useState(false);
|
||||
const [addItemBillPdfReady, setAddItemBillPdfReady] = useState(false);
|
||||
const [addItemBillPdfBlobUrl, setAddItemBillPdfBlobUrl] = useState<string | null>(null);
|
||||
const addItemBillCanvasContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const busy = signLoading || submitLoading || consentLoading || pdfLoading || daojiandanSubmitLoading || addItemBillSubmitLoading;
|
||||
|
||||
@@ -453,6 +463,314 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
||||
renderAllPages();
|
||||
}, [pdfData]);
|
||||
|
||||
// 加载导检单 PDF 数据
|
||||
useEffect(() => {
|
||||
if (!showDaojiandanPreview || !daojiandanUrl) {
|
||||
setDaojiandanPdfData(null);
|
||||
setDaojiandanPdfBlobUrl(null);
|
||||
setDaojiandanPdfReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let objectUrl: string | null = null;
|
||||
setDaojiandanPdfReady(false);
|
||||
setDaojiandanPdfLoading(true);
|
||||
setDaojiandanPdfData(null);
|
||||
|
||||
fetch(daojiandanUrl)
|
||||
.then((resp) => {
|
||||
if (!resp.ok) throw new Error('获取PDF文件失败');
|
||||
return resp.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
objectUrl = URL.createObjectURL(blob);
|
||||
setDaojiandanPdfBlobUrl(objectUrl);
|
||||
return blob.arrayBuffer();
|
||||
})
|
||||
.then((arrayBuffer) => {
|
||||
setDaojiandanPdfData(arrayBuffer);
|
||||
setDaojiandanPdfLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('导检单PDF 拉取失败', err);
|
||||
setDaojiandanPdfLoading(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (objectUrl) {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
};
|
||||
}, [showDaojiandanPreview, daojiandanUrl]);
|
||||
|
||||
// 渲染导检单 PDF
|
||||
useEffect(() => {
|
||||
if (!daojiandanPdfData || !daojiandanCanvasContainerRef.current) return;
|
||||
|
||||
setDaojiandanPdfReady(false);
|
||||
|
||||
const renderAllPages = async () => {
|
||||
try {
|
||||
const pdf = await pdfjsLib.getDocument({ data: daojiandanPdfData }).promise;
|
||||
|
||||
if (!daojiandanCanvasContainerRef.current) return;
|
||||
|
||||
// 清空容器
|
||||
daojiandanCanvasContainerRef.current.innerHTML = '';
|
||||
|
||||
const scale = 1.2;
|
||||
|
||||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
||||
const page = await pdf.getPage(pageNum);
|
||||
const viewport = page.getViewport({ scale });
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (!context) continue;
|
||||
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.style.display = 'block';
|
||||
canvas.style.marginBottom = '10px';
|
||||
canvas.className = 'mx-auto border rounded-lg shadow-sm';
|
||||
|
||||
daojiandanCanvasContainerRef.current.appendChild(canvas);
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
} as any;
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
}
|
||||
|
||||
setDaojiandanPdfReady(true);
|
||||
} catch (err) {
|
||||
console.error('导检单PDF 渲染失败', err);
|
||||
setDaojiandanPdfLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
renderAllPages();
|
||||
}, [daojiandanPdfData]);
|
||||
|
||||
// 加载加项单 PDF 数据
|
||||
useEffect(() => {
|
||||
if (!showAddItemBillPreview || !addItemBillUrl) {
|
||||
setAddItemBillPdfData(null);
|
||||
setAddItemBillPdfBlobUrl(null);
|
||||
setAddItemBillPdfReady(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let objectUrl: string | null = null;
|
||||
setAddItemBillPdfReady(false);
|
||||
setAddItemBillPdfLoading(true);
|
||||
setAddItemBillPdfData(null);
|
||||
|
||||
fetch(addItemBillUrl)
|
||||
.then((resp) => {
|
||||
if (!resp.ok) throw new Error('获取PDF文件失败');
|
||||
return resp.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
objectUrl = URL.createObjectURL(blob);
|
||||
setAddItemBillPdfBlobUrl(objectUrl);
|
||||
return blob.arrayBuffer();
|
||||
})
|
||||
.then((arrayBuffer) => {
|
||||
setAddItemBillPdfData(arrayBuffer);
|
||||
setAddItemBillPdfLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('加项单PDF 拉取失败', err);
|
||||
setAddItemBillPdfLoading(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (objectUrl) {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
};
|
||||
}, [showAddItemBillPreview, addItemBillUrl]);
|
||||
|
||||
// 渲染加项单 PDF
|
||||
useEffect(() => {
|
||||
if (!addItemBillPdfData || !addItemBillCanvasContainerRef.current) return;
|
||||
|
||||
setAddItemBillPdfReady(false);
|
||||
|
||||
const renderAllPages = async () => {
|
||||
try {
|
||||
const pdf = await pdfjsLib.getDocument({ data: addItemBillPdfData }).promise;
|
||||
|
||||
if (!addItemBillCanvasContainerRef.current) return;
|
||||
|
||||
// 清空容器
|
||||
addItemBillCanvasContainerRef.current.innerHTML = '';
|
||||
|
||||
const scale = 1.2;
|
||||
|
||||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
||||
const page = await pdf.getPage(pageNum);
|
||||
const viewport = page.getViewport({ scale });
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (!context) continue;
|
||||
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.style.display = 'block';
|
||||
canvas.style.marginBottom = '10px';
|
||||
canvas.className = 'mx-auto border rounded-lg shadow-sm';
|
||||
|
||||
addItemBillCanvasContainerRef.current.appendChild(canvas);
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
} as any;
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
}
|
||||
|
||||
setAddItemBillPdfReady(true);
|
||||
} catch (err) {
|
||||
console.error('加项单PDF 渲染失败', err);
|
||||
setAddItemBillPdfLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
renderAllPages();
|
||||
}, [addItemBillPdfData]);
|
||||
|
||||
// 导检单预览:使用 pdfjs 渲染到 canvas
|
||||
useEffect(() => {
|
||||
if (!showDaojiandanPreview || !daojiandanUrl || !daojiandanCanvasContainerRef.current) return;
|
||||
|
||||
const container = daojiandanCanvasContainerRef.current;
|
||||
let cancelled = false;
|
||||
|
||||
const renderDaojiandan = async () => {
|
||||
try {
|
||||
setDaojiandanPdfLoading(true);
|
||||
container.innerHTML = '';
|
||||
|
||||
const resp = await fetch(daojiandanUrl);
|
||||
if (!resp.ok) throw new Error('获取PDF文件失败');
|
||||
const blob = await resp.blob();
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
|
||||
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
|
||||
const scale = 1.2;
|
||||
|
||||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
||||
if (cancelled) return;
|
||||
|
||||
const page = await pdf.getPage(pageNum);
|
||||
const viewport = page.getViewport({ scale });
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
if (!context) continue;
|
||||
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.style.display = 'block';
|
||||
canvas.style.marginBottom = '10px';
|
||||
canvas.className = 'mx-auto border rounded-lg shadow-sm';
|
||||
|
||||
container.appendChild(canvas);
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
} as any;
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('导检单PDF 渲染失败', err);
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setDaojiandanPdfLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderDaojiandan();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
container.innerHTML = '';
|
||||
};
|
||||
}, [showDaojiandanPreview, daojiandanUrl]);
|
||||
|
||||
// 加项单预览:使用 pdfjs 渲染到 canvas
|
||||
useEffect(() => {
|
||||
if (!showAddItemBillPreview || !addItemBillUrl || !addItemBillCanvasContainerRef.current) return;
|
||||
|
||||
const container = addItemBillCanvasContainerRef.current;
|
||||
let cancelled = false;
|
||||
|
||||
const renderAddItemBill = async () => {
|
||||
try {
|
||||
setAddItemBillPdfLoading(true);
|
||||
container.innerHTML = '';
|
||||
|
||||
const resp = await fetch(addItemBillUrl);
|
||||
if (!resp.ok) throw new Error('获取PDF文件失败');
|
||||
const blob = await resp.blob();
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
|
||||
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
|
||||
const scale = 1.2;
|
||||
|
||||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
||||
if (cancelled) return;
|
||||
|
||||
const page = await pdf.getPage(pageNum);
|
||||
const viewport = page.getViewport({ scale });
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
if (!context) continue;
|
||||
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.style.display = 'block';
|
||||
canvas.style.marginBottom = '10px';
|
||||
canvas.className = 'mx-auto border rounded-lg shadow-sm';
|
||||
|
||||
container.appendChild(canvas);
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
} as any;
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加项单PDF 渲染失败', err);
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setAddItemBillPdfLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderAddItemBill();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
container.innerHTML = '';
|
||||
};
|
||||
}, [showAddItemBillPreview, addItemBillUrl]);
|
||||
|
||||
const handlePrint = () => {
|
||||
if (!pdfBlobUrl || !pdfReady || !canvasContainerRef.current) return;
|
||||
|
||||
@@ -1574,13 +1892,19 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-1 bg-gray-100 overflow-auto'>
|
||||
<div className='flex justify-center p-4'>
|
||||
<iframe
|
||||
src={daojiandanUrl}
|
||||
className='w-full h-full min-h-[600px] border rounded-lg bg-white'
|
||||
title='导检单预览'
|
||||
/>
|
||||
{daojiandanPdfLoading ? (
|
||||
<div className='flex items-center justify-center h-full text-white'>
|
||||
<div>正在加载PDF...</div>
|
||||
</div>
|
||||
) : daojiandanPdfData ? (
|
||||
<div className='flex justify-center p-4'>
|
||||
<div ref={daojiandanCanvasContainerRef} className='w-full' />
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex items-center justify-center h-full text-white'>
|
||||
<div>PDF加载失败</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -1670,13 +1994,19 @@ export const ExamSignPanel = ({ examId, onBusyChange }: ExamSignPanelProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-1 bg-gray-100 overflow-auto'>
|
||||
<div className='flex justify-center p-4'>
|
||||
<iframe
|
||||
src={addItemBillUrl}
|
||||
className='w-full h-full min-h-[600px] border rounded-lg bg-white'
|
||||
title='加项单预览'
|
||||
/>
|
||||
{addItemBillPdfLoading ? (
|
||||
<div className='flex items-center justify-center h-full text-white'>
|
||||
<div>正在加载PDF...</div>
|
||||
</div>
|
||||
) : addItemBillPdfData ? (
|
||||
<div className='flex justify-center p-4'>
|
||||
<div ref={addItemBillCanvasContainerRef} className='w-full' />
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex items-center justify-center h-full text-white'>
|
||||
<div>PDF加载失败</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user