# 前端生成 PDF 并上传后端技术方案 ## 概述 - 目标:诊断报告页面在前端生成高保真 PDF,上传到 Python 后端持久化,并把文件路径写入对应检测会话。 - 推荐:使用 Electron 主进程 `webContents.printToPDF` 生成 PDF,忠实度高、分页与中文字体友好;作为短期备选,提供 `html2canvas` 截图转 PDF 的实现。 ## 架构流程 - 前端渲染进程:用户在报告页面点击“生成报告”→ 发送 IPC 请求至主进程生成 PDF → 主进程返回 PDF Buffer 给渲染进程 → 渲染进程上传后端。 - Python 后端:接收 PDF 文件流,写入 `backend/static/reports//.pdf`,记录相对路径到 `detection_sessions` 的报告字段。 ## 前端实现(Electron 渲染) ### 报告页面打印版式 - 报告根容器,例如 `#report-root`。 - 打印样式建议: - `@page { size: A4; margin: 12mm }` - 固定宽度设计为 A4 比例,非交互元素隐藏,保留背景。 - 通过添加类名(例如 `.print-mode`)切换打印样式。 ### 渲染进程触发生成 ```ts // renderer: 诊断报告视图中 import { ipcRenderer } from 'electron' async function generatePdf(sessionId: string) { const pdfBuffer: ArrayBuffer = await ipcRenderer.invoke('generate-report-pdf', { selector: '#report-root', pageSize: 'A4', printBackground: true }) const blob = new Blob([pdfBuffer], { type: 'application/pdf' }) const form = new FormData() form.append('file', blob, `${sessionId}.pdf`) const res = await fetch(`${getBackendUrl()}/api/reports/${sessionId}/upload`, { method: 'POST', body: form }) const json = await res.json() if (!json.success) throw new Error(json.error || '上传失败') } ``` ### 主进程生成 PDF ```ts // main: 注册 IPC 处理 import { ipcMain } from 'electron' ipcMain.handle('generate-report-pdf', async (event, payload) => { const win = event.sender.getOwnerBrowserWindow() await win.webContents.executeJavaScript(` (function(){ const root = document.querySelector('${payload.selector}') if (!root) throw new Error('报告根节点缺失') document.body.classList.add('print-mode') return true })() `) const pdf = await win.webContents.printToPDF({ pageSize: payload.pageSize || 'A4', printBackground: payload.printBackground !== false, marginsType: 0 }) await win.webContents.executeJavaScript(` (function(){ document.body.classList.remove('print-mode'); return true })() `) return pdf }) ``` ### 更新数据库记录 ```py # database.py 片段 def update_session_report_path(self, session_id: str, report_path: str): conn = self.get_connection() cursor = conn.cursor() cursor.execute('UPDATE detection_sessions SET detection_report = ? WHERE id = ?', (report_path, session_id)) conn.commit() ``` ## 接口设计 - 上传 PDF:`POST /api/reports//upload` - 请求体:`multipart/form-data`,字段 `file` - 返回:`{ success, path }` ## 权限与安全 - 校验 `session_id` 存在且归属当前登录用户(医生)。 - 限制最大文件大小(例如 20MB),拒绝超限上传。 - 文件名使用 `session_id.pdf`,避免任意文件名写盘。 ## 版式与质量建议 - 中文字体:在渲染环境预装中文字体或通过 `@font-face` 引入。 - 分页:使用 `@page` 与 `page-break-before/after` 控制分页。 - 背景:Electron 打印需开启 `printBackground: true`;确保 CSS 背景不被打印忽略。 ## 错误与排查 - 空白 PDF:确保打印前切入 `.print-mode` 并等待资源加载;检查选择器是否找到根节点。 - 字体方框:安装中文字体或嵌入字体文件。 - 资源丢失:使用相对路径或 base64;`html2canvas` 时启用 `useCORS` 且服务器设置跨域头。 - 过大或截断:分页控制,或拆页生成。 ## 测试用例 - 单页报告:生成并上传,后端返回路径;数据库记录更新;历史档案中可下载或预览。 - 多页报告:存在分页断点,打印后为多页;每页标题和页码正常。 - 异常:网络断开、文件超限、会话不存在;前端提示具体错误。 ## 部署说明 - Electron 打包:已使用 `electron-builder`;生成 exe 后功能一致。 - 后端存储:Windows 下路径统一使用 `/` 展示;静态文件由后端提供下载或前端拼接 `BACKEND_URL + '/' + path` 访问。 ## 后续扩展 - 加页眉页脚(时间、患者信息、页码)。 - 报告水印与签章。 - 历史报告下载与比对视图。