Skip to content

approvals UX: surface OOO/quorum/per_group/attachments SDUI-first — stop hand-editing the inbox per feature #2678

Description

@os-zhuang

后端审批能力最近连续落地(framework#1322 OOO 委派 → #3235/#3255;framework#3266 quorum/per_group 会签 + 决策附件 → #3268),前端需要跟上。本 issue 的核心原则:调整现有代码、最大化利用 SDUI(服务端驱动 UI)能力,让未来的审批能力以元数据下发,而不是每个特性都改一遍前端组件。

调研结论(代码级,file:line 均已核实)

平台的 SDUI 底座其实很强

SDUI 面 现状
通用动作运行时 packages/app-shell/src/hooks/useConsoleActionRuntime.tsx — 服务端声明的 action 按 locations(record_header/list_item/list_toolbar/…)放置,按 type 分发执行(flow/script/api/form…),自带确认框/参数收集对话框/结果框 + token 解析。新增一个服务端 action = 前端零代码
通用对象 CRUD nav contribution(type:'object')→ ObjectView 免费渲染 list + form;@object-ui/fields 已注册 file→FileField(真上传部件)、lookup→LookupField(user picker)、datetime 等
流程设计器属性表单 FlowNodeInspector.tsx:113-120 消费引擎发布的 configSchema(ADR-0018/0019),经 json-schema-to-fields.ts 渲染:enum→select、number、array-of-object→objectList
富递归 SchemaForm views/metadata-admin/SchemaForm.tsx(2019 行)— 支持任意嵌套/数组/union/file/lookup,但只用于 metadata 表单,流程设计器没用它

反模式根源:审批收件箱是手写的

apps/console/src/pages/system/ApprovalsInboxPage.tsx(1961 行)+ 私有 REST 封装 services/approvalsApi.ts:Approve/Reject/Send back/Request info/Reassign/Remind/Recall/Resubmit 全部是硬编码 <Button> + 固定 handler,完全没走上面的动作运行时。这就是"每加一个审批能力都得改前端"的原因。SystemHubPage.tsx:1-17 自己都注明:新管理面必须走 nav contribution,不要 bespoke page。

四个能力的逐项判定

能力 判定 依据
① OOO 自助声明 ✅ 纯 SDUI,前端零代码 后端已有 sys_approval_delegation + Setup nav 项「Delegations (OOO)」;lookup(sys_user)/datetime/file 部件全有,ObjectView 直接可用。只需验证 + 截图
② 委派给 X ✅ 已存在(但是手写的) ApprovalsInboxPage.tsx:1843-1849 Reassign 按钮 + sys_user picker(:1376-1408, :681-691)→ /reassign
③ quorum/minApprovals 设计器编辑 ✅ configSchema 一发布即自动渲染 framework#3268 已把 behavior 枚举 + minApprovals 进 configSchema;json-schema-to-fields.ts 的 enum→select、integer→number 无需前端改动。group 文本列(objectList 行内可选 string)也已支持
④ 决策附件 ❌ 唯一真 bespoke 决策 composer 只有 comment Textarea(:1760-1785);approvalsApi 请求体只带 {actor_id, comment};时间线不渲染附件(:1661-1683)。后端 REST 已支持 attachments(#3268)

方案(SDUI-first,两个阶段)

P1 — 立即可做(小)

  1. 验证 ① 和 ③ 端到端(应为零代码):OOO 对象 CRUD 走 ObjectView;流程设计器审批节点属性表单出现 quorum/per_group/minApprovals/group。
  2. 修剪 stale fallback:flow-node-config.ts:416-509 的硬编码审批节点 fallback 仍是 first_response|unanimous 两值 —— 与引擎 schema 已 drift。configSchema 存在时必须以 schema 为准;fallback 同步或标记弃用。
  3. ④ 决策附件(一次性 bespoke):composer 挂 FileField(复用 packages/fields/src/widgets/FileField.tsx + RecordAttachmentsPanel 的上传管线),approvalsApi.approve/reject/comment 请求体加 attachments: string[],时间线渲染附件行。做成可复用的 attachment-aware composer/timeline,之后审批线程的 payload 扩展不再新开 UI。

P2 — 一次性通用投资(防止未来每特性改前端,本 issue 的重点)

  1. 收件箱动作层 SDUI 化:把 Approve/Reject/Reassign/Recall/… 从硬编码按钮迁移为 sys_approval_request 上服务端声明的 actions(type:'api' 指向既有 /approvals/requests/:id/* 路由 + 类型化 params,如 reassign 的 to: user picker),由 useConsoleActionRuntime 渲染执行。收件箱页面保留布局/时间线,动作区改为读 action 元数据。此后新增审批动作(例如企业版 act-as、cloud#861)= 后端声明,前端零改动。(需 framework 侧配套:在 sys_approval_request 元数据声明这组 actions —— 协同小 PR)
  2. 流程设计器嵌套数组支持(二选一):
    • a)json-schema-to-fields.ts 的 columnsFor/FlowConfigColumn 增加嵌套 objectList/stringList 列种(repeater-in-repeater);或
    • b)更彻底:审批等复杂节点的属性表单切换到已有的递归 SchemaForm.tsx,统一两套渲染器。
      任一使得未来引擎发布的任意嵌套节点配置(不止审批)自动可编辑。推荐先 a) 后评估 b)。

验收

  • OOO 自助:普通用户经 nav 进入即可增删改自己的委派(零前端代码,截图为证)
  • 流程设计器:审批节点显示 behavior 四值 + minApprovals + 每行 group,保存往返正确;fallback 与 schema 不再 drift
  • 决策附件:composer 可上传、决定携带 attachments、时间线可见;复用 FileField
  • 收件箱至少 Reassign/Approve/Reject 三个动作改由服务端声明 action 驱动(运行时 dialog 收参),删除对应硬编码分支
  • 嵌套数组:设计器可编辑一个 array-in-objectList 的演示 schema,不再跌落 Advanced JSON
  • 浏览器端到端验证(对接 framework 后端 :3000,HMR console :5180)

关联

Activity

  1. os-zhuang commented on Jul 19, 2026

    @os-zhuang
    ContributorAuthor

    P1 落地后的 UX 评估(实测补充,作为 P1.5 / P2 范围输入)

    P1(#2681)浏览器实测中发现的真实体验问题,按影响排序:

    1. 两个决策面能力不一致(最实在):记录详情页记录头的 Approve/Reject(useRecordApprovals)没有意见框、没有附件——从记录页可以零意见零附件批掉一单,合规场景是漏洞。根因:动作不是声明一份、两处渲染 → 正是 P2-4 的论据。
    2. 点击目标歧义:收件箱行内点记录名跳离收件箱去记录页(自动化测试第一次就踩中);"点行开抽屉 vs 点名跳走"边界太窄。
    3. 分组/quorum 进度不可见:per_group 抽屉只显示扁平 Waiting on 列表,看不出「manager 组已过 / finance 组待审」;quorum 无「2/3」。后端已快照 __approverGroups,数据在,UI 没用。
    4. 通知不深链:actionUrl 是笼统的 /system/approvals,点进来还要自己找那一单。
    5. 附件 chip 无文件名(后端只存 fileId;时间线显示 "Attachment 1")。
    6. 抽屉记录上下文薄:摘要卡几个字段,看明细必须跳走(回到 2)。
    7. 移动端未验证(平台 gap Add @object-ui/types package - TypeScript protocol definitions #22;Sheet 在 <768px 未测)。

    做得好的(保持):队列推进(批完跳下一单)、批量批准、j/k/a/r 键盘、快捷短语、SLA 逾期显示、空状态、角标。

    P1.5(高性价比打磨,先做)

    • 服务端计算的进度结构(framework 配套):getRequest 返回 groups_progress: [{group, need, got, satisfied}] + behavior/min_approvals,客户端通用渲染 → 问题 3 治本,quorum/per_group/未来 weighted 全覆盖(SDUI:服务端算,客户端渲)
    • 通知深链(framework 配套):actionUrl 携带 ?request=<id>;收件箱识别 query param 自动开抽屉 → 问题 4
    • 行点击目标:整行开抽屉,记录名保留显式外链 → 问题 2
    • 时间线附件解析文件名(批量查 sys_file)→ 问题 5

    P2(结构层,不变)

    • P2-4 动作 SDUI 化同时治问题 1;P2-5 嵌套数组;抽屉记录上下文与移动端随 P2 一并处理。

    Generated by Claude Code

  2. self-assigned this
    on Jul 19, 2026
  3. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊:关闭(duplicate) —— 审批面收敛为「#2763 治本 + #2762 打磨」两个入口,附否决窗口

    本单的两半都已被更晚、更有裁决的单覆盖,继续保留会让同一件事有三个派发入口(查重收敛规则:一件事只留一个)。逐半核过:

    一、P2 结构半边(收件箱动作 SDUI 化 / 嵌套数组)⇒ 已由 #2763 吸收

    #2763 的方案是同一方向的更彻底版本(不止把动作声明化,而是整个收件箱与抽屉重建在 ListView + RecordDetailView + ApiDataSource 上,删掉 bespoke 页面),且已拿到维护者 2026-08-03 的方向批准与排期(epic,v17 后,A2 先行)。本轮已给 #2763 落 pm:on-hold + 三要素。本单的 P2-4 若独立推进,会与它在同一批文件上撞车。

    二、P1 / P1.5 打磨半边 ⇒ 已转记到 #2762(本轮入队)

    三、四个能力的判定表已过期,不必再带着走

    正文里 ①OOO(零代码)②委派(已存在)③quorum configSchema(自动渲染)三项当时就判为「已具备/零代码」,④决策附件已在 #2789 一族的 attachmentChip 键位落地。剩下的是验证类工作,不是开发单。

    四、⚠️ 一条没有跟着关闭的东西

    本单评论里那条最严重的观察 —— 记录详情页记录头的 Approve/Reject 没有意见框、没有附件,可以零意见零附件批掉一单,合规场景是漏洞 —— 已在 #2762 的分诊评论里显式点名并声明未并入,附上「若维护者认为不能等到 v17 后,可下轮单独立 P0 止血单」的路径。关闭本单不等于判定该缺口不存在。

    ⛔ 否决窗口

    若认为 P2-5(流程设计器嵌套数组 / 切 SchemaForm.tsx)与审批无关、应独立存活(它确实是本单里唯一一条不属于审批面的诉求),重开本单或就那一条另立新单即可,本座位不会再次关闭。


    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions