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
558ad817
Commit
558ad817
authored
Sep 02, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: sync view restrictions and short title behavior
parent
91a90cce
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
115 additions
and
19 deletions
+115
-19
index.jsx
src/pages/problem_rectification/list/index.jsx
+33
-10
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+2
-2
problemRectificationListService.js
.../problem_rectification/problemRectificationListService.js
+18
-7
subjectView.js
src/utils/subjectView.js
+62
-0
No files found.
src/pages/problem_rectification/list/index.jsx
View file @
558ad817
...
@@ -92,6 +92,7 @@ import {
...
@@ -92,6 +92,7 @@ import {
normalizeDelayRequest
,
normalizeDelayRequest
,
normalizeDelayRequestItems
,
normalizeDelayRequestItems
,
normalizeDepartmentRectificationItems
,
normalizeDepartmentRectificationItems
,
getViewRestrictionDefaults
,
}
from
'../problemRectificationListService'
;
}
from
'../problemRectificationListService'
;
import
{
import
{
calculateProblemSeverityLevel
,
calculateProblemSeverityLevel
,
...
@@ -399,7 +400,7 @@ function RemoteUserSelect({ label, value, onChange, placeholder, onSearch }) {
...
@@ -399,7 +400,7 @@ function RemoteUserSelect({ label, value, onChange, placeholder, onSearch }) {
);
);
}
}
function
MultiSelectDropdown
({
value
,
options
,
onChange
,
placeholder
=
'请选择'
,
variant
})
{
function
MultiSelectDropdown
({
value
,
options
,
onChange
,
placeholder
=
'请选择'
,
variant
,
disabled
=
false
})
{
return
(
return
(
<
ResourceFilterMultiSelect
<
ResourceFilterMultiSelect
onChange=
{
onChange
}
onChange=
{
onChange
}
...
@@ -408,11 +409,12 @@ function MultiSelectDropdown({ value, options, onChange, placeholder = '请选
...
@@ -408,11 +409,12 @@ function MultiSelectDropdown({ value, options, onChange, placeholder = '请选
searchable
searchable
value=
{
Array
.
isArray
(
value
)
?
value
:
[]
}
value=
{
Array
.
isArray
(
value
)
?
value
:
[]
}
variant=
{
variant
}
variant=
{
variant
}
disabled=
{
disabled
}
/>
/>
);
);
}
}
function
CreateFieldSelect
({
value
,
options
,
onChange
,
placeholder
=
'请选择'
,
variant
})
{
function
CreateFieldSelect
({
value
,
options
,
onChange
,
placeholder
=
'请选择'
,
variant
,
disabled
=
false
})
{
return
(
return
(
<
XinyuanAutocomplete
<
XinyuanAutocomplete
aria
-
label=
{
placeholder
}
aria
-
label=
{
placeholder
}
...
@@ -423,12 +425,13 @@ function CreateFieldSelect({ value, options, onChange, placeholder = '请选择'
...
@@ -423,12 +425,13 @@ function CreateFieldSelect({ value, options, onChange, placeholder = '请选择'
placeholder=
{
placeholder
}
placeholder=
{
placeholder
}
selectedKey=
{
value
||
null
}
selectedKey=
{
value
||
null
}
variant=
{
variant
}
variant=
{
variant
}
isDisabled=
{
disabled
}
onSelectionChange=
{
(
key
)
=>
onChange
(
key
?
String
(
key
)
:
''
)
}
onSelectionChange=
{
(
key
)
=>
onChange
(
key
?
String
(
key
)
:
''
)
}
/>
/>
);
);
}
}
function
CreateField
({
fieldDef
,
value
,
onChange
,
variant
})
{
function
CreateField
({
fieldDef
,
value
,
onChange
,
variant
,
disabled
=
false
})
{
const
options
=
getSelectOptions
(
fieldDef
);
const
options
=
getSelectOptions
(
fieldDef
);
const
isMultiple
=
Boolean
(
fieldDef
?.
multiple
);
const
isMultiple
=
Boolean
(
fieldDef
?.
multiple
);
const
fieldType
=
CREATE_FIELD_TYPE_OVERRIDES
[
fieldDef
?.
name
]
||
fieldDef
?.
type
||
'text'
;
const
fieldType
=
CREATE_FIELD_TYPE_OVERRIDES
[
fieldDef
?.
name
]
||
fieldDef
?.
type
||
'text'
;
...
@@ -458,6 +461,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -458,6 +461,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
variant={variant}
variant={variant}
onChange={(event) => onChange(event.target.value)}
onChange={(event) => onChange(event.target.value)}
placeholder={placeholder}
placeholder={placeholder}
disabled={disabled}
/>
/>
);
);
}
}
...
@@ -467,6 +471,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -467,6 +471,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
<FormDatePicker
<FormDatePicker
value={value || ''}
value={value || ''}
variant={variant}
variant={variant}
disabled={disabled}
onChange={(event) => onChange(event.target.value)}
onChange={(event) => onChange(event.target.value)}
/>
/>
);
);
...
@@ -481,6 +486,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -481,6 +486,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
onChange={onChange}
onChange={onChange}
placeholder="请选择"
placeholder="请选择"
variant={variant}
variant={variant}
disabled={disabled}
/>
/>
);
);
}
}
...
@@ -491,6 +497,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -491,6 +497,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
options={options}
options={options}
onChange={onChange}
onChange={onChange}
variant={variant}
variant={variant}
disabled={disabled}
/>
/>
);
);
}
}
...
@@ -502,6 +509,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -502,6 +509,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
options={overrideOptions.map((option) => ({ label: option, value: option }))}
options={overrideOptions.map((option) => ({ label: option, value: option }))}
onChange={onChange}
onChange={onChange}
variant={variant}
variant={variant}
disabled={disabled}
/>
/>
);
);
}
}
...
@@ -515,6 +523,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
...
@@ -515,6 +523,7 @@ function CreateField({ fieldDef, value, onChange, variant }) {
variant={variant}
variant={variant}
onChange={(event) => onChange(event.target.value)}
onChange={(event) => onChange(event.target.value)}
placeholder={placeholder}
placeholder={placeholder}
disabled={disabled}
/>
/>
);
);
}
}
...
@@ -1058,9 +1067,14 @@ export default function ProblemRectificationListPage() {
...
@@ -1058,9 +1067,14 @@ export default function ProblemRectificationListPage() {
[activeGridView, subject],
[activeGridView, subject],
);
);
const restrictionDefaults = useMemo(
() => getViewRestrictionDefaults(activeGridView, subject),
[activeGridView, subject],
);
const initialCreateValues = useMemo(
const initialCreateValues = useMemo(
() => buildInitialCreateValues(createFields),
() => buildInitialCreateValues(createFields
, restrictionDefaults
),
[createFields],
[createFields
, restrictionDefaults
],
);
);
useEffect(() => {
useEffect(() => {
...
@@ -1103,12 +1117,20 @@ export default function ProblemRectificationListPage() {
...
@@ -1103,12 +1117,20 @@ export default function ProblemRectificationListPage() {
}, [showPushModal, selectedRecords]);
}, [showPushModal, selectedRecords]);
const handleCreate = async () => {
const handleCreate = async () => {
if (restrictionDefaults.errors.length > 0) {
setCreateValidationMessage(restrictionDefaults.errors.join('\n'));
return;
}
const hideTitle = shouldHideViewTitleColumn(activeGridView);
if (
if (
!normalizeText(formValues.title) &&
(!hideTitle && !normalizeText(formValues.title) && !normalizeText(formValues.project_name) && !normalizeText(formValues.problem_brief)) ||
!normalizeText(formValues.project_name) &&
(hideTitle && !normalizeText(formValues.project_name) && !normalizeText(formValues.problem_brief))
!normalizeText(formValues.problem_brief)
) {
) {
setCreateValidationMessage('记录标题、检查项目名称、问题简述至少填写一项。');
setCreateValidationMessage(
hideTitle
? '检查项目名称、问题简述至少填写一项。'
: '记录标题、检查项目名称、问题简述至少填写一项。',
);
return;
return;
}
}
...
@@ -1116,7 +1138,7 @@ export default function ProblemRectificationListPage() {
...
@@ -1116,7 +1138,7 @@ export default function ProblemRectificationListPage() {
setError('');
setError('');
try {
try {
const payload = buildProblemRectificationCreatePayload(formValues, createFields);
const payload = buildProblemRectificationCreatePayload(formValues, createFields
, restrictionDefaults, { hideTitle }
);
await createRecord(payload);
await createRecord(payload);
setCreateValidationMessage('');
setCreateValidationMessage('');
setShowCreateModal(false);
setShowCreateModal(false);
...
@@ -2123,6 +2145,7 @@ export default function ProblemRectificationListPage() {
...
@@ -2123,6 +2145,7 @@ export default function ProblemRectificationListPage() {
<CreateField
<CreateField
fieldDef={fieldDef}
fieldDef={fieldDef}
value={formValues[fieldDef.name]}
value={formValues[fieldDef.name]}
disabled={actionLoading || Object.prototype.hasOwnProperty.call(restrictionDefaults.metadata, fieldDef.name)}
variant={createFieldVariant}
variant={createFieldVariant}
onChange={(nextValue) =>
onChange={(nextValue) =>
setFormValues((current) => {
setFormValues((current) => {
...
...
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
558ad817
...
@@ -58,7 +58,7 @@
...
@@ -58,7 +58,7 @@
问题整改列表的新增表单、筛选和表格均读取当前激活 Grid View:
问题整改列表的新增表单、筛选和表格均读取当前激活 Grid View:
-
新增表单按
`settings.columns`
顺序渲染可见字段,并包含记录标题字段;标题是否必填跟随视图列配置,未配置短标题时使用标题作为默认回退值。
-
新增表单按
`settings.columns`
顺序渲染可见字段,并包含记录标题字段;标题是否必填跟随视图列配置,未配置短标题时使用标题作为默认回退值。
`settings.shortTitle = true`
时隐藏列表及新增表单的根级记录标题,不校验或提交根级
`title`
。
-
筛选控件由视图列的 Subject 字段类型自动生成。
`select`
选项来自
`settings.options`
,日期、数字、布尔、用户和引用字段使用对应控件;提交时按
`field:<path>`
通过
`/record/query`
远程筛选,不再提供独立关键词搜索。
-
筛选控件由视图列的 Subject 字段类型自动生成。
`select`
选项来自
`settings.options`
,日期、数字、布尔、用户和引用字段使用对应控件;提交时按
`field:<path>`
通过
`/record/query`
远程筛选,不再提供独立关键词搜索。
-
引用和用户多选的输入摘要、列表勾选与草稿统一使用归一化 ID;引用写入只保存目标记录
`_id`
或
`_id[]`
。
-
引用和用户多选的输入摘要、列表勾选与草稿统一使用归一化 ID;引用写入只保存目标记录
`_id`
或
`_id[]`
。
-
表格请求期间 DataGrid 使用居中的 loading 遮罩,空结果与加载中状态分开处理。
-
表格请求期间 DataGrid 使用居中的 loading 遮罩,空结果与加载中状态分开处理。
...
@@ -70,7 +70,7 @@
...
@@ -70,7 +70,7 @@
-
`settings.groups`
决定列表列分组,例如把多个问题分类字段归入同一分组;前端需展示分组表头,同一分组下的列即使在
`settings.columns`
中不连续,也应在表格内聚拢展示,并支持每个分组独立折叠 / 展开,折叠后隐藏该分组子列
-
`settings.groups`
决定列表列分组,例如把多个问题分类字段归入同一分组;前端需展示分组表头,同一分组下的列即使在
`settings.columns`
中不连续,也应在表格内聚拢展示,并支持每个分组独立折叠 / 展开,折叠后隐藏该分组子列
-
`settings.shortTitle = true`
仅隐藏列表中的
`title`
展示列,不删除记录标题数据,也不改变标题字段的视图排序、详情标题或权限判断;标题列原本承载的部门明细数量 / 展开、延期申请阶段 / 详情和锁定状态迁移到固定的“问题动态”上下文列,详情、延期申请、锁定、复制和删除继续由固定行操作列提供
-
`settings.shortTitle = true`
仅隐藏列表中的
`title`
展示列,不删除记录标题数据,也不改变标题字段的视图排序、详情标题或权限判断;标题列原本承载的部门明细数量 / 展开、延期申请阶段 / 详情和锁定状态迁移到固定的“问题动态”上下文列,详情、延期申请、锁定、复制和删除继续由固定行操作列提供
-
`settings.quickSelect`
决定列表是否展示勾选列,用于批量推送、删除等操作
-
`settings.quickSelect`
决定列表是否展示勾选列,用于批量推送、删除等操作
-
`settings.restrictions`
与
`settings.filters.conditions`
共同决定该视图下可见的数据范围
-
`settings.restrictions`
与
`settings.filters.conditions`
共同决定该视图下可见的数据范围
;从当前 Grid View 新建时,
`$eq`
或单值
`$in`
约束会自动写入
`metadata`
并锁定对应字段(即使该字段不在弹窗列中也会写入)。多值集合、否定条件或冲突约束无法确定唯一值时禁止创建并提示调整视图,编辑已有记录不覆盖原值
-
Grid 视图的
`settings.viewAuth`
决定当前用户是否能看到该视图;普通用户未配置
`viewAuth`
时默认不可见,仅系统管理员和问题整改应用
`problem_rectification_new`
自身的“管理”角色可见。全部 Grid 视图均无权限时不得生成绕过授权的兜底视图、查询记录或展示新增/批量/行操作入口
-
Grid 视图的
`settings.viewAuth`
决定当前用户是否能看到该视图;普通用户未配置
`viewAuth`
时默认不可见,仅系统管理员和问题整改应用
`problem_rectification_new`
自身的“管理”角色可见。全部 Grid 视图均无权限时不得生成绕过授权的兜底视图、查询记录或展示新增/批量/行操作入口
-
用户获得 Grid Tab 的查看权限后,可以打开该 Tab 中记录的详情并查看 Detail
`columns`
配置的字段;Detail 不使用
`viewAuth`
再次隐藏详情字段,详情字段权限只控制是否允许编辑
-
用户获得 Grid Tab 的查看权限后,可以打开该 Tab 中记录的详情并查看 Detail
`columns`
配置的字段;Detail 不使用
`viewAuth`
再次隐藏详情字段,详情字段权限只控制是否允许编辑
-
`type = "detail"`
的视图用于详情抽屉字段编排,按
`columns`
顺序渲染字段;当前详情视图
`settings.shortTitle = true`
时,同时隐藏弹框 Header 中的记录标题和字段区中的
`title`
字段,未配置或为
`false`
时正常显示。当前 Detail 的
`settings.auth`
或
`settings.disableColumns`
存在有效字段配置时,该 Detail 同时作为详情字段编辑权限来源,切换详情页签后立即按新视图更新展示和权限
-
`type = "detail"`
的视图用于详情抽屉字段编排,按
`columns`
顺序渲染字段;当前详情视图
`settings.shortTitle = true`
时,同时隐藏弹框 Header 中的记录标题和字段区中的
`title`
字段,未配置或为
`false`
时正常显示。当前 Detail 的
`settings.auth`
或
`settings.disableColumns`
存在有效字段配置时,该 Detail 同时作为详情字段编辑权限来源,切换详情页签后立即按新视图更新展示和权限
...
...
src/pages/problem_rectification/problemRectificationListService.js
View file @
558ad817
...
@@ -27,6 +27,7 @@ import {
...
@@ -27,6 +27,7 @@ import {
normalizeText
,
normalizeText
,
recordMatchesRestrictions
,
recordMatchesRestrictions
,
sortRecordsByView
,
sortRecordsByView
,
getViewRestrictionDefaults
,
}
from
'@/utils/subjectView'
;
}
from
'@/utils/subjectView'
;
export
const
PROBLEM_RECTIFICATION_SUBJECT
=
'problem_rectification'
;
export
const
PROBLEM_RECTIFICATION_SUBJECT
=
'problem_rectification'
;
...
@@ -1567,19 +1568,20 @@ export function buildProblemRectificationCreateFormFields(gridView, subject) {
...
@@ -1567,19 +1568,20 @@ export function buildProblemRectificationCreateFormFields(gridView, subject) {
:
fallbackFields
;
:
fallbackFields
;
}
}
export
function
buildInitialCreateValues
(
createFields
)
{
export
function
buildInitialCreateValues
(
createFields
,
restrictionDefaults
=
{}
)
{
const
values
=
createFields
.
reduce
((
nextValues
,
field
)
=>
({
const
values
=
createFields
.
reduce
((
nextValues
,
field
)
=>
({
...
nextValues
,
...
nextValues
,
[
field
.
fieldDef
.
name
]:
emptyValueForField
(
field
.
fieldDef
),
[
field
.
fieldDef
.
name
]:
emptyValueForField
(
field
.
fieldDef
),
}),
{});
}),
{
...(
restrictionDefaults
.
metadata
||
{})
});
const
resolvedValues
=
{
...
values
,
...(
restrictionDefaults
.
metadata
||
{})
};
return
{
return
{
...
v
alues
,
...
resolvedV
alues
,
problem_severity_level
:
calculateProblemSeverityLevel
(
v
alues
),
problem_severity_level
:
calculateProblemSeverityLevel
(
resolvedV
alues
),
};
};
}
}
export
function
buildProblemRectificationCreatePayload
(
formValues
,
createFields
=
[])
{
export
function
buildProblemRectificationCreatePayload
(
formValues
,
createFields
=
[]
,
restrictionDefaults
=
{},
{
hideTitle
=
false
}
=
{}
)
{
const
metadata
=
createFields
.
reduce
((
nextMetadata
,
field
)
=>
{
const
metadata
=
createFields
.
reduce
((
nextMetadata
,
field
)
=>
{
const
fieldName
=
field
.
fieldDef
?.
name
;
const
fieldName
=
field
.
fieldDef
?.
name
;
if
(
!
fieldName
)
{
if
(
!
fieldName
)
{
...
@@ -1599,13 +1601,22 @@ export function buildProblemRectificationCreatePayload(formValues, createFields
...
@@ -1599,13 +1601,22 @@ export function buildProblemRectificationCreatePayload(formValues, createFields
const
projectName
=
normalizeText
(
metadata
.
project_name
);
const
projectName
=
normalizeText
(
metadata
.
project_name
);
const
problemBrief
=
normalizeText
(
metadata
.
problem_brief
);
const
problemBrief
=
normalizeText
(
metadata
.
problem_brief
);
const
recordTitle
=
normalizeText
(
formValues
.
title
);
const
recordTitle
=
normalizeText
(
formValues
.
title
);
const
resolvedTitle
=
hideTitle
?
undefined
:
(
recordTitle
||
restrictionDefaults
.
title
||
projectName
||
problemBrief
||
'问题整改记录'
);
return
{
return
{
subject
:
PROBLEM_RECTIFICATION_SUBJECT
,
subject
:
PROBLEM_RECTIFICATION_SUBJECT
,
title
:
recordTitle
||
projectName
||
problemBrief
||
'问题整改记录'
,
...(
resolvedTitle
?
{
title
:
resolvedTitle
}
:
{})
,
metadata
:
{
metadata
:
{
...
metadata
,
...
metadata
,
problem_severity_level
:
calculateProblemSeverityLevel
(
metadata
),
...(
restrictionDefaults
.
metadata
||
{}),
problem_severity_level
:
calculateProblemSeverityLevel
({
...
metadata
,
...(
restrictionDefaults
.
metadata
||
{}),
}),
},
},
};
};
}
}
export
{
getViewRestrictionDefaults
};
src/utils/subjectView.js
View file @
558ad817
...
@@ -480,6 +480,68 @@ export function mergeRestrictionsIntoFilter(baseFilter = {}, restrictions = [])
...
@@ -480,6 +480,68 @@ export function mergeRestrictionsIntoFilter(baseFilter = {}, restrictions = [])
},
{
...
baseFilter
});
},
{
...
baseFilter
});
}
}
function
hasRestrictionValue
(
value
)
{
return
value
!==
undefined
&&
value
!==
null
&&
value
!==
''
;
}
function
restrictionValuesEqual
(
left
,
right
)
{
if
(
Object
.
is
(
left
,
right
))
return
true
;
try
{
return
JSON
.
stringify
(
left
)
===
JSON
.
stringify
(
right
);
}
catch
(
error
)
{
return
false
;
}
}
/** Resolve only restrictions that identify one safe value for a newly created record. */
export
function
getViewRestrictionDefaults
(
view
,
subject
)
{
const
assignments
=
new
Map
();
const
unresolved
=
new
Set
();
const
errors
=
new
Map
();
const
addError
=
(
fieldPath
,
fieldDef
)
=>
{
if
(
errors
.
has
(
fieldPath
))
return
;
const
label
=
fieldDef
?.
label
||
getFieldNameFromPath
(
fieldPath
)
||
fieldPath
;
errors
.
set
(
fieldPath
,
`
${
label
}
的视图约束无法自动确定唯一值,请先调整视图配置。`
);
};
toArray
(
view
?.
settings
?.
restrictions
).
forEach
((
restriction
)
=>
{
if
(
restriction
?.
type
!==
'field'
||
!
restriction
.
field
||
restriction
.
value
===
undefined
)
return
;
const
fieldPath
=
restriction
.
field
===
'title'
||
restriction
.
field
.
startsWith
(
'metadata.'
)
?
restriction
.
field
:
`metadata.
${
restriction
.
field
}
`
;
const
fieldDef
=
getFieldDefinition
(
fieldPath
,
subject
);
const
comparator
=
normalizeText
(
restriction
.
comparator
||
'$in'
);
const
values
=
toArray
(
restriction
.
value
).
filter
(
hasRestrictionValue
);
if
(
!
[
'$eq'
,
'$in'
].
includes
(
comparator
)
||
values
.
length
!==
1
)
{
unresolved
.
add
(
fieldPath
);
addError
(
fieldPath
,
fieldDef
);
return
;
}
if
(
unresolved
.
has
(
fieldPath
))
return
;
let
value
=
normalizeValueForSubmit
(
values
[
0
],
fieldDef
);
if
(
fieldDef
?.
multiple
)
value
=
toArray
(
value
);
if
(
assignments
.
has
(
fieldPath
)
&&
!
restrictionValuesEqual
(
assignments
.
get
(
fieldPath
),
value
))
{
assignments
.
delete
(
fieldPath
);
unresolved
.
add
(
fieldPath
);
addError
(
fieldPath
,
fieldDef
);
return
;
}
assignments
.
set
(
fieldPath
,
value
);
});
const
metadata
=
{};
let
title
;
assignments
.
forEach
((
value
,
fieldPath
)
=>
{
if
(
unresolved
.
has
(
fieldPath
))
return
;
if
(
fieldPath
===
'title'
)
title
=
value
;
else
{
const
fieldName
=
getFieldNameFromPath
(
fieldPath
);
if
(
fieldName
)
metadata
[
fieldName
]
=
value
;
}
});
return
{
metadata
,
title
,
errors
:
[...
errors
.
values
()]
};
}
export
function
sortRecordsByView
(
records
,
view
)
{
export
function
sortRecordsByView
(
records
,
view
)
{
const
sortColumns
=
toArray
(
view
?.
settings
?.
columns
)
const
sortColumns
=
toArray
(
view
?.
settings
?.
columns
)
.
filter
((
column
)
=>
column
.
sort
)
.
filter
((
column
)
=>
column
.
sort
)
...
...
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