Skip to content

[UX] Studio Access 权限矩阵活栈走查收敛:3 个编辑安全缺陷(已开修)+ 设计迭代清单 #2600

Description

@os-zhuang

对 Studio Access pillar(权限矩阵,/studio/:packageId/access)做了一次活栈 UI/UX 走查:真 framework showcase 后端(:3000)+ console dev(:5180),浏览器实操(勾选/切换/筛选/解释面板/窄宽渲染),关键行为与源码逐一对照。总体:信息架构(左栏集 → 对象×CRUD 矩阵 → 字段级展开 → 折叠分面)和包作用域(ADR-0086,矩阵 20/85 对象)是对的;问题集中在编辑安全感和嵌入缝隙两类。本 issue 跟踪全部发现的收敛。

A. 缺陷(已全部修复落地)

  • A1 切换权限集静默丢弃未保存改动(🔴)——活栈复现:勾选后点左栏另一集再切回,改动无声消失。根因:rail onClick 直接 setCurrent,矩阵 key={current} 重挂载,内部 dirty 未上抛,无确认、无 unload 守卫(StudioDesignSurface.tsx ~3327/3389)。修复方向:onDirtyChange 上抛 + 切换前确认(同文件 navDirty 模式)。
  • A2 只读包下矩阵仍可编辑、保存钮高亮(🔴)——readOnly 只挡了「新建权限集」,未传给 PermissionMatrixEditPage(props 无此口子);内部 writable=allowOrgOverride 是 type 级另一概念。同屏「只读」徽标 + 「可写」徽标 + 蓝色保存钮三者矛盾。修复方向:编辑器加 readOnly prop 与 writable 取与,驱动复选框 disabled / 保存隐藏 / 行内提示,并统一徽标语义。
  • A3 「历史」按钮与面包屑逃逸 Studio(🟡)——navigate('./history?type=permission') 在 Studio 路由下解析为 /studio/…/access/history,无该路由,命中 App.tsx:354 的 path="*" → Navigate to "/":点「历史」= 静默弹回平台首页,Studio 上下文全丢。面包屑「元数据 › 权限集」同样链出 Studio 到 /metadata。修复方向:嵌入模式下历史改抽屉/对话框,面包屑隐藏或改包内返回。

B. 设计迭代(按优先级,待排)

  • B1 首屏见矩阵——pillar 名叫「权限矩阵」,但首屏是 名称/标签表单 + 系统能力芯片,矩阵首行被推到折叠线下一屏半。名称/标签改动频率极低:压成一行摘要(名称 · 标签 · 来源徽标,点击展开),系统能力零授予时折叠为「未授予 + 添加」(现在零授予也铺满 7 个 outline 芯片,视觉上像已拥有的能力)。
  • B2 解释访问权限面板——对象靠手输 API 名(AccessExplainPanel.tsx ~286 自由文本),本包对象仅 20 个应给下拉;占位示例 crm_lead 在本包不存在;「解释」未填对象时看似可点实际禁用;面板占 70% 宽大面积留白。
  • B3 窄宽(≈800px)布局——面包屑与「安全/可写」徽标重叠;「批量」列被推出视口无滚动暗示(<table className="w-full"> 无 min-width/overflow-x 容器);不同集之间行高从 ~70px 跳到 ~200px;左栏固定 208px 不自适应。
  • B4 字段级批量与搜索——展开后逐字段两个小复选框,几十字段的对象(field_zoo)很痛;对象行有「读/增改删/全选/清空」快捷,字段级应对齐(全部可读/可写/清空 + 字段过滤)。
  • B5 细项——能力芯片标签与 PLATFORM/ORGANIZATION 分组标题英文硬拼在中文界面(sys_capability registry label 未走翻译束);左栏权限集按钮在可达性树读不到名称(truncate span 未兜底 aria-label,需真实读屏器复核);列头缩写 tooltip 触屏不可达(前置图例已缓解)。

做得好的(保持)

服务端包作用域(20/85 对象、8/15 集);缩写图例前置展开;破坏性变更 409 → 明细确认对话框;CEL 错误阻断保存;行内 OWD 徽标深链总览;「只看已授权」+ 计数徽标;高级分面默认折叠;移动端 rail 抽屉;矩阵复选框 aria-label 完整。

合并注意

A1/A2/A3 三个分支改同两个文件(StudioDesignSurface.tsx AccessPillar 挂载点、PermissionMatrixEditor.tsx props/头部),A1 与 A2 都给 PermissionMatrixEditPage 加新 prop,必然冲突——串行合:建议 A2(props 改动最大)先进,A1/A3 rebase 后再进;不要三个同时 merge-on-green。B 组刻意排在 A 组落地之后,避免同文件并行打架。

关联

