日期: 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)docs/mockups/oa-related-approval-nested-demo.html、docs/mockups/oa-related-approval-print-demo.html决策记录:
instanceId 去重防环V1 规格 §5 规定:嵌套 relatedApproval 扁平化为「已关联 M 条(不递归展开)」。
实测:发起/详情选中一张本身也含「关联审批单」的历史单时,卡片上只见「已关联 M 条」,无缩略、无展开子内容;同时打印路径未特判该控件,会把 string[] instanceId 打成 UUID,纸面不可读。
产品确认:
| 端 | 期望 |
|---|---|
| 屏幕 | 嵌套保持计数文案,但须可跳转;不在卡片内画子树 |
| 打印 | 多行展示关联单信息,含业务字段;嵌套最多递归 3 层 |
本文件 修订 V1 在嵌套/打印上的行为;form_data 契约与选单范围不变。
relatedApproval 字段输出可读多行块(含对方业务字段),支持最多 3 层递归| 项 | 说明 |
|---|---|
| enrichment 扁平化 | 嵌套 relatedApproval 字段增加可选 relatedIds: string[],value 仍为「已关联 M 条」 |
OaRelatedApproval.vue |
识别 relatedIds,渲染可点跳转 |
oa-approval-print.ts + OaApprovalPrintSheet.vue |
特判关联控件;递归组装打印块;版式多行缩进 |
| 规格交叉引用 | 回写 V1 §5 嵌套行,指向本文 |
summaries?depth= 树形 API(否决实现方案 2)get_instance_detail(否决实现方案 3)form.disabled 下展开钮须可点(已用原生 button 规避 el-button 继承禁用)relatedApproval 字段| 规则 | 行为 |
|---|---|
| 展示文案 | 已关联 M 条(M 为 id 个数;0 则「已关联 0 条」) |
| 不展示 | 子卡片缩略、子级展开树 |
| 可点跳转 | 字段带 relatedIds 时:M=1 进该实例;M>1 进列表顺序第一条 |
| 不可见 | 保持现有「无法查看」;无 relatedIds 时纯文本、不可点 |
普通字段:两列「标签 | 值」。
relatedApproval 字段:右侧为多行块,示意:
关联审批单
├─ 1. 外出申请 · 2026-07-10
│ 目的地:上海
│ 起止:07-10 ~ 07-12
│ 关联审批单:
│ └─ 1.1 立项申请 · 2026-06-01
│ 项目名称:XX
│ 关联审批单:已关联 1 条 ← 已到第 3 层,不再展开
└─ 2. 另一张单 · 2026-06-15
…
| 规则 | 定义 |
|---|---|
| 层数 | 当前被打印单上的直接关联 = 第 1 层;其子关联 = 第 2 层;再下 = 第 3 层 |
| 上限 | 最多展开到第 3 层;更深嵌套字段只保留文案「已关联 M 条」 |
| 去重 | visited 集合;已出现过的 id 输出「(已在上文出现)」,不重复字段体 |
| 不可见 | 该条标题「无法查看」,无字段体;不中断整份 PDF |
| 加载失败 | 该层提示「关联信息加载失败」;其它行照常 |
{序号} {appName} · {applyTime 日期}fields[] 的 label + value(附件等沿用现打印 URL/既有格式)relatedIds 且 depth < 3:在该字段下缩进递归子块type: relatedApproval,form_data 为 string[]completed + 租户隔离{
label: string
value: string
fullWidth?: boolean
relatedIds?: string[] // 仅嵌套/本控件扁平化为「已关联 M 条」时可选带出
}
relatedIds 仍可展示文案relatedIds;缺省则不可点在现有 OaPrintFieldRow 上扩展其一即可(实现时择一,保持单一来源):
text / urls(现有),或blocks?: RelatedPrintBlock[](关联专用多行树)RelatedPrintBlock 建议:
{
indexLabel: string // "1" | "1.1"
title: string // "外出申请 · 2026-07-10" | "无法查看" | "(已在上文出现)"
lines?: { label: string; value: string }[]
children?: RelatedPrintBlock[]
}
列表「打印」
→ 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 跳转。
| 模块 | 改动 |
|---|---|
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 嵌套行改为交叉引用本文 |
| 场景 | 行为 |
|---|---|
| 嵌套 id 不可见 | 打印块「无法查看」;屏幕跳转后走现有详情权限 |
| summaries 部分失败 | 失败 id 占位;成功 id 正常 |
| 环引用 | visited 去重 |
| 无关联控件旧单 | 打印行为与现网一致 |
M≥1 且有 relatedIds 时可点进详情;无嵌套缩略卡flatten_form_fields_for_summary 对嵌套关联产出 relatedIdsrelatedIds + 单测| V1 §5 原文 | 本文 |
|---|---|
嵌套 relatedApproval →「已关联 M 条(不递归展开)」 |
屏幕仍不递归缩略,但可跳转;打印最多递归 3 层打出业务字段 |
若与 V1 冲突,嵌套展示与打印以本文为准;V1 其余条款(选单、持久化、四层注册等)不变。