Quellcode durchsuchen

docs: add mobile OA print-to-PDF download design for WeChat

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu vor 2 Monaten
Ursprung
Commit
433eb5d113

+ 100 - 0
docs/superpowers/specs/2026-08-13-mobile-oa-print-pdf-download-design.md

@@ -0,0 +1,100 @@
+# 移动端 OA「打印」改为下载 PDF(微信预览)
+
+> 日期:2026-08-13  
+> 范围:`mobile-oa-h5`(打印导出)+ `mobile-h5`(微信宿主文档预览)  
+> 状态:已确认  
+> 关联:`docs/superpowers/specs/2026-07-15-oa-approval-print-pdf-design.md`(web 端 PDF 权威版式)
+
+## 1. 目标
+
+将移动端 OA 详情「打印」与列表长按「打印」(`autoPrint=1`)从当前的 `window.print()`(微信 web-view 内基本不可用)改为与 **web-admin 一致**:前端用钉钉风格模板生成 PDF,并在微信内打开**系统文档预览**(可转发 /「用其他应用打开」保存)。
+
+主运行环境:微信小程序内 `mobile-h5` → `web-view` → `mobile-oa-h5`。
+
+## 2. 非目标
+
+- 不新增后端 PDF 渲染引擎(本期仍前端生成)
+- 不改 web-admin 打印实现
+- 关联审批嵌套块本期仍可用「已关联 N 条」摘要(不对齐 web 完整嵌套打印树);若需对齐另开变更
+- 不导出附件二进制;附件行仍展示 URL / 文件名文本(与 web 一致)
+
+## 3. 方案选型
+
+采用 **前端 html2canvas + jspdf → 上传附件存储 → 宿主 `openDocument` 预览**:
+
+| 步骤 | 负责端 | 说明 |
+|------|--------|------|
+| 1 | `mobile-oa-h5` | 组装 print model,离屏渲染打印 DOM |
+| 2 | `mobile-oa-h5` | `html2canvas` + `jspdf` 生成 PDF Blob(算法对齐 web `oa-approval-print.ts`) |
+| 3 | `mobile-oa-h5` | `uploadAttachment` → MinIO(现有 `/web/admin/attachment/upload`,`folder=oa-approval`) |
+| 4 | `mobile-oa-h5` | `wx.miniProgram.navigateTo` 打开宿主预览页(带文件 URL + 文件名) |
+| 5 | `mobile-h5` | `uni.downloadFile` → `uni.openDocument({ showMenu: true })` |
+
+不采用:纯 `pdf.save()` / `a[download]`(微信不可靠);后端生成 PDF(版式双端维护成本高)。
+
+微信外(普通手机浏览器 / H5 iframe):可回退为 `jspdf` 的 `save()` 直接下载,无需跳转宿主。
+
+## 4. 交互
+
+| 入口 | 行为 |
+|------|------|
+| 详情底栏「打印」 | loading → 生成 → 上传 → 跳转预览;失败 toast,留在详情 |
+| 列表长按「打印」→ `autoPrint=1` | 详情加载完成后自动走同一导出链路 |
+
+按钮:导出过程中 `printing` loading,防重复点击。
+
+文件名(与 web 一致):`{发起人姓名}提交的{流程名称}.pdf`;缺省用「用户」/「审批单」;非法路径字符替换为 `_`。
+
+预览页:打开后自动下载并调起系统预览;失败 toast。**预览调起成功后自动 `navigateBack`** 回详情 web-view,避免预览页在导航栈堆积;用户从系统预览返回后仍在详情。
+
+## 5. PDF 版式(对齐 web)
+
+权威:web `OaApprovalPrintSheet` + `oa-approval-print.ts` / `2026-07-15-oa-approval-print-pdf-design.md`。
+
+结构自上而下:
+
+1. 标题(`appName` 优先)
+2. 元信息:公司名固定 `安徽中科兴皖医学研究有限公司` + `创建时间:YYYY-MM-DD`
+3. 主表(标签 | 值):审批编号、创建人、创建人部门 + 表单字段 + 审批流程行
+4. 右上角钉钉风印章(状态映射与 web / `resolveStamp` 一致)
+5. 页脚:打印时间、打印人
+6. 水印:打印人姓名斜向淡色铺底
+7. 页码:`第 N 页`(canvas 绘制中文,避免 jsPDF 内置字体乱码)
+
+纸张:A4 竖向;切片与略超一页容差对齐 web(`EPS_MM ≈ 1.5`)。
+
+实现方式:可继续用现有 `buildPrintHtml` 字符串模板渲染到离屏 iframe/div,再 `html2canvas`;CSS/印章 DOM 须向 web 打印页收敛,避免与详情页 72px 印章混用两套视觉。
+
+## 6. 微信通信约束
+
+- `wx.miniProgram.postMessage` **不会立即送达**宿主(多在后退/销毁时触发),**不能**靠 postMessage 传 PDF。
+- 大文件不能塞进 `navigateTo` query;必须先上传,query 只传 **HTTPS 文件 URL + 文件名**。
+- 预签名:优先 `fetchAttachmentDownloadUrl` 得到带 `Content-Disposition` 的地址再下载;失败则回退直连上传返回的 `url`。
+
+## 7. 主要改动文件
+
+| 路径 | 职责 |
+|------|------|
+| `mobile-oa-h5/package.json` | 增加依赖 `html2canvas`、`jspdf` |
+| `mobile-oa-h5/src/oa/print/exportOaApprovalPdf.js` | 改为 canvas→PDF;微信上传+跳转;浏览器 `save` 回退 |
+| `mobile-oa-h5/src/views/OaDetail.vue` | 打印成功/失败文案对齐「导出 PDF」;去掉「系统打印不可用」类提示 |
+| `mobile-oa-h5/src/api/oa.js` | 复用已有 `uploadAttachment` / `fetchAttachmentDownloadUrl` |
+| `mobile-h5/pages/oa/pdf-preview/index.vue` | 新建:download + openDocument |
+| `mobile-h5/pages.json` | 注册 `pages/oa/pdf-preview/index` |
+
+## 8. 错误处理
+
+| 场景 | 表现 |
+|------|------|
+| 无详情 / 生成失败 | toast,不跳转 |
+| 上传失败 | toast,不跳转 |
+| 非微信且无 `save` 能力 | toast |
+| 宿主 download / openDocument 失败 | 预览页 toast |
+
+## 9. 验收
+
+1. 微信小程序:详情点「打印」→ 出现系统 PDF 预览,可转发 / 用其他应用打开。
+2. 列表长按「打印」:进入详情后自动导出并预览。
+3. PDF 视觉与 web「打印」导出大体一致(标题、表、印章、水印、页脚、页码)。
+4. 普通浏览器打开 `mobile-oa-h5`:点打印直接下载 PDF 文件。
+5. 导出失败时详情页不关闭,可重试。