Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
P
problem_rectification
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
problem_rectification
Commits
6763c26b
Commit
6763c26b
authored
Aug 26, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: prefer detail view field permissions
parent
c047ec4d
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
52 additions
and
15 deletions
+52
-15
xinyuan-cross-app-feature-contract.md
docs/xinyuan-cross-app-feature-contract.md
+4
-2
xinyuan-design-system.md
docs/xinyuan-design-system.md
+1
-1
useSubjectRecord.js
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
+20
-1
index.jsx
src/pages/RecordDetailDrawer/index.jsx
+12
-6
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+7
-5
subjectView.js
src/utils/subjectView.js
+8
-0
No files found.
docs/xinyuan-cross-app-feature-contract.md
View file @
6763c26b
...
@@ -42,11 +42,13 @@
...
@@ -42,11 +42,13 @@
## 4. Subject View 权限
## 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`
。
-
普通用户遇到缺失
`viewAuth`
的 View 或缺失字段
`auth`
的字段时默认拒绝。系统管理员、系统账号和目标应用自身的“管理”角色可以旁路 View/字段授权,但不能旁路
`disableColumns`
。
-
仅从
`user.applications[]`
中
`name`
等于目标应用真实稳定标识的单个应用项读取
`groups`
、
`roles`
和
`permissions`
。每个项目必须使用自身应用标识,不得复制其它应用的标识,也不得合并顶层身份或其它应用授权。
-
仅从
`user.applications[]`
中
`name`
等于目标应用真实稳定标识的单个应用项读取
`groups`
、
`roles`
和
`permissions`
。每个项目必须使用自身应用标识,不得复制其它应用的标识,也不得合并顶层身份或其它应用授权。
-
授权类型只接受
`group`
、
`role`
、
`permission`
;缺失或不支持的类型按“管理”角色规则处理。
-
授权类型只接受
`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 轨道。
-
没有可访问 Grid View 时不创建兜底 View、不查询记录,并隐藏新增、批量和行操作。只有一个可访问 View 时保留其 key、查询和筛选,但隐藏单项 Tabs 轨道。
-
View 的 restrictions、filters、排序、分页和请求参数继续按目标应用原协议执行。
-
View 的 restrictions、filters、排序、分页和请求参数继续按目标应用原协议执行。
...
...
docs/xinyuan-design-system.md
View file @
6763c26b
...
@@ -112,7 +112,7 @@ Surface 用于表达页面内的包含关系,Overlay 用于表达浮动关系
...
@@ -112,7 +112,7 @@ Surface 用于表达页面内的包含关系,Overlay 用于表达浮动关系
-
所有业务列表使用 HeroUI Pro DataGrid,原生承载选择、排序、列宽、固定列和空状态。
-
所有业务列表使用 HeroUI Pro DataGrid,原生承载选择、排序、列宽、固定列和空状态。
-
Subject View 的
`settings.shortTitle = true`
统一解释为仅隐藏
`title`
展示列;共享列解析器必须保留标题数据在查询、排序、权限和详情上下文中的作用。标题列承载的业务信息与入口必须通过
`RecordWorkbench.contextColumns`
迁移到独立、可见的上下文列,并继续在固定操作列保留关键入口;禁止因隐藏标题而同时移除明细、详情、状态查看或行操作能力。上下文列不属于
`settings.columns`
业务字段,不参与数据查询、排序或字段权限。
-
Subject View 的
`settings.shortTitle = true`
统一解释为仅隐藏
`title`
展示列;共享列解析器必须保留标题数据在查询、排序、权限和详情上下文中的作用。标题列承载的业务信息与入口必须通过
`RecordWorkbench.contextColumns`
迁移到独立、可见的上下文列,并继续在固定操作列保留关键入口;禁止因隐藏标题而同时移除明细、详情、状态查看或行操作能力。上下文列不属于
`settings.columns`
业务字段,不参与数据查询、排序或字段权限。
-
`type = "detail"`
的视图使用同一
`shortTitle`
语义:为
`true`
时隐藏详情弹框 Header 中的记录标题和字段区中的
`title`
字段;功能标题必须始终保留作为 Modal 的可访问名称。存在多个详情视图时,以当前激活视图为准并随页签切换即时更新。
-
`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。
-
选择列使用
`showSelectionCheckboxes`
,不得手写选择列或在 Cell 内嵌旧 Checkbox。
-
固定列、最小列宽和横向滚动是数据可读性行为,不通过 CSS 改写 DataGrid 表头、行高、边框、背景或 Hover。
-
固定列、最小列宽和横向滚动是数据可读性行为,不通过 CSS 改写 DataGrid 表头、行高、边框、背景或 Hover。
-
DataGrid 无论列数多少都占满列表可用宽度。非宽表使用 HeroUI Pro 原生列尺寸:选择列和操作列保持固定,第一业务主列使用
`1fr`
吸收剩余空间,表格内容保持
`min-width: 100%`
;只有真实最小列宽超过视口时才使用像素列宽和内部横向滚动,不允许出现表面全宽但正文只停在左侧的空白断层。
-
DataGrid 无论列数多少都占满列表可用宽度。非宽表使用 HeroUI Pro 原生列尺寸:选择列和操作列保持固定,第一业务主列使用
`1fr`
吸收剩余空间,表格内容保持
`min-width: 100%`
;只有真实最小列宽超过视口时才使用像素列宽和内部横向滚动,不允许出现表面全宽但正文只停在左侧的空白断层。
...
...
src/pages/RecordDetailDrawer/hooks/useSubjectRecord.js
View file @
6763c26b
import
{
useEffect
,
useRef
,
useState
}
from
'react'
;
import
{
useEffect
,
useRef
,
useState
}
from
'react'
;
import
{
import
{
getSubjectView
,
getSubjectView
,
getSubjectViewDetail
,
loadRecord
,
loadRecord
,
loadSubject
,
loadSubject
,
updateRecord
,
updateRecord
,
...
@@ -97,9 +98,27 @@ export function useSubjectRecord(
...
@@ -97,9 +98,27 @@ export function useSubjectRecord(
try {
try {
const record = await loadRecord(recordName);
const record = await loadRecord(recordName);
const resolvedSubjectName = subjectName || record?.__t || record?.subject?.name || record?.subject;
const resolvedSubjectName = subjectName || record?.__t || record?.subject?.name || record?.subject;
const [subjectData, view
sData
] = resolvedSubjectName
const [subjectData, view
Summaries
] = resolvedSubjectName
? await Promise.all([loadSubject(resolvedSubjectName), getSubjectView(resolvedSubjectName)])
? await Promise.all([loadSubject(resolvedSubjectName), getSubjectView(resolvedSubjectName)])
: [null, []];
: [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) {
if (!cancelled && requestIdRef.current === requestId) {
setRemoteRecord(record);
setRemoteRecord(record);
...
...
src/pages/RecordDetailDrawer/index.jsx
View file @
6763c26b
...
@@ -20,6 +20,7 @@ import {
...
@@ -20,6 +20,7 @@ import {
buildViewColumns
,
buildViewColumns
,
checkViewCondition
,
checkViewCondition
,
getFieldValue
,
getFieldValue
,
hasFieldAuthorizationConfiguration
,
setFieldValue
,
setFieldValue
,
shouldHideViewTitleColumn
,
shouldHideViewTitleColumn
,
}
from
'@/utils/subjectView'
;
}
from
'@/utils/subjectView'
;
...
@@ -177,9 +178,16 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -177,9 +178,16 @@ const RecordDetailDrawer = (props, ref) => {
),
),
[
disabledFields
],
[
disabledFields
],
);
);
const
isDrawerFieldDisabled
=
(
fieldPath
,
currentView
)
=>
const
isDrawerFieldDisabled
=
(
fieldPath
,
currentView
)
=>
{
const
effectiveAuthorizationView
=
hasFieldAuthorizationConfiguration
(
currentView
)
?
currentView
:
authorizationView
||
currentView
;
return
(
disabledFieldSet
.
has
(
fieldPath
)
||
disabledFieldSet
.
has
(
fieldPath
)
||
isFieldDisabled
(
fieldPath
,
authorizationView
||
currentView
);
isFieldDisabled
(
fieldPath
,
effectiveAuthorizationView
)
);
};
const
hasEditableFields
=
const
hasEditableFields
=
Boolean
(
editableRecord
)
&&
Boolean
(
editableRecord
)
&&
...
@@ -187,12 +195,10 @@ const RecordDetailDrawer = (props, ref) => {
...
@@ -187,12 +195,10 @@ const RecordDetailDrawer = (props, ref) => {
!
error
&&
!
error
&&
!
effectiveReadonly
&&
!
effectiveReadonly
&&
!
minimalContent
&&
!
minimalContent
&&
detailViews
.
some
((
view
)
=>
buildViewColumns
(
activeView
,
subject
).
some
(
buildViewColumns
(
view
,
subject
).
some
(
(
field
)
=>
(
field
)
=>
!
NON_EDITABLE_FIELD_TYPES
.
has
(
field
.
fieldDef
?.
type
)
&&
!
NON_EDITABLE_FIELD_TYPES
.
has
(
field
.
fieldDef
?.
type
)
&&
!
isDrawerFieldDisabled
(
field
.
field
,
view
),
!
isDrawerFieldDisabled
(
field
.
field
,
activeView
),
),
);
);
const
hasDirtyFields
=
Object
.
keys
(
dirtyFields
).
length
>
0
;
const
hasDirtyFields
=
Object
.
keys
(
dirtyFields
).
length
>
0
;
const
hasValidationErrors
=
Object
.
values
(
validationErrors
).
some
(
Boolean
);
const
hasValidationErrors
=
Object
.
values
(
validationErrors
).
some
(
Boolean
);
...
...
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
6763c26b
...
@@ -65,10 +65,12 @@
...
@@ -65,10 +65,12 @@
-
`settings.quickSelect`
决定列表是否展示勾选列,用于批量推送、删除等操作
-
`settings.quickSelect`
决定列表是否展示勾选列,用于批量推送、删除等操作
-
`settings.restrictions`
与
`settings.filters.conditions`
共同决定该视图下可见的数据范围
-
`settings.restrictions`
与
`settings.filters.conditions`
共同决定该视图下可见的数据范围
-
Grid 视图的
`settings.viewAuth`
决定当前用户是否能看到该视图;普通用户未配置
`viewAuth`
时默认不可见,仅系统管理员和问题整改应用
`problem_rectification_new`
自身的“管理”角色可见。全部 Grid 视图均无权限时不得生成绕过授权的兜底视图、查询记录或展示新增/批量/行操作入口
-
Grid 视图的
`settings.viewAuth`
决定当前用户是否能看到该视图;普通用户未配置
`viewAuth`
时默认不可见,仅系统管理员和问题整改应用
`problem_rectification_new`
自身的“管理”角色可见。全部 Grid 视图均无权限时不得生成绕过授权的兜底视图、查询记录或展示新增/批量/行操作入口
-
`type = "detail"`
的视图用于详情抽屉字段编排,按
`columns`
顺序渲染字段;当前详情视图
`settings.shortTitle = true`
时,同时隐藏弹框 Header 中的记录标题和字段区中的
`title`
字段,未配置或为
`false`
时正常显示,切换详情视图页签后立即按新视图更新
-
用户获得 Grid Tab 的查看权限后,可以打开该 Tab 中记录的详情并查看 Detail
`columns`
配置的字段;Detail 不使用
`viewAuth`
再次隐藏详情字段,详情字段权限只控制是否允许编辑
-
表格 Tab 的
`settings.auth`
按字段路径控制详情弹框内对应字段是否允许编辑,规则结构与多维表格一致:
`field`
使用
`title`
或
`metadata.<fieldName>`
,
`type`
支持
`group`
、
`role`
、
`permission`
,授权值分别从
`groups`
、
`roles`
、
`permissions`
读取;普通用户遇到未配置
`auth`
的字段时默认只读
-
`type = "detail"`
的视图用于详情抽屉字段编排,按
`columns`
顺序渲染字段;当前详情视图
`settings.shortTitle = true`
时,同时隐藏弹框 Header 中的记录标题和字段区中的
`title`
字段,未配置或为
`false`
时正常显示。当前 Detail 的
`settings.auth`
或
`settings.disableColumns`
存在有效字段配置时,该 Detail 同时作为详情字段编辑权限来源,切换详情页签后立即按新视图更新展示和权限
-
表格 Tab 的
`settings.disableColumns`
始终优先,命中后字段保留展示但进入只读状态,即使系统管理员或“管理”角色也不得编辑;未命中时,系统管理员、系统账号和问题整改应用
`problem_rectification_new`
自身的“管理”角色旁路字段
`auth`
-
Detail 或回退表格视图的
`settings.auth`
按字段路径控制详情弹框内对应字段是否允许编辑,规则结构与多维表格一致:
`field`
使用
`title`
或
`metadata.<fieldName>`
,
`type`
支持
`group`
、
`role`
、
`permission`
,授权值分别从
`groups`
、
`roles`
、
`permissions`
读取;选定权限来源后,普通用户遇到未配置
`auth`
的字段时默认只读
-
普通用户只使用
`applications[]`
中
`name = "problem_rectification_new"`
的用户组、角色和权限进行匹配,不合并顶层用户组或其他应用授权。多维表格配置转换只接受
`group`
、
`role`
、
`permission`
,缺失或其他类型均按“管理”角色处理。进入详情弹框后,当前 Grid Tab 是唯一的字段编辑权限来源,Detail 视图只负责字段编排、条件显示与
`shortTitle`
,两套字段权限不得叠加。切换 Grid Tab 后,详情弹框必须立即改用新 Tab 的权限配置
-
当前权限来源视图的
`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 配置,而不是直接修改页面常量。
因此前端新增字段展示、部门视图调整、列顺序变化时,应优先改 Subject View 配置,而不是直接修改页面常量。
...
@@ -262,7 +264,7 @@
...
@@ -262,7 +264,7 @@
-
列表页采用分页加载,默认每页 20 条,可切换为 10、50、100;分页、筛选、视图切换均由服务端查询结果驱动
-
列表页采用分页加载,默认每页 20 条,可切换为 10、50、100;分页、筛选、视图切换均由服务端查询结果驱动
-
字段编辑统一进入右侧详情抽屉
-
字段编辑统一进入右侧详情抽屉
-
长文本、附件、状态类字段在详情抽屉中直接维护
-
长文本、附件、状态类字段在详情抽屉中直接维护
-
详情弹框内字段顺序与条件显示跟随
`detail view`
配置;
从 Grid 进入时,字段编辑权限只跟随当前 Grid 视图配置
-
详情弹框内字段顺序与条件显示跟随
`detail view`
配置;
当前 Detail 配置了字段权限时优先使用 Detail 权限,未配置或没有实际 Detail 时回退当前 Grid 权限
---
---
...
...
src/utils/subjectView.js
View file @
6763c26b
...
@@ -417,6 +417,14 @@ export function isViewAccessible(
...
@@ -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
(
export
function
isFieldDisabledByView
(
fieldPath
,
fieldPath
,
view
,
view
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment