修复打印和exam_id问题
This commit is contained in:
190
electron/main.js
190
electron/main.js
@@ -4,6 +4,7 @@ const https = require("https");
|
||||
const http = require("http");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const ptp = require("pdf-to-printer");
|
||||
|
||||
let mainWindow = null;
|
||||
const { Worker } = require("worker_threads");
|
||||
@@ -169,12 +170,11 @@ ipcMain.handle("get-printers", async (event) => {
|
||||
|
||||
// 处理PDF打印请求
|
||||
ipcMain.handle("print-pdf", async (event, pdfDataOrUrl, printOptions = {}) => {
|
||||
let printWindow = null;
|
||||
let tempFilePath = null;
|
||||
const targetPrinterName = printOptions?.printerName;
|
||||
|
||||
try {
|
||||
let pdfPath = pdfDataOrUrl;
|
||||
let pdfFilePath = null;
|
||||
|
||||
// 如果是 base64 data URI,转换为临时文件
|
||||
if (pdfDataOrUrl.startsWith("data:application/pdf;base64,")) {
|
||||
@@ -182,95 +182,130 @@ ipcMain.handle("print-pdf", async (event, pdfDataOrUrl, printOptions = {}) => {
|
||||
"data:application/pdf;base64,",
|
||||
""
|
||||
);
|
||||
const buffer = Buffer.from(base64Data, "base64");
|
||||
|
||||
// 创建临时文件
|
||||
tempFilePath = path.join(os.tmpdir(), `print_${Date.now()}.pdf`);
|
||||
fs.writeFileSync(tempFilePath, buffer);
|
||||
pdfPath = `file://${tempFilePath}`;
|
||||
log.info("PDF written to temp file:", tempFilePath);
|
||||
// 清理base64字符串中的空白字符
|
||||
const cleanBase64 = base64Data.replace(/\s/g, "");
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(cleanBase64, "base64");
|
||||
|
||||
// 验证PDF文件头(%PDF)
|
||||
if (buffer.length < 4 || buffer.toString("ascii", 0, 4) !== "%PDF") {
|
||||
throw new Error("无效的PDF数据:文件头不正确");
|
||||
}
|
||||
|
||||
// 创建临时文件
|
||||
tempFilePath = path.join(os.tmpdir(), `print_${Date.now()}_${Math.random().toString(36).substring(7)}.pdf`);
|
||||
fs.writeFileSync(tempFilePath, buffer);
|
||||
pdfFilePath = tempFilePath;
|
||||
|
||||
const fileSize = fs.statSync(tempFilePath).size;
|
||||
log.info(`PDF written to temp file: ${tempFilePath}, size: ${fileSize} bytes`);
|
||||
|
||||
if (fileSize < 1024) {
|
||||
throw new Error(`PDF文件大小异常(${fileSize} bytes),可能数据不完整`);
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("Base64解码或文件写入失败:", err);
|
||||
throw new Error(`PDF数据处理失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
// 如果是相对路径(Vite 打包后的资源),转换为绝对路径
|
||||
// 如果是HTTP/HTTPS URL,先下载
|
||||
else if (pdfDataOrUrl.startsWith("http://") || pdfDataOrUrl.startsWith("https://")) {
|
||||
log.info("Downloading PDF from URL:", pdfDataOrUrl);
|
||||
const protocol = pdfDataOrUrl.startsWith("https") ? https : http;
|
||||
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
protocol.get(pdfDataOrUrl, (response) => {
|
||||
// 处理重定向
|
||||
if (response.statusCode === 301 || response.statusCode === 302) {
|
||||
const redirectUrl = response.headers.location;
|
||||
protocol.get(redirectUrl, (redirectResponse) => {
|
||||
const chunks = [];
|
||||
redirectResponse.on("data", (chunk) => chunks.push(chunk));
|
||||
redirectResponse.on("end", () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
});
|
||||
redirectResponse.on("error", reject);
|
||||
}).on("error", reject);
|
||||
} else {
|
||||
const chunks = [];
|
||||
response.on("data", (chunk) => chunks.push(chunk));
|
||||
response.on("end", () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
});
|
||||
response.on("error", reject);
|
||||
}
|
||||
}).on("error", reject);
|
||||
});
|
||||
|
||||
// 验证PDF文件头
|
||||
if (buffer.length < 4 || buffer.toString("ascii", 0, 4) !== "%PDF") {
|
||||
throw new Error("下载的文件不是有效的PDF");
|
||||
}
|
||||
|
||||
tempFilePath = path.join(os.tmpdir(), `print_${Date.now()}_${Math.random().toString(36).substring(7)}.pdf`);
|
||||
fs.writeFileSync(tempFilePath, buffer);
|
||||
pdfFilePath = tempFilePath;
|
||||
|
||||
const fileSize = fs.statSync(tempFilePath).size;
|
||||
log.info(`PDF downloaded to temp file: ${tempFilePath}, size: ${fileSize} bytes`);
|
||||
}
|
||||
// 如果是本地文件路径
|
||||
else if (pdfDataOrUrl.startsWith("file://")) {
|
||||
pdfFilePath = pdfDataOrUrl.replace("file://", "");
|
||||
}
|
||||
// 如果是相对路径
|
||||
else if (pdfDataOrUrl.startsWith("/assets/")) {
|
||||
const isDev = !app.isPackaged;
|
||||
if (!isDev) {
|
||||
// 生产环境:从 dist 目录加载
|
||||
const absolutePath = path.join(__dirname, "..", "dist", pdfDataOrUrl);
|
||||
pdfPath = `file://${absolutePath}`;
|
||||
log.info("Using bundled PDF:", absolutePath);
|
||||
pdfFilePath = path.join(__dirname, "..", "dist", pdfDataOrUrl);
|
||||
} else {
|
||||
// 开发环境:Vite 会处理,保持原样
|
||||
pdfPath = `http://localhost:5173${pdfDataOrUrl}`;
|
||||
log.info("Using dev server PDF:", pdfPath);
|
||||
throw new Error("开发环境不支持相对路径PDF打印,请使用完整URL或base64");
|
||||
}
|
||||
}
|
||||
// 如果已经是 file:// 或 http(s):// 协议,保持原样
|
||||
else if (
|
||||
!pdfDataOrUrl.startsWith("file://") &&
|
||||
!pdfDataOrUrl.startsWith("http")
|
||||
) {
|
||||
pdfPath = `file://${pdfDataOrUrl}`;
|
||||
// 直接作为文件路径
|
||||
else {
|
||||
pdfFilePath = pdfDataOrUrl;
|
||||
}
|
||||
|
||||
// 创建一个隐藏的窗口用于加载PDF
|
||||
printWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
plugins: true, // 启用插件以支持 PDF 查看
|
||||
},
|
||||
});
|
||||
// 验证文件存在
|
||||
if (!pdfFilePath || !fs.existsSync(pdfFilePath)) {
|
||||
throw new Error(`PDF文件不存在: ${pdfFilePath}`);
|
||||
}
|
||||
|
||||
// 加载PDF
|
||||
await printWindow.loadURL(pdfPath);
|
||||
log.info("PDF loaded for printing");
|
||||
// 验证文件大小
|
||||
const stats = fs.statSync(pdfFilePath);
|
||||
if (stats.size < 1024) {
|
||||
throw new Error(`PDF文件大小异常(${stats.size} bytes),可能文件损坏`);
|
||||
}
|
||||
|
||||
// 等待更长时间确保 PDF 完全渲染
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
log.info(`准备打印PDF: ${pdfFilePath}, 打印机: ${targetPrinterName || "默认打印机"}`);
|
||||
|
||||
// 静默打印(直接调用系统打印对话框)
|
||||
return new Promise((resolve) => {
|
||||
const basePrintOptions = {
|
||||
silent: Boolean(targetPrinterName),
|
||||
printBackground: true,
|
||||
margins: {
|
||||
marginType: "none",
|
||||
},
|
||||
};
|
||||
// 使用 pdf-to-printer 打印
|
||||
const printOptions_ptp = {
|
||||
printer: targetPrinterName || undefined,
|
||||
silent: true, // 静默打印
|
||||
};
|
||||
|
||||
if (targetPrinterName) {
|
||||
basePrintOptions.deviceName = targetPrinterName;
|
||||
await ptp.print(pdfFilePath, printOptions_ptp);
|
||||
|
||||
log.info("PDF打印请求已发送成功");
|
||||
|
||||
// 清理临时文件
|
||||
if (tempFilePath && fs.existsSync(tempFilePath)) {
|
||||
try {
|
||||
// 延迟删除,确保打印任务已提交
|
||||
setTimeout(() => {
|
||||
fs.unlinkSync(tempFilePath);
|
||||
log.info("Temp file deleted");
|
||||
}, 5000);
|
||||
} catch (err) {
|
||||
log.error("Failed to delete temp file:", err);
|
||||
}
|
||||
}
|
||||
|
||||
printWindow.webContents.print(
|
||||
basePrintOptions,
|
||||
(success, errorType) => {
|
||||
// 清理临时文件
|
||||
if (tempFilePath && fs.existsSync(tempFilePath)) {
|
||||
try {
|
||||
fs.unlinkSync(tempFilePath);
|
||||
log.info("Temp file deleted");
|
||||
} catch (err) {
|
||||
log.error("Failed to delete temp file:", err);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭窗口
|
||||
if (printWindow && !printWindow.isDestroyed()) {
|
||||
printWindow.close();
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
log.error("Print failed:", errorType);
|
||||
resolve({ success: false, error: errorType });
|
||||
} else {
|
||||
log.info("Print completed");
|
||||
resolve({ success: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
log.error("Print error:", error);
|
||||
|
||||
@@ -283,10 +318,7 @@ ipcMain.handle("print-pdf", async (event, pdfDataOrUrl, printOptions = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (printWindow && !printWindow.isDestroyed()) {
|
||||
printWindow.close();
|
||||
}
|
||||
return { success: false, error: error.message };
|
||||
return { success: false, error: error.message || String(error) };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
350
package-lock.json
generated
350
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "Electron application compatible with Windows 7",
|
||||
"main": "electron/main.js",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"vite\" \"wait-on tcp:5173 && electron .\"",
|
||||
"dev": "concurrently \"vite --host\" \"wait-on tcp:5173 && electron .\"",
|
||||
"build": "vite build",
|
||||
"pack": "npm run build && electron-builder --dir",
|
||||
"pack:ia32": "npm run build && electron-builder --dir --ia32",
|
||||
@@ -20,6 +20,7 @@
|
||||
"electron-log": "^5.4.3",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"koffi": "^2.14.1",
|
||||
"pdf-to-printer": "^5.6.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-pdf": "5.7.2",
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface PackagItemInfo {
|
||||
package_code: string;
|
||||
package_name: string;
|
||||
registration_time: string;
|
||||
physical_exam_id: number;
|
||||
}
|
||||
|
||||
// 套餐详情
|
||||
|
||||
@@ -34,6 +34,7 @@ const UI6: React.FC = () => {
|
||||
const res = await getPackagItemDetail(id_no as string);
|
||||
if (res.Status === 200) {
|
||||
localStorage.setItem("package_code", res.Data.packagItemInfo.package_code || "");
|
||||
localStorage.setItem("selectedExamId", res.Data.packagItemInfo.physical_exam_id.toString() || "0");
|
||||
// 处理数据:将 project_id 和 project_name 字符串分离为数组
|
||||
const processedData = res.Data.listPackDetail.map((item: any) => {
|
||||
// 将 project_id 字符串按中文顿号分割为数组
|
||||
|
||||
@@ -291,49 +291,73 @@ const UI8: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const primaryPdf = pdfFiles[0] || originPdfUrls[0];
|
||||
if (!primaryPdf) {
|
||||
if (printers.length > 0 && !selectedPrinter) {
|
||||
alert("请先选择打印机");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pdfFiles.length === 0 && originPdfUrls.length === 0) {
|
||||
const errorMsg = "PDF 尚未加载完成,请稍候";
|
||||
window.electronAPI?.log("warn", errorMsg);
|
||||
alert(errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (printers.length > 0 && !selectedPrinter) {
|
||||
alert("请先选择打印机");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsPrinting(true);
|
||||
try {
|
||||
const printData =
|
||||
primaryPdf.startsWith("data:") && pdfFiles[0]
|
||||
? primaryPdf
|
||||
: originPdfUrls[0];
|
||||
const dataType = printData.startsWith("data:") ? "base64数据" : "远程文件路径";
|
||||
window.electronAPI.log("info", `开始打印PDF (${dataType}): ${printData.substring(0, 100)}...`);
|
||||
// 优先使用已加载的 base64 数据,确保数据完整
|
||||
let printData: string;
|
||||
let dataType: string;
|
||||
|
||||
if (pdfFiles[0] && pdfFiles[0].startsWith("data:application/pdf;base64,")) {
|
||||
printData = pdfFiles[0];
|
||||
dataType = "base64数据";
|
||||
|
||||
// 验证 base64 数据长度(至少应该有几KB)
|
||||
const base64Part = printData.replace("data:application/pdf;base64,", "");
|
||||
const estimatedSize = (base64Part.length * 3) / 4;
|
||||
window.electronAPI.log("info", `使用base64数据打印,估算大小: ${Math.round(estimatedSize)} bytes`);
|
||||
|
||||
if (estimatedSize < 1024) {
|
||||
window.electronAPI.log("warn", "base64数据可能不完整,尝试使用原始URL");
|
||||
// 如果base64数据太小,回退到URL
|
||||
if (originPdfUrls[0]) {
|
||||
printData = originPdfUrls[0];
|
||||
dataType = "远程文件路径";
|
||||
}
|
||||
}
|
||||
} else if (originPdfUrls[0]) {
|
||||
printData = originPdfUrls[0];
|
||||
dataType = "远程文件路径";
|
||||
} else {
|
||||
throw new Error("没有可用的PDF数据");
|
||||
}
|
||||
|
||||
window.electronAPI.log("info", `开始打印PDF (${dataType}): ${dataType === "base64数据" ? `base64长度${printData.length}` : printData.substring(0, 100)}...`);
|
||||
|
||||
const printResult = await window.electronAPI.printPdf(
|
||||
printData,
|
||||
selectedPrinter ? { printerName: selectedPrinter } : undefined
|
||||
);
|
||||
|
||||
if (!printResult.success) {
|
||||
const errorMsg = `打印失败: ${printResult.error || "未知错误"}`;
|
||||
window.electronAPI.log("error", errorMsg);
|
||||
alert(errorMsg);
|
||||
} else {
|
||||
window.electronAPI.log("info", "PDF打印请求已发送");
|
||||
return;
|
||||
}
|
||||
|
||||
window.electronAPI.log("info", "PDF打印请求已发送成功");
|
||||
navigate("/UI81");
|
||||
} catch (error) {
|
||||
const errorMsg = `Print error: ${error}`;
|
||||
const errorMsg = `打印错误: ${(error as Error).message || error}`;
|
||||
console.error(errorMsg);
|
||||
window.electronAPI.log("error", errorMsg);
|
||||
alert("打印失败,请重试");
|
||||
} finally {
|
||||
setIsPrinting(false);
|
||||
navigate("/UI81");
|
||||
}
|
||||
}, [originPdfUrls, pdfFiles, printers.length, selectedPrinter]);
|
||||
}, [originPdfUrls, pdfFiles, printers.length, selectedPrinter, navigate]);
|
||||
|
||||
const handlePageCountUpdate = useCallback((index: number, numPages: number) => {
|
||||
window.electronAPI?.log("info", `[UI8] PDF渲染成功 (index=${index}),共 ${numPages} 页`);
|
||||
|
||||
Reference in New Issue
Block a user