马一丁

Optimize Export to PDF

@@ -2062,6 +2062,12 @@ body.exporting {{ @@ -2062,6 +2062,12 @@ body.exporting {{
2062 margin: 0; 2062 margin: 0;
2063 font-size: 1rem; 2063 font-size: 1rem;
2064 }} 2064 }}
  2065 +.exporting *,
  2066 +.exporting *::before,
  2067 +.exporting *::after {{
  2068 + animation: none !important;
  2069 + transition: none !important;
  2070 +}}
2065 .export-progress {{ 2071 .export-progress {{
2066 width: 220px; 2072 width: 220px;
2067 height: 6px; 2073 height: 6px;
@@ -2121,6 +2127,10 @@ ul, ol {{ @@ -2121,6 +2127,10 @@ ul, ol {{
2121 margin-left: 1.5em; 2127 margin-left: 1.5em;
2122 padding-left: 0; 2128 padding-left: 0;
2123 }} 2129 }}
  2130 +img, canvas, svg {{
  2131 + max-width: 100%;
  2132 + height: auto;
  2133 +}}
2124 .meta-card {{ 2134 .meta-card {{
2125 background: rgba(0,0,0,0.02); 2135 background: rgba(0,0,0,0.02);
2126 border-radius: 12px; 2136 border-radius: 12px;
@@ -2344,21 +2354,38 @@ pre.code-block {{ @@ -2344,21 +2354,38 @@ pre.code-block {{
2344 }} 2354 }}
2345 .chapter > *, 2355 .chapter > *,
2346 .hero-section, 2356 .hero-section,
2347 - .callout,  
2348 - .chart-card,  
2349 - .kpi-grid,  
2350 - .table-wrap,  
2351 - figure,  
2352 - blockquote {{  
2353 - break-inside: avoid;  
2354 - page-break-inside: avoid;  
2355 - }}  
2356 - .chapter h2,  
2357 - .chapter h3,  
2358 - .chapter h4 {{  
2359 - break-after: avoid;  
2360 - page-break-after: avoid;  
2361 - }} 2357 +.callout,
  2358 +.chart-card,
  2359 +.kpi-grid,
  2360 +.table-wrap,
  2361 +figure,
  2362 +blockquote {{
  2363 + break-inside: avoid;
  2364 + page-break-inside: avoid;
  2365 +}}
  2366 +.chapter h2,
  2367 +.chapter h3,
  2368 +.chapter h4 {{
  2369 + break-after: avoid;
  2370 + page-break-after: avoid;
  2371 + break-inside: avoid;
  2372 +}}
  2373 +.chart-card,
  2374 +.table-wrap {{
  2375 + overflow: visible !important;
  2376 +}}
  2377 +.chart-card canvas {{
  2378 + width: 100% !important;
  2379 + height: auto !important;
  2380 +}}
  2381 +.table-wrap table {{
  2382 + table-layout: fixed;
  2383 + width: 100%;
  2384 +}}
  2385 +.table-wrap table th,
  2386 +.table-wrap table td {{
  2387 + word-break: break-word;
  2388 +}}
2362 }} 2389 }}
2363 """ 2390 """
2364 2391
@@ -2821,6 +2848,7 @@ function exportPdf() { @@ -2821,6 +2848,7 @@ function exportPdf() {
2821 exportBtn.disabled = true; 2848 exportBtn.disabled = true;
2822 } 2849 }
2823 showExportOverlay('正在导出PDF,请稍候...'); 2850 showExportOverlay('正在导出PDF,请稍候...');
  2851 + document.body.classList.add('exporting');
2824 const pdf = new jspdf.jsPDF('p', 'mm', 'a4'); 2852 const pdf = new jspdf.jsPDF('p', 'mm', 'a4');
2825 try { 2853 try {
2826 if (window.pdfFontData) { 2854 if (window.pdfFontData) {
@@ -2832,14 +2860,25 @@ function exportPdf() { @@ -2832,14 +2860,25 @@ function exportPdf() {
2832 console.warn('Custom PDF font setup failed, fallback to default', err); 2860 console.warn('Custom PDF font setup failed, fallback to default', err);
2833 } 2861 }
2834 const pageWidth = pdf.internal.pageSize.getWidth(); 2862 const pageWidth = pdf.internal.pageSize.getWidth();
2835 - const pxWidth = Math.max(target.scrollWidth, document.documentElement.scrollWidth); 2863 + const pxWidth = Math.max(
  2864 + target.scrollWidth,
  2865 + document.documentElement.scrollWidth,
  2866 + Math.round(pageWidth * 3.78)
  2867 + );
2836 const restoreButton = () => { 2868 const restoreButton = () => {
2837 if (exportBtn) { 2869 if (exportBtn) {
2838 exportBtn.disabled = false; 2870 exportBtn.disabled = false;
2839 } 2871 }
  2872 + document.body.classList.remove('exporting');
2840 }; 2873 };
2841 let renderTask; 2874 let renderTask;
2842 try { 2875 try {
  2876 + // force charts to rerender at full width before capture
  2877 + chartRegistry.forEach(chart => {
  2878 + if (chart && typeof chart.resize === 'function') {
  2879 + chart.resize();
  2880 + }
  2881 + });
2843 renderTask = pdf.html(target, { 2882 renderTask = pdf.html(target, {
2844 x: 8, 2883 x: 8,
2845 y: 12, 2884 y: 12,
@@ -2847,15 +2886,25 @@ function exportPdf() { @@ -2847,15 +2886,25 @@ function exportPdf() {
2847 margin: [12, 12, 20, 12], 2886 margin: [12, 12, 20, 12],
2848 autoPaging: 'text', 2887 autoPaging: 'text',
2849 windowWidth: pxWidth, 2888 windowWidth: pxWidth,
2850 - pagebreak: {  
2851 - mode: ['css', 'legacy'],  
2852 - avoid: ['.chapter > *', '.callout', '.chart-card', '.table-wrap', '.kpi-grid', '.hero-section']  
2853 - },  
2854 html2canvas: { 2889 html2canvas: {
2855 - scale: 0.72, 2890 + scale: Math.min(1.2, Math.max(0.8, pageWidth / (target.clientWidth || pageWidth))),
2856 useCORS: true, 2891 useCORS: true,
  2892 + scrollX: 0,
  2893 + scrollY: -window.scrollY,
2857 logging: false 2894 logging: false
2858 }, 2895 },
  2896 + pagebreak: {
  2897 + mode: ['css', 'legacy'],
  2898 + avoid: [
  2899 + '.chapter > *',
  2900 + '.callout',
  2901 + '.chart-card',
  2902 + '.table-wrap',
  2903 + '.kpi-grid',
  2904 + '.hero-section'
  2905 + ],
  2906 + before: '.chapter-divider'
  2907 + },
2859 callback: (doc) => doc.save('report.pdf') 2908 callback: (doc) => doc.save('report.pdf')
2860 }); 2909 });
2861 } catch (err) { 2910 } catch (err) {