Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
I
integrity_practice_management_new
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_practice_management_new
Commits
fb868f94
Commit
fb868f94
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
2ebc3759
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
352 additions
and
9 deletions
+352
-9
TableFieldValue.jsx
src/components/TableFieldValue.jsx
+27
-0
index.jsx
src/components/record-workbench/index.jsx
+8
-2
SubjectWorkbench.jsx
src/pages/integrity_practice_management/SubjectWorkbench.jsx
+9
-2
integrity-practice-management.business-flow.md
...management/integrity-practice-management.business-flow.md
+60
-1
integrityPracticeWorkbenchService.js
..._practice_management/integrityPracticeWorkbenchService.js
+6
-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
+132
-0
No files found.
src/components/TableFieldValue.jsx
0 → 100644
View file @
fb868f94
import
{
Chip
,
Tooltip
}
from
'@heroui/react'
;
import
{
DataTableText
}
from
'./DataTable'
;
import
{
getTableFieldDisplay
}
from
'@/utils/tableFieldDisplay'
;
export
default
function
TableFieldValue
({
value
,
column
})
{
const
display
=
getTableFieldDisplay
(
value
,
column
);
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 @
fb868f94
import
TableFieldValue
from
'@/components/TableFieldValue'
;
import
{
formatTableFieldValue
,
getTableFieldDisplayConfig
}
from
'@/utils/tableFieldDisplay'
;
import
{
ChevronDown
,
Close
,
Eye
,
More
,
Reload
,
Search
}
from
'@/components/AppIcons'
;
import
{
TableActionButton
,
TableActionGroup
}
from
'@/components/TableActionButton'
;
import
FilterSelect
from
'@/components/FilterSelect'
;
...
...
@@ -41,7 +43,7 @@ function getFilterPlaceholder(field, fieldType) {
function estimatePrimaryColumnWidth(records, column) {
const values = records.map((record) => {
const value = format
FieldValue(getFieldValue(record?.raw || record, column.field), column.fieldDef
) || '-';
const value = format
TableFieldValue(getFieldValue(record?.raw || record, column.field), column
) || '-';
return String(value);
});
const longestUnits = values.reduce((maximum, value) => {
...
...
@@ -256,7 +258,10 @@ export default function RecordWorkbench({
cell: (record) => {
const custom = renderCell?.({ record, column, value: getFieldValue(record?.raw || record, column.field), openDetail });
if (custom !== undefined && custom !== null) return custom;
const value = formatFieldValue(getFieldValue(record?.raw || record, column.field), column.fieldDef) || '-';
if (getTableFieldDisplayConfig(column).isConfigured) {
return <TableFieldValue value={getFieldValue(record?.raw || record, column.field)} column={column} />;
}
const value = formatTableFieldValue(getFieldValue(record?.raw || record, column.field), column) || '-';
if (index === 0) {
return (
<Tooltip delay={0}>
...
...
@@ -464,6 +469,7 @@ export default function RecordWorkbench({
) : null}
</header>
) : null}
{states.warning ? <Alert status="warning"><Alert.Content><Alert.Description>{states.warning}</Alert.Description></Alert.Content></Alert> : null}
{states.error ? <Alert status="danger"><Alert.Content><Alert.Description>{states.error}</Alert.Description></Alert.Content></Alert> : null}
<div className="rc-list-data-stack rc-data-table-sticky relative min-w-0" ref={gridWrapRef}>
{accessDenied ? (
...
...
src/pages/integrity_practice_management/SubjectWorkbench.jsx
View file @
fb868f94
...
...
@@ -81,6 +81,8 @@ export default function SubjectWorkbench() {
const
gridConfig
=
subjectConfig
.
gridRecordConfig
||
{};
const
paginationEnabled
=
gridConfig
.
pagination
!==
false
;
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
displayConfig
,
setDisplayConfig
]
=
useState
({});
const
[
displayConfigError
,
setDisplayConfigError
]
=
useState
(
''
);
const
[
views
,
setViews
]
=
useState
(
EMPTY_ARRAY
);
const
[
activeViewName
,
setActiveViewName
]
=
useState
(
''
);
const
[
records
,
setRecords
]
=
useState
(
EMPTY_ARRAY
);
...
...
@@ -105,8 +107,8 @@ export default function SubjectWorkbench() {
);
const
hasAccessibleView
=
views
.
length
>
0
;
const
columns
=
useMemo
(
()
=>
buildSubjectColumns
(
activeView
,
subject
),
[
activeView
,
subject
],
()
=>
buildSubjectColumns
(
activeView
,
subject
,
displayConfig
),
[
activeView
,
subject
,
displayConfig
],
);
const
filterSchema
=
useMemo
(()
=>
buildSubjectFilterSchema
(
activeView
,
subject
),
[
activeView
,
subject
]);
...
...
@@ -118,6 +120,8 @@ export default function SubjectWorkbench() {
let
cancelled
=
false
;
setLoading
(
true
);
setError
(
''
);
setDisplayConfig
({});
setDisplayConfigError
(
''
);
setRecords
(
EMPTY_ARRAY
);
setSelectedIds
(
EMPTY_ARRAY
);
setFilters
({});
...
...
@@ -128,6 +132,8 @@ export default function SubjectWorkbench() {
.
then
((
resources
)
=>
{
if
(
cancelled
)
return
;
setSubject
(
resources
.
subject
);
setDisplayConfig
(
resources
.
displayConfig
);
setDisplayConfigError
(
resources
.
displayConfigError
);
setViews
(
resources
.
views
);
setActiveViewName
(
resources
.
views
[
0
]?.
name
||
''
);
})
...
...
@@ -345,6 +351,7 @@ export default function SubjectWorkbench() {
states=
{
{
loading
,
error
,
warning
:
displayConfigError
,
loadingTitle
:
'正在加载业务数据'
,
loadingDescription
:
'正在读取对象结构、视图配置和记录数据。'
,
}
}
...
...
src/pages/integrity_practice_management/integrity-practice-management.business-flow.md
View file @
fb868f94
...
...
@@ -21,7 +21,7 @@
## 2. 列表与视图流程
1.
进入页面后并行调用
`GET /subject/:name`
和
`GET /subject/:name/view
`
;打开详情后再按 View 名调用
`GET /subject/:subjectName/view/:viewName`
获取完整 Detail 配置。
1.
进入页面后并行调用
`GET /subject/:name`
、
`GET /subject/:name/view`
和
`GET /application/:name
`
;打开详情后再按 View 名调用
`GET /subject/:subjectName/view/:viewName`
获取完整 Detail 配置。
2.
只展示当前用户有权访问的
`grid`
视图;授权只读取
`user.applications[]`
中
`name = integrity_practice_management`
的项目。系统管理员、系统账号和该应用“管理”角色可旁路,但不能旁路
`disableColumns`
。配置包含
`filterView`
且非空时,只展示指定视图。
3.
切换视图后,根据视图的
`settings.restrictions`
、
`settings.filters.conditions`
、列排序配置调用
`POST /record/query`
;筛选控件默认由当前 View 的可见字段自动生成。若 View 配置了
`settings.filters.fields`
,则仅按该字段数组的顺序生成筛选控件,支持
`metadata.field`
、
`field`
字符串及
`{ field }`
、
`{ path }`
、
`{ name }`
对象项,显式配置可引用隐藏列;空数组表示该视图不展示筛选控件,无效字段会被忽略。文本字段使用远程模糊查询,Select/日期字段使用字段配置值。
4.
`gridRecordConfig.pagination = false`
时单次最多读取 1000 条;其余页面使用服务端分页。
...
...
@@ -46,6 +46,65 @@
-
多值集合、否定条件或同一字段互相冲突时无法确定唯一值,禁止创建并提示“请先调整视图配置”。
-
新建请求使用
`POST /record`
,所有业务字段放在
`metadata`
;编辑已有记录只提交用户修改字段,不使用 Grid View 限制覆盖原值。
### 2.2 表格字段显示配置
入口为 streams-admin 的应用布局配置或 Subject 的 Grid 视图配置,作用于本模块七个业务列表的共享 RecordWorkbench 表格中的引用、选择、子对象及 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`
指定的子字段,例如
`actual_filling_department`
;也支持
`metadata`
下的子字段。未配置或值为空时使用已有
`title/name/_id`
,仍无标题时展示“第 N 条”,不猜测业务字段。form 对象为
`{form,data}`
时按
`data`
中的条目计数。
-
未配置显示方式或阈值时保留现有单元格行为;非法显示方式或阈值不修改记录。普通文本、数字、日期、布尔、附件和人员字段不受这项集合显示配置影响。
-
应用配置读取失败时显示警告并使用视图配置;列表数据和详情入口仍可使用。重新加载页面会重试读取配置。配置不会改变筛选条件、排序、分页、审批状态、详情编辑、关联 ID 或 Record 的
`metadata`
。
应用配置示例(
`integrity_practice_management`
应用):
```
json
{
"layouts"
:
{
"default"
:
{
"subjects"
:
{
"self_check_the_results_of_the_working_draft"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"maxTagCount"
:
3
}
}
},
"self_inspection_annual_submission_materials"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"displayMode"
:
"count"
}
}
}
}
}
}
}
```
Grid 视图配置示例(列举的是配置片段,修改时应保留完整原列列表):
```
json
{
"settings"
:
{
"displayConfig"
:
{
"displayMode"
:
"auto"
,
"maxTagCount"
:
3
},
"columns"
:
[
{
"field"
:
"metadata.actual_filling_department"
,
"displayMode"
:
"labels"
},
{
"field"
:
"metadata.project_outcome"
,
"displayMode"
:
"count"
,
"titleField"
:
"title"
},
{
"field"
:
"metadata.reference_integrity_practice_report"
,
"displayMode"
:
"labels"
,
"titleField"
:
"title"
}
]
}
}
```
## 3. 记录选择与动作规则
-
行动作来自
`gridRecordConfig.menuConfig`
,顶部动作来自
`gridRecordConfig.tabBarRightSlot`
。
...
...
src/pages/integrity_practice_management/integrityPracticeWorkbenchService.js
View file @
fb868f94
import
{
loadApplicationTableDisplayConfig
}
from
'@/services/tableFieldDisplayService'
;
import
{
batchRemoveRecords
,
batchUpdateRecords
,
...
...
@@ -140,9 +141,10 @@ function buildViewSort(view) {
}
export
async
function
loadSubjectResources
(
subjectName
,
config
=
{})
{
const
[
subject
,
views
]
=
await
Promise
.
all
([
const
[
subject
,
views
,
tableDisplayConfig
]
=
await
Promise
.
all
([
loadSubject
(
subjectName
),
getSubjectView
(
subjectName
),
loadApplicationTableDisplayConfig
(
subjectName
),
]);
const
configuredViewNames
=
config
?.
gridRecordConfig
?.
filterView
;
const
availableViews
=
toArray
(
views
)
...
...
@@ -158,6 +160,7 @@ export async function loadSubjectResources(subjectName, config = {}) {
return
{
subject
,
views
:
availableViews
,
...
tableDisplayConfig
,
};
}
...
...
@@ -194,8 +197,8 @@ export async function loadSubjectRecords({
};
}
export
function
buildSubjectColumns
(
view
,
subject
)
{
return
buildViewColumns
(
view
,
subject
);
export
function
buildSubjectColumns
(
view
,
subject
,
displayConfig
=
{}
)
{
return
buildViewColumns
(
view
,
subject
,
{
displayConfig
}
);
}
export
function
buildSubjectFilterSchema
(
view
,
subject
)
{
...
...
src/services/record-api.md
View file @
fb868f94
...
...
@@ -44,6 +44,14 @@
-
`404`
`{"code":404,"message":"record '<name>' not found"}`
-
`400`
业务校验失败(缺少必填字段、主键冲突、类型不匹配等)
### 1.2 表格显示配置的关联读取接口
廉洁从业管理列表通过已有
`DataService.loadApplication(APPLICATION_NAME)`
调用
`GET /application/:name`
(应用名
`integrity_practice_management`
),读取
`layouts.default.subjects.<subjectName>.gridRecordConfig.displayConfig`
,与 streams-admin 使用相同路径。
视图继续通过
`GET /subject/:name/view`
读取。视图级配置为
`settings.displayConfig`
,单列覆盖为
`settings.columns[]`
中的
`displayConfig`
或直接属性。支持
`displayMode: labels | count | auto`
、非负整数
`maxTagCount`
和子字段路径
`titleField`
。
这里只读取配置;请求失败时显示警告并使用视图配置,记录加载与写入协议保持不变。优先级及示例见廉洁从业管理流程文档第 2.2 节。
## 2. Record 核心模型字段
不同
`subject`
通过
`metadata`
扩展业务字段。
...
...
src/services/tableFieldDisplayService.js
0 → 100644
View file @
fb868f94
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 @
fb868f94
...
...
@@ -277,8 +277,9 @@ export function shouldHideViewTitleColumn(view) {
return
view
?.
settings
?.
shortTitle
===
true
;
}
export
function
buildViewColumns
(
view
,
subject
,
{
includeHidden
=
false
}
=
{})
{
export
function
buildViewColumns
(
view
,
subject
,
{
includeHidden
=
false
,
displayConfig
=
{}
}
=
{})
{
const
configuredColumns
=
view
?.
settings
?.
columns
||
[];
const
viewDisplayConfig
=
{
...
displayConfig
,
...
view
?.
settings
?.
displayConfig
};
const
hideTitleColumn
=
!
includeHidden
&&
shouldHideViewTitleColumn
(
view
);
if
(
configuredColumns
.
length
>
0
)
{
...
...
@@ -290,6 +291,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
const
label
=
getFieldLabel
(
column
.
field
,
fieldDef
,
column
.
label
);
return
{
...
column
,
displayConfig
:
{
...
viewDisplayConfig
,
...
column
.
displayConfig
},
width
:
column
.
width
||
180
,
label
,
fieldDef
:
fieldDef
||
{
...
...
@@ -305,6 +307,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'
},
...
...
@@ -315,6 +318,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
const
fieldPath
=
`metadata.
${
field
.
name
}
`
;
defaultColumns
.
push
({
field
:
fieldPath
,
displayConfig
:
viewDisplayConfig
,
width
:
180
,
label
:
getFieldLabel
(
fieldPath
,
field
),
fieldDef
:
field
,
...
...
src/utils/tableFieldDisplay.js
0 → 100644
View file @
fb868f94
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 @
fb868f94
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
}
=
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'
)),
JSON
.
stringify
(
value
));
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
:
{
self_check_the_results_of_the_working_draft
:
{
gridRecordConfig
:
{
displayConfig
:
{
maxTagCount
:
2
}
},
}
}
}
}
};
},
});
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'self_check_the_results_of_the_working_draft'
),
{
displayConfig
:
{
maxTagCount
:
2
},
displayConfigError
:
''
,
});
assert
.
deepEqual
(
requests
,
[
'integrity_practice_management'
]);
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'self_inspection_annual_submission_materials'
),
{
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
(
'self_check_the_results_of_the_working_draft'
);
assert
.
deepEqual
(
result
.
displayConfig
,
{});
assert
.
match
(
result
.
displayConfigError
,
/暂使用视图默认显示.*无权限/
);
});
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