Commit 6763c26b authored by David Yang's avatar David Yang

feat: prefer detail view field permissions

parent c047ec4d
......@@ -42,11 +42,13 @@
## 4. Subject View 权限
- Grid View 的 `settings.viewAuth` 只控制视图可见性;活动 Grid View 的 `settings.auth` 控制详情字段编辑能力;`settings.disableColumns` 对所有用户始终只读。
- Grid View 的 `settings.viewAuth` 只控制视图可见性;详情字段编辑优先使用当前 Detail View 的 `settings.auth` 与 `settings.disableColumns`,Detail 未配置字段权限或不存在实际 Detail 时回退活动 Grid View;选定权限来源中的 `settings.disableColumns` 对所有用户始终只读。
- 用户通过 `viewAuth` 获得 Grid Tab 查看权限后即可打开记录详情并查看该 Detail `columns` 中的字段;Detail View 不再使用 `viewAuth` 单独隐藏详情字段,字段授权只影响编辑能力。
- 普通用户遇到缺失 `viewAuth` 的 View 或缺失字段 `auth` 的字段时默认拒绝。系统管理员、系统账号和目标应用自身的“管理”角色可以旁路 View/字段授权,但不能旁路 `disableColumns`。
- 仅从 `user.applications[]` 中 `name` 等于目标应用真实稳定标识的单个应用项读取 `groups`、`roles` 和 `permissions`。每个项目必须使用自身应用标识,不得复制其它应用的标识,也不得合并顶层身份或其它应用授权。
- 授权类型只接受 `group`、`role`、`permission`;缺失或不支持的类型按“管理”角色规则处理。
- 从 Grid 打开详情时,活动 Grid View 是唯一字段编辑权限来源;Detail View 只负责字段编排、条件和 `shortTitle`,不得叠加第二套字段权限。
- 从 Grid 打开详情时,当前激活 Detail View 存在有效 `auth` 或 `disableColumns` 配置则使用该 Detail 的整套字段权限;两项均无有效配置或不存在实际 Detail View 时才使用活动 Grid View。切换 Detail 或 Grid 后必须立即重新计算,不能合并两套字段授权。
- Detail 的 `columns` 与 `auth` 相互独立:`columns` 只决定详情字段的展示范围和顺序,`auth` 可以配置更多字段;已展示且命中 `auth` 的字段正常执行授权,未展示字段的授权暂不产生界面效果。Detail 一旦存在任意有效字段权限配置,已展示但未配置 `auth` 的字段对普通用户保持只读,不得逐字段回退 Grid。
- 没有可访问 Grid View 时不创建兜底 View、不查询记录,并隐藏新增、批量和行操作。只有一个可访问 View 时保留其 key、查询和筛选,但隐藏单项 Tabs 轨道。
- View 的 restrictions、filters、排序、分页和请求参数继续按目标应用原协议执行。
......
......@@ -112,7 +112,7 @@ Surface 用于表达页面内的包含关系,Overlay 用于表达浮动关系
- 所有业务列表使用 HeroUI Pro DataGrid,原生承载选择、排序、列宽、固定列和空状态。
- Subject View 的 `settings.shortTitle = true` 统一解释为仅隐藏 `title` 展示列;共享列解析器必须保留标题数据在查询、排序、权限和详情上下文中的作用。标题列承载的业务信息与入口必须通过 `RecordWorkbench.contextColumns` 迁移到独立、可见的上下文列,并继续在固定操作列保留关键入口;禁止因隐藏标题而同时移除明细、详情、状态查看或行操作能力。上下文列不属于 `settings.columns` 业务字段,不参与数据查询、排序或字段权限。
- `type = "detail"` 的视图使用同一 `shortTitle` 语义:为 `true` 时隐藏详情弹框 Header 中的记录标题和字段区中的 `title` 字段;功能标题必须始终保留作为 Modal 的可访问名称。存在多个详情视图时,以当前激活视图为准并随页签切换即时更新。
- Subject Grid 权限必须与历史多维表格一致:`viewAuth` 只决定视图可见性,字段 `auth` 只决定编辑能力;无 `viewAuth` 的视图和无字段 `auth` 的字段对普通用户默认拒绝。系统管理员和当前业务所属历史应用的“管理”角色可旁路两类授权,但 `disableColumns` 对所有账号始终只读。授权数据只从当前历史应用对应的 `user.applications[]` 项读取,不得跨应用合并角色、用户组或权限。由 Grid 进入详情弹框时,Grid 视图是唯一字段编辑权限来源,Detail 视图不得再次叠加字段权限。
- Subject Grid 权限必须与历史多维表格一致:`viewAuth` 只决定 Grid 视图可见性,用户获得 Grid Tab 查看权限后即可查看其记录详情,Detail 不以 `viewAuth` 再次隐藏字段;字段 `auth` 只决定编辑能力。无 `viewAuth` 的 Grid 和选定权限来源中无字段 `auth` 的字段对普通用户默认拒绝。系统管理员和当前业务所属历史应用的“管理”角色可旁路两类授权,但 `disableColumns` 对所有账号始终只读。授权数据只从当前历史应用对应的 `user.applications[]` 项读取,不得跨应用合并角色、用户组或权限。由 Grid 进入详情弹框时,当前激活 Detail 视图存在有效 `auth` 或 `disableColumns` 配置则优先使用其整套字段权限;Detail 未配置字段权限或不存在实际 Detail 时回退当前 Grid,禁止合并两套授权。Detail `columns` 仅决定展示范围和顺序,允许 `auth` 覆盖更多字段;仅展示字段的授权会产生界面效果,Detail 权限来源中缺少 `auth` 的展示字段不得逐字段回退 Grid。
- 选择列使用 `showSelectionCheckboxes`,不得手写选择列或在 Cell 内嵌旧 Checkbox。
- 固定列、最小列宽和横向滚动是数据可读性行为,不通过 CSS 改写 DataGrid 表头、行高、边框、背景或 Hover。
- DataGrid 无论列数多少都占满列表可用宽度。非宽表使用 HeroUI Pro 原生列尺寸:选择列和操作列保持固定,第一业务主列使用 `1fr` 吸收剩余空间,表格内容保持 `min-width: 100%`;只有真实最小列宽超过视口时才使用像素列宽和内部横向滚动,不允许出现表面全宽但正文只停在左侧的空白断层。
......
import { useEffect, useRef, useState } from 'react';
import {
getSubjectView,
getSubjectViewDetail,
loadRecord,
loadSubject,
updateRecord,
......@@ -97,9 +98,27 @@ export function useSubjectRecord(
try {
const record = await loadRecord(recordName);
const resolvedSubjectName = subjectName || record?.__t || record?.subject?.name || record?.subject;
const [subjectData, viewsData] = resolvedSubjectName
const [subjectData, viewSummaries] = resolvedSubjectName
? await Promise.all([loadSubject(resolvedSubjectName), getSubjectView(resolvedSubjectName)])
: [null, []];
const viewsData = await Promise.all(
(Array.isArray(viewSummaries) ? viewSummaries : []).map(async (view) => {
if (!view?.name || !resolvedSubjectName) {
return view;
}
try {
const detail = await getSubjectViewDetail(resolvedSubjectName, view.name);
return {
...view,
...detail,
settings: detail?.settings || view?.settings || {},
};
} catch (error) {
return view;
}
}),
);
if (!cancelled && requestIdRef.current === requestId) {
setRemoteRecord(record);
......
......@@ -20,6 +20,7 @@ import {
buildViewColumns,
checkViewCondition,
getFieldValue,
hasFieldAuthorizationConfiguration,
setFieldValue,
shouldHideViewTitleColumn,
} from '@/utils/subjectView';
......@@ -177,9 +178,16 @@ const RecordDetailDrawer = (props, ref) => {
),
[disabledFields],
);
const isDrawerFieldDisabled = (fieldPath, currentView) =>
disabledFieldSet.has(fieldPath) ||
isFieldDisabled(fieldPath, authorizationView || currentView);
const isDrawerFieldDisabled = (fieldPath, currentView) => {
const effectiveAuthorizationView = hasFieldAuthorizationConfiguration(currentView)
? currentView
: authorizationView || currentView;
return (
disabledFieldSet.has(fieldPath) ||
isFieldDisabled(fieldPath, effectiveAuthorizationView)
);
};
const hasEditableFields =
Boolean(editableRecord) &&
......@@ -187,12 +195,10 @@ const RecordDetailDrawer = (props, ref) => {
!error &&
!effectiveReadonly &&
!minimalContent &&
detailViews.some((view) =>
buildViewColumns(view, subject).some(
(field) =>
!NON_EDITABLE_FIELD_TYPES.has(field.fieldDef?.type) &&
!isDrawerFieldDisabled(field.field, view),
),
buildViewColumns(activeView, subject).some(
(field) =>
!NON_EDITABLE_FIELD_TYPES.has(field.fieldDef?.type) &&
!isDrawerFieldDisabled(field.field, activeView),
);
const hasDirtyFields = Object.keys(dirtyFields).length > 0;
const hasValidationErrors = Object.values(validationErrors).some(Boolean);
......
......@@ -65,10 +65,12 @@
- `settings.quickSelect` 决定列表是否展示勾选列,用于批量推送、删除等操作
- `settings.restrictions` 与 `settings.filters.conditions` 共同决定该视图下可见的数据范围
- Grid 视图的 `settings.viewAuth` 决定当前用户是否能看到该视图;普通用户未配置 `viewAuth` 时默认不可见,仅系统管理员和问题整改应用 `problem_rectification_new` 自身的“管理”角色可见。全部 Grid 视图均无权限时不得生成绕过授权的兜底视图、查询记录或展示新增/批量/行操作入口
- `type = "detail"` 的视图用于详情抽屉字段编排,按 `columns` 顺序渲染字段;当前详情视图 `settings.shortTitle = true` 时,同时隐藏弹框 Header 中的记录标题和字段区中的 `title` 字段,未配置或为 `false` 时正常显示,切换详情视图页签后立即按新视图更新
- 表格 Tab 的 `settings.auth` 按字段路径控制详情弹框内对应字段是否允许编辑,规则结构与多维表格一致:`field` 使用 `title` 或 `metadata.<fieldName>`,`type` 支持 `group`、`role`、`permission`,授权值分别从 `groups`、`roles`、`permissions` 读取;普通用户遇到未配置 `auth` 的字段时默认只读
- 表格 Tab 的 `settings.disableColumns` 始终优先,命中后字段保留展示但进入只读状态,即使系统管理员或“管理”角色也不得编辑;未命中时,系统管理员、系统账号和问题整改应用 `problem_rectification_new` 自身的“管理”角色旁路字段 `auth`
- 普通用户只使用 `applications[]` 中 `name = "problem_rectification_new"` 的用户组、角色和权限进行匹配,不合并顶层用户组或其他应用授权。多维表格配置转换只接受 `group`、`role`、`permission`,缺失或其他类型均按“管理”角色处理。进入详情弹框后,当前 Grid Tab 是唯一的字段编辑权限来源,Detail 视图只负责字段编排、条件显示与 `shortTitle`,两套字段权限不得叠加。切换 Grid Tab 后,详情弹框必须立即改用新 Tab 的权限配置
- 用户获得 Grid Tab 的查看权限后,可以打开该 Tab 中记录的详情并查看 Detail `columns` 配置的字段;Detail 不使用 `viewAuth` 再次隐藏详情字段,详情字段权限只控制是否允许编辑
- `type = "detail"` 的视图用于详情抽屉字段编排,按 `columns` 顺序渲染字段;当前详情视图 `settings.shortTitle = true` 时,同时隐藏弹框 Header 中的记录标题和字段区中的 `title` 字段,未配置或为 `false` 时正常显示。当前 Detail 的 `settings.auth` 或 `settings.disableColumns` 存在有效字段配置时,该 Detail 同时作为详情字段编辑权限来源,切换详情页签后立即按新视图更新展示和权限
- Detail 或回退表格视图的 `settings.auth` 按字段路径控制详情弹框内对应字段是否允许编辑,规则结构与多维表格一致:`field` 使用 `title` 或 `metadata.<fieldName>`,`type` 支持 `group`、`role`、`permission`,授权值分别从 `groups`、`roles`、`permissions` 读取;选定权限来源后,普通用户遇到未配置 `auth` 的字段时默认只读
- 当前权限来源视图的 `settings.disableColumns` 始终优先,命中后字段保留展示但进入只读状态,即使系统管理员或“管理”角色也不得编辑;未命中时,系统管理员、系统账号和问题整改应用 `problem_rectification_new` 自身的“管理”角色旁路字段 `auth`
- 普通用户只使用 `applications[]` 中 `name = "problem_rectification_new"` 的用户组、角色和权限进行匹配,不合并顶层用户组或其他应用授权。多维表格配置转换只接受 `group`、`role`、`permission`,缺失或其他类型均按“管理”角色处理。进入详情弹框后,当前激活 Detail 存在有效 `auth` 或 `disableColumns` 配置时优先使用该 Detail 的整套字段权限;Detail 完全未配置字段权限或没有实际 Detail 视图时,回退到打开弹框的当前 Grid Tab。切换 Detail 或 Grid Tab 后必须立即重新计算权限来源
- Detail 的 `columns` 只决定展示字段及顺序,与 `auth` 条目数量不要求一致。例如 Detail 展示 4 个字段而 `auth` 配置 8 个字段时,命中这 4 个展示字段的授权正常生效,其余 4 个未展示字段不产生界面效果。只要 Detail 存在任意有效字段权限配置,就不得将其中缺少 `auth` 的展示字段逐项回退 Grid;普通用户对这类字段保持只读
因此前端新增字段展示、部门视图调整、列顺序变化时,应优先改 Subject View 配置,而不是直接修改页面常量。
......@@ -262,7 +264,7 @@
- 列表页采用分页加载,默认每页 20 条,可切换为 10、50、100;分页、筛选、视图切换均由服务端查询结果驱动
- 字段编辑统一进入右侧详情抽屉
- 长文本、附件、状态类字段在详情抽屉中直接维护
- 详情弹框内字段顺序与条件显示跟随 `detail view` 配置;从 Grid 进入时,字段编辑权限只跟随当前 Grid 视图配置
- 详情弹框内字段顺序与条件显示跟随 `detail view` 配置;当前 Detail 配置了字段权限时优先使用 Detail 权限,未配置或没有实际 Detail 时回退当前 Grid 权限
---
......
......@@ -417,6 +417,14 @@ export function isViewAccessible(
);
}
export function hasFieldAuthorizationConfiguration(view) {
const settings = view?.settings;
return (
toArray(settings?.auth).some((rule) => normalizeText(rule?.field)) ||
toArray(settings?.disableColumns).some((column) => normalizeText(column?.field))
);
}
export function isFieldDisabledByView(
fieldPath,
view,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment