From b114ee6e0ac056d0f380d97b9dbf669520b55b94 Mon Sep 17 00:00:00 2001 From: limengnan <420004014@qq.com> Date: Tue, 9 Dec 2025 13:31:01 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- document/前端生成PDF并上传后端技术方案.md | 113 ++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 document/前端生成PDF并上传后端技术方案.md diff --git a/document/前端生成PDF并上传后端技术方案.md b/document/前端生成PDF并上传后端技术方案.md new file mode 100644 index 00000000..6dde504f --- /dev/null +++ b/document/前端生成PDF并上传后端技术方案.md @@ -0,0 +1,113 @@ +# 前端生成 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` 访问。 + +## 后续扩展 +- 加页眉页脚(时间、患者信息、页码)。 +- 报告水印与签章。 +- 历史报告下载与比对视图。