Activity

  1. self-assigned this
    on Jul 16, 2026
  2. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    进展同步(2026-07-16):

    合并顺序维持 issue 正文建议:A2 → A1/A3 rebase 串行。

  3. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    活栈回归 + 走查收尾(2026-07-16 下午,main@4b0aee6,真 showcase 后端 :3000 + console dev :5180,agent-browser 实操):

    A2 只读贯通 → 已合并(#2570)且活栈回归通过 ✅

    • 只读包下:矩阵 180 个复选框全部 disabled、名称/标签锁定、保存按钮消失、行级安全分面字段全禁用(每策略「测试」按钮保留可用——只读求值,合理)。
    • 徽标措辞按触发闸区分,顶栏「只读」与编辑器状态不再自相矛盾。

    A3 前提活栈实锤:Studio 内点「历史」→ 实际跳转 /home(命中 catch-all),与路由表推断一致;#2599 的 in-place 抽屉方案对症,已初审待合。

    补完的走查项(此前被工具故障挡住):

    • 对象筛选:英文 api-name(invoice → 2/20)与中文标签(发票 → 1/20)均命中,计数徽标联动 ✓
    • 行级安全分面:结构化策略编辑器(名称/对象/操作/CEL USING/CHECK)正常 ✓
    • 深链:rail 选中集镜像到 ?surface=permission:<name> ✓(加分项)

    B5 修订:「左栏按钮无可访问名称」一条撤销——agent-browser 的可达性树里名称完整,原报告是浏览器面板 read_page 的工具怪癖,非真实 a11y 问题。

    剩余:A1(脏状态守卫)重试会话进行中;A3 #2599 待合(建议 rebase 到含 #2570 的 main)。

  4. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    A1 已完成,勾选 ✅ — 修复由 #2588 落地(merge commit 1e8764909,2026-07-16):

    • PermissionMatrixEditPage 新增 onDirtyChange 上抛(ref 稳定化,卸载时归零),AccessPillar 持有 matrixDirty;
    • 四条重挂载路径全部 confirm 门控:rail 切换权限集、切到 OWD 总览、新建权限集、矩阵内 OWD badge 深链(重复点击当前集不误弹);
    • 补了根因里点名缺失的 beforeunload(刷新/关 tab);
    • 测试:PermissionMatrixEditor.dirty.test.tsx + StudioDesignSurface.accessGuard.test.tsx,6 用例已基于 main(d50977c20)复跑通过。

    相邻缺口(已开修):顶栏 pillar 切换 / Home / PackageSwitcher 是 SPA 客户端导航,不触发 beforeunload,dirty 时仍静默丢弃 —— #2606 处理中(Access matrixDirty + Interfaces navDirty 上抛到 StudioDesignSurface 顶层 + surface 级 beforeunload)。同改 AccessPillar 挂载点,按「合并注意」与 #2599 / A2 串行合。

    再残留(待排,建议补为 A 组条目):PackageOwdOverviewPanel 的批量编辑 dirty(内部 dirtyCount)未接入任何守卫 —— OWD 总览有未保存改动时,rail 切回权限集或顶栏导航都不 confirm;A1 与 #2606 均只覆盖 matrixDirty/navDirty。等 #2599 / A2 / #2606 落地后再动,避免第四个分支并行改同一挂载点。

  5. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    A1 系列再补一块:OWD 总览面板(PackageOwdOverviewPanel)的未保存行此前没接入任何守卫——rail 切换回权限集 / 新建流程 / #2606 的顶栏导航守卫都会静默丢弃。PR #2610 给面板加了 onDirtyChange(同 PR #2588 契约),AccessPillar 合并 matrixDirty‖owdDirty 同时喂本地确认与上抛 pillarDirty。堆叠在 #2606 之上(含其 commit),#2606 先合,#2610 rebase 后再合——继续串行。

  6. os-zhuang commented on Jul 16, 2026

    @os-zhuang
    ContributorAuthor

    A 组全部落地(2026-07-16,串行合完成,合并顺序 A2 → A3 → 顶栏守卫):

    • A2 只读包锁矩阵 → fix(app-shell): lock the Access pillar permission matrix in read-only packages #2570(57a61b097):PermissionMatrixEditPage 新增 readOnly prop 并入 writable,一个开关锁全部(对象/字段复选框、批量、名称/标签、能力选择器、高级分面、保存);只读徽标按触发的门分别措辞(包门对齐顶栏芯片,type 门保留 OS_METADATA_WRITABLE 措辞)。RTL 套件覆盖双门 + 可编辑回归路径。
    • A3 历史/面包屑逃逸 → fix(app-shell): Studio Access matrix — history opens in-place sheet, breadcrumb stops escaping the pillar #2599(770e40528):PageShell + PermissionMatrixEditPage 加 embedded 开关(同 MetadataResourceEditPage 模式)。嵌入态「历史」改右侧 sheet 复用 HistoryPanel(只读、无回滚,ADR-0086 D6/D7);面包屑渲染为纯文本。独立 metadata-admin 行为不变。
    • A1 后续:顶栏 SPA 导航同样静默丢编辑 → fix(studio): confirm before header SPA nav discards unsaved pillar edits (#2600) #2606(99356470f):A1 只守住了 rail/OWD/新建/badge 深链四路,但顶栏 pillar 切换(react-router <Link>)、Home、包切换器都是 SPA 客户端导航,不触发 beforeunload,dirty 时切走整体卸载 AccessPillar。现 AccessPillar matrixDirty / InterfacesPillar navDirty 经 onDirtyChange 上抛到 StudioDesignSurface,三处导航 dirty 时先 confirm,并补了 surface 级 beforeunload(navDirty 原先连刷新守卫都没有)。测试 StudioDesignSurface.pillarNavGuard.test.tsx(6 用例)。

    三个 PR 同改 AccessPillar 挂载点,按「合并注意」串行 rebase,最终形态:readOnly={readOnly} + onDirtyChange={reportMatrixDirty} + embedded。

    已知残留(另行跟踪,不在本 issue A 组范围):PackageOwdOverviewPanel 内部 dirtyCount 未接入任何离开守卫(rail 从 OWD 切回权限集、顶栏导航均不 confirm)——已起独立任务接入 #2606 的 pillarDirty 通道。

    B 组设计迭代现在可以排了:A 组落地后同文件并行打架的约束解除。

  7. os-zhuang commented on Jul 18, 2026

    @os-zhuang
    ContributorAuthor

    B 组设计迭代全部落地(2026-07-18) — 逐条独立 PR、串行 rebase(每条基于含前一条的 main)、CI 全绿后 squash 合。活栈复验方式与走查一致(真 showcase 后端 :3000 + console,浏览器实操 + DOM/源码对照)。

    项 PR commit 活栈实证
    B1 首屏见矩阵 #2649 8d5534f24 名称/标签折叠成一行摘要(basicsOpen)、写包零授予能力从铺 7 芯片改「无附加系统能力 + 添加」(capsOpen);矩阵头升进首屏;展开双向可用
    B2 解释面板对象下拉 #2650 a6c4f4d5a AccessExplainPanel 加 packageId,对象字段改本包对象 <select>(label (api-name),22 项,无 crm_lead);未选禁用 opacity-0.5、选中启用;无 packageId 回退自由文本
    B3 窄宽批量列可达 #2652 4d6fb926e 矩阵表加 min-w-[960px],外层 overflow-auto 真横滚;820px 验:table 960 / 容器 612 canScrollX,右滚露出「批量」列(读/增改删/全选/清空)
    B4 字段级批量+筛选 #2651 d121cb3d5 展开对象加字段筛选(>6 字段显)+ 全部可读/全部可写/清空(作用于筛选后可见字段)+ visible/total 计数;只读包 writable-gated 隐藏批量钮。活栈只读验筛选+计数(name→1/12),批量逻辑 RTL 覆盖
    B5 分组标题 i18n #2653 e4e8d3fe1 能力选择器 PLATFORM/ORGANIZATION → capability.group.<scope> 翻译束,全 11 语言包;中文验:平台 / 组织
    B5 能力标签 i18n #2657 be0712c9d 见下

    B5 标签本地化的分叉记录:原计划「sys_capability registry label 走翻译束」= framework。深挖后确认 framework 没有记录级 i18n 约定可复用——schema 翻译束(translateObject / *.objects.generated.ts)只译 object/field schema;能力 label 是走通用 /api/v1/data/sys_capability 的记录数据;后端修得新引入一整套 pattern(read middleware + 翻译存储 + locale 来源问题:数据路径只有 ctx.context.locale=租户/用户 localization 设置,不是 metadata 层用的 Accept-Language)。对本 issue 最低优先级项不划算,故改走 objectui 客户端映射:7 个 curated 平台能力(固定已知集)走 capability.label.<name> 翻译束,package/admin 自建能力保留注册表 label。中文验:管理元数据 / 管理平台设置 / 管理用户 / 访问 Setup / 访问 Studio / 管理组织用户 / 保存设置。

    撤销/闭环的 B5 子项:「左栏权限集按钮无可访问名称」此前已在走查中撤销(agent-browser 可达性树名称完整,系浏览器面板 read_page 工具怪癖);列头缩写 tooltip 触屏不可达由前置图例缓解,不再单列。

    至此 A 组(A1/A2/A3 编辑安全缺陷)+ B 组(B1–B5 设计迭代)全部收敛。

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