Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
audit_project_manager
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
audit_project_manager
Commits
febb9274
Commit
febb9274
authored
Sep 01, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix reference selector selection state
parent
6c4d8a36
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
75 additions
and
47 deletions
+75
-47
ReferenceSearchSelect.jsx
src/components/ReferenceSearchSelect.jsx
+18
-10
XinyuanAutocomplete.jsx
src/components/XinyuanAutocomplete.jsx
+48
-34
project-management.business-flow.md
...es/project_manager_xy/project-management.business-flow.md
+3
-1
subjectView.js
src/utils/subjectView.js
+6
-2
No files found.
src/components/ReferenceSearchSelect.jsx
View file @
febb9274
...
@@ -15,6 +15,22 @@ function normalizeText(value) {
...
@@ -15,6 +15,22 @@ function normalizeText(value) {
return
String
(
value
||
''
).
trim
();
return
String
(
value
||
''
).
trim
();
}
}
function
normalizeRecordId
(
value
)
{
if
(
value
===
null
||
value
===
undefined
||
value
===
''
)
{
return
''
;
}
if
(
typeof
value
===
'object'
)
{
const
candidate
=
value
.
_id
??
value
.
id
??
value
.
$oid
??
value
.
name
;
if
(
candidate
===
value
)
{
return
''
;
}
return
normalizeRecordId
(
candidate
);
}
return
normalizeText
(
value
);
}
function
escapeMongoRegex
(
value
)
{
function
escapeMongoRegex
(
value
)
{
return
normalizeText
(
value
).
replace
(
/
[
.*+?^${}()|[
\]\\]
/g
,
'
\\
$&'
);
return
normalizeText
(
value
).
replace
(
/
[
.*+?^${}()|[
\]\\]
/g
,
'
\\
$&'
);
}
}
...
@@ -74,19 +90,11 @@ function resolveRecordId(record) {
...
@@ -74,19 +90,11 @@ function resolveRecordId(record) {
}
}
// Reference 写入协议:只接受 _id / id
// Reference 写入协议:只接受 _id / id
return
record
.
id
||
record
.
_id
;
return
normalizeRecordId
(
record
.
id
??
record
.
_id
)
;
}
}
function
resolveReferenceValueId
(
value
)
{
function
resolveReferenceValueId
(
value
)
{
if
(
!
value
)
{
return
normalizeRecordId
(
value
);
return
''
;
}
if
(
typeof
value
===
'object'
)
{
return
normalizeText
(
resolveRecordId
(
value
));
}
return
normalizeText
(
value
);
}
}
function
resolveRecordSubjectName
(
record
,
fallbackSubject
=
''
)
{
function
resolveRecordSubjectName
(
record
,
fallbackSubject
=
''
)
{
...
...
src/components/XinyuanAutocomplete.jsx
View file @
febb9274
...
@@ -20,10 +20,22 @@ function defaultFilter(textValue, inputValue) {
...
@@ -20,10 +20,22 @@ function defaultFilter(textValue, inputValue) {
function
getSelectedKeySet
(
selectionMode
,
selectedKey
,
selectedKeys
)
{
function
getSelectedKeySet
(
selectionMode
,
selectedKey
,
selectedKeys
)
{
if
(
selectionMode
===
'multiple'
)
{
if
(
selectionMode
===
'multiple'
)
{
return
new
Set
(
selectedKeys
||
[]);
if
(
selectedKeys
===
'all'
)
{
return
new
Set
();
}
return
new
Set
(
Array
.
from
(
selectedKeys
||
[])
.
map
(
toSelectionKey
)
.
filter
(
Boolean
),
);
}
}
return
new
Set
(
selectedKey
===
null
||
selectedKey
===
undefined
?
[]
:
[
selectedKey
]);
return
new
Set
(
selectedKey
===
null
||
selectedKey
===
undefined
?
[]
:
[
toSelectionKey
(
selectedKey
)].
filter
(
Boolean
),
);
}
}
function
toSelectionKey
(
value
)
{
function
toSelectionKey
(
value
)
{
...
@@ -79,42 +91,48 @@ export default function XinyuanAutocomplete({
...
@@ -79,42 +91,48 @@ export default function XinyuanAutocomplete({
(
selectedOptions
.
length
>
0
(
selectedOptions
.
length
>
0
?
selectedOptions
.
map
((
option
)
=>
option
.
label
).
join
(
'、'
)
?
selectedOptions
.
map
((
option
)
=>
option
.
label
).
join
(
'、'
)
:
placeholder
);
:
placeholder
);
const
isMultiple
=
selectionMode
===
'multiple'
;
const
normalizedSelectedKeys
=
selectedKeySet
;
const
rootSelectionProps
=
const
rootSelectionProps
=
selectionMode
===
'multiple'
isMultiple
?
{
selectedKeys
}
?
{
selectedKey
:
null
}
:
{
selectedKey
};
:
{
selectedKey
:
normalizedSelectedKeys
.
values
().
next
().
value
||
null
};
const
listBoxSelectionProps
=
const
handleMultipleOptionAction
=
(
optionKey
)
=>
{
selectionMode
===
'multiple'
if
(
!
onSelectionChange
)
{
?
{
selectionMode
,
selectedKeys
,
}
:
{};
const
visibleOptions
=
isLoading
||
errorText
?
[]
:
options
;
const
handleMultipleOptionPress
=
(
option
)
=>
{
if
(
selectionMode
!==
'multiple'
||
!
onSelectionChange
||
option
?.
isDisabled
)
{
return
;
return
;
}
}
const
optionKey
=
toSelectionKey
(
option
.
id
);
const
nextKeys
=
new
Set
(
normalizedSelectedKeys
);
if
(
!
optionKey
)
{
const
normalizedOptionKey
=
toSelectionKey
(
optionKey
);
if
(
!
normalizedOptionKey
)
{
return
;
return
;
}
}
const
nextKeys
=
new
Set
(
if
(
nextKeys
.
has
(
normalizedOptionKey
))
{
Array
.
from
(
selectedKeyStringSet
).
filter
(
Boolean
),
nextKeys
.
delete
(
normalizedOptionKey
);
);
if
(
nextKeys
.
has
(
optionKey
))
{
nextKeys
.
delete
(
optionKey
);
}
else
{
}
else
{
nextKeys
.
add
(
o
ptionKey
);
nextKeys
.
add
(
normalizedO
ptionKey
);
}
}
onSelectionChange
(
nextKeys
);
onSelectionChange
(
nextKeys
);
};
};
const
listBoxSelectionProps
=
isMultiple
?
{
// Autocomplete is single-selection under the hood. Use item actions
// for multi-select so the list cannot replace the controlled values.
selectionMode
:
'single'
,
onAction
:
handleMultipleOptionAction
,
}
:
{
selectionMode
:
'single'
,
selectedKeys
:
normalizedSelectedKeys
,
onSelectionChange
,
};
const
visibleOptions
=
isLoading
||
errorText
?
[]
:
options
.
map
((
option
)
=>
({
...
option
,
isSelected
:
selectedKeyStringSet
.
has
(
toSelectionKey
(
option
.
id
)),
}));
return
(
return
(
<
Autocomplete
<
Autocomplete
...
@@ -128,7 +146,7 @@ export default function XinyuanAutocomplete({
...
@@ -128,7 +146,7 @@ export default function XinyuanAutocomplete({
isRequired=
{
isRequired
}
isRequired=
{
isRequired
}
name=
{
name
}
name=
{
name
}
onClear=
{
onClear
}
onClear=
{
onClear
}
selectionMode=
{
selectionMode
}
selectionMode=
{
isMultiple
?
'single'
:
selectionMode
}
variant=
{
variant
}
variant=
{
variant
}
onOpenChange=
{
onOpenChange
}
onOpenChange=
{
onOpenChange
}
onSelectionChange=
{
onSelectionChange
}
onSelectionChange=
{
onSelectionChange
}
...
@@ -187,18 +205,14 @@ export default function XinyuanAutocomplete({
...
@@ -187,18 +205,14 @@ export default function XinyuanAutocomplete({
)
}
)
}
>
>
{
(
option
)
=>
{
{
(
option
)
=>
{
const
isSelected
=
selectedKeyStringSet
.
has
(
const
isSelected
=
Boolean
(
option
.
isSelected
);
toSelectionKey
(
option
.
id
),
);
return
(
return
(
<
ListBox
.
Item
<
ListBox
.
Item
id=
{
option
.
id
}
id=
{
option
.
id
}
isDisabled=
{
option
.
isDisabled
}
isDisabled=
{
option
.
isDisabled
}
isSelected=
{
isSelected
}
textValue=
{
option
.
textValue
||
option
.
label
}
textValue=
{
option
.
textValue
||
option
.
label
}
{
...
(
selectionMode
===
'
multiple
'
?
{
onPress
:
()
=
>
handleMultipleOptionPress(option) }
:
{}
)}
>
>
{
renderOption
?
(
{
renderOption
?
(
renderOption
(
option
)
renderOption
(
option
)
...
...
src/pages/project_manager_xy/project-management.business-flow.md
View file @
febb9274
...
@@ -42,7 +42,9 @@
...
@@ -42,7 +42,9 @@
Modal 可访问名称。标题、阶段 Tabs 和顶部业务操作固定在 Header,字段内容只在 Body 内纵向
Modal 可访问名称。标题、阶段 Tabs 和顶部业务操作固定在 Header,字段内容只在 Body 内纵向
滚动,滚动内容不得穿过或遮挡阶段 Tabs。查看态的
`reference`
、
`document`
、
`view`
、
滚动,滚动内容不得穿过或遮挡阶段 Tabs。查看态的
`reference`
、
`document`
、
`view`
、
`template`
、
`task`
、
`tag`
、
`group`
、
`category`
字段显示关联记录表,并在字段具备编辑权限时
`template`
、
`task`
、
`tag`
、
`group`
、
`category`
字段显示关联记录表,并在字段具备编辑权限时
提供“编辑关联记录”入口;保存时只向
`metadata`
写入目标记录
`_id`
(多值字段写入
`_id[]`
)。
提供“编辑关联记录”入口;引用选择器的输入摘要、列表勾选和当前草稿始终由同一组归一化记录
`_id`
驱动,多值选择支持逐项追加、取消和清空;保存时只向
`metadata`
写入目标记录
`_id`
(多值字段写入
`_id[]`
)。
引用目标缺少 Subject、加载失败或保存失败时在字段附近保留可读错误反馈,不丢失当前编辑值。
引用目标缺少 Subject、加载失败或保存失败时在字段附近保留可读错误反馈,不丢失当前编辑值。
8.
列表页不展示前端写死的项目总数、完成率等摘要卡片,仅展示由服务端 Subject/View 配置驱动的视图、筛选和表格内容;项目统计统一在驾驶舱查看。
8.
列表页不展示前端写死的项目总数、完成率等摘要卡片,仅展示由服务端 Subject/View 配置驱动的视图、筛选和表格内容;项目统计统一在驾驶舱查看。
9.
业务列表、动态表单数据表和关联记录表中的长文本最多展示两行,内容始终限制在所属列内;超出内容通过 HeroUI Tooltip 或项目详情查看,避免遮挡相邻列或由单条长文本过度拉高整行。弹窗内只读关联记录表按实际数据行高度收缩,不提供无业务价值的列宽拖拽。
9.
业务列表、动态表单数据表和关联记录表中的长文本最多展示两行,内容始终限制在所属列内;超出内容通过 HeroUI Tooltip 或项目详情查看,避免遮挡相邻列或由单条长文本过度拉高整行。弹窗内只读关联记录表按实际数据行高度收缩,不提供无业务价值的列宽拖拽。
...
...
src/utils/subjectView.js
View file @
febb9274
...
@@ -260,12 +260,16 @@ export function isDateFieldType(fieldType) {
...
@@ -260,12 +260,16 @@ export function isDateFieldType(fieldType) {
}
}
export
function
normalizeRecordId
(
value
)
{
export
function
normalizeRecordId
(
value
)
{
if
(
!
value
)
{
if
(
value
===
null
||
value
===
undefined
||
value
===
''
)
{
return
''
;
return
''
;
}
}
if
(
typeof
value
===
'object'
)
{
if
(
typeof
value
===
'object'
)
{
return
value
.
_id
||
value
.
id
||
value
.
name
||
''
;
const
candidate
=
value
.
_id
??
value
.
id
??
value
.
$oid
??
value
.
name
;
if
(
candidate
===
value
)
{
return
''
;
}
return
normalizeRecordId
(
candidate
);
}
}
return
normalizeText
(
value
);
return
normalizeText
(
value
);
...
...
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