修复打印和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) };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user