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
3c5cc2ab
Commit
3c5cc2ab
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
e2c3190a
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
392 additions
and
18 deletions
+392
-18
TableFieldValue.jsx
src/components/TableFieldValue.jsx
+27
-0
index.jsx
src/components/record-workbench/index.jsx
+16
-1
archiveWorkbenchService.js
.../problem_rectification/archive/archiveWorkbenchService.js
+8
-5
index.jsx
src/pages/problem_rectification/archive/index.jsx
+15
-2
index.jsx
src/pages/problem_rectification/list/index.jsx
+15
-2
problem-rectification.business-flow.md
...blem_rectification/problem-rectification.business-flow.md
+59
-0
problemRectificationListService.js
.../problem_rectification/problemRectificationListService.js
+10
-7
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 @
3c5cc2ab
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 @
3c5cc2ab
...
@@ -24,7 +24,9 @@ import FilterSelect from '@/components/FilterSelect';
...
@@ -24,7 +24,9 @@ import FilterSelect from '@/components/FilterSelect';
import
FormDatePicker
from
'@/components/FormDatePicker'
;
import
FormDatePicker
from
'@/components/FormDatePicker'
;
import
ReferenceSearchSelect
from
'@/pages/RecordDetailDrawer/components/ReferenceSearchSelect'
;
import
ReferenceSearchSelect
from
'@/pages/RecordDetailDrawer/components/ReferenceSearchSelect'
;
import
UserSearchSelect
from
'@/components/UserSearchSelect'
;
import
UserSearchSelect
from
'@/components/UserSearchSelect'
;
import
TableFieldValue
from
'@/components/TableFieldValue'
;
import
{
formatFieldValue
,
getFieldValue
,
normalizeText
}
from
'@/utils/subjectView'
;
import
{
formatFieldValue
,
getFieldValue
,
normalizeText
}
from
'@/utils/subjectView'
;
import
{
formatTableFieldValue
,
getTableFieldDisplayConfig
}
from
'@/utils/tableFieldDisplay'
;
import
{
import
{
Alert
,
Alert
,
Button
,
Button
,
...
@@ -292,6 +294,9 @@ function defaultGetRecordLabel(record) {
...
@@ -292,6 +294,9 @@ function defaultGetRecordLabel(record) {
function
defaultRenderCell
(
record
,
column
)
{
function
defaultRenderCell
(
record
,
column
)
{
const
sourceRecord
=
record
?.
raw
||
record
;
const
sourceRecord
=
record
?.
raw
||
record
;
const
value
=
getFieldValue
(
sourceRecord
,
column
.
field
);
const
value
=
getFieldValue
(
sourceRecord
,
column
.
field
);
if
(
getTableFieldDisplayConfig
(
column
).
isConfigured
)
{
return
<
TableFieldValue
value=
{
value
}
column=
{
column
}
/>;
}
const
text
=
formatFieldValue
(
value
,
column
.
fieldDef
)
||
'-'
;
const
text
=
formatFieldValue
(
value
,
column
.
fieldDef
)
||
'-'
;
return
(
return
(
<
DataTableText
<
DataTableText
...
@@ -323,6 +328,10 @@ const SHORT_VALUE_FIELD_TYPES = new Set([
...
@@ -323,6 +328,10 @@ const SHORT_VALUE_FIELD_TYPES = new Set([
]);
]);
function
resolveColumnDataGridPresentation
(
column
,
records
=
[])
{
function
resolveColumnDataGridPresentation
(
column
,
records
=
[])
{
const
display
=
getTableFieldDisplayConfig
(
column
);
if
(
display
.
isConfigured
&&
display
.
displayMode
===
'count'
)
{
return
{
isLongText
:
false
,
maxLines
:
1
,
showTooltip
:
false
};
}
const
label
=
String
(
column
?.
label
||
''
);
const
label
=
String
(
column
?.
label
||
''
);
const
field
=
String
(
column
?.
field
||
''
).
toLowerCase
();
const
field
=
String
(
column
?.
field
||
''
).
toLowerCase
();
const
fieldType
=
String
(
column
?.
fieldDef
?.
type
||
''
).
toLowerCase
();
const
fieldType
=
String
(
column
?.
fieldDef
?.
type
||
''
).
toLowerCase
();
...
@@ -334,7 +343,7 @@ function resolveColumnDataGridPresentation(column, records = []) {
...
@@ -334,7 +343,7 @@ function resolveColumnDataGridPresentation(column, records = []) {
?
records
.
slice
(
0
,
20
).
reduce
((
longest
,
record
)
=>
{
?
records
.
slice
(
0
,
20
).
reduce
((
longest
,
record
)
=>
{
const
sourceRecord
=
record
?.
raw
||
record
;
const
sourceRecord
=
record
?.
raw
||
record
;
const
value
=
getFieldValue
(
sourceRecord
,
column
.
field
);
const
value
=
getFieldValue
(
sourceRecord
,
column
.
field
);
const
text
=
normalizeText
(
format
FieldValue
(
value
,
column
.
fieldDef
));
const
text
=
normalizeText
(
format
TableFieldValue
(
value
,
column
));
return
Math
.
max
(
longest
,
Array
.
from
(
text
).
length
);
return
Math
.
max
(
longest
,
Array
.
from
(
text
).
length
);
},
0
)
},
0
)
:
0
;
:
0
;
...
@@ -1280,6 +1289,12 @@ export default function RecordWorkbench({
...
@@ -1280,6 +1289,12 @@ export default function RecordWorkbench({
</Alert>
</Alert>
) : null}
) : null}
{states?.warning ? (
<Alert status="warning">
<Alert.Content><Alert.Description>{states.warning}</Alert.Description></Alert.Content>
</Alert>
) : null}
{states?.success ? (
{states?.success ? (
<Alert status="success">
<Alert status="success">
<Alert.Content><Alert.Description>{states.success}</Alert.Description></Alert.Content>
<Alert.Content><Alert.Description>{states.success}</Alert.Description></Alert.Content>
...
...
src/pages/problem_rectification/archive/archiveWorkbenchService.js
View file @
3c5cc2ab
...
@@ -11,6 +11,7 @@ import {
...
@@ -11,6 +11,7 @@ import {
sortRecordsByView
,
sortRecordsByView
,
}
from
'@/utils/subjectView'
;
}
from
'@/utils/subjectView'
;
import
{
ARCHIVE_SNAPSHOT_ITEM_FIELDS
}
from
'./archiveSnapshotItems.mjs'
;
import
{
ARCHIVE_SNAPSHOT_ITEM_FIELDS
}
from
'./archiveSnapshotItems.mjs'
;
import
{
loadApplicationTableDisplayConfig
}
from
'@/services/tableFieldDisplayService'
;
export
const
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
=
'issue_rectification_archive'
;
export
const
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
=
'issue_rectification_archive'
;
export
const
PROBLEM_RECTIFICATION_ARCHIVE_RECORDS_SUBJECT
=
'problem_rectification_archive_records'
;
export
const
PROBLEM_RECTIFICATION_ARCHIVE_RECORDS_SUBJECT
=
'problem_rectification_archive_records'
;
...
@@ -42,9 +43,10 @@ export function normalizeArchiveBatchRecord(record) {
...
@@ -42,9 +43,10 @@ export function normalizeArchiveBatchRecord(record) {
}
}
export
async
function
loadArchiveWorkbenchResources
()
{
export
async
function
loadArchiveWorkbenchResources
()
{
const
[
archiveSubject
,
archiveViews
]
=
await
Promise
.
all
([
const
[
archiveSubject
,
archiveViews
,
tableDisplayConfig
]
=
await
Promise
.
all
([
loadSubject
(
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
),
loadSubject
(
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
),
getSubjectView
(
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
),
getSubjectView
(
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
),
loadApplicationTableDisplayConfig
(
ISSUE_RECTIFICATION_ARCHIVE_SUBJECT
),
]);
]);
const
accessibleViews
=
buildArchiveGridViews
(
archiveViews
||
[]);
const
accessibleViews
=
buildArchiveGridViews
(
archiveViews
||
[]);
const
archiveRecordsResult
=
accessibleViews
.
length
>
0
const
archiveRecordsResult
=
accessibleViews
.
length
>
0
...
@@ -62,6 +64,7 @@ export async function loadArchiveWorkbenchResources() {
...
@@ -62,6 +64,7 @@ export async function loadArchiveWorkbenchResources() {
records
,
records
,
subject
:
archiveSubject
,
subject
:
archiveSubject
,
views
:
archiveViews
||
[],
views
:
archiveViews
||
[],
...
tableDisplayConfig
,
};
};
}
}
...
@@ -82,11 +85,11 @@ export function buildArchiveGridViews(views = []) {
...
@@ -82,11 +85,11 @@ export function buildArchiveGridViews(views = []) {
return
isViewAccessible
(
fallbackView
)
?
[
fallbackView
]
:
[];
return
isViewAccessible
(
fallbackView
)
?
[
fallbackView
]
:
[];
}
}
export
function
buildArchiveColumns
(
view
,
subject
)
{
export
function
buildArchiveColumns
(
view
,
subject
,
displayConfig
=
{}
)
{
return
buildViewColumns
(
view
,
subject
);
return
buildViewColumns
(
view
,
subject
,
{
displayConfig
}
);
}
}
export
function
buildArchiveConfiguredView
(
records
,
filters
,
gridView
,
subject
)
{
export
function
buildArchiveConfiguredView
(
records
,
filters
,
gridView
,
subject
,
displayConfig
=
{}
)
{
if
(
!
gridView
)
{
if
(
!
gridView
)
{
return
{
return
{
filteredRecords
:
[],
filteredRecords
:
[],
...
@@ -95,7 +98,7 @@ export function buildArchiveConfiguredView(records, filters, gridView, subject)
...
@@ -95,7 +98,7 @@ export function buildArchiveConfiguredView(records, filters, gridView, subject)
}
}
const
keyword
=
normalizeText
(
filters
?.
keyword
).
toLowerCase
();
const
keyword
=
normalizeText
(
filters
?.
keyword
).
toLowerCase
();
const
columns
=
buildArchiveColumns
(
gridView
,
subject
);
const
columns
=
buildArchiveColumns
(
gridView
,
subject
,
displayConfig
);
const
searchColumns
=
buildViewSearchColumns
(
gridView
,
subject
);
const
searchColumns
=
buildViewSearchColumns
(
gridView
,
subject
);
const
filteredRecords
=
records
.
filter
((
record
)
=>
{
const
filteredRecords
=
records
.
filter
((
record
)
=>
{
const
rawRecord
=
record
.
raw
||
record
;
const
rawRecord
=
record
.
raw
||
record
;
...
...
src/pages/problem_rectification/archive/index.jsx
View file @
3c5cc2ab
import
RecordWorkbench
from
'@/components/record-workbench'
;
import
RecordWorkbench
from
'@/components/record-workbench'
;
import
TableFieldValue
from
'@/components/TableFieldValue'
;
import
{
getTableFieldDisplayConfig
}
from
'@/utils/tableFieldDisplay'
;
import
{
getFieldValue
}
from
'@/utils/subjectView'
;
import
{
Archive
,
Database
}
from
'@/components/AppIcons'
;
import
{
Archive
,
Database
}
from
'@/components/AppIcons'
;
import
RecordDetailDrawer
from
'@/pages/RecordDetailDrawer'
;
import
RecordDetailDrawer
from
'@/pages/RecordDetailDrawer'
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
'react'
;
...
@@ -17,6 +20,8 @@ export default function ProblemRectificationArchivePage() {
...
@@ -17,6 +20,8 @@ export default function ProblemRectificationArchivePage() {
const
[
records
,
setRecords
]
=
useState
([]);
const
[
records
,
setRecords
]
=
useState
([]);
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
subject
,
setSubject
]
=
useState
(
null
);
const
[
views
,
setViews
]
=
useState
([]);
const
[
views
,
setViews
]
=
useState
([]);
const
[
tableDisplayConfig
,
setTableDisplayConfig
]
=
useState
({});
const
[
tableDisplayConfigError
,
setTableDisplayConfigError
]
=
useState
(
''
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
error
,
setError
]
=
useState
(
''
);
const
[
error
,
setError
]
=
useState
(
''
);
const
[
filters
,
setFilters
]
=
useState
({
const
[
filters
,
setFilters
]
=
useState
({
...
@@ -36,10 +41,14 @@ export default function ProblemRectificationArchivePage() {
...
@@ -36,10 +41,14 @@ export default function ProblemRectificationArchivePage() {
setRecords
(
resources
.
records
||
[]);
setRecords
(
resources
.
records
||
[]);
setSubject
(
resources
.
subject
||
null
);
setSubject
(
resources
.
subject
||
null
);
setViews
(
resources
.
views
||
[]);
setViews
(
resources
.
views
||
[]);
setTableDisplayConfig
(
resources
.
displayConfig
||
{});
setTableDisplayConfigError
(
resources
.
displayConfigError
||
''
);
}
catch
(
requestError
)
{
}
catch
(
requestError
)
{
setRecords
([]);
setRecords
([]);
setSubject
(
null
);
setSubject
(
null
);
setViews
([]);
setViews
([]);
setTableDisplayConfig
({});
setTableDisplayConfigError
(
''
);
setError
(
requestError
?.
message
||
'归档批次列表加载失败,请稍后重试。'
);
setError
(
requestError
?.
message
||
'归档批次列表加载失败,请稍后重试。'
);
}
finally
{
}
finally
{
setLoading
(
false
);
setLoading
(
false
);
...
@@ -69,8 +78,8 @@ export default function ProblemRectificationArchivePage() {
...
@@ -69,8 +78,8 @@ export default function ProblemRectificationArchivePage() {
);
);
const
configuredView
=
useMemo
(
const
configuredView
=
useMemo
(
()
=>
buildArchiveConfiguredView
(
records
,
filters
,
activeGridView
,
subject
),
()
=>
buildArchiveConfiguredView
(
records
,
filters
,
activeGridView
,
subject
,
tableDisplayConfig
),
[
records
,
filters
,
activeGridView
,
subject
],
[
records
,
filters
,
activeGridView
,
subject
,
tableDisplayConfig
],
);
);
const
filteredRecords
=
configuredView
.
filteredRecords
||
[];
const
filteredRecords
=
configuredView
.
filteredRecords
||
[];
...
@@ -185,6 +194,7 @@ export default function ProblemRectificationArchivePage() {
...
@@ -185,6 +194,7 @@ export default function ProblemRectificationArchivePage() {
states=
{
{
states=
{
{
loading
,
loading
,
error
,
error
,
warning
:
tableDisplayConfigError
,
loadingTitle
:
'正在加载归档批次'
,
loadingTitle
:
'正在加载归档批次'
,
loadingDescription
:
'正在读取归档对象、视图配置和批次记录,请稍候。'
,
loadingDescription
:
'正在读取归档对象、视图配置和批次记录,请稍候。'
,
emptyTitle
:
'暂无归档批次'
,
emptyTitle
:
'暂无归档批次'
,
...
@@ -192,6 +202,9 @@ export default function ProblemRectificationArchivePage() {
...
@@ -192,6 +202,9 @@ export default function ProblemRectificationArchivePage() {
}
}
}
}
renderCell=
{
({
record
,
column
})
=>
{
renderCell=
{
({
record
,
column
})
=>
{
if
(
column
.
field
===
'metadata.reference_records'
)
{
if
(
column
.
field
===
'metadata.reference_records'
)
{
if
(
getTableFieldDisplayConfig
(
column
).
isConfigured
)
{
return
<
TableFieldValue
column=
{
column
}
value=
{
getFieldValue
(
record
.
raw
||
record
,
column
.
field
)
}
/>;
}
return
(
return
(
<
span
className=
"tabular-nums"
>
<
span
className=
"tabular-nums"
>
{
record
.
archiveCount
||
0
}
条明细
{
record
.
archiveCount
||
0
}
条明细
...
...
src/pages/problem_rectification/list/index.jsx
View file @
3c5cc2ab
...
@@ -17,6 +17,8 @@ import {
...
@@ -17,6 +17,8 @@ import {
}
from
'@/components/TableActionButton'
;
}
from
'@/components/TableActionButton'
;
import
XinyuanModal
from
'@/components/XinyuanModal'
;
import
XinyuanModal
from
'@/components/XinyuanModal'
;
import
RecordWorkbench
from
'@/components/record-workbench'
;
import
RecordWorkbench
from
'@/components/record-workbench'
;
import
TableFieldValue
from
'@/components/TableFieldValue'
;
import
{
getTableFieldDisplayConfig
}
from
'@/utils/tableFieldDisplay'
;
import
RecordDetailDrawer
from
'@/pages/RecordDetailDrawer'
;
import
RecordDetailDrawer
from
'@/pages/RecordDetailDrawer'
;
import
{
import
{
batchRemoveRecords
,
batchRemoveRecords
,
...
@@ -625,6 +627,10 @@ function FieldValuePreview({ column, record }) {
...
@@ -625,6 +627,10 @@ function FieldValuePreview({ column, record }) {
return <div className="font-medium">{record.title || '(无标题)'}</div>;
return <div className="font-medium">{record.title || '(无标题)'}</div>;
}
}
if (getTableFieldDisplayConfig(column).isConfigured) {
return <TableFieldValue value={value} column={column} />;
}
if (fieldName === 'rectification_progress') {
if (fieldName === 'rectification_progress') {
const text = formatFieldValue(value, column.fieldDef) || '未填写';
const text = formatFieldValue(value, column.fieldDef) || '未填写';
return <StatusPill text={text} tone={resolveProgressTone(text)} />;
return <StatusPill text={text} tone={resolveProgressTone(text)} />;
...
@@ -788,6 +794,8 @@ export default function ProblemRectificationListPage() {
...
@@ -788,6 +794,8 @@ export default function ProblemRectificationListPage() {
const [records, setRecords] = useState([]);
const [records, setRecords] = useState([]);
const [subject, setSubject] = useState(null);
const [subject, setSubject] = useState(null);
const [views, setViews] = useState([]);
const [views, setViews] = useState([]);
const [tableDisplayConfig, setTableDisplayConfig] = useState({});
const [tableDisplayConfigError, setTableDisplayConfigError] = useState('');
const [loading, setLoading] = useState(true);
const [loading, setLoading] = useState(true);
const [recordsLoading, setRecordsLoading] = useState(false);
const [recordsLoading, setRecordsLoading] = useState(false);
const [error, setError] = useState('');
const [error, setError] = useState('');
...
@@ -843,10 +851,14 @@ export default function ProblemRectificationListPage() {
...
@@ -843,10 +851,14 @@ export default function ProblemRectificationListPage() {
const resources = await loadProblemRectificationListResources();
const resources = await loadProblemRectificationListResources();
setSubject(resources.subject || null);
setSubject(resources.subject || null);
setViews(resources.views || []);
setViews(resources.views || []);
setTableDisplayConfig(resources.displayConfig || {});
setTableDisplayConfigError(resources.displayConfigError || '');
} catch (requestError) {
} catch (requestError) {
setRecords([]);
setRecords([]);
setSubject(null);
setSubject(null);
setViews([]);
setViews([]);
setTableDisplayConfig({});
setTableDisplayConfigError('');
setError(requestError?.message || '问题整改列表加载失败,请稍后重试。');
setError(requestError?.message || '问题整改列表加载失败,请稍后重试。');
} finally {
} finally {
setLoading(false);
setLoading(false);
...
@@ -1087,8 +1099,8 @@ export default function ProblemRectificationListPage() {
...
@@ -1087,8 +1099,8 @@ export default function ProblemRectificationListPage() {
}, [reloadList]);
}, [reloadList]);
const configuredView = useMemo(
const configuredView = useMemo(
() => buildProblemRectificationConfiguredListView(records, {}, activeGridView, subject),
() => buildProblemRectificationConfiguredListView(records, {}, activeGridView, subject
, tableDisplayConfig
),
[records, activeGridView, subject],
[records, activeGridView, subject
, tableDisplayConfig
],
);
);
const dynamicFilterSchema = useMemo(
const dynamicFilterSchema = useMemo(
() =>
() =>
...
@@ -2070,6 +2082,7 @@ export default function ProblemRectificationListPage() {
...
@@ -2070,6 +2082,7 @@ export default function ProblemRectificationListPage() {
states={{
states={{
loading: loading || recordsLoading,
loading: loading || recordsLoading,
error,
error,
warning: tableDisplayConfigError,
success: '',
success: '',
loadingTitle: '正在加载问题整改列表',
loadingTitle: '正在加载问题整改列表',
loadingDescription: '正在读取对象结构、视图配置和记录数据,请稍候。',
loadingDescription: '正在读取对象结构、视图配置和记录数据,请稍候。',
...
...
src/pages/problem_rectification/problem-rectification.business-flow.md
View file @
3c5cc2ab
...
@@ -287,6 +287,65 @@
...
@@ -287,6 +287,65 @@
---
---
### 5.10 表格字段显示配置
入口为 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`
指定的子字段,例如
`responsible_department`
;也支持
`metadata`
下的子字段。未配置或值为空时使用已有
`title/name/_id`
,仍无标题时展示“第 N 条”,不猜测业务字段。form 对象为
`{form,data}`
时按
`data`
中的条目计数。
-
未配置显示方式或阈值时保留现有单元格行为,包括归档批次的“X 条明细”;非法显示方式或阈值不修改记录。普通文本、数字、日期、布尔、附件和人员字段不受这项集合显示配置影响。
-
应用配置读取失败时显示警告并使用视图配置;列表数据和详情入口仍可使用。重新加载页面会重试读取配置。配置不会改变筛选条件、排序、分页、审批状态、详情编辑、关联 ID 或 Record 的
`metadata`
。
应用配置示例(
`problem_rectification_new`
应用):
```
json
{
"layouts"
:
{
"default"
:
{
"subjects"
:
{
"problem_rectification"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"maxTagCount"
:
3
}
}
},
"issue_rectification_archive"
:
{
"gridRecordConfig"
:
{
"displayConfig"
:
{
"displayMode"
:
"count"
}
}
}
}
}
}
}
```
Grid 视图配置示例(列举的是配置片段,修改时应保留完整原列列表):
```
json
{
"settings"
:
{
"displayConfig"
:
{
"displayMode"
:
"auto"
,
"maxTagCount"
:
3
},
"columns"
:
[
{
"field"
:
"metadata.responsible_department"
,
"displayMode"
:
"labels"
},
{
"field"
:
"metadata.department_rectification_items"
,
"displayMode"
:
"count"
,
"titleField"
:
"responsible_department"
},
{
"field"
:
"metadata.problem_rectification_delay_request_items"
,
"displayMode"
:
"labels"
,
"titleField"
:
"responsible_department"
}
]
}
}
```
## 6. 流程角色
## 6. 流程角色
-
发起人:通常为问题整改列表操作人,负责发起填报和最终审核;可编辑本次推送关联的父问题记录以及全部部门整改明细业务字段
-
发起人:通常为问题整改列表操作人,负责发起填报和最终审核;可编辑本次推送关联的父问题记录以及全部部门整改明细业务字段
...
...
src/pages/problem_rectification/problemRectificationListService.js
View file @
3c5cc2ab
...
@@ -13,11 +13,12 @@ import {
...
@@ -13,11 +13,12 @@ import {
DEPARTMENT_GROUPS
,
DEPARTMENT_GROUPS
,
}
from
'./problemRectificationDashboardService'
;
}
from
'./problemRectificationDashboardService'
;
import
{
calculateProblemSeverityLevel
}
from
'./problemSeverity'
;
import
{
calculateProblemSeverityLevel
}
from
'./problemSeverity'
;
import
{
loadApplicationTableDisplayConfig
}
from
'@/services/tableFieldDisplayService'
;
import
{
formatTableFieldValue
}
from
'@/utils/tableFieldDisplay'
;
import
{
import
{
buildViewColumns
,
buildViewColumns
,
checkViewCondition
,
checkViewCondition
,
emptyValueForField
,
emptyValueForField
,
formatFieldValue
,
getFieldNameFromPath
,
getFieldNameFromPath
,
getFieldValue
,
getFieldValue
,
getSelectOptions
,
getSelectOptions
,
...
@@ -1346,9 +1347,10 @@ export async function loadProblemRectificationListData({
...
@@ -1346,9 +1347,10 @@ export async function loadProblemRectificationListData({
}
}
export
async
function
loadProblemRectificationListResources
()
{
export
async
function
loadProblemRectificationListResources
()
{
const
[
subject
,
viewSummaries
]
=
await
Promise
.
all
([
const
[
subject
,
viewSummaries
,
tableDisplayConfig
]
=
await
Promise
.
all
([
loadSubject
(
PROBLEM_RECTIFICATION_SUBJECT
),
loadSubject
(
PROBLEM_RECTIFICATION_SUBJECT
),
getSubjectView
(
PROBLEM_RECTIFICATION_SUBJECT
),
getSubjectView
(
PROBLEM_RECTIFICATION_SUBJECT
),
loadApplicationTableDisplayConfig
(
PROBLEM_RECTIFICATION_SUBJECT
),
]);
]);
const
views
=
await
Promise
.
all
(
const
views
=
await
Promise
.
all
(
toArray
(
viewSummaries
).
map
(
async
(
view
)
=>
{
toArray
(
viewSummaries
).
map
(
async
(
view
)
=>
{
...
@@ -1375,6 +1377,7 @@ export async function loadProblemRectificationListResources() {
...
@@ -1375,6 +1377,7 @@ export async function loadProblemRectificationListResources() {
return
{
return
{
subject
,
subject
,
views
,
views
,
...
tableDisplayConfig
,
};
};
}
}
...
@@ -1429,8 +1432,8 @@ export function buildGridViews(views = []) {
...
@@ -1429,8 +1432,8 @@ export function buildGridViews(views = []) {
return
isViewAccessible
(
fallbackView
)
?
[
fallbackView
]
:
[];
return
isViewAccessible
(
fallbackView
)
?
[
fallbackView
]
:
[];
}
}
export
function
buildViewColumnsForList
(
view
,
subject
)
{
export
function
buildViewColumnsForList
(
view
,
subject
,
displayConfig
=
{}
)
{
return
buildViewColumns
(
view
,
subject
);
return
buildViewColumns
(
view
,
subject
,
{
displayConfig
}
);
}
}
export
function
getRecordCellText
(
record
,
column
)
{
export
function
getRecordCellText
(
record
,
column
)
{
...
@@ -1441,10 +1444,10 @@ export function getRecordCellText(record, column) {
...
@@ -1441,10 +1444,10 @@ export function getRecordCellText(record, column) {
return
formatDateText
(
value
)
||
'-'
;
return
formatDateText
(
value
)
||
'-'
;
}
}
return
format
FieldValue
(
value
,
column
.
fieldDef
)
||
'-'
;
return
format
TableFieldValue
(
value
,
column
)
||
'-'
;
}
}
export
function
buildProblemRectificationConfiguredListView
(
records
,
filters
,
gridView
,
subject
)
{
export
function
buildProblemRectificationConfiguredListView
(
records
,
filters
,
gridView
,
subject
,
displayConfig
=
{}
)
{
if
(
!
gridView
)
{
if
(
!
gridView
)
{
return
{
return
{
filteredRecords
:
[],
filteredRecords
:
[],
...
@@ -1460,7 +1463,7 @@ export function buildProblemRectificationConfiguredListView(records, filters, gr
...
@@ -1460,7 +1463,7 @@ export function buildProblemRectificationConfiguredListView(records, filters, gr
};
};
}
}
const
columns
=
buildViewColumnsForList
(
gridView
,
subject
);
const
columns
=
buildViewColumnsForList
(
gridView
,
subject
,
displayConfig
);
const
filteredRecords
=
records
.
filter
((
record
)
=>
{
const
filteredRecords
=
records
.
filter
((
record
)
=>
{
const
rawRecord
=
record
.
raw
||
record
;
const
rawRecord
=
record
.
raw
||
record
;
...
...
src/services/record-api.md
View file @
3c5cc2ab
...
@@ -44,6 +44,14 @@
...
@@ -44,6 +44,14 @@
-
`404`
`{"code":404,"message":"record '<name>' not found"}`
-
`404`
`{"code":404,"message":"record '<name>' not found"}`
-
`400`
业务校验失败(缺少必填字段、主键冲突、类型不匹配等)
-
`400`
业务校验失败(缺少必填字段、主键冲突、类型不匹配等)
### 1.2 表格显示配置的关联读取接口
问题清单与归档批次通过已有
`DataService.loadApplication(APPLICATION_NAME)`
调用
`GET /application/:name`
,读取应用的
`layouts.default.subjects.<subjectName>.gridRecordConfig.displayConfig`
,路径与 streams-admin 的
`useAppLayout().getSubjectConfig()`
/
`RecordGridView`
一致。
视图继续使用
`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/problem_rectification/problem-rectification.business-flow.md`
的“5.10 表格字段显示配置”。
## 2. Record 核心模型字段
## 2. Record 核心模型字段
不同
`subject`
通过
`metadata`
扩展业务字段。
不同
`subject`
通过
`metadata`
扩展业务字段。
...
...
src/services/tableFieldDisplayService.js
0 → 100644
View file @
3c5cc2ab
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 @
3c5cc2ab
...
@@ -290,9 +290,10 @@ export function shouldHideViewTitleColumn(view) {
...
@@ -290,9 +290,10 @@ export function shouldHideViewTitleColumn(view) {
return
view
?.
settings
?.
shortTitle
===
true
;
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
configuredColumns
=
view
?.
settings
?.
columns
||
[];
const
hideTitleColumn
=
!
includeHidden
&&
shouldHideViewTitleColumn
(
view
);
const
hideTitleColumn
=
!
includeHidden
&&
shouldHideViewTitleColumn
(
view
);
const
viewDisplayConfig
=
{
...
displayConfig
,
...
view
?.
settings
?.
displayConfig
};
if
(
configuredColumns
.
length
>
0
)
{
if
(
configuredColumns
.
length
>
0
)
{
return
configuredColumns
return
configuredColumns
...
@@ -305,6 +306,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
...
@@ -305,6 +306,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
const
fieldDef
=
getFieldDefinition
(
column
.
field
,
subject
);
const
fieldDef
=
getFieldDefinition
(
column
.
field
,
subject
);
return
{
return
{
...
column
,
...
column
,
displayConfig
:
{
...
viewDisplayConfig
,
...
column
.
displayConfig
},
width
:
column
.
width
||
180
,
width
:
column
.
width
||
180
,
label
:
getConfiguredColumnLabel
(
column
,
fieldDef
),
label
:
getConfiguredColumnLabel
(
column
,
fieldDef
),
fieldDef
:
fieldDef
||
{
name
:
column
.
field
,
label
:
column
.
field
,
type
:
'text'
},
fieldDef
:
fieldDef
||
{
name
:
column
.
field
,
label
:
column
.
field
,
type
:
'text'
},
...
@@ -316,6 +318,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
...
@@ -316,6 +318,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
if
(
!
hideTitleColumn
)
{
if
(
!
hideTitleColumn
)
{
defaultColumns
.
push
({
defaultColumns
.
push
({
field
:
'title'
,
field
:
'title'
,
displayConfig
:
viewDisplayConfig
,
width
:
180
,
width
:
180
,
label
:
'标题'
,
label
:
'标题'
,
fieldDef
:
{
name
:
'title'
,
label
:
'标题'
,
type
:
'text'
},
fieldDef
:
{
name
:
'title'
,
label
:
'标题'
,
type
:
'text'
},
...
@@ -325,6 +328,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
...
@@ -325,6 +328,7 @@ export function buildViewColumns(view, subject, { includeHidden = false } = {})
toArray
(
subject
?.
fields
).
forEach
((
field
)
=>
{
toArray
(
subject
?.
fields
).
forEach
((
field
)
=>
{
defaultColumns
.
push
({
defaultColumns
.
push
({
field
:
`metadata.
${
field
.
name
}
`
,
field
:
`metadata.
${
field
.
name
}
`
,
displayConfig
:
viewDisplayConfig
,
width
:
180
,
width
:
180
,
label
:
field
.
label
||
field
.
name
,
label
:
field
.
label
||
field
.
name
,
fieldDef
:
field
,
fieldDef
:
field
,
...
...
src/utils/tableFieldDisplay.js
0 → 100644
View file @
3c5cc2ab
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 @
3c5cc2ab
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
:
{
problem_rectification
:
{
gridRecordConfig
:
{
displayConfig
:
{
maxTagCount
:
2
}
},
}
}
}
}
};
},
});
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'problem_rectification'
),
{
displayConfig
:
{
maxTagCount
:
2
},
displayConfigError
:
''
,
});
assert
.
deepEqual
(
requests
,
[
'problem_rectification_new'
]);
assert
.
deepEqual
(
await
service
.
loadApplicationTableDisplayConfig
(
'issue_rectification_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
(
'problem_rectification'
);
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