docs: add OA person select control design spec.
Capture FormCreate person field (dept-aligned UI), form_data contract, and contains-only condition branching for workflow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
be41e8f202
commit
39d1031f6c
1 file changed
+261
@@ -0,0 +1,261 @@
|
||||
# OA「人员」控件设计文档
|
||||
|
||||
**日期:** 2026-07-30
|
||||
**范围:** `web-admin` 审批设计器步骤 2(表单设计)+ 步骤 3(条件分支)+ 发起/详情/打印运行时;`backend` 发布白名单、发起校验、条件匹配
|
||||
**关联:** `docs/superpowers/specs/2026-07-27-oa-dept-select-design.md`;`docs/superpowers/specs/2026-07-15-oa-condition-from-form-design.md`;`docs/superpowers/specs/2026-07-30-oa-money-field-design.md`
|
||||
**状态:** 已评审待实现
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
OA 审批「2 表单设计」需提供可拖拽的 **人员** 控件:从本租户系统用户表选择一人或多名人员;展示与交互对齐已落地的 **部门** 控件。勾选「参与流程条件分支」后,可在「3 流程设计」中配置钉钉式「包含」条件(右侧可选多人,任一命中即通过)。
|
||||
|
||||
### 1.1 产品结论(已确认)
|
||||
|
||||
| 项 | 结论 |
|
||||
|----|------|
|
||||
| 展示/交互 | 对齐「部门」:PC 下拉 + 窄屏抽屉;多选 tag 在选择框内 |
|
||||
| 选择能力 | 默认可多选;设计器可改为单选;可配 `max` |
|
||||
| 数据源 | `sys_user`(`fetchUserList`,`status=1` 启用用户);租户隔离走现有 admin API |
|
||||
| 存值 | 单选 `{ id, name }`;多选同结构数组(`name` 为展示名快照) |
|
||||
| 条件运算符 | **仅「包含」**(`contains`);不提供等于/不等于 |
|
||||
| 包含语义 | 条件右侧可多选人;表单人员 id 集合 ∩ 条件 id 集合 **非空** 即命中(OR) |
|
||||
| 实现路径 | 自定义 FormCreate `type=person`(四层扩展,同部门/金额) |
|
||||
|
||||
### 1.2 非目标
|
||||
|
||||
- 等于 / 不等于 运算符
|
||||
- 按部门树筛人(可后续加)
|
||||
- 用本控件结果直接改写审批人解析(仍用现有 `assigneeType` 策略)
|
||||
- 本期小程序运行时渲染器(Schema/`form_data` 须可映射)
|
||||
- 行业通讯录 / 外部联系人
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构
|
||||
|
||||
对齐 FormCreate / FcDesigner **四层扩展**:
|
||||
|
||||
| 层 | 内容 |
|
||||
|----|------|
|
||||
| Runtime | `OaPersonSelect.vue`:`v-model` 为对象或对象数组 |
|
||||
| FormCreate 组件注册 | `formCreate.component('person', …)` |
|
||||
| DragRule | `menu: 'main'`;`label: '人员'`;`props.multiple` 默认 `true` |
|
||||
| 双侧注册 | 主应用 + `oa-fc-host` 均调用 `setupOaPersonSelect` |
|
||||
|
||||
```
|
||||
[设计器 DragRule] → form_schema_json.type = "person"
|
||||
↓
|
||||
[OaPersonSelect] ← fetchUserList(status=1)
|
||||
↓
|
||||
form_data[field] = { id, name } | { id, name }[]
|
||||
↓
|
||||
[条件] useInCondition → op=contains, value=string[] (user ids)
|
||||
↓
|
||||
[引擎] id 集合交集非空 → 命中分支
|
||||
[详情/打印] 优先展示 name 快照
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Schema 与属性
|
||||
|
||||
### 3.1 规则形态(示例)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "person",
|
||||
"field": "Fxxxx",
|
||||
"title": "协同人",
|
||||
"info": "",
|
||||
"$required": false,
|
||||
"useInCondition": false,
|
||||
"props": {
|
||||
"multiple": true,
|
||||
"max": null,
|
||||
"useInCondition": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 设计器可配属性
|
||||
|
||||
| 属性 | 说明 |
|
||||
|------|------|
|
||||
| 标题 / 提示信息 / 必填 | 与现有控件一致 |
|
||||
| 允许多选 | Switch,**默认 `true`** |
|
||||
| 最大可选数 | 可选;仅多选时生效;不填不限制 |
|
||||
| 参与流程条件分支 | 基础配置全局开关(`person` 加入 `CONDITION_ELIGIBLE_TYPES` 后可见);勾选后强制必填 |
|
||||
|
||||
---
|
||||
|
||||
## 4. `form_data` 契约(锁定)
|
||||
|
||||
```ts
|
||||
type OaPersonValueItem = {
|
||||
id: string // sys_user.id (UUID)
|
||||
name: string // 提交时展示名快照:优先 nickName,空则 userNo
|
||||
}
|
||||
|
||||
// 单选(props.multiple === false)
|
||||
type PersonFormValueSingle = OaPersonValueItem | null
|
||||
|
||||
// 多选(props.multiple === true,默认)
|
||||
type PersonFormValueMulti = OaPersonValueItem[]
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `id`、`name` 均为非空字符串。
|
||||
- 禁止与 `multiple` 不一致的标量/对象混用。
|
||||
- 用户改名后,历史实例 **SHALL** 仍展示提交时的 `name` 快照。
|
||||
- 后端发起校验 **SHALL** 校验值形态;**MAY** 校验 id 对应当前租户启用用户(建议校验,失败返回中文错误)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 运行时交互
|
||||
|
||||
### 5.1 PC(视口宽度 ≥ 768)
|
||||
|
||||
- `el-select`:`filterable`、`clearable`;`multiple` 时多选,已选以 select 内 tag 展示(对齐部门 tree-select 多选表现)。
|
||||
- 选项 label:`nickName`(空则 `userNo`);可附带次要信息区分重名,写入 `name` 的仍为主展示名。
|
||||
|
||||
### 5.2 窄屏(< 768)
|
||||
|
||||
- 触发按钮 + 底部 `el-drawer`(仿部门)。
|
||||
- 抽屉内:搜索 + 单选点选 / 多选勾选;底栏取消(Close)/ 确定(Check)。
|
||||
|
||||
### 5.3 只读
|
||||
|
||||
- 单选:展示 `name`;空为 `—`。
|
||||
- 多选:姓名用「、」拼接。
|
||||
|
||||
### 5.4 超限
|
||||
|
||||
- `max` 有值且超限:前端 `ElMessage.warning`,不写入;后端校验兜底。
|
||||
|
||||
### 5.5 数据加载
|
||||
|
||||
- `fetchUserList({ status: '1', page, pageSize })` 分页拉全量(与发起页 / 流程设计器选人一致)。
|
||||
- 组件内维护 id→name 映射,将 select 的 id 还原为 `{ id, name }`。
|
||||
|
||||
---
|
||||
|
||||
## 6. 条件分支
|
||||
|
||||
### 6.1 资格
|
||||
|
||||
- 前端 `CONDITION_ELIGIBLE_TYPES` 与后端 `oa_publish.CONDITION_ELIGIBLE_TYPES` **均加入** `person`。
|
||||
- 勾选 `useInCondition` 后出现在条件候选列表;发布前强制 `$required` / `required`(现有归一化逻辑)。
|
||||
|
||||
### 6.2 规则形态
|
||||
|
||||
```ts
|
||||
// ConditionOp 扩展
|
||||
type ConditionOp = '>' | '>=' | '<' | '<=' | '==' | '!=' | 'contains'
|
||||
|
||||
interface ConditionRule {
|
||||
field: string
|
||||
op: 'contains' // person 字段仅允许此 op
|
||||
value: string[] // 用户 id 列表,长度 ≥ 1
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 命中语义
|
||||
|
||||
```
|
||||
formIds = extract_user_ids(form_data[field]) // 单对象或数组 → Set<id>
|
||||
condIds = Set(rule.value)
|
||||
hit = |formIds ∩ condIds| > 0
|
||||
```
|
||||
|
||||
类比钉钉审批「发起人属于这些人」:条件侧多人,**任一**出现在表单已选人员中即命中。
|
||||
|
||||
### 6.4 条件编辑器 UI
|
||||
|
||||
- 选中 `person` 字段:运算符固定为「包含」(可只读展示)。
|
||||
- 右侧:人员多选(可搜),`value` 为 `string[]`。
|
||||
- 预览:`{标题} 包含 张三、李四`。
|
||||
- 发布/保存流程时:`contains` 且 `value` 为空 → 拒绝。
|
||||
|
||||
### 6.5 引擎实现要点
|
||||
|
||||
- **SHALL NOT** 将人员对象塞进现有 `{field} == 'x'` 公式字符串做比较。
|
||||
- 在 `_match_condition_group`(或等价路径)对 `op === 'contains'` 走专用集合判断;从 form 值中抽取 `id`(兼容单对象与数组)。
|
||||
- 其他字段的 `==` / 数值比较逻辑保持不变。
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件改动清单
|
||||
|
||||
### 7.1 前端(拟新增)
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `web-admin/src/components/oa-approval/OaPersonSelect.vue` | 运行时控件 |
|
||||
| `web-admin/src/utils/oa-person-value.ts` | normalize / 展示 |
|
||||
| `web-admin/src/utils/oa-person-select-rule.ts` | DragRule |
|
||||
| `web-admin/src/utils/oa-form-create-person.ts` | 注册入口 |
|
||||
|
||||
### 7.2 前端(拟修改)
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `web-admin/src/main.ts`、`oa-fc-host/main.ts` | `setupOaPersonSelect` |
|
||||
| `oa-condition-fields.ts` | 合格类型 + `contains` + `opsForFieldType` |
|
||||
| `flow-tree/types.ts` | `ConditionOp` / `ConditionRule.value` |
|
||||
| `ConditionRuleEditor.vue` | person 右侧多选 |
|
||||
| `utils/oa-approval-print.ts` 等 | `person` 打印展示 |
|
||||
|
||||
### 7.3 后端
|
||||
|
||||
| 路径 | 职责 |
|
||||
|------|------|
|
||||
| `app/services/oa_publish.py` | `FILLABLE_TYPES` / `CONDITION_ELIGIBLE_TYPES` 加入 `person` |
|
||||
| `app/services/oa_person_select.py`(新建) | form_data 值形态校验(仿 `oa_dept_select`) |
|
||||
| `app/services/oa_engine.py` | `contains` 集合匹配 |
|
||||
| `app/services/oa_related_approval.py` | 摘要格式化(仿 dept) |
|
||||
| 发起/提交入口 | 挂接 person 校验 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 错误处理
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 用户列表加载失败 | 选项空;可提示;不阻断设计器 |
|
||||
| 超 `max` | 前端拦截 + 后端校验 |
|
||||
| 条件 `contains` 且 value 空 | 发布/保存流程拒绝 |
|
||||
| 值形态非法 | 发起返回中文错误列表 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 测试要点
|
||||
|
||||
- 单选/多选存值与回显;窄屏抽屉确认写入。
|
||||
- `useInCondition` 对 `person` 可见;勾选后必填。
|
||||
- 条件:表单选 A,条件 `[A,B]` → 命中;条件仅 `[B]` 且表单无 B → 不命中。
|
||||
- 发布含 `person` 的应用不报未知控件。
|
||||
- 打印 / 关联审批摘要显示姓名快照。
|
||||
- 回归:金额数值条件、部门存值与打印不受影响。
|
||||
|
||||
---
|
||||
|
||||
## 10. 实现顺序建议
|
||||
|
||||
1. 值工具 + 后端 `FILLABLE`/`CONDITION` 白名单 + person 校验骨架
|
||||
2. `OaPersonSelect` + DragRule + 双侧注册
|
||||
3. 条件类型/`contains` + ConditionRuleEditor 人员多选
|
||||
4. 引擎集合匹配 + 单测
|
||||
5. 打印/关联审批格式化 + 端到端手测
|
||||
|
||||
---
|
||||
|
||||
## 11. 验收标准
|
||||
|
||||
- [ ] 设计器左侧可拖入「人员」,可配单多选与 max
|
||||
- [ ] 发起页可选人并正确提交 `{ id, name }` / 数组
|
||||
- [ ] 勾选参与条件后,流程条件仅「包含」,右侧可多选人,任一命中走对应分支
|
||||
- [ ] 详情与打印展示姓名;历史快照不随用户改名变化
|
||||
- [ ] 未知控件发布校验通过;既有金额/部门行为无回归
|
||||
Reference in new issue
Block a user