Showing
2 changed files
with
66 additions
and
7 deletions
| @@ -244,8 +244,6 @@ class HTMLRenderer: | @@ -244,8 +244,6 @@ class HTMLRenderer: | ||
| 244 | str: head片段HTML。 | 244 | str: head片段HTML。 |
| 245 | """ | 245 | """ |
| 246 | css = self._build_css(theme_tokens) | 246 | css = self._build_css(theme_tokens) |
| 247 | - pdf_font_b64 = self._load_pdf_font_data() | ||
| 248 | - pdf_font_literal = json.dumps(pdf_font_b64) | ||
| 249 | 247 | ||
| 250 | # 加载第三方库 | 248 | # 加载第三方库 |
| 251 | chartjs = self._load_lib("chart.js") | 249 | chartjs = self._load_lib("chart.js") |
| @@ -288,10 +286,6 @@ class HTMLRenderer: | @@ -288,10 +286,6 @@ class HTMLRenderer: | ||
| 288 | {css} | 286 | {css} |
| 289 | </style> | 287 | </style> |
| 290 | <script> | 288 | <script> |
| 291 | - // 预载 PDF 字体 Base64 数据,后续由 jspdf addFileToVFS 使用 | ||
| 292 | - window.pdfFontData = {pdf_font_literal}; | ||
| 293 | - </script> | ||
| 294 | - <script> | ||
| 295 | document.documentElement.classList.remove('no-js'); | 289 | document.documentElement.classList.remove('no-js'); |
| 296 | document.documentElement.classList.add('js-ready'); | 290 | document.documentElement.classList.add('js-ready'); |
| 297 | </script> | 291 | </script> |
| @@ -2845,6 +2839,7 @@ function hideExportOverlay(delay) { | @@ -2845,6 +2839,7 @@ function hideExportOverlay(delay) { | ||
| 2845 | } | 2839 | } |
| 2846 | } | 2840 | } |
| 2847 | 2841 | ||
| 2842 | +// exportPdf已移除 | ||
| 2848 | function exportPdf() { | 2843 | function exportPdf() { |
| 2849 | const target = document.querySelector('main'); | 2844 | const target = document.querySelector('main'); |
| 2850 | if (!target || typeof jspdf === 'undefined' || typeof jspdf.jsPDF !== 'function') { | 2845 | if (!target || typeof jspdf === 'undefined' || typeof jspdf.jsPDF !== 'function') { |
| @@ -3150,6 +3150,7 @@ | @@ -3150,6 +3150,7 @@ | ||
| 3150 | <div class="report-controls"> | 3150 | <div class="report-controls"> |
| 3151 | <button class="report-button primary" id="generateReportButton">生成最终报告</button> | 3151 | <button class="report-button primary" id="generateReportButton">生成最终报告</button> |
| 3152 | <button class="report-button" id="downloadReportButton" disabled>下载HTML</button> | 3152 | <button class="report-button" id="downloadReportButton" disabled>下载HTML</button> |
| 3153 | + <button class="report-button" id="downloadPdfButton" disabled>下载PDF</button> | ||
| 3153 | </div> | 3154 | </div> |
| 3154 | 3155 | ||
| 3155 | <!-- 任务进度区域 --> | 3156 | <!-- 任务进度区域 --> |
| @@ -3212,10 +3213,15 @@ | @@ -3212,10 +3213,15 @@ | ||
| 3212 | } | 3213 | } |
| 3213 | 3214 | ||
| 3214 | const downloadButton = document.getElementById('downloadReportButton'); | 3215 | const downloadButton = document.getElementById('downloadReportButton'); |
| 3216 | + const downloadPdfButton = document.getElementById('downloadPdfButton'); | ||
| 3215 | if (downloadButton && !downloadButton.dataset.bound) { | 3217 | if (downloadButton && !downloadButton.dataset.bound) { |
| 3216 | downloadButton.dataset.bound = 'true'; | 3218 | downloadButton.dataset.bound = 'true'; |
| 3217 | downloadButton.addEventListener('click', () => downloadReport()); | 3219 | downloadButton.addEventListener('click', () => downloadReport()); |
| 3218 | } | 3220 | } |
| 3221 | + if (downloadPdfButton && !downloadPdfButton.dataset.bound) { | ||
| 3222 | + downloadPdfButton.dataset.bound = 'true'; | ||
| 3223 | + downloadPdfButton.addEventListener('click', () => downloadPdfFromPreview()); | ||
| 3224 | + } | ||
| 3219 | 3225 | ||
| 3220 | if (reportTaskId) { | 3226 | if (reportTaskId) { |
| 3221 | setGenerateButtonState(true); | 3227 | setGenerateButtonState(true); |
| @@ -3247,7 +3253,8 @@ | @@ -3247,7 +3253,8 @@ | ||
| 3247 | 3253 | ||
| 3248 | function updateDownloadButtonState(task) { | 3254 | function updateDownloadButtonState(task) { |
| 3249 | const downloadButton = document.getElementById('downloadReportButton'); | 3255 | const downloadButton = document.getElementById('downloadReportButton'); |
| 3250 | - if (!downloadButton) return; | 3256 | + const downloadPdfButton = document.getElementById('downloadPdfButton'); |
| 3257 | + if (!downloadButton || !downloadPdfButton) return; | ||
| 3251 | 3258 | ||
| 3252 | if (task && task.status === 'completed' && (task.report_file_ready || task.report_file_path)) { | 3259 | if (task && task.status === 'completed' && (task.report_file_ready || task.report_file_path)) { |
| 3253 | downloadButton.disabled = false; | 3260 | downloadButton.disabled = false; |
| @@ -3255,12 +3262,16 @@ | @@ -3255,12 +3262,16 @@ | ||
| 3255 | downloadButton.dataset.filename = task.report_file_name || ''; | 3262 | downloadButton.dataset.filename = task.report_file_name || ''; |
| 3256 | const label = task.report_file_name ? `下载HTML (${task.report_file_name})` : '下载HTML'; | 3263 | const label = task.report_file_name ? `下载HTML (${task.report_file_name})` : '下载HTML'; |
| 3257 | downloadButton.textContent = label; | 3264 | downloadButton.textContent = label; |
| 3265 | + downloadPdfButton.disabled = false; | ||
| 3266 | + downloadPdfButton.dataset.taskId = task.task_id; | ||
| 3258 | lastCompletedReportTask = task; | 3267 | lastCompletedReportTask = task; |
| 3259 | } else if (!lastCompletedReportTask || (task && task.status !== 'completed')) { | 3268 | } else if (!lastCompletedReportTask || (task && task.status !== 'completed')) { |
| 3260 | downloadButton.disabled = true; | 3269 | downloadButton.disabled = true; |
| 3261 | downloadButton.dataset.taskId = ''; | 3270 | downloadButton.dataset.taskId = ''; |
| 3262 | downloadButton.dataset.filename = ''; | 3271 | downloadButton.dataset.filename = ''; |
| 3263 | downloadButton.textContent = '下载HTML'; | 3272 | downloadButton.textContent = '下载HTML'; |
| 3273 | + downloadPdfButton.disabled = true; | ||
| 3274 | + downloadPdfButton.dataset.taskId = ''; | ||
| 3264 | if (!reportTaskId) { | 3275 | if (!reportTaskId) { |
| 3265 | lastCompletedReportTask = null; | 3276 | lastCompletedReportTask = null; |
| 3266 | } | 3277 | } |
| @@ -3320,6 +3331,59 @@ | @@ -3320,6 +3331,59 @@ | ||
| 3320 | }); | 3331 | }); |
| 3321 | } | 3332 | } |
| 3322 | 3333 | ||
| 3334 | + async function downloadPdfFromPreview() { | ||
| 3335 | + const iframe = document.getElementById('report-iframe'); | ||
| 3336 | + const btn = document.getElementById('downloadPdfButton'); | ||
| 3337 | + if (!iframe || !iframe.contentDocument) { | ||
| 3338 | + showMessage('请先加载报告预览再下载PDF', 'error'); | ||
| 3339 | + return; | ||
| 3340 | + } | ||
| 3341 | + const target = iframe.contentDocument.documentElement; | ||
| 3342 | + if (!target) { | ||
| 3343 | + showMessage('报告内容未就绪', 'error'); | ||
| 3344 | + return; | ||
| 3345 | + } | ||
| 3346 | + if (btn) btn.disabled = true; | ||
| 3347 | + showMessage('正在生成PDF,请稍候...', 'info'); | ||
| 3348 | + try { | ||
| 3349 | + const { jsPDF } = window.jspdf || {}; | ||
| 3350 | + if (!jsPDF) { | ||
| 3351 | + throw new Error('PDF依赖未加载'); | ||
| 3352 | + } | ||
| 3353 | + const pdf = new jsPDF('p', 'mm', 'a4'); | ||
| 3354 | + const pageWidth = pdf.internal.pageSize.getWidth(); | ||
| 3355 | + const pxWidth = Math.max(target.scrollWidth || 0, Math.round(pageWidth * 3.78)); | ||
| 3356 | + const renderTask = pdf.html(target, { | ||
| 3357 | + x: 10, | ||
| 3358 | + y: 10, | ||
| 3359 | + width: pageWidth - 20, | ||
| 3360 | + windowWidth: pxWidth, | ||
| 3361 | + margin: [10, 10, 16, 10], | ||
| 3362 | + autoPaging: 'text', | ||
| 3363 | + html2canvas: { | ||
| 3364 | + scale: Math.min(1.2, Math.max(0.8, pageWidth / (target.clientWidth || pageWidth))), | ||
| 3365 | + useCORS: true, | ||
| 3366 | + scrollX: 0, | ||
| 3367 | + scrollY: -iframe.contentWindow.scrollY, | ||
| 3368 | + logging: false | ||
| 3369 | + }, | ||
| 3370 | + pagebreak: { | ||
| 3371 | + mode: ['css', 'legacy'], | ||
| 3372 | + avoid: ['.chapter', '.callout', '.chart-card', '.table-wrap', '.kpi-grid', '.hero-section'], | ||
| 3373 | + before: '.chapter-divider' | ||
| 3374 | + } | ||
| 3375 | + }); | ||
| 3376 | + await (renderTask && typeof renderTask.then === 'function' ? renderTask : Promise.resolve()); | ||
| 3377 | + pdf.save('report.pdf'); | ||
| 3378 | + showMessage('PDF生成完成,已开始下载', 'success'); | ||
| 3379 | + } catch (err) { | ||
| 3380 | + console.error('生成PDF失败:', err); | ||
| 3381 | + showMessage('生成PDF失败: ' + err.message, 'error'); | ||
| 3382 | + } finally { | ||
| 3383 | + if (btn) btn.disabled = false; | ||
| 3384 | + } | ||
| 3385 | + } | ||
| 3386 | + | ||
| 3323 | // 渲染任务状态(使用新的进度条样式) | 3387 | // 渲染任务状态(使用新的进度条样式) |
| 3324 | function renderTaskStatus(task) { | 3388 | function renderTaskStatus(task) { |
| 3325 | // 状态文本的中文映射 | 3389 | // 状态文本的中文映射 |
-
Please register or login to post a comment