Skip to content

fix(plugin-grid): 批量操作按钮忽略 requiredPermissions,且布尔 visible 被判为故障导致按钮全员隐藏 #3492

Description

@baozhoutao

批量操作栏(BulkActionBar)的按钮显隐规则和其它三个动作面(列表工具栏 / 详情页头 / 行内 kebab)不一致,有两个独立缺口。

问题 1:requiredPermissions 在批量按钮上完全不生效

ADR-0066 D4 的能力门是「服务端 403 + UI 镜像隐藏」的双面契约,ActionSchema.requiredPermissions 是 spec 正式属性。其它三个面都接了 useCapabilityGate:

面 位置
列表工具栏 packages/components/src/renderers/action/action-bar.tsx:133
详情页头 / ⋯ packages/components/src/renderers/layout/containers.tsx:1018
行内 kebab packages/plugin-grid/src/components/RowActionMenu.tsx:244
ActionEngine packages/core/src/actions/ActionEngine.ts:222

批量按钮两处都缺:

  • packages/plugin-grid/src/resolveBulkActions.ts:135 的 toBulkActionDef 把对象动作提升成 bulk def 时只转发了 visible,没有转发 requiredPermissions;
  • packages/plugin-grid/src/components/BulkActionBar.tsx 从未调用 useCapabilityGate。

后果:同一个 requiredPermissions: ['x'] 的动作,在行 kebab 里对无权用户隐藏,在选中记录后的批量栏里照常出现。对于 type: 'api' 指向自定义端点的动作,服务端也未必兜底(spec 描述里明说该端点要自己复查),等于权限门整个失效。

问题 2:布尔 visible 被当成求值故障,按钮对所有人隐藏

其它三面的 visible 走 useCondition / useRowPredicate,两者都对布尔短路。批量这条路是 BulkActionButton → partitionBulkRows(packages/plugin-grid/src/bulkEligibility.ts:70)→ 直调 evalRowPredicate,没有布尔短路:

evalRowPredicate(true, …) → toExpression(true) → { dialect: 'cel', source: undefined } → 引擎报 AST-only evaluation not yet supported; persist 'source' → evalCel 判定 fault → fallback false → 每条选中记录都不合格 → def.visible && eligible.length === 0 → 按钮不渲染。

后果:visible: true 的效果是「全员看不见」,语义完全反了(visible: false 只是碰巧结果正确)。

缓解:spec 的 BulkActionDefSchema.visible 是 ExpressionInputSchema(string | { dialect, source },不含 boolean),所以 objectstack build 走不到这条路;但手写 view JSON、或在 objectui 内部构造 bulkActionDefs 的调用方能踩到,且行为是静默的。

方案

  1. packages/types/src/objectql.ts — BulkActionDef 增加 requiredPermissions?: string[](注释说明它来自源动作,语义与 ActionSchema.requiredPermissions 一致)。
  2. packages/plugin-grid/src/resolveBulkActions.ts — toBulkActionDef 转发 requiredPermissions。
  3. packages/plugin-grid/src/components/BulkActionBar.tsx — 用 useCapabilityGate 过滤 actionDefs,与另外三面同一姿态:声明为空恒通过、多个取 AND、能力未知时 fail-open(服务端为准)。
  4. packages/plugin-grid/src/bulkEligibility.ts — partitionBulkRows 对布尔 visible 短路:true → 全部合格,false → 全部不合格,不进引擎。
  5. 测试:
    • 提升的 def 携带 requiredPermissions;
    • 持有 / 不持有能力时按钮的显隐;能力未知时 fail-open;
    • visible: true 按钮渲染且 skipped === 0;visible: false 按钮消失;
    • 现有的逐条记录求值 / skipped 计数行为不回归。

验收

同一个带 requiredPermissions 的对象动作,在列表工具栏、详情页头、行内 kebab、批量栏四个面对同一用户给出相同的显隐结论;批量 def 的布尔 visible 与另外三面语义一致。

Activity

  1. self-assigned this
    on Aug 6, 2026
  2. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊:pm:queue(objectui 仓内单,按整仓座位规则不打 domain:*)。

    • 归属核对:落点全在本仓 —— packages/types/src/objectql.ts(BulkActionDef 加 requiredPermissions?)、packages/plugin-grid/src/resolveBulkActions.ts:135、components/BulkActionBar.tsx、bulkEligibility.ts:70。⛔ 不触 framework 的 packages/spec:正文引用 BulkActionDefSchema.visible 是 ExpressionInputSchema(不含 boolean)只作为「objectstack build 走不到这条路」的缓解说明,修法是让运行时对布尔输入短路、与另外三面(useCondition / useRowPredicate)语义一致,不是去改 spec 放宽声明。⇒ 不走跨座位转移,留在 objectui 队列。
    • 入队而非决策箱:两处缺口都是面间不一致而非新语义 —— 问题 1 的姿态由已存在的三处 useCapabilityGate(action-bar.tsx:133 / containers.tsx:1018 / RowActionMenu.tsx:244)与 ActionEngine.ts:222 钉死(声明为空恒通过、多个 AND、能力未知 fail-open);问题 2 的正确行为由 useCondition / useRowPredicate 的既有布尔短路钉死。验收标准(四个动作面对同一用户给出相同显隐结论)正文已写好。
    • ⚠️ 已有 assignee(baozhoutao):分诊只打标签,不动认领、不派发。执行座位选批次时按认领协议对待 —— 标签在,不等于可派;若确认是死认领再按 stale-claim 规程处置。
    • 查重:三仓 open issue/PR 搜 BulkActionBar / requiredPermissions / partitionBulkRows / evalRowPredicate 仅命中本单,无重复入口。
    • 过时前提:git -C objectui log --oneline origin/main -- packages/plugin-grid/src 无触及 resolveBulkActions / bulkEligibility 的近期改动,两处缺口现状成立。

    本评论来自分诊座位 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

pm:queuetarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions