2026-07-22-oa-related-approval-nested-print-design.md 8.8 KB

OA「关联审批单」嵌套展示与打印修订设计

日期: 2026-07-22
状态: 已批准(实现计划见 docs/superpowers/plans/2026-07-22-oa-related-approval-nested-print.md)
方案: 方案 1 — 屏幕保持计数可跳转;打印专用递归组装(最多 3 层)
界面模式: 屏幕对齐既有关联卡片;打印对齐现有钉钉风 PDF 主表
前置文档:

  • docs/superpowers/specs/2026-07-22-oa-related-approval-design.md(V1 基线)
  • docs/superpowers/specs/2026-07-15-oa-approval-print-pdf-design.md(打印 PDF)
  • Demo:docs/mockups/oa-related-approval-nested-demo.html、docs/mockups/oa-related-approval-print-demo.html

决策记录:

  • 屏幕 · 嵌套关联:方案 B — 显示「已关联 M 条」,可跳转详情,不嵌套缩略卡
  • 打印 · 布局:方案 3 — 多行清单,每条关联单独成块
  • 打印 · 内容:打出对方单 业务字段摘要;嵌套关联继续递归
  • 打印 · 深度:最多 3 层;同一 instanceId 去重防环
  • 实现路径:方案 1 — 打印专用递归;不改为统一树形 enrichment API

1. 背景与问题

V1 规格 §5 规定:嵌套 relatedApproval 扁平化为「已关联 M 条(不递归展开)」。

实测:发起/详情选中一张本身也含「关联审批单」的历史单时,卡片上只见「已关联 M 条」,无缩略、无展开子内容;同时打印路径未特判该控件,会把 string[] instanceId 打成 UUID,纸面不可读。

产品确认:

端 期望
屏幕 嵌套保持计数文案,但须可跳转;不在卡片内画子树
打印 多行展示关联单信息,含业务字段;嵌套最多递归 3 层

本文件 修订 V1 在嵌套/打印上的行为;form_data 契约与选单范围不变。


2. 目标与范围

2.1 目标

  1. 屏幕:嵌套「已关联 M 条」可跳转至被关联实例详情
  2. 打印:relatedApproval 字段输出可读多行块(含对方业务字段),支持最多 3 层递归
  3. 防环:同一 instanceId 在一份 PDF 中不重复展开字段体

2.2 包含

项 说明
enrichment 扁平化 嵌套 relatedApproval 字段增加可选 relatedIds: string[],value 仍为「已关联 M 条」
OaRelatedApproval.vue 识别 relatedIds,渲染可点跳转
oa-approval-print.ts + OaApprovalPrintSheet.vue 特判关联控件;递归组装打印块;版式多行缩进
规格交叉引用 回写 V1 §5 嵌套行,指向本文

2.3 不包含

  • 屏幕端一层/无限嵌套缩略卡片(否决嵌套 Demo C/D)
  • 改选单范围、自动串子流程、回填其它字段
  • 后端生成 PDF
  • 统一 summaries?depth= 树形 API(否决实现方案 2)
  • 打印时对每个关联 id 全量 get_instance_detail(否决实现方案 3)

3. 屏幕行为

3.1 直接关联(不变)

  • 应用名 · 时间;收起前 3 字段 / 展开全字段;点标题进详情
  • 详情页 form.disabled 下展开钮须可点(已用原生 button 规避 el-button 继承禁用)

3.2 嵌套 relatedApproval 字段

规则 行为
展示文案 已关联 M 条(M 为 id 个数;0 则「已关联 0 条」)
不展示 子卡片缩略、子级展开树
可点跳转 字段带 relatedIds 时:M=1 进该实例;M>1 进列表顺序第一条
不可见 保持现有「无法查看」;无 relatedIds 时纯文本、不可点

4. 打印行为

4.1 主表内版式

普通字段:两列「标签 | 值」。

relatedApproval 字段:右侧为多行块,示意:

关联审批单
├─ 1. 外出申请 · 2026-07-10
│     目的地:上海
│     起止:07-10 ~ 07-12
│     关联审批单:
│       └─ 1.1 立项申请 · 2026-06-01
│             项目名称:XX
│             关联审批单:已关联 1 条    ← 已到第 3 层,不再展开
└─ 2. 另一张单 · 2026-06-15
      …

4.2 递归与安全

规则 定义
层数 当前被打印单上的直接关联 = 第 1 层;其子关联 = 第 2 层;再下 = 第 3 层
上限 最多展开到第 3 层;更深嵌套字段只保留文案「已关联 M 条」
去重 visited 集合;已出现过的 id 输出「(已在上文出现)」,不重复字段体
不可见 该条标题「无法查看」,无字段体;不中断整份 PDF
加载失败 该层提示「关联信息加载失败」;其它行照常

