BodyBalanceEvaluation/document/前端生成PDF并上传后端技术方案.md
2025-12-09 13:31:01 +08:00

4.5 KiB
Raw Blame History

前端生成 PDF 并上传后端技术方案

概述

  • 目标:诊断报告页面在前端生成高保真 PDF上传到 Python 后端持久化,并把文件路径写入对应检测会话。
  • 推荐:使用 Electron 主进程 webContents.printToPDF 生成 PDF忠实度高、分页与中文字体友好作为短期备选提供 html2canvas 截图转 PDF 的实现。

架构流程

  • 前端渲染进程:用户在报告页面点击“生成报告”→ 发送 IPC 请求至主进程生成 PDF → 主进程返回 PDF Buffer 给渲染进程 → 渲染进程上传后端。
  • Python 后端:接收 PDF 文件流,写入 backend/static/reports/<YYYY-MM-DD>/<session_id>.pdf,记录相对路径到 detection_sessions 的报告字段。

前端实现Electron 渲染)

报告页面打印版式

  • 报告根容器,例如 #report-root
  • 打印样式建议:
    • @page { size: A4; margin: 12mm }
    • 固定宽度设计为 A4 比例,非交互元素隐藏,保留背景。
    • 通过添加类名(例如 .print-mode)切换打印样式。

渲染进程触发生成

// 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

// 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
})

更新数据库记录

# 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()

接口设计

  • 上传 PDFPOST /api/reports/<session_id>/upload
    • 请求体:multipart/form-data,字段 file
    • 返回:{ success, path }

权限与安全

  • 校验 session_id 存在且归属当前登录用户(医生)。
  • 限制最大文件大小(例如 20MB拒绝超限上传。
  • 文件名使用 session_id.pdf,避免任意文件名写盘。

版式与质量建议

  • 中文字体:在渲染环境预装中文字体或通过 @font-face 引入。
  • 分页:使用 @pagepage-break-before/after 控制分页。
  • 背景Electron 打印需开启 printBackground: true;确保 CSS 背景不被打印忽略。

错误与排查

  • 空白 PDF确保打印前切入 .print-mode 并等待资源加载;检查选择器是否找到根节点。
  • 字体方框:安装中文字体或嵌入字体文件。
  • 资源丢失:使用相对路径或 base64html2canvas 时启用 useCORS 且服务器设置跨域头。
  • 过大或截断:分页控制,或拆页生成。

测试用例

  • 单页报告:生成并上传,后端返回路径;数据库记录更新;历史档案中可下载或预览。
  • 多页报告:存在分页断点,打印后为多页;每页标题和页码正常。
  • 异常:网络断开、文件超限、会话不存在;前端提示具体错误。

部署说明

  • Electron 打包:已使用 electron-builder;生成 exe 后功能一致。
  • 后端存储Windows 下路径统一使用 / 展示;静态文件由后端提供下载或前端拼接 BACKEND_URL + '/' + path 访问。

后续扩展

  • 加页眉页脚(时间、患者信息、页码)。
  • 报告水印与签章。
  • 历史报告下载与比对视图。