docs(oa): 发起审批预览改为钉钉式最终路径规格
条件字段未齐时占位提示,齐后仅展示命中审批链;并在首版预览规格中加交叉引用。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
a4fd31f254
commit
417d6c9c51
2 files changed
+254
No files matched your search
@@ -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(灰色圆点时间轴、单行紧凑卡片)。
|
||||
|
||||
@@ -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 | 初稿:钉钉式待确定占位 + 确定后最终路径;用户确认自选不挡整段预览 |
|
||||
Reference in new issue
Block a user