4.3 单条块内容

  1. 标题行:{序号} {appName} · {applyTime 日期}
  2. 正文:enrichment fields[] 的 label + value(附件等沿用现打印 URL/既有格式)
  3. 若某 field 含 relatedIds 且 depth < 3:在该字段下缩进递归子块

5. 数据契约

5.1 不变

  • Schema type: relatedApproval,form_data 为 string[]
  • 选单:本人发起 + completed + 租户隔离

5.2 enrichment field 扩展(向后兼容)

{
  label: string
  value: string
  fullWidth?: boolean
  relatedIds?: string[]  // 仅嵌套/本控件扁平化为「已关联 M 条」时可选带出
}
  • 旧客户端忽略 relatedIds 仍可展示文案
  • 屏幕跳转依赖 relatedIds;缺省则不可点

5.3 打印行模型扩展(前端)

在现有 OaPrintFieldRow 上扩展其一即可(实现时择一,保持单一来源):

  • text / urls(现有),或
  • blocks?: RelatedPrintBlock[](关联专用多行树)

RelatedPrintBlock 建议:

{
  indexLabel: string      // "1" | "1.1"
  title: string           // "外出申请 · 2026-07-10" | "无法查看" | "(已在上文出现)"
  lines?: { label: string; value: string }[]
  children?: RelatedPrintBlock[]
}

6. 数据流(方案 1)

列表「打印」
  → fetchOaInstanceDetail(id)
  → buildOaPrintModel(detail)
       → walk schema
       → relatedApproval 字段:
            assembleRelatedPrintBlocks(ids, depth=1, visited, maxDepth=3)
              → fetchOaRelatedSummaries(batch ids)
              → 每条 summary → RelatedPrintBlock
              → fields 中带 relatedIds 且 depth < maxDepth → 递归
  → OaApprovalPrintSheet 渲染块
  → html2canvas + jspdf

屏幕路径不走上述递归;仅消费扁平 fields + relatedIds 跳转。


7. 模块落点

模块 改动
backend/app/services/oa_related_approval.py 扁平化 relatedApproval 时写入 relatedIds;value 保持「已关联 M 条」
backend/tests/test_oa_related_approval.py 断言 relatedIds
web-admin/src/components/oa-approval/OaRelatedApproval.vue 「已关联 M 条」可点跳转
web-admin/src/api/oaApproval.ts 类型补 relatedIds?(若有)
web-admin/src/utils/oa-approval-print.ts 特判 + 递归组装 + 批量去重请求
web-admin/src/components/oa-approval/OaApprovalPrintSheet.vue 关联块多行缩进 UI
docs/superpowers/specs/2026-07-22-oa-related-approval-design.md §5 嵌套行改为交叉引用本文

8. 错误处理

场景 行为
嵌套 id 不可见 打印块「无法查看」;屏幕跳转后走现有详情权限
summaries 部分失败 失败 id 占位;成功 id 正常
环引用 visited 去重
无关联控件旧单 打印行为与现网一致

9. 验收标准

  1. 屏幕:嵌套字段为「已关联 M 条」;M≥1 且有 relatedIds 时可点进详情;无嵌套缩略卡
  2. 打印:关联字段为多行清单,含对方业务字段;非 UUID、非仅计数
  3. PDF 中嵌套最多 3 层;更深为计数文案
  4. 重复 id 不重复字段体,有「已在上文出现」
  5. 部分不可见时整份 PDF 仍可生成
  6. 无该控件旧应用打印回归通过
  7. 页眉/页脚/印章/水印等既有打印行为不变

10. 测试建议

  • 后端:flatten_form_fields_for_summary 对嵌套关联产出 relatedIds
  • 前端打印:depth=1..3、visited 环、并列多条、unavailable
  • 手工:A←B←C←D 链;A↔B 环;列表打印目检 PDF

11. 实现顺序建议

  1. 后端扁平化补 relatedIds + 单测
  2. 屏幕「已关联 M 条」可跳转
  3. 打印组装递归 + PrintSheet 版式
  4. 回归旧应用与权限占位
  5. 回写 V1 规格交叉引用

12. 修订说明(相对 V1)

V1 §5 原文 本文
嵌套 relatedApproval →「已关联 M 条(不递归展开)」 屏幕仍不递归缩略,但可跳转;打印最多递归 3 层打出业务字段

若与 V1 冲突,嵌套展示与打印以本文为准;V1 其余条款(选单、持久化、四层注册等)不变。