4.5 KiB
4.5 KiB
前端生成 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()
接口设计
- 上传 PDF:
POST /api/reports/<session_id>/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访问。
后续扩展
- 加页眉页脚(时间、患者信息、页码)。
- 报告水印与签章。
- 历史报告下载与比对视图。