Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
audit_manager_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
audit_manager_new
Commits
c4324c3a
Commit
c4324c3a
authored
Sep 29, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: show selected audit references
parent
c61a6514
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
79 additions
and
9 deletions
+79
-9
ControlPointDialog.jsx
src/pages/audit_management/ControlPointDialog.jsx
+16
-3
ReferenceSearchSelect.jsx
src/pages/audit_management/ReferenceSearchSelect.jsx
+47
-6
auditManagementService.js
src/pages/audit_management/auditManagementService.js
+16
-0
No files found.
src/pages/audit_management/ControlPointDialog.jsx
View file @
c4324c3a
...
@@ -6,6 +6,7 @@ import ReferenceSearchSelect from './ReferenceSearchSelect';
...
@@ -6,6 +6,7 @@ import ReferenceSearchSelect from './ReferenceSearchSelect';
import
{
ControlPointPreviewFields
}
from
'./ControlPointPreviewDialog'
;
import
{
ControlPointPreviewFields
}
from
'./ControlPointPreviewDialog'
;
import
{
import
{
AUDIT_SUBJECTS
,
AUDIT_SUBJECTS
,
getReferenceRecordsByIds
,
normalizeRecordId
,
normalizeRecordId
,
searchReferenceRecords
,
searchReferenceRecords
,
searchTags
,
searchTags
,
...
@@ -52,6 +53,18 @@ export default function UnifiedControlPointDialog({
...
@@ -52,6 +53,18 @@ export default function UnifiedControlPointDialog({
const
loadDepartmentOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
'department'
,
keyword
),
[]);
const
loadDepartmentOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
'department'
,
keyword
),
[]);
const
loadRegulationOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
'internal_regulation'
,
keyword
),
[]);
const
loadRegulationOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
'internal_regulation'
,
keyword
),
[]);
const
loadIndicatorOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
AUDIT_SUBJECTS
.
INDICATOR
,
keyword
),
[]);
const
loadIndicatorOptions
=
useCallback
((
keyword
)
=>
searchReferenceRecords
(
AUDIT_SUBJECTS
.
INDICATOR
,
keyword
),
[]);
const
resolveDepartmentIds
=
useCallback
(
(
ids
)
=>
getReferenceRecordsByIds
(
'department'
,
ids
),
[],
);
const
resolveRegulationIds
=
useCallback
(
(
ids
)
=>
getReferenceRecordsByIds
(
'internal_regulation'
,
ids
),
[],
);
const
resolveIndicatorIds
=
useCallback
(
(
ids
)
=>
getReferenceRecordsByIds
(
AUDIT_SUBJECTS
.
INDICATOR
,
ids
),
[],
);
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
!
open
)
return
;
if
(
!
open
)
return
;
...
@@ -130,13 +143,13 @@ export default function UnifiedControlPointDialog({
...
@@ -130,13 +143,13 @@ export default function UnifiedControlPointDialog({
<
Modal
.
Body
className=
"grid gap-5 sm:grid-cols-2"
>
<
Modal
.
Body
className=
"grid gap-5 sm:grid-cols-2"
>
<
TextField
className=
"rc-form-field sm:col-span-2"
isRequired
value=
{
form
.
name
}
onChange=
{
(
value
)
=>
change
(
'name'
,
value
)
}
><
Label
>
名称
</
Label
><
Input
placeholder=
"请输入控制点名称"
variant=
"secondary"
/></
TextField
>
<
TextField
className=
"rc-form-field sm:col-span-2"
isRequired
value=
{
form
.
name
}
onChange=
{
(
value
)
=>
change
(
'name'
,
value
)
}
><
Label
>
名称
</
Label
><
Input
placeholder=
"请输入控制点名称"
variant=
"secondary"
/></
TextField
>
<
TextField
className=
"rc-form-field sm:col-span-2"
value=
{
form
.
description
}
onChange=
{
(
value
)
=>
change
(
'description'
,
value
)
}
><
Label
>
描述信息
</
Label
><
TextArea
placeholder=
"请输入控制点描述"
rows=
{
4
}
variant=
"secondary"
/></
TextField
>
<
TextField
className=
"rc-form-field sm:col-span-2"
value=
{
form
.
description
}
onChange=
{
(
value
)
=>
change
(
'description'
,
value
)
}
><
Label
>
描述信息
</
Label
><
TextArea
placeholder=
"请输入控制点描述"
rows=
{
4
}
variant=
"secondary"
/></
TextField
>
<
div
className=
"rc-form-field"
><
Label
>
{
requiredLabel
(
'责任人'
,
true
)
}
</
Label
><
ReferenceSearchSelect
value=
{
form
.
owner
}
onChange=
{
(
value
)
=>
change
(
'owner'
,
value
)
}
loadOptions=
{
loadDepartmentOptions
}
placeholder=
"搜索责任部门"
/></
div
>
<
div
className=
"rc-form-field"
><
Label
>
{
requiredLabel
(
'责任人'
,
true
)
}
</
Label
><
ReferenceSearchSelect
value=
{
form
.
owner
}
onChange=
{
(
value
)
=>
change
(
'owner'
,
value
)
}
loadOptions=
{
loadDepartmentOptions
}
resolveMissingIds=
{
resolveDepartmentIds
}
placeholder=
"搜索责任部门"
/></
div
>
<
div
className=
"rc-form-field"
><
Label
>
相关标签
</
Label
><
ReferenceSearchSelect
multiple
value=
{
form
.
related_tags
}
onChange=
{
(
value
)
=>
change
(
'related_tags'
,
value
)
}
loadOptions=
{
searchTags
}
placeholder=
"搜索并添加标签"
/></
div
>
<
div
className=
"rc-form-field"
><
Label
>
相关标签
</
Label
><
ReferenceSearchSelect
multiple
value=
{
form
.
related_tags
}
onChange=
{
(
value
)
=>
change
(
'related_tags'
,
value
)
}
loadOptions=
{
searchTags
}
placeholder=
"搜索并添加标签"
/></
div
>
<
div
className=
"rc-form-field sm:col-span-2"
><
Label
>
相关制度
</
Label
><
ReferenceSearchSelect
multiple
value=
{
form
.
related_regulations
}
onChange=
{
(
value
)
=>
change
(
'related_regulations'
,
value
)
}
loadOptions=
{
loadRegulationOptions
}
placeholder=
"搜索并添加制度"
/></
div
>
<
div
className=
"rc-form-field sm:col-span-2"
><
Label
>
相关制度
</
Label
><
ReferenceSearchSelect
multiple
value=
{
form
.
related_regulations
}
onChange=
{
(
value
)
=>
change
(
'related_regulations'
,
value
)
}
loadOptions=
{
loadRegulationOptions
}
resolveMissingIds=
{
resolveRegulationIds
}
placeholder=
"搜索并添加制度"
/></
div
>
<
div
className=
"rc-form-field"
><
Label
>
{
requiredLabel
(
'控制措施'
,
Boolean
(
typeField
?.
required
))
}
</
Label
><
FilterSelect
aria
-
label=
"控制措施"
options=
{
typeOptions
}
value=
{
form
.
type
}
variant=
"secondary"
onChange=
{
(
value
)
=>
change
(
'type'
,
value
)
}
/></
div
>
<
div
className=
"rc-form-field"
><
Label
>
{
requiredLabel
(
'控制措施'
,
Boolean
(
typeField
?.
required
))
}
</
Label
><
FilterSelect
aria
-
label=
"控制措施"
options=
{
typeOptions
}
value=
{
form
.
type
}
variant=
"secondary"
onChange=
{
(
value
)
=>
change
(
'type'
,
value
)
}
/></
div
>
{
form
.
type
===
'form'
?
<
TextField
className=
"rc-form-field"
value=
{
form
.
form_label
}
onChange=
{
(
value
)
=>
change
(
'form_label'
,
value
)
}
><
Label
>
文本描述
</
Label
><
Input
placeholder=
"请输入文本描述"
variant=
"secondary"
/></
TextField
>
:
null
}
{
form
.
type
===
'form'
?
<
TextField
className=
"rc-form-field"
value=
{
form
.
form_label
}
onChange=
{
(
value
)
=>
change
(
'form_label'
,
value
)
}
><
Label
>
文本描述
</
Label
><
Input
placeholder=
"请输入文本描述"
variant=
"secondary"
/></
TextField
>
:
null
}
{
form
.
type
===
'attachment'
?
<
div
className=
"rc-form-field sm:col-span-2"
><
Label
>
文件描述
</
Label
><
div
className=
"space-y-2"
>
{
form
.
attachment_label
.
map
((
label
,
index
)
=>
<
div
key=
{
index
}
className=
"flex items-center gap-2"
><
TextField
aria
-
label=
{
`文件描述 ${index + 1}`
}
className=
"min-w-0 flex-1"
value=
{
label
}
onChange=
{
(
value
)
=>
{
const
labels
=
[...
form
.
attachment_label
];
labels
[
index
]
=
value
;
change
(
'attachment_label'
,
labels
);
}
}
><
Input
placeholder=
"请输入文件类别"
variant=
"secondary"
/></
TextField
><
Tooltip
delay=
{
0
}
><
Button
isIconOnly
type=
"button"
aria
-
label=
"删除文件描述"
size=
"sm"
variant=
"ghost"
onPress=
{
()
=>
change
(
'attachment_label'
,
form
.
attachment_label
.
filter
((
_
,
itemIndex
)
=>
itemIndex
!==
index
))
}
><
Delete
size=
{
16
}
/></
Button
><
Tooltip
.
Content
>
删除文件描述
</
Tooltip
.
Content
></
Tooltip
></
div
>)
}
<
Button
variant=
"secondary"
type=
"button"
onPress=
{
()
=>
change
(
'attachment_label'
,
[...
form
.
attachment_label
,
''
])
}
>
添加文件描述
</
Button
></
div
></
div
>
:
null
}
{
form
.
type
===
'attachment'
?
<
div
className=
"rc-form-field sm:col-span-2"
><
Label
>
文件描述
</
Label
><
div
className=
"space-y-2"
>
{
form
.
attachment_label
.
map
((
label
,
index
)
=>
<
div
key=
{
index
}
className=
"flex items-center gap-2"
><
TextField
aria
-
label=
{
`文件描述 ${index + 1}`
}
className=
"min-w-0 flex-1"
value=
{
label
}
onChange=
{
(
value
)
=>
{
const
labels
=
[...
form
.
attachment_label
];
labels
[
index
]
=
value
;
change
(
'attachment_label'
,
labels
);
}
}
><
Input
placeholder=
"请输入文件类别"
variant=
"secondary"
/></
TextField
><
Tooltip
delay=
{
0
}
><
Button
isIconOnly
type=
"button"
aria
-
label=
"删除文件描述"
size=
"sm"
variant=
"ghost"
onPress=
{
()
=>
change
(
'attachment_label'
,
form
.
attachment_label
.
filter
((
_
,
itemIndex
)
=>
itemIndex
!==
index
))
}
><
Delete
size=
{
16
}
/></
Button
><
Tooltip
.
Content
>
删除文件描述
</
Tooltip
.
Content
></
Tooltip
></
div
>)
}
<
Button
variant=
"secondary"
type=
"button"
onPress=
{
()
=>
change
(
'attachment_label'
,
[...
form
.
attachment_label
,
''
])
}
>
添加文件描述
</
Button
></
div
></
div
>
:
null
}
{
form
.
type
===
'computed'
?
<><
div
className=
"rc-form-field"
><
Label
>
量化指标
</
Label
><
ReferenceSearchSelect
value=
{
form
.
indicator
}
onChange=
{
(
value
)
=>
change
(
'indicator'
,
value
)
}
loadOptions=
{
loadIndicatorOptions
}
placeholder=
"搜索量化指标"
/></
div
><
TextField
className=
"rc-form-field"
value=
{
String
(
form
.
computed_interval
)
}
onChange=
{
(
value
)
=>
change
(
'computed_interval'
,
value
)
}
><
Label
>
执行频率(天)
</
Label
><
Input
type=
"number"
min=
"1"
variant=
"secondary"
/></
TextField
></>
:
null
}
{
form
.
type
===
'computed'
?
<><
div
className=
"rc-form-field"
><
Label
>
量化指标
</
Label
><
ReferenceSearchSelect
value=
{
form
.
indicator
}
onChange=
{
(
value
)
=>
change
(
'indicator'
,
value
)
}
loadOptions=
{
loadIndicatorOptions
}
resolveMissingIds=
{
resolveIndicatorIds
}
placeholder=
"搜索量化指标"
/></
div
><
TextField
className=
"rc-form-field"
value=
{
String
(
form
.
computed_interval
)
}
onChange=
{
(
value
)
=>
change
(
'computed_interval'
,
value
)
}
><
Label
>
执行频率(天)
</
Label
><
Input
type=
"number"
min=
"1"
variant=
"secondary"
/></
TextField
></>
:
null
}
{
error
?
<
FieldError
className=
"sm:col-span-2"
>
{
error
}
</
FieldError
>
:
null
}
{
error
?
<
FieldError
className=
"sm:col-span-2"
>
{
error
}
</
FieldError
>
:
null
}
</
Modal
.
Body
>
</
Modal
.
Body
>
)
:
(
)
:
(
...
...
src/pages/audit_management/ReferenceSearchSelect.jsx
View file @
c4324c3a
...
@@ -15,17 +15,20 @@ export default function ReferenceSearchSelect({
...
@@ -15,17 +15,20 @@ export default function ReferenceSearchSelect({
value
,
value
,
onChange
,
onChange
,
loadOptions
,
loadOptions
,
resolveMissingIds
,
multiple
=
false
,
multiple
=
false
,
placeholder
=
'搜索并选择'
,
placeholder
=
'搜索并选择'
,
})
{
})
{
const
[
selectedRecordMap
,
setSelectedRecordMap
]
=
useState
(()
=>
new
Map
());
const
valueRecords
=
useMemo
(
const
valueRecords
=
useMemo
(
()
=>
(
Array
.
isArray
(
value
)
?
value
:
value
?
[
value
]
:
[])
()
=>
(
Array
.
isArray
(
value
)
?
value
:
value
?
[
value
]
:
[])
.
filter
((
item
)
=>
typeof
item
!==
'string'
),
.
filter
((
item
)
=>
typeof
item
!==
'string'
),
[
value
],
[
value
],
);
);
const
[
query
,
setQuery
]
=
useState
(
''
);
const
[
query
,
setQuery
]
=
useState
(
''
);
const
[
options
,
setOptions
]
=
useState
(
valueRecords
);
const
[
options
,
setOptions
]
=
useState
(
[]
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
(
''
);
const
selectedIds
=
useMemo
(
const
selectedIds
=
useMemo
(
()
=>
(
Array
.
isArray
(
value
)
?
value
:
value
?
[
value
]
:
[])
()
=>
(
Array
.
isArray
(
value
)
?
value
:
value
?
[
value
]
:
[])
.
map
(
normalizeRecordId
)
.
map
(
normalizeRecordId
)
...
@@ -33,20 +36,52 @@ export default function ReferenceSearchSelect({
...
@@ -33,20 +36,52 @@ export default function ReferenceSearchSelect({
[
value
],
[
value
],
);
);
const
mergeRecords
=
(
records
)
=>
{
setSelectedRecordMap
((
current
)
=>
{
const
nextMap
=
new
Map
(
current
);
records
.
filter
(
Boolean
).
forEach
((
record
)
=>
{
const
id
=
normalizeRecordId
(
record
);
if
(
id
)
nextMap
.
set
(
id
,
record
);
});
return
nextMap
;
});
};
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
valueRecords
.
length
>
0
)
{
if
(
valueRecords
.
length
>
0
)
{
setOptions
((
current
)
=>
uniqueRecords
([...
current
,
...
valueRecords
])
);
mergeRecords
(
valueRecords
);
}
}
},
[
valueRecords
]);
},
[
valueRecords
]);
const
missingSelectedIds
=
selectedIds
.
filter
(
(
id
)
=>
!
selectedRecordMap
.
get
(
id
),
);
useEffect
(()
=>
{
if
(
!
resolveMissingIds
||
missingSelectedIds
.
length
===
0
)
return
;
let
cancelled
=
false
;
resolveMissingIds
([...
missingSelectedIds
]).
then
((
records
)
=>
{
if
(
!
cancelled
)
mergeRecords
(
records
);
}).
catch
((
requestError
)
=>
{
if
(
!
cancelled
)
setError
(
requestError
?.
message
||
'加载已选引用记录失败'
);
});
return
()
=>
{
cancelled
=
true
;
};
},
[
missingSelectedIds
,
resolveMissingIds
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
let
cancelled
=
false
;
let
cancelled
=
false
;
const
timer
=
window
.
setTimeout
(
async
()
=>
{
const
timer
=
window
.
setTimeout
(
async
()
=>
{
setLoading
(
true
);
setLoading
(
true
);
setError
(
''
);
try
{
try
{
const
records
=
await
loadOptions
(
query
);
const
records
=
await
loadOptions
(
query
);
if
(
!
cancelled
)
{
if
(
!
cancelled
)
{
setOptions
((
current
)
=>
uniqueRecords
([...
current
,
...(
records
||
[])]));
mergeRecords
(
records
);
setOptions
(
records
||
[]);
}
}
}
finally
{
}
finally
{
if
(
!
cancelled
)
setLoading
(
false
);
if
(
!
cancelled
)
setLoading
(
false
);
...
@@ -59,7 +94,10 @@ export default function ReferenceSearchSelect({
...
@@ -59,7 +94,10 @@ export default function ReferenceSearchSelect({
};
};
},
[
loadOptions
,
query
]);
},
[
loadOptions
,
query
]);
const
normalizedOptions
=
options
.
map
((
record
)
=>
({
const
normalizedOptions
=
uniqueRecords
([
...
selectedIds
.
map
((
id
)
=>
selectedRecordMap
.
get
(
id
)).
filter
(
Boolean
),
...
options
,
]).
map
((
record
)
=>
({
id
:
normalizeRecordId
(
record
),
id
:
normalizeRecordId
(
record
),
label
:
normalizeRecordLabel
(
record
)
||
normalizeRecordId
(
record
),
label
:
normalizeRecordLabel
(
record
)
||
normalizeRecordId
(
record
),
record
,
record
,
...
@@ -74,7 +112,9 @@ export default function ReferenceSearchSelect({
...
@@ -74,7 +112,9 @@ export default function ReferenceSearchSelect({
:
`已选择
${
selectedIds
.
length
}
项`
;
:
`已选择
${
selectedIds
.
length
}
项`
;
return
(
return
(
<
XinyuanAutocomplete
<
div
className=
"space-y-1"
>
{
error
?
<
div
className=
"text-sm text-rose-600"
>
{
error
}
</
div
>
:
null
}
<
XinyuanAutocomplete
aria
-
label=
{
placeholder
}
aria
-
label=
{
placeholder
}
inputValue=
{
query
}
inputValue=
{
query
}
isLoading=
{
loading
}
isLoading=
{
loading
}
...
@@ -100,6 +140,7 @@ export default function ReferenceSearchSelect({
...
@@ -100,6 +140,7 @@ export default function ReferenceSearchSelect({
onChange
(
multiple
?
[]
:
''
);
onChange
(
multiple
?
[]
:
''
);
setQuery
(
''
);
setQuery
(
''
);
}
}
}
}
/>
/>
</
div
>
);
);
}
}
src/pages/audit_management/auditManagementService.js
View file @
c4324c3a
...
@@ -196,6 +196,22 @@ export async function searchReferenceRecords(subject, keyword = '') {
...
@@ -196,6 +196,22 @@ export async function searchReferenceRecords(subject, keyword = '') {
return
result
?.
records
||
[];
return
result
?.
records
||
[];
}
}
export
async
function
getReferenceRecordsByIds
(
subject
,
ids
=
[])
{
const
recordIds
=
ids
.
map
(
normalizeRecordId
).
filter
(
Boolean
);
if
(
recordIds
.
length
===
0
)
return
[];
const
result
=
await
queryRecords
({
subject
,
page
:
0
,
page_size
:
recordIds
.
length
,
filter
:
{
_id
:
{
$in
:
recordIds
},
},
mode
:
'basic'
,
});
return
result
?.
records
||
[];
}
export
async
function
searchTags
(
keyword
=
''
)
{
export
async
function
searchTags
(
keyword
=
''
)
{
const
result
=
await
queryTags
({
const
result
=
await
queryTags
({
page
:
0
,
page
:
0
,
...
...
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