From 417d6c9c51569d3266ac373b1fd3c4efbe22b439 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 17 Jul 2026 16:10:55 +0800 Subject: [PATCH] =?UTF-8?q?docs(oa):=20=E5=8F=91=E8=B5=B7=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E9=A2=84=E8=A7=88=E6=94=B9=E4=B8=BA=E9=92=89=E9=92=89?= =?UTF-8?q?=E5=BC=8F=E6=9C=80=E7=BB=88=E8=B7=AF=E5=BE=84=E8=A7=84=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 条件字段未齐时占位提示,齐后仅展示命中审批链;并在首版预览规格中加交叉引用。 Co-authored-by: Cursor --- ...2026-07-15-oa-start-flow-preview-design.md | 4 + ...start-flow-preview-resolved-path-design.md | 250 ++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md diff --git a/docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md b/docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md index f92f4b75..c634ac08 100644 --- a/docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md +++ b/docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md @@ -4,6 +4,10 @@ **范围:** `web-admin` `/#/system-manage/oa-approval/start` 发起弹窗 **Demo:** `docs/mockups/oa-approval-start-flow-preview-demo.html`(已定稿样式) +> **修订(2026-07-17):** 条件分支展示已改为钉钉式「未齐占位 / 齐后最终路径」,见 +> `docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md`。 +> 下文「全部支路展开」与「非目标:条件随表单实时裁剪」以新规格为准。 + ## 目标 发起人填单时,在表单下方展示审批链预览:节点名称 + 签批方式 + 配置说明 + 解析出的具体人员,样式对齐 Demo(灰色圆点时间轴、单行紧凑卡片)。 diff --git a/docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md b/docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md new file mode 100644 index 00000000..973b61e3 --- /dev/null +++ b/docs/superpowers/specs/2026-07-17-oa-start-flow-preview-resolved-path-design.md @@ -0,0 +1,250 @@ +# 发起审批 · 条件可确定后展示最终审批链(钉钉式) + +**日期:** 2026-07-17 +**范围:** `web-admin` `/#/system-manage/oa-approval/start` 发起弹窗底部「审批流程」;`backend` `preview_start_flow` / `flow-preview` API +**关联:** + +- `docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md`(首版方案 B:条件全展) +- `docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md`(条件字段来源与必填约定) +- `backend/app/services/oa_engine.py` — `_match_condition_group`、`preview_start_flow` + +**关系:** 本规格**取代**首版中「条件分支全部支路展开」与非目标「条件随表单实时裁剪」;人员解析、卡片样式、线性流程展示仍沿用 2026-07-15 规格。 + +--- + +## 1. 背景与目标 + +### 现状问题 + +含条件分支的流程在发起预览中并列展示所有支路(如「条件1」与「默认」),并标注「实际路径以提交时表单为准」。用户无法在提交前看到**将实际走的那一条链**,与钉钉「填齐必填后再出流程」的体验不一致。 + +### 目标 + +1. 条件相关表单字段**未齐**时:底部不展开支路,仅提示「必填信息填写后,流程将自动显示」。 +2. 字段**齐且可匹配**后:只展示**命中路径**上的节点,并解析审批人/抄送人(与提交后引擎行为一致)。 +3. 无条件分支的线性流程:打开弹窗即可展示完整链(行为不变)。 + +### 非目标 + +- 全展 + 命中支路高亮(备选路径仍可见)。 +- 条件规则依赖「已解析出的审批人是谁」(条件仍只认表单字段)。 +- 改造审批详情页进度轴。 +- 在流程节点卡片内点选自选审批人(自选仍用弹窗顶部选择器)。 + +--- + +## 2. 产品约定(已确认) + +| 项 | 结论 | +|----|------| +| 未确定时 UI | 钉钉式占位文案,**不**展开条件1/默认等支路 | +| 占位文案 | 「必填信息填写后,流程将自动显示」 | +| 已确定时 UI | 仅最终路径时间轴(节点名 + 签批方式 + meta + 人员 chip) | +| 「可确定」依据 | 流程树中条件分支所引用的表单字段在 `formData` 中均已有有效值 | +| 发起人自选 | **不**作为整段预览的门闩;路径已确定即出链,未选人时该节点 `people` 为空 | +| 与提交一致性 | 分支匹配复用 `_match_condition_group(form_data)`,与 `advance_from` 同源 | +| 刷新时机 | 表单值变化防抖刷新;自选人变化立即/短防抖刷新 | + +--- + +## 3. 状态机 + +``` + ┌─────────────────────┐ + │ 打开发起弹窗 / 改表单 │ + └──────────┬──────────┘ + ▼ + 流程树是否含 condition? + / \ + 否 是 + │ │ + ▼ ▼ + 【已确定】 条件引用字段是否均有值? + 展示完整链 / \ + 否 是 + │ │ + ▼ ▼ + 【待确定】 【已确定】 + 仅占位文案 只走命中支路 + 解析人 +``` + +| 状态 | `resolved` | 前端展示 | +|------|------------|----------| +| 待确定 | `false` | 标题「审批流程」+ 占位文案;`steps` 为空或忽略 | +| 已确定 | `true` | 标题 + 时间轴 `steps`(可含右侧 hint,如「按当前发起人解析审批人」) | + +加载中:沿用现有 `v-loading`,不闪占位与时间轴之间的错误态。 + +--- + +## 4. 「可确定」判定细则 + +### 4.1 收集条件字段 + +从发布版 `flow_tree_json` 根节点递归: + +- 对每个 `type=condition` 节点,遍历 `conditionGroups`; +- 跳过默认分支(`isDefault` 或无 `rules`/`when` 的空规则组); +- 从非默认组的 `rules[]` 收集 `field`;若仅有历史 `when` 字符串,用与引擎一致的方式解析出 `{field}` 占位符(若无法解析,视为该组不增加门闩字段,匹配仍走引擎)。 + +去重后得到集合 `conditionFields`。 + +### 4.2 字段「有值」 + +对每个 `field`,取 `formData[field]`: + +- `null` / `undefined` / `''`(trim 后)→ 未填; +- 数组:长度为 0 → 未填; +- 数字 `0`、布尔 `false` → **视为已填**(合法取值)。 + +任一 `conditionFields` 未填 → `resolved=false`。 + +无 `condition` 节点,或 `conditionFields` 为空(仅有默认支)→ 直接 `resolved=true`,按主链(或默认支)预览。 + +### 4.3 匹配后行走 + +`resolved=true` 时: + +- 遇到 `condition`:调用 `_match_condition_group(groups, form_data)`,**仅**递归命中组的 `child`,**不**再把其它组写入 `steps`; +- **不**再输出 `type=branch` 的「条件1 / 默认」占位步(最终链上无需分支标题卡,与钉钉「直接出后续审批人」一致); +- 条件节点汇合后的 `child`(若有)继续行走; +- `approval` / `cc` / `start` / `end` 人员解析逻辑与现网 `preview_start_flow` 一致。 + +若字段齐但 `_match_condition_group` 返回 `None`(异常配置):`resolved=false`,占位文案可改为「流程条件无法匹配,请检查表单或联系管理员」(可选;首版可仍用统一占位句,并在 `resolveError` 级日志/接口字段暴露)。 + +--- + +## 5. API + +### 5.1 变更 + +将 + +`GET /api/admin/oa-approval/templates/{appId}/flow-preview?initiatorSelectUserIds=` + +改为(推荐): + +`POST /api/admin/oa-approval/templates/{appId}/flow-preview` + +Body: + +```json +{ + "formData": { "days": 5 }, + "initiatorSelectUserIds": ["user-id-1"] +} +``` + +兼容:若短期内保留 GET,则无法可靠传复杂 `formData`;**本期以 POST 为准**,前端切换后可删除 GET 或让 GET 仅支持无条件线性预览。 + +### 5.2 响应 + +```json +{ + "resolved": true, + "message": "", + "steps": [ + { + "id": "n_start", + "type": "start", + "name": "发起人", + "signMode": "", + "assigneeType": "", + "meta": "", + "people": [{ "id": "...", "nickName": "系统管理员" }], + "resolveError": "" + } + ] +} +``` + +| 字段 | 说明 | +|------|------| +| `resolved` | `false` 时前端只显示 `message`(或默认占位句),不渲染时间轴 | +| `message` | `resolved=false` 时默认「必填信息填写后,流程将自动显示」;可覆盖异常文案 | +| `steps` | `resolved=true` 时为最终路径;`false` 时建议 `[]` | + +单步结构与现有 `OaFlowPreviewStep` 一致。 + +--- + +## 6. 前端 + +### 6.1 组件 + +- `OaFlowPreview.vue`:增加 `resolved`(或由父级根据接口决定);`resolved===false` 时展示占位区块(样式对齐 Fresh Mint:浅底、居中/左对齐 muted 文案),隐藏 chain。 +- `start/index.vue`: + - `loadFlowPreview` 改为 POST,传入当前 `formData` 与自选人; + - `watch(formData, …, { deep: true })` + debounce(建议 300ms); + - 保留对 `initiatorSelectUserIds` 的 watch; + - 打开弹窗、详情加载完成后拉一次预览。 + +### 6.2 文案与 hint + +- 待确定:主文案固定为「必填信息填写后,流程将自动显示」;标题旁原 hint「按当前发起人解析审批人」可不显示。 +- 已确定:可保留 hint。 + +### 6.3 API 模块 + +`web-admin/src/api/oaApproval.ts` — `fetchOaFlowPreview(appId, { formData, initiatorSelectUserIds })` 改为 POST;类型增加 `resolved`、`message`。 + +--- + +## 7. 后端 + +### 7.1 主要改动 + +- `oa_approval.py`:新增/替换为 POST handler,解析 body。 +- `oa_engine.preview_start_flow`:增加 `form_data` 参数;实现字段收集、`resolved` 门闩、条件单路行走。 +- 抽取 `_collect_condition_fields(root) -> List[str]`、`_form_value_present(form_data, field) -> bool`,便于单测。 +- 删除或停用「全展 + meta=条件分支(实际路径以提交时表单为准)」分支路径(仅 `resolved=false` 时根本不 walk 条件子树亦可:直接返回空 steps)。 + +### 7.2 测试建议 + +- 无条件:始终 `resolved=true`,steps 含 start→approval→end。 +- 有条件、字段空:`resolved=false`,steps=`[]`,message 为占位句。 +- 有条件、字段满足条件1:仅条件1子树节点出现,无默认支、无 branch 占位步。 +- 有条件、字段走默认:仅默认子树。 +- 自选人未选但字段齐:`resolved=true`,initiator_select 节点 people 为空。 + +--- + +## 8. 主要文件 + +| 文件 | 职责 | +|------|------| +| `backend/app/services/oa_engine.py` | `preview_start_flow` 门闩 + 单路 walk | +| `backend/app/api/oa_approval.py` | POST `flow-preview` | +| `backend/tests/…` | 预览解析单测(新建或挂现有 oa 测试) | +| `web-admin/src/api/oaApproval.ts` | POST 客户端 | +| `web-admin/src/components/oa-approval/OaFlowPreview.vue` | 待确定占位 UI | +| `web-admin/src/views/oa-approval/start/index.vue` | formData 防抖刷新 | +| `docs/superpowers/specs/2026-07-15-oa-start-flow-preview-design.md` | 文首增加「条件展示见 2026-07-17 规格」指向(实现时可补一行) | + +--- + +## 9. 验收标准 + +1. 含条件分支的应用:打开发起弹窗且条件字段未填时,底部仅见「必填信息填写后,流程将自动显示」,**不出现**「条件1 / 默认」双支路。 +2. 填齐条件字段后,底部自动变为单路径时间轴,节点与人员与按该表单提交后的引擎路径一致。 +3. 修改条件字段导致走另一支时,预览链随之切换。 +4. 无条件分支的应用:行为与改前一致,可立即看到审批人。 +5. 仅缺自选审批人、条件已齐:仍显示最终链,自选节点可无人 chip。 +6. 遮罩无 blur、弹窗仍为 `dialog-form-layout`;预览区块视觉延续 Fresh Mint / 现有 `OaFlowPreview` 风格。 + +--- + +## 10. 分阶段 + +| 阶段 | 内容 | +|------|------| +| P0(本期) | POST + resolved 门闩 + 单路预览 + 占位文案 + 前端防抖 | +| P1(可选) | 匹配失败专用文案;从 `when` 公式提取字段的更完整解析;mockup HTML 更新 | + +--- + +## 11. 修订记录 + +| 日期 | 说明 | +|------|------| +| 2026-07-17 | 初稿:钉钉式待确定占位 + 确定后最终路径;用户确认自选不挡整段预览 |