修复打印和exam_id问题

This commit is contained in:
xianyi
2025-12-03 14:33:23 +08:00
parent 408221fc66
commit 60ac086d7d
6 changed files with 420 additions and 185 deletions

View File

@@ -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) };
}
});