Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
I
integrity_risk_prevention
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
integrity_risk_prevention
Commits
adccd409
Commit
adccd409
authored
Oct 07, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: configure table field count and label display
parent
197a63dc
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
401 additions
and
6 deletions
+401
-6
SubjectRecordWorkbench.jsx
src/components/SubjectRecordWorkbench.jsx
+9
-2
TableFieldValue.jsx
src/components/TableFieldValue.jsx
+46
-0
index.jsx
src/components/record-workbench/index.jsx
+16
-0
integrity-risk-prevention.business-flow.md
...isk_prevention/integrity-risk-prevention.business-flow.md
+61
-0
SubjectWorkbenchService.js
src/services/SubjectWorkbenchService.js
+7
-3
record-api.md
src/services/record-api.md
+8
-0
tableFieldDisplayService.js
src/services/tableFieldDisplayService.js
+17
-0
subjectView.js
src/utils/subjectView.js
+5
-1
tableFieldDisplay.js
src/utils/tableFieldDisplay.js
+80
-0
tableFieldDisplay.test.mjs
tests/tableFieldDisplay.test.mjs
+152
-0
No files found.
src/components/SubjectRecordWorkbench.jsx
View file @
adccd409
...
...
@@ -195,6 +195,8 @@ export default function SubjectRecordWorkbench({
const
[
records
,
setRecords
]
=
useState
([]);
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
views
,
setViews
]
=
useState
([]);
const
[
tableDisplayConfig
,
setTableDisplayConfig
]
=
useState
({});
const
[
tableDisplayConfigError
,
setTableDisplayConfigError
]
=
useState
(
''
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
error
,
setError
]
=
useState
(
''
);
const
[
success
,
setSuccess
]
=
useState
(
''
);
...
...
@@ -221,10 +223,14 @@ export default function SubjectRecordWorkbench({
setRecords
(
resources
.
records
||
[]);
setSubject
(
resources
.
subject
||
null
);
setViews
(
resources
.
views
||
[]);
setTableDisplayConfig
(
resources
.
displayConfig
||
{});
setTableDisplayConfigError
(
resources
.
displayConfigError
||
''
);
}
catch
(
requestError
)
{
setRecords
([]);
setSubject
(
null
);
setViews
([]);
setTableDisplayConfig
({});
setTableDisplayConfigError
(
''
);
setError
(
requestError
?.
message
||
'列表加载失败,请稍后重试。'
);
}
finally
{
setLoading
(
false
);
...
...
@@ -253,8 +259,8 @@ export default function SubjectRecordWorkbench({
);
const
hasAccessibleGridView
=
Boolean
(
activeView
);
const
configuredView
=
useMemo
(
()
=>
buildConfiguredWorkbenchView
(
records
,
filters
,
activeView
,
subject
),
[
records
,
filters
,
activeView
,
subject
],
()
=>
buildConfiguredWorkbenchView
(
records
,
filters
,
activeView
,
subject
,
tableDisplayConfig
),
[
records
,
filters
,
activeView
,
subject
,
tableDisplayConfig
],
);
const
filteredRecords
=
configuredView
.
filteredRecords
||
[];
const
visibleColumns
=
useMemo
(()
=>
{
...
...
@@ -565,6 +571,7 @@ export default function SubjectRecordWorkbench({
states=
{
{
loading
,
error
,
warning
:
tableDisplayConfigError
,
success
,
loadingTitle
:
`正在加载${title}`
,
loadingDescription
:
'正在读取对象结构、视图配置和记录数据,请稍候。'
,
...
...
src/components/TableFieldValue.jsx
0 → 100644
View file @
adccd409
import
{
Button
,
Chip
,
Tooltip
}
from
'@heroui/react'
;
import
{
DataTableText
}
from
'./DataTable'
;
import
{
getTableFieldDisplay
}
from
'@/utils/tableFieldDisplay'
;
export
default
function
TableFieldValue
({
value
,
column
,
onPress
})
{
const
display
=
getTableFieldDisplay
(
value
,
column
);
if
(
onPress
)
{
const
action
=
(
<
Button
variant=
"ghost"
className=
"h-auto min-w-0 justify-start px-0 text-left"
onPress=
{
onPress
}
>
{
display
.
showCount
?
<
Chip
size=
"sm"
aria
-
label=
{
`${column.label || '关联条目'}:${display.count} 项`
}
>
{
display
.
count
}
</
Chip
>
:
<
DataTableText
text=
{
display
.
text
||
'-'
}
maxLines=
{
2
}
showTooltip=
{
false
}
/>
}
</
Button
>
);
return
display
.
labels
.
length
>
0
?
(
<
Tooltip
>
{
action
}
<
Tooltip
.
Content
>
<
div
className=
"max-h-64 max-w-80 overflow-auto whitespace-pre-wrap break-words"
>
{
display
.
labels
.
join
(
'、'
)
}
</
div
>
</
Tooltip
.
Content
>
</
Tooltip
>
)
:
action
;
}
if
(
display
.
showCount
)
{
const
count
=
(
<
Chip
size=
"sm"
aria
-
label=
{
`${column.label || '关联条目'}:${display.count} 项`
}
>
{
display
.
count
}
</
Chip
>
);
return
display
.
labels
.
length
>
0
?
(
<
Tooltip
>
<
Tooltip
.
Trigger
aria
-
label=
{
`${column.label || '关联条目'}:${display.count} 项,查看标题`
}
>
{
count
}
</
Tooltip
.
Trigger
>
<
Tooltip
.
Content
>
<
div
className=
"max-h-64 max-w-80 overflow-auto whitespace-pre-wrap break-words"
>
{
display
.
labels
.
join
(
'、'
)
}
</
div
>
</
Tooltip
.
Content
>
</
Tooltip
>
)
:
count
;
}
return
<
DataTableText
text=
{
display
.
text
||
'-'
}
maxLines=
{
2
}
showTooltip
/>;
}
src/components/record-workbench/index.jsx
View file @
adccd409
...
...
@@ -27,6 +27,8 @@ import FilterSelect from '@/components/FilterSelect';
import
FormDatePicker
from
'@/components/FormDatePicker'
;
import
ReferenceSearchSelect
from
'@/pages/RecordDetailDrawer/components/ReferenceSearchSelect'
;
import
UserSearchSelect
from
'@/components/UserSearchSelect'
;
import
TableFieldValue
from
'@/components/TableFieldValue'
;
import
{
getTableFieldDisplayConfig
}
from
'@/utils/tableFieldDisplay'
;
import
{
formatFieldValue
,
getFieldValue
,
...
...
@@ -329,6 +331,9 @@ function getFilterPlaceholder(field) {
function
defaultRenderCell
(
record
,
column
)
{
const
sourceRecord
=
record
?.
raw
||
record
;
const
value
=
resolveFieldValue
(
sourceRecord
,
column
.
field
,
column
.
fieldDef
);
if
(
getTableFieldDisplayConfig
(
column
).
isConfigured
)
{
return
<
TableFieldValue
value=
{
value
}
column=
{
column
}
/>;
}
const
text
=
formatFieldValue
(
value
,
column
.
fieldDef
)
||
'-'
;
if
(
column
.
fieldDef
?.
type
===
'user'
)
{
...
...
@@ -939,6 +944,11 @@ export default function RecordWorkbench({
column.field,
column.fieldDef,
);
if (getTableFieldDisplayConfig(column).isConfigured) {
return <TableFieldValue value={value} column={column} onPress={() => row.rowContext.openDetail(
row.isExpandedRecord || row.isExpandedPanel ? row.parentRecord : row.record,
)} />;
}
const text = formatFieldValue(value, column.fieldDef) || '-';
return (
<DataTablePrimaryAction
...
...
@@ -1250,6 +1260,12 @@ export default function RecordWorkbench({
</Alert>
) : null}
{states?.warning ? (
<Alert status="warning">
<Alert.Content><Alert.Description>{states.warning}</Alert.Description></Alert.Content>
</Alert>
) : null}
<div ref={tableScrollRef} className={tableContentClass}>
<DataTable
ariaLabel={title || '业务数据列表'}
...
...
src/pages/integrity_risk_prevention/integrity-risk-prevention.business-flow.md
View file @
adccd409
...
...
@@ -132,6 +132,67 @@
-
本应用
`/integrity_risk_prevention_new`
前缀只在站内路由前移除;其他应用路径和外部 HTTP(S)
链接在新窗口打开,不改变原通知接口、
`notice_id`
payload 或鉴权。
### 2.4 表格字段显示配置
入口为 streams-admin 的应用布局配置或 Subject 的 Grid 视图配置,作用于过程表、展示表、措施库、往期材料归档的共享工作台表格中的引用、选择、子对象及 form 字段。页面加载应用及视图配置后按列渲染,切换视图时使用该视图的配置。
-
应用默认值读取
`layouts.default.subjects.<subjectName>.gridRecordConfig.displayConfig`
,兼容 streams-admin 的
`displayConfig.maxTagCount`
。
-
视图默认值读取
`settings.displayConfig`
,同名属性覆盖应用默认值;
`settings.columns[]`
中的单列
`displayConfig`
再覆盖视图默认值,列上的
`displayMode`
、
`maxTagCount`
、
`titleField`
优先级最高。
-
`displayMode: "labels"`
显示已选选项或关联标题;选项文案来自字段
`settings.options`
,仅展示当前记录实际选中的值。
-
`displayMode: "count"`
显示有效条目数量,空值显示
`0`
,单值显示
`1`
。数量可通过鼠标悬停或键盘聚焦查看完整标题;空值不产生提示。计数只基于该字段已有数据,不额外查询关联记录总数。
-
`displayMode: "auto"`
在条目数达到
`maxTagCount`
时显示数量,否则显示标题或选项。只配置
`maxTagCount`
时自动启用此模式,判断使用
`>=`
,与 streams-admin 一致。显式
`auto`
未配置合法阈值时使用
`3`
;阈值为
`0`
时所有非空字段显示数量。
-
子对象标签优先使用
`titleField`
指定的子字段,例如
`business_unit`
;也支持
`metadata`
下的子字段。未配置或值为空时使用已有
`title/name/_id`
,仍无标题时展示“第 N 条”,不猜测业务字段。form 对象为
`{form,data}`
时按
`data`
中的条目计数。
-
未配置显示方式或阈值时保留现有单元格行为,包括现有子对象明细摘要;非法显示方式或阈值不修改记录。普通文本、数字、日期、布尔、附件和人员字段不受这项集合显示配置影响。
-
应用配置读取失败时显示警告并使用视图配置;列表数据和详情入口仍可使用。重新加载页面会重试读取配置。配置不会改变筛选条件、排序、分页、审批状态、详情编辑、关联 ID 或 Record 的
`metadata`
。
应用配置示例(
`integrity_risk_prevention_new`
应用):
```
json
{
"layouts"
:
{
"default"
:
{
"subjects"
:
{
"risk_prevention_control_measures_table"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"maxTagCount"
:
3
}
}
},
"integrity_risk_control_archive"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"displayMode"
:
"count"
}
}
}
}
}
}
}
```
Grid 视图配置示例(列举的是配置片段,修改时应保留完整原列列表):
```
json
{
"settings"
:
{
"displayConfig"
:
{
"displayMode"
:
"auto"
,
"maxTagCount"
:
3
},
"columns"
:
[
{
"field"
:
"metadata.file_name_and_document_number"
,
"displayMode"
:
"labels"
},
{
"field"
:
"metadata.department_control_items"
,
"displayMode"
:
"count"
,
"titleField"
:
"business_unit"
},
{
"field"
:
"metadata.subsidiary_system"
,
"displayMode"
:
"auto"
,
"maxTagCount"
:
3
}
]
}
}
```
过程表仍按现有规则隐藏
`department_control_items`
主表列,部门展开行中的引用与选择字段应用相同列配置。首个可见字段配置为数量或标签时仍可点击进入详情。详情抽屉及归档嵌套明细弹窗沿用已有展示逻辑。
## 3. 过程表操作
### 3.0 推送链接与登录回跳
...
...
src/services/SubjectWorkbenchService.js
View file @
adccd409
...
...
@@ -14,6 +14,8 @@ import {
toArray
,
}
from
'@/utils/subjectView'
;
import
{
loadApplicationTableDisplayConfig
}
from
'@/services/tableFieldDisplayService'
;
const
QUERY_PAGE_SIZE
=
1000
;
const
RECORD_LEVEL_FIELDS
=
new
Set
([
'_id'
,
'name'
,
'title'
,
'status'
,
'subject'
,
'content'
,
'content_type'
,
'order'
,
'created_at'
,
'updated_at'
]);
...
...
@@ -123,9 +125,10 @@ export async function queryAllSubjectRecords(subjectName, query = {}) {
}
export
async
function
loadSubjectWorkbenchResources
(
subjectName
,
query
=
{},
options
=
{})
{
const
[
subject
,
viewSummaries
]
=
await
Promise
.
all
([
const
[
subject
,
viewSummaries
,
tableDisplayConfig
]
=
await
Promise
.
all
([
loadSubject
(
subjectName
),
getSubjectView
(
subjectName
),
loadApplicationTableDisplayConfig
(
subjectName
),
]);
const
views
=
await
Promise
.
all
(
(
Array
.
isArray
(
viewSummaries
)
?
viewSummaries
:
[]).
map
(
async
(
view
)
=>
{
...
...
@@ -156,6 +159,7 @@ export async function loadSubjectWorkbenchResources(subjectName, query = {}, opt
records
:
records
.
map
(
normalizeWorkbenchRecord
),
subject
,
views
:
views
||
[],
...
tableDisplayConfig
,
};
}
...
...
@@ -165,8 +169,8 @@ export function buildGridViews(views = []) {
.
filter
((
view
)
=>
isViewAccessible
(
view
));
}
export
function
buildConfiguredWorkbenchView
(
records
,
filters
,
view
,
subject
)
{
const
columns
=
buildViewColumns
(
view
,
subject
);
export
function
buildConfiguredWorkbenchView
(
records
,
filters
,
view
,
subject
,
displayConfig
=
{}
)
{
const
columns
=
buildViewColumns
(
view
,
subject
,
{
displayConfig
}
);
const
filteredRecords
=
records
.
filter
((
record
)
=>
{
const
rawRecord
=
record
.
raw
||
record
;
...
...
src/services/record-api.md
View file @
adccd409
...
...
@@ -44,6 +44,14 @@
-
`404`
`{"code":404,"message":"record '<name>' not found"}`
-
`400`
业务校验失败(缺少必填字段、主键冲突、类型不匹配等)
### 1.2 表格显示配置的关联读取接口
通用列表通过已有
`DataService.loadApplication(APPLICATION_NAME)`
调用
`GET /application/:name`
,当前应用为
`integrity_risk_prevention_new`
,读取
`layouts.default.subjects.<subjectName>.gridRecordConfig.displayConfig`
。
视图继续使用
`GET /subject/:subjectName/view`
与
`GET /subject/:subjectName/view/:viewName`
,分别对应
`getSubjectView`
/
`getSubjectViewDetail`
。视图默认值在
`settings.displayConfig`
,单列覆盖在
`settings.columns[]`
。支持
`displayMode: labels | count | auto`
、
`maxTagCount`
(非负整数)和
`titleField`
(子字段路径)。
这里只读取配置,不更新应用、视图、Subject 或 Record。应用配置失败时提示警告并使用视图配置,记录正常加载;刷新重试。完整规则见
`src/pages/integrity_risk_prevention/integrity-risk-prevention.business-flow.md`
的“2.4 表格字段显示配置”。
## 2. Record 核心模型字段
不同
`subject`
通过
`metadata`
扩展业务字段。
...
...
src/services/tableFieldDisplayService.js
0 → 100644
View file @
adccd409
import
{
APPLICATION_NAME
}
from
'@/config/application'
;
import
{
loadApplication
}
from
'@/services/DataService'
;
export
async
function
loadApplicationTableDisplayConfig
(
subjectName
)
{
try
{
const
application
=
await
loadApplication
(
APPLICATION_NAME
);
return
{
displayConfig
:
application
?.
layouts
?.
default
?.
subjects
?.[
subjectName
]?.
gridRecordConfig
?.
displayConfig
||
{},
displayConfigError
:
''
,
};
}
catch
(
error
)
{
return
{
displayConfig
:
{},
displayConfigError
:
`应用表格显示配置读取失败,暂使用视图默认显示:
${
error
?.
message
||
'请稍后重试'
}
`,
};
}
}
src/utils/subjectView.js
View file @
adccd409
...
...
@@ -473,9 +473,10 @@ export function shouldHideViewTitleColumn(view, fallbackView = null) {
return
(
view
?.
settings
?.
shortTitle
??
fallbackView
?.
settings
?.
shortTitle
)
===
true
;
}
export
function
buildViewColumns
(
view
,
subject
,
{
includeHidden
=
false
}
=
{})
{
export
function
buildViewColumns
(
view
,
subject
,
{
includeHidden
=
false
,
displayConfig
=
{}
}
=
{})
{
const
configuredColumns
=
view
?.
settings
?.
columns
||
[];
const
hideTitleColumn
=
!
includeHidden
&&
shouldHideViewTitleColumn
(
view
);
const
viewDisplayConfig
=
{
...
displayConfig
,
...
view
?.
settings
?.
displayConfig
};
if
(
configuredColumns
.
length
>
0
)
{
return
configuredColumns
...
...
@@ -488,6 +489,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
const
fieldDef
=
getFieldDefinition
(
column
.
field
,
subject
);
return
{
...
column
,
displayConfig
:
{
...
viewDisplayConfig
,
...
column
.
displayConfig
},
width
:
column
.
width
||
180
,
label
:
getFieldDisplayLabel
(
fieldDef
,
column
.
field
),
fieldDef
:
fieldDef
||
{
name
:
column
.
field
,
label
:
column
.
field
,
type
:
'text'
},
...
...
@@ -499,6 +501,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
if
(
!
hideTitleColumn
)
{
defaultColumns
.
push
({
field
:
'title'
,
displayConfig
:
viewDisplayConfig
,
width
:
180
,
label
:
'标题'
,
fieldDef
:
{
name
:
'title'
,
label
:
'标题'
,
type
:
'text'
},
...
...
@@ -508,6 +511,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
toArray
(
subject
?.
fields
).
forEach
((
field
)
=>
{
defaultColumns
.
push
({
field
:
`metadata.
${
field
.
name
}
`
,
displayConfig
:
viewDisplayConfig
,
width
:
180
,
label
:
getFieldDisplayLabel
(
field
,
`metadata.
${
field
.
name
}
`
),
fieldDef
:
field
,
...
...
src/utils/tableFieldDisplay.js
0 → 100644
View file @
adccd409
import
{
formatFieldValue
,
getFieldValue
,
getSelectOptions
,
isReferenceFieldType
,
normalizeRecordLabel
,
normalizeText
,
toArray
,
}
from
'./subjectView'
;
export
const
TABLE_FIELD_DISPLAY_MODES
=
[
'labels'
,
'count'
,
'auto'
];
export
const
DEFAULT_TABLE_MAX_TAG_COUNT
=
3
;
export
function
isTableFieldDisplayConfigurable
(
column
)
{
const
type
=
column
?.
fieldDef
?.
type
;
return
isReferenceFieldType
(
type
)
||
[
'select'
,
'subject'
,
'form'
].
includes
(
type
);
}
export
function
getTableFieldDisplayConfig
(
column
)
{
const
config
=
column
?.
displayConfig
||
{};
const
configuredMode
=
column
?.
displayMode
??
config
.
displayMode
;
const
configuredThreshold
=
column
?.
maxTagCount
??
config
.
maxTagCount
;
const
threshold
=
Number
(
configuredThreshold
);
const
hasThreshold
=
[
'number'
,
'string'
].
includes
(
typeof
configuredThreshold
)
&&
normalizeText
(
configuredThreshold
)
!==
''
&&
Number
.
isInteger
(
threshold
)
&&
threshold
>=
0
;
const
hasMode
=
TABLE_FIELD_DISPLAY_MODES
.
includes
(
configuredMode
);
return
{
isConfigured
:
isTableFieldDisplayConfigurable
(
column
)
&&
(
hasMode
||
hasThreshold
),
displayMode
:
hasMode
?
configuredMode
:
hasThreshold
?
'auto'
:
'labels'
,
maxTagCount
:
hasThreshold
?
threshold
:
DEFAULT_TABLE_MAX_TAG_COUNT
,
titleField
:
normalizeText
(
column
?.
titleField
??
config
.
titleField
),
};
}
function
getDisplayItems
(
value
,
fieldDef
)
{
const
items
=
fieldDef
?.
type
===
'form'
&&
value
&&
!
Array
.
isArray
(
value
)
&&
Array
.
isArray
(
value
.
data
)
?
value
.
data
:
toArray
(
value
);
return
items
.
filter
((
item
)
=>
item
!==
null
&&
item
!==
undefined
&&
!
(
typeof
item
===
'string'
&&
!
item
.
trim
()),
);
}
export
function
getTableFieldDisplay
(
value
,
column
)
{
const
config
=
getTableFieldDisplayConfig
(
column
);
const
items
=
getDisplayItems
(
value
,
column
?.
fieldDef
);
const
type
=
column
?.
fieldDef
?.
type
;
const
options
=
type
===
'select'
?
getSelectOptions
(
column
.
fieldDef
)
:
[];
const
labels
=
items
.
map
((
item
,
index
)
=>
{
if
(
type
===
'select'
)
{
const
optionValue
=
item
&&
typeof
item
===
'object'
?
item
.
value
:
item
;
const
option
=
options
.
find
((
candidate
)
=>
String
(
candidate
.
value
)
===
String
(
optionValue
));
return
normalizeText
(
option
?.
label
??
optionValue
);
}
if
([
'subject'
,
'form'
].
includes
(
type
))
{
const
titleValue
=
config
.
titleField
?
getFieldValue
(
item
,
config
.
titleField
)
??
getFieldValue
(
item
?.
metadata
,
config
.
titleField
)
:
null
;
return
formatFieldValue
(
titleValue
)
||
normalizeRecordLabel
(
item
)
||
`第
${
index
+
1
}
条`
;
}
if
(
type
===
'tag'
)
return
normalizeText
(
item
?.
label
)
||
normalizeRecordLabel
(
item
);
return
normalizeRecordLabel
(
item
);
}).
filter
(
Boolean
);
const
showCount
=
config
.
displayMode
===
'count'
||
(
config
.
displayMode
===
'auto'
&&
items
.
length
>
0
&&
items
.
length
>=
config
.
maxTagCount
);
return
{
...
config
,
count
:
items
.
length
,
labels
,
showCount
,
text
:
showCount
?
String
(
items
.
length
)
:
labels
.
join
(
'、'
),
};
}
export
function
formatTableFieldValue
(
value
,
column
)
{
const
display
=
getTableFieldDisplay
(
value
,
column
);
return
display
.
isConfigured
?
display
.
text
:
formatFieldValue
(
value
,
column
?.
fieldDef
);
}
tests/tableFieldDisplay.test.mjs
0 → 100644
View file @
adccd409
import
assert
from
'node:assert/strict'
;
import
{
readFileSync
}
from
'node:fs'
;
import
{
createRequire
}
from
'node:module'
;
import
{
dirname
,
resolve
}
from
'node:path'
;
import
test
from
'node:test'
;
import
ts
from
'typescript'
;
const
require
=
createRequire
(
import
.
meta
.
url
);
const
sourceRoot
=
new
URL
(
'../src/'
,
import
.
meta
.
url
).
pathname
;
function
loadModule
(
entry
,
dataService
=
{})
{
const
modules
=
new
Map
();
const
load
=
(
path
)
=>
{
if
(
modules
.
has
(
path
))
return
modules
.
get
(
path
);
const
exports
=
{};
modules
.
set
(
path
,
exports
);
const
{
outputText
}
=
ts
.
transpileModule
(
readFileSync
(
path
,
'utf8'
),
{
compilerOptions
:
{
module
:
ts
.
ModuleKind
.
CommonJS
,
target
:
ts
.
ScriptTarget
.
ES2022
,
esModuleInterop
:
true
},
});
const
localRequire
=
(
specifier
)
=>
{
if
(
specifier
===
'./DataService'
||
specifier
===
'@/services/DataService'
)
return
dataService
;
if
(
!
specifier
.
startsWith
(
'.'
)
&&
!
specifier
.
startsWith
(
'@/'
))
return
require
(
specifier
);
const
dependencyPath
=
specifier
.
startsWith
(
'@/'
)
?
resolve
(
sourceRoot
,
specifier
.
slice
(
2
))
:
resolve
(
dirname
(
path
),
specifier
);
return
load
(
`
${
dependencyPath
}
.js`
);
};
new
Function
(
'require'
,
'exports'
,
outputText
)(
localRequire
,
exports
);
return
exports
;
};
return
load
(
resolve
(
sourceRoot
,
entry
));
}
const
{
getTableFieldDisplay
,
getTableFieldDisplayConfig
,
formatTableFieldValue
}
=
loadModule
(
'utils/tableFieldDisplay.js'
);
const
{
buildViewColumns
,
formatFieldValue
}
=
loadModule
(
'utils/subjectView.js'
);
const
column
=
(
type
,
config
=
{})
=>
({
field
:
'metadata.values'
,
fieldDef
:
{
type
},
...
config
});
test
(
'count mode counts selected values, including zero, and excludes empty entries'
,
()
=>
{
const
config
=
column
(
'select'
,
{
displayMode
:
'count'
});
assert
.
equal
(
formatTableFieldValue
([
0
,
'选中'
,
null
,
undefined
,
''
,
' '
],
config
),
'2'
);
for
(
const
value
of
[
null
,
undefined
,
''
,
[]])
assert
.
equal
(
formatTableFieldValue
(
value
,
config
),
'0'
);
assert
.
equal
(
formatTableFieldValue
(
0
,
config
),
'1'
);
assert
.
equal
(
formatTableFieldValue
({
_id
:
'one'
,
title
:
'关联标题'
},
column
(
'reference'
,
{
displayMode
:
'count'
})),
'1'
);
});
test
(
'labels show selected option labels and populated record titles'
,
()
=>
{
const
config
=
column
(
'select'
,
{
displayMode
:
'labels'
});
config
.
fieldDef
.
settings
=
{
options
:
[{
value
:
0
,
label
:
'零号'
},
{
value
:
'a'
,
label
:
'选项 A'
},
{
value
:
'b'
,
label
:
'未选项'
}]
};
assert
.
equal
(
formatTableFieldValue
([
'0'
,
'a'
],
config
),
'零号、选项 A'
);
assert
.
equal
(
formatTableFieldValue
([{
_id
:
'id-1'
,
title
:
'记录 A'
},
{
_id
:
'id-2'
,
title
:
'记录 B'
}],
column
(
'reference'
,
{
displayMode
:
'labels'
})),
'记录 A、记录 B'
);
assert
.
equal
(
formatTableFieldValue
([{
name
:
'risk'
,
label
:
'风险标签'
}],
column
(
'tag'
,
{
displayMode
:
'labels'
})),
'风险标签'
);
});
test
(
'maxTagCount alone matches streams-admin at the exact threshold'
,
()
=>
{
const
config
=
column
(
'reference'
,
{
displayConfig
:
{
maxTagCount
:
2
}
});
assert
.
equal
(
formatTableFieldValue
([{
title
:
'A'
}],
config
),
'A'
);
assert
.
equal
(
formatTableFieldValue
([{
title
:
'A'
},
{
title
:
'B'
}],
config
),
'2'
);
assert
.
equal
(
formatTableFieldValue
([],
config
),
''
);
assert
.
equal
(
formatTableFieldValue
([
'A'
],
column
(
'reference'
,
{
displayMode
:
'auto'
,
maxTagCount
:
0
})),
'1'
);
assert
.
equal
(
getTableFieldDisplayConfig
(
column
(
'reference'
,
{
displayMode
:
'auto'
})).
maxTagCount
,
3
);
});
test
(
'embedded subjects and forms use configured child labels without changing data'
,
()
=>
{
const
value
=
[{
responsible_department
:
'审计部'
},
{
metadata
:
{
responsible_department
:
'风险管理部'
}
}];
const
original
=
JSON
.
stringify
(
value
);
const
config
=
column
(
'subject'
,
{
displayMode
:
'labels'
,
titleField
:
'responsible_department'
});
assert
.
equal
(
formatTableFieldValue
(
value
,
config
),
'审计部、风险管理部'
);
assert
.
equal
(
getTableFieldDisplay
(
value
,
{
...
config
,
displayMode
:
'count'
}).
count
,
2
);
assert
.
equal
(
formatTableFieldValue
([{},
{}],
config
),
'第 1 条、第 2 条'
);
assert
.
equal
(
formatTableFieldValue
({
form
:
{
title
:
'表单'
},
data
:
value
},
column
(
'form'
,
{
displayMode
:
'count'
})),
'2'
);
assert
.
equal
(
JSON
.
stringify
(
value
),
original
);
});
test
(
'application defaults, view settings, and column overrides keep independent modes'
,
()
=>
{
const
subject
=
{
fields
:
[{
name
:
'values'
,
type
:
'select'
}]
};
const
view
=
{
settings
:
{
displayConfig
:
{
maxTagCount
:
2
},
columns
:
[{
field
:
'metadata.values'
,
displayMode
:
'labels'
}],
}
};
const
original
=
JSON
.
stringify
(
view
);
const
[
config
]
=
buildViewColumns
(
view
,
subject
,
{
displayConfig
:
{
displayMode
:
'count'
,
maxTagCount
:
5
}
});
assert
.
equal
(
getTableFieldDisplayConfig
(
config
).
maxTagCount
,
2
);
assert
.
equal
(
formatTableFieldValue
([
'A'
,
'B'
],
config
),
'A、B'
);
const
[
nestedConfig
]
=
buildViewColumns
({
settings
:
{
displayConfig
:
{
displayMode
:
'count'
,
maxTagCount
:
5
},
columns
:
[{
field
:
'metadata.values'
,
displayConfig
:
{
displayMode
:
'auto'
,
maxTagCount
:
2
}
}],
}
},
subject
);
assert
.
equal
(
formatTableFieldValue
([
'A'
,
'B'
],
nestedConfig
),
'2'
);
const
fallback
=
buildViewColumns
({
settings
:
{}
},
subject
,
{
displayConfig
:
{
displayMode
:
'count'
}
});
assert
.
equal
(
formatTableFieldValue
([
'A'
,
'B'
],
fallback
.
find
((
item
)
=>
item
.
field
===
'metadata.values'
)),
'2'
);
assert
.
equal
(
JSON
.
stringify
(
view
),
original
);
});
test
(
'missing or invalid config preserves current behavior and does not affect scalar fields'
,
()
=>
{
const
value
=
[{
responsible_department
:
'审计部'
}];
assert
.
equal
(
formatTableFieldValue
(
value
,
column
(
'subject'
)),
formatFieldValue
(
value
,
{
type
:
'subject'
}));
assert
.
equal
(
formatTableFieldValue
(
'A'
,
column
(
'select'
,
{
displayMode
:
'invalid'
,
maxTagCount
:
-
1
})),
'A'
);
for
(
const
type
of
[
'text'
,
'number'
,
'date'
,
'boolean'
,
'user'
,
'file'
,
'object'
])
{
assert
.
equal
(
getTableFieldDisplayConfig
(
column
(
type
,
{
displayMode
:
'count'
})).
isConfigured
,
false
);
}
for
(
const
maxTagCount
of
[
'invalid'
,
-
1
,
1.5
,
null
,
''
,
' '
,
false
,
true
,
[]])
{
assert
.
equal
(
getTableFieldDisplayConfig
(
column
(
'reference'
,
{
displayConfig
:
{
maxTagCount
}
})).
isConfigured
,
false
);
}
});
test
(
'application config is read from the same layout path as streams-admin'
,
async
()
=>
{
const
requests
=
[];
const
service
=
loadModule
(
'services/tableFieldDisplayService.js'
,
{
loadApplication
:
async
(
name
)
=>
{
requests
.
push
(
name
);
return
{
layouts
:
{
default
:
{
subjects
:
{
risk_prevention_control_measures_table
:
{
gridRecordConfig
:
{
displayConfig
:
{
maxTagCount
:
2
}
},
}
}
}
}
};
},
});
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'risk_prevention_control_measures_table'
),
{
displayConfig
:
{
maxTagCount
:
2
},
displayConfigError
:
''
,
});
assert
.
deepEqual
(
requests
,
[
'integrity_risk_prevention_new'
]);
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'integrity_risk_control_archive'
),
{
displayConfig
:
{},
displayConfigError
:
''
,
});
});
test
(
'unavailable application config returns a warning and usable view fallback'
,
async
()
=>
{
const
service
=
loadModule
(
'services/tableFieldDisplayService.js'
,
{
loadApplication
:
async
()
=>
{
throw
new
Error
(
'无权限'
);
},
});
const
result
=
await
service
.
loadApplicationTableDisplayConfig
(
'risk_prevention_control_measures_table'
);
assert
.
deepEqual
(
result
.
displayConfig
,
{});
assert
.
match
(
result
.
displayConfigError
,
/暂使用视图默认显示.*无权限/
);
});
test
(
'shared workbench propagates app config while preserving restrictions and hidden columns'
,
()
=>
{
const
{
buildConfiguredWorkbenchView
}
=
loadModule
(
'services/SubjectWorkbenchService.js'
);
const
subject
=
{
fields
:
[{
name
:
'values'
,
type
:
'reference'
}]
};
const
records
=
[
{
id
:
'a'
,
raw
:
{
metadata
:
{
values
:
[{
title
:
'A'
}],
allowed
:
'yes'
}
}
},
{
id
:
'b'
,
raw
:
{
metadata
:
{
values
:
[{
title
:
'B'
}],
allowed
:
'no'
}
}
},
];
const
view
=
{
settings
:
{
shortTitle
:
true
,
columns
:
[{
field
:
'title'
},
{
field
:
'metadata.values'
},
{
field
:
'metadata.allowed'
,
hide
:
true
}],
restrictions
:
[{
type
:
'field'
,
field
:
'metadata.allowed'
,
comparator
:
'$in'
,
value
:
[
'yes'
]
}],
}
};
const
baseline
=
buildConfiguredWorkbenchView
(
records
,
{},
view
,
subject
);
const
configured
=
buildConfiguredWorkbenchView
(
records
,
{},
view
,
subject
,
{
displayMode
:
'count'
});
assert
.
deepEqual
(
configured
.
filteredRecords
,
baseline
.
filteredRecords
);
assert
.
deepEqual
(
configured
.
filteredRecords
.
map
((
record
)
=>
record
.
id
),
[
'a'
]);
assert
.
deepEqual
(
configured
.
columns
.
map
((
item
)
=>
item
.
field
),
[
'metadata.values'
]);
assert
.
equal
(
formatTableFieldValue
(
records
[
0
].
raw
.
metadata
.
values
,
configured
.
columns
[
0
]),
'1'
);
});
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