Optimize Export to PDF

This commit is contained in:
马一丁
2025-11-18 01:18:04 +08:00
parent dffe1618d5
commit 0707c6f7a7
+71 -22
View File
@@ -2062,6 +2062,12 @@ body.exporting {{
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
}} }}
.exporting *,
.exporting *::before,
.exporting *::after {{
animation: none !important;
transition: none !important;
}}
.export-progress {{ .export-progress {{
width: 220px; width: 220px;
height: 6px; height: 6px;
@@ -2121,6 +2127,10 @@ ul, ol {{
margin-left: 1.5em; margin-left: 1.5em;
padding-left: 0; padding-left: 0;
}} }}
img, canvas, svg {{
max-width: 100%;
height: auto;
}}
.meta-card {{ .meta-card {{
background: rgba(0,0,0,0.02); background: rgba(0,0,0,0.02);
border-radius: 12px; border-radius: 12px;
@@ -2344,21 +2354,38 @@ pre.code-block {{
}} }}
.chapter > *, .chapter > *,
.hero-section, .hero-section,
.callout, .callout,
.chart-card, .chart-card,
.kpi-grid, .kpi-grid,
.table-wrap, .table-wrap,
figure, figure,
blockquote {{ blockquote {{
break-inside: avoid; break-inside: avoid;
page-break-inside: avoid; page-break-inside: avoid;
}} }}
.chapter h2, .chapter h2,
.chapter h3, .chapter h3,
.chapter h4 {{ .chapter h4 {{
break-after: avoid; break-after: avoid;
page-break-after: avoid; page-break-after: avoid;
}} break-inside: avoid;
}}
.chart-card,
.table-wrap {{
overflow: visible !important;
}}
.chart-card canvas {{
width: 100% !important;
height: auto !important;
}}
.table-wrap table {{
table-layout: fixed;
width: 100%;
}}
.table-wrap table th,
.table-wrap table td {{
word-break: break-word;
}}
}} }}
""" """
@@ -2821,6 +2848,7 @@ function exportPdf() {
exportBtn.disabled = true; exportBtn.disabled = true;
} }
showExportOverlay('正在导出PDF,请稍候...'); showExportOverlay('正在导出PDF,请稍候...');
document.body.classList.add('exporting');
const pdf = new jspdf.jsPDF('p', 'mm', 'a4'); const pdf = new jspdf.jsPDF('p', 'mm', 'a4');
try { try {
if (window.pdfFontData) { if (window.pdfFontData) {
@@ -2832,14 +2860,25 @@ function exportPdf() {
console.warn('Custom PDF font setup failed, fallback to default', err); console.warn('Custom PDF font setup failed, fallback to default', err);
} }
const pageWidth = pdf.internal.pageSize.getWidth(); const pageWidth = pdf.internal.pageSize.getWidth();
const pxWidth = Math.max(target.scrollWidth, document.documentElement.scrollWidth); const pxWidth = Math.max(
target.scrollWidth,
document.documentElement.scrollWidth,
Math.round(pageWidth * 3.78)
);
const restoreButton = () => { const restoreButton = () => {
if (exportBtn) { if (exportBtn) {
exportBtn.disabled = false; exportBtn.disabled = false;
} }
document.body.classList.remove('exporting');
}; };
let renderTask; let renderTask;
try { try {
// force charts to rerender at full width before capture
chartRegistry.forEach(chart => {
if (chart && typeof chart.resize === 'function') {
chart.resize();
}
});
renderTask = pdf.html(target, { renderTask = pdf.html(target, {
x: 8, x: 8,
y: 12, y: 12,
@@ -2847,14 +2886,24 @@ function exportPdf() {
margin: [12, 12, 20, 12], margin: [12, 12, 20, 12],
autoPaging: 'text', autoPaging: 'text',
windowWidth: pxWidth, windowWidth: pxWidth,
html2canvas: {
scale: Math.min(1.2, Math.max(0.8, pageWidth / (target.clientWidth || pageWidth))),
useCORS: true,
scrollX: 0,
scrollY: -window.scrollY,
logging: false
},
pagebreak: { pagebreak: {
mode: ['css', 'legacy'], mode: ['css', 'legacy'],
avoid: ['.chapter > *', '.callout', '.chart-card', '.table-wrap', '.kpi-grid', '.hero-section'] avoid: [
}, '.chapter > *',
html2canvas: { '.callout',
scale: 0.72, '.chart-card',
useCORS: true, '.table-wrap',
logging: false '.kpi-grid',
'.hero-section'
],
before: '.chapter-divider'
}, },
callback: (doc) => doc.save('report.pdf') callback: (doc) => doc.save('report.pdf')
}); });