diff --git a/docs/mockups/oa-approval-condition-from-form-demo.html b/docs/mockups/oa-approval-condition-from-form-demo.html new file mode 100644 index 00000000..b49236b7 --- /dev/null +++ b/docs/mockups/oa-approval-condition-from-form-demo.html @@ -0,0 +1,716 @@ + + + + + + OA 条件分支 · 表单字段驱动 Demo + + + +
+ + +
+
+ +
+ + +
+
+
+ + + + +
+ + +
+
+ +
+
请假申请 · 表单画布
+
+ 用于分支 +
请假天数 *
+
请输入数字
+
+
+ 用于分支 +
请假事由 *
+
请输入
+
+
+ 用于分支 +
请假类型
+
事假 / 病假 / 年假
+
+
+ 用于分支 +
附件
+
上传附件
+
+
+ +
+
+ + +
+
+
+
+
发起人
+
所有人可发起
+
+
+
+
+
+
+
条件分支
+
2 个分支 · 点击右侧配置
+
+
+
+ 请假天数 > 3 +
+
+
审批人
+
总经理
+
+
+
结束
流程结束
+
+
+ 默认 · 其他情况 +
+
+
审批人
+
部门负责人
+
+
+
结束
流程结束
+
+
+
+ + +
+
+ +
+ (本 Demo 不展开)基础设置:图标、名称、说明… +
+
+ (本 Demo 不展开)高级设置:…
+
+ + +
+ + + + diff --git a/docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md b/docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md new file mode 100644 index 00000000..f5b31eaa --- /dev/null +++ b/docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md @@ -0,0 +1,196 @@ +# OA 条件分支:表单字段驱动可视化配置 + +**日期:** 2026-07-15 +**范围:** `web-admin` 审批设计器步骤 2(表单设计)+ 步骤 3(流程设计);`backend` 发布校验(引擎匹配逻辑已具备) +**Demo:** `docs/mockups/oa-approval-condition-from-form-demo.html` +**关联:** `docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md` § 条件分支;`backend/app/services/oa_engine.py` `_match_condition_group` + +## 1. 背景与目标 + +### 现状问题 + +流程「条件分支」配置要求用户手写公式,例如 `{days} > 5`。普通配置者不了解表单字段 `field` 标识,学习成本高、易配错。 + +### 目标 + +1. 在**表单设计**中显式勾选哪些控件「参与流程条件分支」。 +2. 勾选后该字段在发起时**必填**,且必填开关在勾选期间**锁定不可关**。 +3. 在**流程设计**中用可视化规则配置条件:选择字段标题 + 运算符 + 比较值;**不提供**自定义公式入口。 +4. 运行时仍用表单填报值做分支匹配,不引入独立的「流程实例变量」配置概念。 + +### 非目标 + +- 系统变量(发起人部门等)进入条件(可二期)。 +- 条件组内 OR、跨组复杂布尔、嵌套括号。 +- 保留或新增「高级:自定义公式」折叠区(已否定,降低双模式复杂度)。 +- 改写公式求值引擎核心算法(已有 `rules` → 表达式路径够用)。 + +## 2. 产品约定(已确认) + +| 项 | 结论 | +|----|------| +| 字段入池方式 | 表单控件属性勾选「参与流程条件分支」(方案 A) | +| 必填联动 | 勾选 ⇒ 强制 `required=true`,必填开关禁用;取消勾选后才能改回选填 | +| 可勾选控件 | 仅:数字、金额、单选、下拉、日期 | +| 条件 UI | 纯可视化;无高级公式 | +| 多条件 | 同一分支内多条规则 **AND** | +| 默认分支 | `isDefault=true`,不配 `rules` | +| 取消勾选有引用 | **拦截**,提示先到流程设计删除对该字段的条件引用 | +| 架构方案 | 方案 1:Schema 打标 + `conditionGroups[].rules` | + +## 3. 架构 + +``` +[2 表单设计] [3 流程设计] + FormCreate 字段属性 FlowDesigner 条件抽屉 + · props.useInCondition · 下拉 = 已勾选字段(展示 title) + · 勾选 ⇒ required 锁定 · 编辑 rules[{field,op,value}] + ↓ ↓ + form_schema_json flow_tree_json + ↘ ↙ + 发布校验 / 运行引擎 + · 参与字段必须 required + · rules 只引用合法参与字段 + · _match_condition_group 优先 rules +``` + +**职责边界** + +- **表单:** 谁可以进入条件池,并保证运行时有值(必填)。 +- **流程:** 如何用这些字段做比较。 +- **引擎:** 基本沿用现有 `rules` 匹配;新配置不再依赖用户手写 `when`。 + +## 4. 数据契约 + +### 4.1 表单字段(`form_schema_json`) + +在合格控件的 `props`(或项目统一挂载的扩展字段,与 FormCreate 属性面板约定一致)增加: + +| 属性 | 类型 | 说明 | +|------|------|------| +| `useInCondition` | `boolean` | 是否参与流程条件分支 | +| `required` | `boolean` | `useInCondition === true` 时必须为 `true` | + +**可勾选 type(实现时映射现有 FormCreate type 名):** + +- 数字:如 `inputNumber` +- 金额:如项目内金额控件 type(若与数字共用则同一逻辑) +- 单选:如 `radio` +- 下拉:如 `select` +- 日期:如 `datePicker` / `date` + +附件、说明、布局、公式展示等控件**不展示**该开关。 + +### 4.2 条件分支组(`flow_tree_json`) + +```ts +interface ConditionGroup { + name: string + isDefault?: boolean + /** 新配置主存;默认分支为空数组 */ + rules: Array<{ + field: string + op: '>' | '>=' | '<' | '<=' | '==' | '!=' + value: string | number + }> + /** 存量兼容:仅运行时读取;新保存应清空 */ + when?: string + child: FlowNode | null +} +``` + +**运算符策略** + +| 字段类型 | 可用 op | +|----------|---------| +| 数字 / 金额 / 日期 | `>` `>=` `<` `<=` `==` `!=` | +| 单选 / 下拉 | `==` `!=` | + +同组多条 `rules`:引擎现有逻辑拼为 **AND**(与 `oa_engine._match_condition_group` 一致)。 + +### 4.3 引擎行为(保持兼容) + +`_match_condition_group` 已支持: + +1. `isDefault` 或空规则 → 记为默认分支候选 +2. `when` 字符串公式 +3. `rules` 列表 → 生成临时公式再 `eval_formula` + +**本需求约定:** 设计器新写入以 `rules` 为准;保存可视化配置后清空同组 `when`,避免双源不一致。存量仅有 `when` 的实例版本仍可运行。 + +## 5. 交互 + +### 5.1 表单设计 + +1. 选中合格控件:属性面板显示「参与流程条件分支」。 +2. 打开开关:设置 `useInCondition=true`、`required=true`,禁用必填控件。 +3. 关闭开关:若任意条件节点 `rules[].field` 仍引用当前 `field` → 拦截并提示去流程设计处理;无引用则允许关闭并恢复必填开关可编辑(关闭后不强制把 `required` 改回 `false`,仅解锁)。 +4. 可选:画布角标「用于分支」(Demo 已示意,实现按需)。 + +### 5.2 流程设计 + +1. 条件节点抽屉:去掉「条件公式」输入框。 +2. 规则行:字段下拉(label=标题,value=`field`)+ 运算符 + 值;单选/下拉值用选项列表。 +3. 空池:提示先到步骤 2 勾选;禁用添加规则。 +4. 默认分支:禁用规则编辑,`rules=[]`。 +5. 非默认:至少一行完整规则;支持「+ 且条件」。 +6. 树上分支标签:人话预览(如「请假天数 大于 3」),不展示 `{field}`。 + +### 5.3 设计器数据流 + +- `designer/index.vue` 将当前 `formSchema`(或从中解析的条件候选字段列表)传入 `FlowDesigner`。 +- 候选列表过滤:`useInCondition === true` 且 type 在白名单。 + +## 6. 校验 + +### 6.1 前端(保存草稿 / 切步) + +- 建议:切换到流程步或保存草稿时做软提示;**发布**做硬拦截。 + +### 6.2 发布(`oa_publish` + 前端发布前) + +| 检查 | 处理 | +|------|------| +| `useInCondition` 但 `required` 不为真 | 发布前自动纠正为 required,或拦截(推荐自动纠正并写入 schema) | +| 非默认分支 `rules` 为空或 `field`/`op`/`value` 缺失 | 拦截:「请完善条件分支」 | +| `rules.field` 在 schema 中不存在或未 `useInCondition` | 拦截:「条件引用了无效字段」 | +| 分支仅有存量 `when`、无有效 `rules` | **不允许发布**;提示用可视化重新配置 | +| 默认分支缺失或不唯一 | 沿用现有发布规则 | + +### 6.3 运行时 + +- 发起校验:必填字段由动态表单 required 保障。 +- 分支匹配:`form_data[field]` + `rules`;不另建变量表。 + +## 7. 存量与迁移 + +| 场景 | 行为 | +|------|------| +| 已发布版本仅含 `when` | 运行时继续按公式走;不影响在途实例 | +| 打开草稿/新版本编辑 | 条件抽屉:若无 `rules` 仅有 `when`,展示提示「请用可视化重新配置」;保存后写入 `rules` 并清空 `when` | +| V1 不做自动公式→rules 解析 | 避免半吊子解析;配置者重配一次即可 | + +## 8. 改动面(实现指引) + +| 层 | 内容 | +|----|------| +| 前端表单 | FormCreate 属性面板扩展 `useInCondition` + 必填锁定 | +| 前端流程 | `FlowDesigner.vue` 条件 UI;`types.ts` 明确 `rules` 形状 | +| 前端设计器壳 | `designer/index.vue` 传递 form 候选字段给流程步 | +| 后端 | `oa_publish` 校验补强;引擎优先不改或仅单测覆盖 rules 路径 | +| 文档/Demo | 本 spec + 已有 mockup | + +## 9. 验收标准 + +1. 配置者无需知道 `field` 变量名即可配置「天数大于 3」类分支。 +2. 未勾选参与的字段不会出现在条件字段下拉中。 +3. 勾选参与后发起页该字段必填且属性上无法关掉必填。 +4. 发布应用后:满足条件走对应分支,否则走默认分支。 +5. UI 上不再出现条件公式输入框。 +6. 试图取消仍被引用的「参与分支」勾选时被拦截并有中文提示。 + +## 10. 测试要点 + +- 单元:`rules` 匹配(数字比较、字符串等于、默认分支回落)— 扩展现有 `test_oa_engine` / `test_oa_publish`。 +- 发布:引用未勾选字段应失败;仅有 `when` 无 `rules` 应失败。 +- 前端手测:勾选锁定必填、取消有引用拦截、下拉只含勾选字段、人话标签、端到端请假式流程。