修复鸿蒙打印导检单
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import * as pdfjsLib from 'pdfjs-dist';
|
||||
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
|
||||
|
||||
import { getDaojiandanPdf as getDaojiandanPdfApi, submitDaojiandanSign } from '../../api';
|
||||
import type { ExamClient } from '../../data/mockData';
|
||||
@@ -6,6 +8,22 @@ import { setExamActionRecord, setDaojiandanPdf, getDaojiandanPdf } from '../../u
|
||||
import type { SignaturePadHandle } from '../ui';
|
||||
import { Button, SignaturePad } from '../ui';
|
||||
|
||||
// Polyfill for Promise.withResolvers
|
||||
if (typeof (Promise as any).withResolvers === 'undefined') {
|
||||
(Promise as any).withResolvers = function <T>() {
|
||||
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||
let reject!: (reason?: any) => void;
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
};
|
||||
}
|
||||
|
||||
// 配置 PDF.js worker
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
|
||||
|
||||
export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -17,9 +35,10 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
|
||||
const [showPreview, setShowPreview] = useState(false);
|
||||
const [fetchLoading, setFetchLoading] = useState(false);
|
||||
const [pdfData, setPdfData] = useState<ArrayBuffer | null>(null);
|
||||
const signaturePadRef = useRef<SignaturePadHandle | null>(null);
|
||||
const printRef = useRef<HTMLDivElement>(null);
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
const canvasContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleSubmitSign = async () => {
|
||||
const examId = Number(client.id);
|
||||
@@ -126,12 +145,14 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 第一步:加载 PDF 数据
|
||||
useEffect(() => {
|
||||
if (!pdfUrl) return;
|
||||
|
||||
let objectUrl: string | null = null;
|
||||
setPdfReady(false);
|
||||
setLoading(true);
|
||||
setPdfData(null);
|
||||
|
||||
fetch(pdfUrl)
|
||||
.then((resp) => {
|
||||
@@ -141,12 +162,15 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
.then((blob) => {
|
||||
objectUrl = URL.createObjectURL(blob);
|
||||
setPdfBlobUrl(objectUrl);
|
||||
return blob.arrayBuffer();
|
||||
})
|
||||
.then((arrayBuffer) => {
|
||||
setPdfData(arrayBuffer);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('PDF 拉取失败', err);
|
||||
setError('PDF 加载失败,请稍后重试');
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
@@ -157,15 +181,133 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
};
|
||||
}, [pdfUrl]);
|
||||
|
||||
// 第二步:渲染 PDF
|
||||
useEffect(() => {
|
||||
if (!pdfData || !canvasContainerRef.current) return;
|
||||
|
||||
console.log('开始渲染 PDF');
|
||||
setPdfReady(false);
|
||||
|
||||
const renderAllPages = async () => {
|
||||
try {
|
||||
const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||
console.log('PDF 加载成功,共 ' + pdf.numPages + ' 页');
|
||||
|
||||
if (!canvasContainerRef.current) {
|
||||
console.log('canvasContainerRef 仍为 null');
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空容器
|
||||
canvasContainerRef.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) {
|
||||
console.log('无法获取 canvas 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';
|
||||
|
||||
canvasContainerRef.current.appendChild(canvas);
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
} as any;
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
console.log('渲染完成第 ' + pageNum + ' 页');
|
||||
}
|
||||
|
||||
setPdfReady(true);
|
||||
console.log('所有页面渲染完成');
|
||||
} catch (err) {
|
||||
console.error('PDF 渲染失败', err);
|
||||
setError('PDF 渲染失败,请稍后重试: ' + err);
|
||||
}
|
||||
};
|
||||
|
||||
renderAllPages();
|
||||
}, [pdfData]);
|
||||
|
||||
const handlePrint = () => {
|
||||
if (!pdfBlobUrl || !pdfReady) return;
|
||||
const printWindow = window.open(pdfBlobUrl, '_blank');
|
||||
if (printWindow) {
|
||||
printWindow.onload = () => {
|
||||
printWindow.focus();
|
||||
if (!pdfBlobUrl || !pdfReady || !canvasContainerRef.current) return;
|
||||
|
||||
// 创建打印窗口
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) return;
|
||||
|
||||
// 获取所有的 canvas
|
||||
const canvases = canvasContainerRef.current.querySelectorAll('canvas');
|
||||
|
||||
// 构建打印页面
|
||||
printWindow.document.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>导检单打印</title>
|
||||
<style>
|
||||
@media print {
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
page-break-after: always;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
img:last-child {
|
||||
page-break-after: auto;
|
||||
}
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`);
|
||||
|
||||
// 将每个 canvas 转换为图片并添加到打印窗口
|
||||
canvases.forEach((canvas) => {
|
||||
const imgData = (canvas as HTMLCanvasElement).toDataURL('image/png');
|
||||
printWindow.document.write(`<img src="${imgData}" />`);
|
||||
});
|
||||
|
||||
printWindow.document.write(`
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
|
||||
// 等待图片加载完成后执行打印
|
||||
printWindow.onload = () => {
|
||||
printWindow.focus();
|
||||
setTimeout(() => {
|
||||
printWindow.print();
|
||||
};
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -306,13 +448,7 @@ export const ExamPrintPanel = ({ client }: { client: ExamClient }) => {
|
||||
<div className='w-full'>
|
||||
<div ref={printRef} className='print-content'>
|
||||
<div className='flex justify-center border rounded-lg p-4 bg-gray-50 overflow-auto max-h-[600px]'>
|
||||
<iframe
|
||||
src={pdfBlobUrl || ''}
|
||||
className='w-full h-[600px] border rounded-lg'
|
||||
title='导检单PDF预览'
|
||||
onLoad={() => setPdfReady(true)}
|
||||
ref={iframeRef}
|
||||
/>
|
||||
<div ref={canvasContainerRef} className='w-full' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user