2026-07-27-oa-dept-select-design.md 7.1 KB

OA「部门选择」控件设计文档

日期: 2026-07-27
范围: web-admin 审批设计器步骤 2(表单设计)+ 发起/详情运行时;backend 发布白名单已有 dept,补齐值形态校验与打印
状态: 已评审待实现


1. 背景与目标

OA 审批「2 表单设计」需提供可拖拽的 部门 控件,供发起人选择本租户组织树(sys_dept),与钉钉式「部门」及既有规格 docs/superpowers/specs/2026-07-14-oa-approval-dingtalk-design.md §4.5 / §4.6 对齐。

当前发布字典已允许 type: "dept",但设计器与运行时尚未落地自定义控件。

1.1 产品结论(已确认)

项 结论
选择能力 默认可多选;设计器属性可改为仅单选
存值 单选 { id, name };多选同结构数组
name 全路径快照(分隔符 /)
可选范围 任意层级均可选(非仅末级)
实现路径 自定义 type=dept(对齐关联审批四层扩展);PC 用 el-tree-select
移动兼容 窄屏禁止仅依赖宽屏下拉;须全屏/半屏选择器;Schema/form_data 双端同一份

1.2 非目标

  • 行业通讯录部门
  • 用本控件结果改写审批人解析(仍用发起人部门 / oa_role 等既有策略)
  • 本期实现小程序页面(但契约与交互必须可映射)
  • 本控件「参与流程条件分支」(对象/多选不便进比较器;需要时二期单开)

2. 架构

对齐 FormCreate / FcDesigner 四层扩展(与「关联审批单」同路径):

层 内容
Runtime OaDeptSelect.vue:v-model 为对象或对象数组
FormCreate 组件注册 formCreate.component('dept', …)
DragRule menu: 'main';name/label 为部门;props.multiple 默认 true
双侧注册 主应用(发起/详情)+ oa-fc-host(设计器 iframe)均调用 setupOaDeptSelect

数据源:现有 fetchDeptTree()(本租户组织树),不另造 API。

[设计器 DragRule] → form_schema_json.type = "dept"
        ↓
[OaDeptSelect] ← fetchDeptTree
        ↓
form_data[field] = { id, name } | { id, name }[]
        ↓
[后端发起校验] → 详情 / 打印优先展示 name 快照

3. Schema 与属性

3.1 规则形态(示例)

{
  "type": "dept",
  "field": "Fxxxx",
  "title": "所属部门",
  "info": "",
  "$required": false,
  "props": {
    "multiple": true,
    "max": null
  }
}

3.2 设计器可配属性

属性 说明
标题 / 提示信息 / 必填 与现有控件一致
允许多选 Switch,默认 true;false 时仅单选
最大可选数 可选;仅多选时生效;不填则不限制

树下拉节点文案:本级名称;选中后输入框 / 发起页 / 详情 / 打印:全路径(与 tree-path 约定一致)。


4. form_data 契约(锁定)

type DeptValueItem = {
  id: string   // sys_dept.id
  name: string // 提交时全路径快照,如 "中科兴皖 / 研发中心 / 前端组"
}

// 单选(props.multiple === false)
type DeptFormValueSingle = DeptValueItem | null

// 多选(props.multiple === true,默认)
type DeptFormValueMulti = DeptValueItem[]

约束:

  • id、name 均为非空字符串。
  • 禁止写入 DOM、组件实例、仅本级名却标注为全路径、或与 multiple 不一致的标量/对象混用。
  • 组织改名/调整后,历史实例 SHALL 仍展示提交时的 name 快照;不强制按 id 重算路径。
  • 后端 SHALL NOT 强制校验 id 仍存在于当前组织树(允许已删/历史部门快照可读)。

本决定更新钉钉规格 §4.6 值类型表中「部门」一行的歧义:发布锁定为 { id, name } / 数组,不再使用裸 string deptId。


5. 运行时交互

5.1 PC(视口宽度 ≥ 768)

  • 使用 el-tree-select(或等价树选)。
  • multiple 跟随 props.multiple。
  • 触发条/标签展示全路径;多选以 chip 展示,可删除单条。

5.2 窄屏 / 设计器手机预览(宽度 < 768)

对齐双端规格:「部门/省市区 → 移动端 全屏或半屏选择器,禁止依赖宽屏下拉才能选完」。

  • 表单项为触发条(展示已选全路径或占位「请选择部门」)。
  • 点击后打开 抽屉或全屏面板:搜索 + 组织树 +(多选)勾选 + 取消/确定。
  • 未点「确定」前不写回 v-model。
  • 与 PC 共用同一 v-model 形态,仅交互壳不同。

5.3 只读

详情 / 审批页:仅展示路径文案(单选一个 name;多选多个 name 用中文顿号「、」拼接),不可打开选择器。

5.4 条件分支

dept 不注册「参与流程条件分支」开关;CONDITION_ELIGIBLE_TYPES 不包含 dept。


6. 校验

6.1 前端

  • 必填:单选无值或 null;多选空数组 → 拦截提示。
  • 多选且配置了 max:达到上限不可再选并提示。
  • 窄屏面板未确认不算变更。

6.2 后端(发起 / 再次提交)

  • 按 schema 遍历 type === "dept"。
  • 单选:非必填允许缺省/null;否则须为 { id, name }。
  • 多选:须为数组;每项为 { id, name };必填则 length >= 1;有 max 则 length <= max。
  • 发布:ALLOWED_CONTROL_TYPES 已含 dept,保持即可。

7. 打印与详情

  • 单选:输出 name。
  • 多选:多个 name 用中文顿号「、」拼接。
  • SHALL NOT 将对象 JSON 原样打印到纸面。
  • 实现落点:oa-approval-print(及详情只读渲染)对 type === "dept" 特判。

8. 主要改动面

区域 文件(预期)
Runtime web-admin/src/components/oa-approval/OaDeptSelect.vue
DragRule web-admin/src/utils/oa-dept-select-rule.ts
注册 web-admin/src/utils/oa-form-create-dept.ts;oa-fc-host/main.ts;主应用 form-create 注册点
打印 web-admin/src/utils/oa-approval-print.ts
后端校验 backend/app/services/oa_publish.py 或发起校验模块中的 form_data 校验
Demo(可选) docs/mockups/oa-dept-select-demo.html 与实现对齐

9. 验收标准

  1. 「2 表单设计」左侧组件库出现「部门」,可拖入画布;属性可关闭多选。
  2. PC:树选可用;设计器切换手机预览(或窄视口)时可用半屏/全屏选完,不必依赖宽屏下拉。
  3. 提交后 form_data 形态符合 §4;组织改名后旧实例仍显示快照路径。
  4. 含 dept 的应用可发布;未知 type 仍拒绝。
  5. 无该控件的旧应用行为不变。
  6. 详情与打印展示人类可读路径,不出现原始 JSON。

10. 测试建议

  • 单元:form_data 形态校验(单选/多选/必填/max)。
  • 手工:设计器拖入 → 发起多选/单选 → 详情/打印;窄屏或手机预览走完选择;改部门名称后回看历史单。

11. 开放问题

无。若二期要做「部门字段参与条件分支」或小程序原生页,另开变更,不修改本节契约。