This commit is contained in:
2026-03-11 15:21:53 +08:00
parent 03cb6fd92b
commit 454e60dd72
7 changed files with 240 additions and 133 deletions

View File

@@ -2185,16 +2185,11 @@
}
loadReport();
// 绑定「保存为 PDF」按钮html2canvas 截取 .page → 转 JPEG → jsPDF 分页生成 PDF按钮在 .page 外固定悬浮,不会进 PDF
// 绑定「保存为 PDF」按钮调用后端 /reports/qygl/:id/pdf 接口,由服务器端 headless Chrome 生成高质量 PDF
var saveBtn = document.getElementById("btnSavePdf");
var loadingOverlay = document.getElementById("pdfLoadingOverlay");
if (saveBtn && window.html2canvas && window.jspdf && window.jspdf.jsPDF) {
if (saveBtn) {
saveBtn.addEventListener("click", function () {
var pageEl = document.querySelector(".page");
if (!pageEl) {
console.error("未找到 .page 容器");
return;
}
var btnText = saveBtn.textContent;
saveBtn.disabled = true;
saveBtn.textContent = "生成中...";
@@ -2208,135 +2203,68 @@
loadingOverlay.setAttribute("aria-hidden", "true");
}
}
// 等待浏览器完成布局与绘制,再截取,避免截到白屏
function doCapture() {
requestAnimationFrame(function () {
requestAnimationFrame(function () {
try {
(function () {
var scale = 1.25;
var pdf = new jspdf.jsPDF("p", "mm", "a4");
var pageWidthMm = pdf.internal.pageSize.getWidth();
var pageHeightMm = pdf.internal.pageSize.getHeight();
// 以元素宽度为基准,计算每一页对应的像素高度(避免生成超长大图导致 jsPDF 白页)
var targetWidthPx = Math.max(
1,
Math.floor(pageEl.scrollWidth),
);
var pageHeightPx = Math.max(
1,
Math.floor((targetWidthPx * pageHeightMm) / pageWidthMm),
);
var totalHeightPx = Math.max(
1,
Math.floor(pageEl.scrollHeight),
);
try {
// 从当前 URL 中解析报告编号,路径形如 /reports/qygl/:id
var path = window.location.pathname || "";
var match = path.match(/\\/reports\\/qygl\\/([^/]+)/);
if (!match || !match[1]) {
console.error("无法从当前 URL 解析报告编号,路径为", path);
restoreBtn();
return;
}
var reportId = match[1];
var pdfUrl = "/reports/qygl/" + encodeURIComponent(reportId) + "/pdf";
var offsetY = 0;
var pageIndex = 0;
function renderSlice() {
var sliceHeight = Math.min(
pageHeightPx,
totalHeightPx - offsetY,
);
if (sliceHeight <= 0) {
// 结束,保存
var fileName = "企业全景报告.pdf";
if (
reportData &&
reportData.entName &&
typeof reportData.entName === "string"
) {
fileName =
reportData.entName +
"_企业全景报告.pdf";
}
pdf.save(fileName);
restoreBtn();
return;
}
html2canvas(pageEl, {
scale: scale,
useCORS: true,
allowTaint: false,
backgroundColor: "#ffffff",
x: 0,
y: offsetY,
width: targetWidthPx,
height: sliceHeight,
scrollX: 0,
scrollY: 0,
windowWidth: targetWidthPx,
windowHeight: sliceHeight,
onclone: function (clonedDoc) {
var body = clonedDoc.body;
if (body) {
body.style.backgroundColor = "#ffffff";
body.style.overflow = "visible";
}
var clonePage = clonedDoc.querySelector(".page");
if (clonePage) {
clonePage.style.overflow = "visible";
clonePage.style.backgroundColor = "#ffffff";
}
},
})
.then(function (canvas) {
if (!canvas.width || !canvas.height) {
console.error("分片截图为空,宽或高为 0", {
offsetY: offsetY,
sliceHeight: sliceHeight,
});
restoreBtn();
return;
}
// 调试:输出每页分片信息
console.info("PDF分片截图信息", {
page: pageIndex + 1,
w: canvas.width,
h: canvas.height,
offsetY: offsetY,
sliceHeight: sliceHeight,
totalHeightPx: totalHeightPx,
});
var imgData = canvas.toDataURL("image/jpeg", 0.9);
if (pageIndex > 0) pdf.addPage();
pdf.addImage(
imgData,
"JPEG",
0,
0,
pageWidthMm,
pageHeightMm,
);
pageIndex += 1;
offsetY += sliceHeight;
// 给 UI 一点喘息,避免长任务卡死
setTimeout(renderSlice, 0);
})
.catch(function (e) {
console.error("生成 PDF 失败(分片截图阶段)", e);
restoreBtn();
});
}
renderSlice();
})();
} catch (e) {
console.error("触发生成 PDF 失败", e);
restoreBtn();
// 通过 fetch 获取 PDF 二进制并触发下载
fetch(pdfUrl, {
method: "GET",
})
.then(function (resp) {
if (!resp.ok) {
throw new Error("生成 PDF 接口返回错误状态:" + resp.status);
}
var contentType = resp.headers.get("Content-Type") || "";
if (
contentType.indexOf("application/pdf") === -1 &&
contentType.indexOf("application/octet-stream") === -1
) {
// 可能返回的是错误字符串
return resp.text().then(function (txt) {
throw new Error("生成 PDF 失败:" + txt);
});
}
return resp.blob();
})
.then(function (blob) {
var fileName = "企业全景报告.pdf";
if (
reportData &&
reportData.entName &&
typeof reportData.entName === "string"
) {
fileName =
reportData.entName + "_企业全景报告.pdf";
}
var url = window.URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
restoreBtn();
})
.catch(function (e) {
console.error("生成 PDF 失败(后端接口)", e);
restoreBtn();
alert("生成 PDF 失败,请稍后重试");
});
});
} catch (e) {
console.error("触发生成 PDF 失败", e);
restoreBtn();
}
doCapture();
});
}
})();