Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
M
model_analysis
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
model_analysis
Commits
07133902
Commit
07133902
authored
Oct 08, 2026
by
drigle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: improve save feedback and toast dismissal
parent
ec8c19b4
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
58 additions
and
27 deletions
+58
-27
xinyuan-design-system.md
docs/xinyuan-design-system.md
+1
-0
manifest.json
public/manifest.json
+1
-1
AppFeedback.jsx
src/components/AppFeedback.jsx
+13
-3
ForwardApprovals.jsx
src/pages/project_manager_xy/ForwardApprovals.jsx
+11
-2
Models.jsx
src/pages/project_manager_xy/Models.jsx
+22
-6
model-monitor.business-flow.md
src/pages/project_manager_xy/model-monitor.business-flow.md
+5
-2
notifications.business-flow.md
...t_manager_xy/notifications/notifications.business-flow.md
+1
-1
tailwind.css
src/tailwind.css
+4
-12
No files found.
docs/xinyuan-design-system.md
View file @
07133902
...
@@ -42,6 +42,7 @@ Card、Surface、字段和浮层仍按 HeroUI 原有 Token 渲染。渐变仅限
...
@@ -42,6 +42,7 @@ Card、Surface、字段和浮层仍按 HeroUI 原有 Token 渲染。渐变仅限
## 业务组件
## 业务组件
-
接口反馈统一使用根部
`AppFeedback`
的 HeroUI Toast,保持视口顶部和层级 300,高于 Modal 与移动侧栏。关闭按钮同步问题整改
`bfc5862`
:直接组合 HeroUI
`Button`
(
`ghost`
、
`sm`
、
`isIconOnly`
)及
`AppIcons.Close`
,使用官方
`slot="close"`
接入关闭行为,位于正文右侧并随内容参与布局。前排按钮始终可见,后排按钮隐藏;移除原 Toast.CloseButton 的放大边框、绝对位置和正文右侧占位,Hover/焦点使用官方默认实现。成功提示默认 4 秒、错误/警告默认 8 秒,关闭只影响展示。
-
筛选:HeroUI Surface、SearchField、Autocomplete、DatePicker、Disclosure。
-
筛选:HeroUI Surface、SearchField、Autocomplete、DatePicker、Disclosure。
-
列表:HeroUI Pro DataGrid,原生选择/排序/固定列/横向滚动;空态使用
`renderEmptyState`
。
-
列表:HeroUI Pro DataGrid,原生选择/排序/固定列/横向滚动;空态使用
`renderEmptyState`
。
-
分页:HeroUI compound Pagination 与 HeroUI Autocomplete 页容量选择器组合;业务列表默认 20 条/页,支持 20/50/100 条切换,最多五个连续页码,有数据才显示。
-
分页:HeroUI compound Pagination 与 HeroUI Autocomplete 页容量选择器组合;业务列表默认 20 条/页,支持 20/50/100 条切换,最多五个连续页码,有数据才显示。
...
...
public/manifest.json
View file @
07133902
{
{
"name"
:
"model_analysis"
,
"name"
:
"model_analysis"
,
"version"
:
"1.0.
2
"
,
"version"
:
"1.0.
5
"
,
"build"
:
"abc"
,
"build"
:
"abc"
,
"description"
:
"模型分析"
,
"description"
:
"模型分析"
,
"date"
:
"2025-07-14 19:27:20"
,
"date"
:
"2025-07-14 19:27:20"
,
...
...
src/components/AppFeedback.jsx
View file @
07133902
import
{
Toast
}
from
'@heroui/react'
;
import
{
Button
,
Toast
}
from
'@heroui/react'
;
import
{
Close
}
from
'@/components/AppIcons'
;
export
default
function
AppFeedback
({
children
})
{
export
default
function
AppFeedback
({
children
})
{
return
(
return
(
...
@@ -12,7 +13,7 @@ export default function AppFeedback({ children }) {
...
@@ -12,7 +13,7 @@ export default function AppFeedback({ children }) {
width=
"min(30rem, calc(100vw - 2rem))"
width=
"min(30rem, calc(100vw - 2rem))"
>
>
{
({
toast
})
=>
(
{
({
toast
})
=>
(
<
Toast
className=
"app-feedback-toast"
toast=
{
toast
}
variant=
{
toast
.
content
.
variant
}
>
<
Toast
className=
"app-feedback-toast
gap-2
"
toast=
{
toast
}
variant=
{
toast
.
content
.
variant
}
>
<
Toast
.
Indicator
variant=
{
toast
.
content
.
variant
}
/>
<
Toast
.
Indicator
variant=
{
toast
.
content
.
variant
}
/>
<
Toast
.
Content
className=
"min-w-0"
>
<
Toast
.
Content
className=
"min-w-0"
>
<
Toast
.
Title
>
{
toast
.
content
.
title
}
</
Toast
.
Title
>
<
Toast
.
Title
>
{
toast
.
content
.
title
}
</
Toast
.
Title
>
...
@@ -23,7 +24,16 @@ export default function AppFeedback({ children }) {
...
@@ -23,7 +24,16 @@ export default function AppFeedback({ children }) {
)
:
null
}
)
:
null
}
{
toast
.
content
.
actionProps
?
<
Toast
.
ActionButton
{
...
toast
.
content
.
actionProps
}
/>
:
null
}
{
toast
.
content
.
actionProps
?
<
Toast
.
ActionButton
{
...
toast
.
content
.
actionProps
}
/>
:
null
}
</
Toast
.
Content
>
</
Toast
.
Content
>
<
Toast
.
CloseButton
aria
-
label=
"关闭提示"
className=
"app-feedback-close"
/>
<
Button
aria
-
label=
"关闭提示"
className=
"app-feedback-close shrink-0 self-start"
isIconOnly
size=
"sm"
slot=
"close"
variant=
"ghost"
>
<
Close
size=
{
16
}
/>
</
Button
>
</
Toast
>
</
Toast
>
)
}
)
}
</
Toast
.
Provider
>
</
Toast
.
Provider
>
...
...
src/pages/project_manager_xy/ForwardApprovals.jsx
View file @
07133902
...
@@ -101,6 +101,7 @@ export default function ForwardApprovals() {
...
@@ -101,6 +101,7 @@ export default function ForwardApprovals() {
const
[
editSaving
,
setEditSaving
]
=
useState
(
false
);
const
[
editSaving
,
setEditSaving
]
=
useState
(
false
);
const
[,
setError
]
=
useFeedbackState
();
const
[,
setError
]
=
useFeedbackState
();
const
[,
setNotice
]
=
useFeedbackState
(
'success'
);
const
[,
setNotice
]
=
useFeedbackState
(
'success'
);
const
[,
setSaveSuccess
]
=
useFeedbackState
(
'success'
);
const
[,
setNotificationWarning
]
=
useFeedbackState
(
'warning'
);
const
[,
setNotificationWarning
]
=
useFeedbackState
(
'warning'
);
const
[
activeRecordId
,
setActiveRecordId
]
=
useState
(
''
);
const
[
activeRecordId
,
setActiveRecordId
]
=
useState
(
''
);
const
[
isDetailDialogOpen
,
setIsDetailDialogOpen
]
=
useState
(
false
);
const
[
isDetailDialogOpen
,
setIsDetailDialogOpen
]
=
useState
(
false
);
...
@@ -259,6 +260,7 @@ export default function ForwardApprovals() {
...
@@ -259,6 +260,7 @@ export default function ForwardApprovals() {
setIsEditDialogOpen
(
true
);
setIsEditDialogOpen
(
true
);
setEditLoading
(
true
);
setEditLoading
(
true
);
setError
(
''
);
setError
(
''
);
setSaveSuccess
(
''
);
try
{
try
{
const
result
=
await
loadForwardEditableRecords
(
record
);
const
result
=
await
loadForwardEditableRecords
(
record
);
...
@@ -277,6 +279,7 @@ export default function ForwardApprovals() {
...
@@ -277,6 +279,7 @@ export default function ForwardApprovals() {
}
}
function
handleChangeEditableRecord
(
recordId
,
fieldAlias
,
nextValue
)
{
function
handleChangeEditableRecord
(
recordId
,
fieldAlias
,
nextValue
)
{
setSaveSuccess
(
''
);
const
aliasMap
=
{
const
aliasMap
=
{
truth_status
:
'truthStatus'
,
truth_status
:
'truthStatus'
,
rectification_plan
:
'rectificationPlan'
,
rectification_plan
:
'rectificationPlan'
,
...
@@ -298,12 +301,15 @@ export default function ForwardApprovals() {
...
@@ -298,12 +301,15 @@ export default function ForwardApprovals() {
}
}
async
function
handleSaveEditableRecords
()
{
async
function
handleSaveEditableRecords
()
{
if
(
editLoading
||
editSaving
||
editableRecords
.
length
===
0
)
return
;
if
(
!
canRecipientEdit
(
editingRecord
,
currentUserId
))
{
if
(
!
canRecipientEdit
(
editingRecord
,
currentUserId
))
{
setError
(
'当前用户不是可填写接收人,或该转发记录已完成填写。'
);
setError
(
'当前用户不是可填写接收人,或该转发记录已完成填写。'
);
return
;
return
;
}
}
setEditSaving
(
true
);
setEditSaving
(
true
);
setError
(
''
);
setSaveSuccess
(
''
);
try
{
try
{
const
saved
=
await
saveForwardEditableRecords
(
const
saved
=
await
saveForwardEditableRecords
(
editingRecord
,
editingRecord
,
...
@@ -311,7 +317,7 @@ export default function ForwardApprovals() {
...
@@ -311,7 +317,7 @@ export default function ForwardApprovals() {
userInfo
,
userInfo
,
);
);
setEditableRecords
(
saved
);
setEditableRecords
(
saved
);
set
Notice
(
'填写数据已保存。'
);
set
SaveSuccess
(
'填写数据已保存。'
);
setError
(
''
);
setError
(
''
);
}
catch
(
saveError
)
{
}
catch
(
saveError
)
{
setError
(
saveError
?.
message
||
'填写数据保存失败'
);
setError
(
saveError
?.
message
||
'填写数据保存失败'
);
...
@@ -321,6 +327,8 @@ export default function ForwardApprovals() {
...
@@ -321,6 +327,8 @@ export default function ForwardApprovals() {
}
}
async
function
handleCompleteEditing
()
{
async
function
handleCompleteEditing
()
{
if
(
editLoading
||
editSaving
)
return
;
setSaveSuccess
(
''
);
if
(
!
canRecipientEdit
(
editingRecord
,
currentUserId
))
{
if
(
!
canRecipientEdit
(
editingRecord
,
currentUserId
))
{
setError
(
'当前用户不是可填写接收人,或该转发记录已完成填写。'
);
setError
(
'当前用户不是可填写接收人,或该转发记录已完成填写。'
);
return
;
return
;
...
@@ -614,12 +622,13 @@ export default function ForwardApprovals() {
...
@@ -614,12 +622,13 @@ export default function ForwardApprovals() {
</
XinyuanModal
>
</
XinyuanModal
>
<
XinyuanModal
<
XinyuanModal
footer=
{
<><
Button
variant=
"secondary"
onPress=
{
()
=>
{
setIsEditDialogOpen
(
false
);
setEditingRecord
(
null
);
}
}
>
关闭
</
Button
><
Button
isDisabled=
{
editLoading
||
editSaving
||
editableRecords
.
length
===
0
||
!
canEditingRecordEdit
}
isPending=
{
editSaving
}
variant=
"secondary"
onPress=
{
handleSaveEditableRecords
}
>
保存修改
</
Button
><
Button
isDisabled=
{
editLoading
||
editSaving
||
editableRecords
.
length
===
0
||
!
canEditingRecordEdit
}
isPending=
{
editSaving
}
variant=
"primary"
onPress=
{
handleCompleteEditing
}
>
完成填写
</
Button
></>
}
footer=
{
<><
Button
isDisabled=
{
editSaving
}
variant=
"secondary"
onPress=
{
()
=>
{
setIsEditDialogOpen
(
false
);
setEditingRecord
(
null
);
}
}
>
关闭
</
Button
><
Button
isDisabled=
{
editLoading
||
editSaving
||
editableRecords
.
length
===
0
||
!
canEditingRecordEdit
}
isPending=
{
editSaving
}
variant=
"secondary"
onPress=
{
handleSaveEditableRecords
}
>
保存修改
</
Button
><
Button
isDisabled=
{
editLoading
||
editSaving
||
editableRecords
.
length
===
0
||
!
canEditingRecordEdit
}
isPending=
{
editSaving
}
variant=
"primary"
onPress=
{
handleCompleteEditing
}
>
完成填写
</
Button
></>
}
isDismissable=
{
!
editSaving
}
isDismissable=
{
!
editSaving
}
isOpen=
{
isEditDialogOpen
}
isOpen=
{
isEditDialogOpen
}
size=
"4xl"
size=
"4xl"
title=
"填写下发数据"
title=
"填写下发数据"
onOpenChange=
{
(
nextOpen
)
=>
{
onOpenChange=
{
(
nextOpen
)
=>
{
if
(
editSaving
)
return
;
setIsEditDialogOpen
(
nextOpen
);
setIsEditDialogOpen
(
nextOpen
);
if
(
!
nextOpen
)
setEditingRecord
(
null
);
if
(
!
nextOpen
)
setEditingRecord
(
null
);
}
}
}
}
...
...
src/pages/project_manager_xy/Models.jsx
View file @
07133902
...
@@ -149,12 +149,15 @@ export default function Models() {
...
@@ -149,12 +149,15 @@ export default function Models() {
const
[
reloadKey
,
setReloadKey
]
=
useState
(
0
);
const
[
reloadKey
,
setReloadKey
]
=
useState
(
0
);
const
[,
setError
]
=
useFeedbackState
();
const
[,
setError
]
=
useFeedbackState
();
const
[,
setNotice
]
=
useFeedbackState
(
'success'
);
const
[,
setNotice
]
=
useFeedbackState
(
'success'
);
const
[,
setModelSaveSuccess
]
=
useFeedbackState
(
'success'
);
const
[,
setModelSaveWarning
]
=
useFeedbackState
(
'warning'
);
const
[
isGroupDialogOpen
,
setIsGroupDialogOpen
]
=
useState
(
false
);
const
[
isGroupDialogOpen
,
setIsGroupDialogOpen
]
=
useState
(
false
);
const
[
isDeleteGroupDialogOpen
,
setIsDeleteGroupDialogOpen
]
=
useState
(
false
);
const
[
isDeleteGroupDialogOpen
,
setIsDeleteGroupDialogOpen
]
=
useState
(
false
);
const
[
deleteTargetGroup
,
setDeleteTargetGroup
]
=
useState
(
null
);
const
[
deleteTargetGroup
,
setDeleteTargetGroup
]
=
useState
(
null
);
const
[
groupForm
,
setGroupForm
]
=
useState
({
recordName
:
''
,
groupName
:
''
,
parentGroupId
:
''
});
const
[
groupForm
,
setGroupForm
]
=
useState
({
recordName
:
''
,
groupName
:
''
,
parentGroupId
:
''
});
const
[
isModelDialogOpen
,
setIsModelDialogOpen
]
=
useState
(
false
);
const
[
isModelDialogOpen
,
setIsModelDialogOpen
]
=
useState
(
false
);
const
[
modelSaving
,
setModelSaving
]
=
useState
(
false
);
const
[
editingModel
,
setEditingModel
]
=
useState
(
null
);
const
[
editingModel
,
setEditingModel
]
=
useState
(
null
);
const
[
modelForm
,
setModelForm
]
=
useState
({
description
:
''
,
groupId
:
''
,
tags
:
''
});
const
[
modelForm
,
setModelForm
]
=
useState
({
description
:
''
,
groupId
:
''
,
tags
:
''
});
...
@@ -296,14 +299,21 @@ export default function Models() {
...
@@ -296,14 +299,21 @@ export default function Models() {
}
}
function
openModelDialog
(
model
)
{
function
openModelDialog
(
model
)
{
setError
(
''
);
setModelSaveSuccess
(
''
);
setModelSaveWarning
(
''
);
setEditingModel
(
model
);
setEditingModel
(
model
);
setModelForm
({
description
:
model
.
description
,
groupId
:
model
.
groupId
,
tags
:
model
.
tags
.
join
(
', '
)
});
setModelForm
({
description
:
model
.
description
,
groupId
:
model
.
groupId
,
tags
:
model
.
tags
.
join
(
', '
)
});
setIsModelDialogOpen
(
true
);
setIsModelDialogOpen
(
true
);
}
}
async
function
handleSaveModel
()
{
async
function
handleSaveModel
()
{
if
(
!
editingModel
)
return
;
if
(
!
editingModel
||
modelSaving
)
return
;
const
group
=
groups
.
find
((
item
)
=>
item
.
id
===
modelForm
.
groupId
);
const
group
=
groups
.
find
((
item
)
=>
item
.
id
===
modelForm
.
groupId
);
setModelSaving
(
true
);
setError
(
''
);
setModelSaveSuccess
(
''
);
setModelSaveWarning
(
''
);
try
{
try
{
await
saveModelDetails
(
editingModel
,
{
await
saveModelDetails
(
editingModel
,
{
description
:
modelForm
.
description
,
description
:
modelForm
.
description
,
...
@@ -311,12 +321,17 @@ export default function Models() {
...
@@ -311,12 +321,17 @@ export default function Models() {
groupName
:
group
?.
groupName
||
''
,
groupName
:
group
?.
groupName
||
''
,
tags
:
modelForm
.
tags
.
split
(
/
[
,,
]
/
).
map
((
tag
)
=>
tag
.
trim
()).
filter
(
Boolean
),
tags
:
modelForm
.
tags
.
split
(
/
[
,,
]
/
).
map
((
tag
)
=>
tag
.
trim
()).
filter
(
Boolean
),
});
});
await
refresh
();
setIsModelDialogOpen
(
false
);
setIsModelDialogOpen
(
false
);
setNotice
(
'模型归档信息已保存。'
);
setModelSaveSuccess
(
'模型详情已保存。'
);
setError
(
''
);
try
{
await
refresh
();
}
catch
(
refreshError
)
{
setModelSaveWarning
(
`模型详情已保存,但列表刷新失败,请重新加载。
${
refreshError
?.
message
?
`
${
refreshError
.
message
}
`
:
''
}
`);
}
} catch (saveError) {
} catch (saveError) {
setError(saveError?.message || '模型信息保存失败。');
setError(saveError?.message || '模型信息保存失败。');
} finally {
setModelSaving(false);
}
}
}
}
...
@@ -577,11 +592,12 @@ export default function Models() {
...
@@ -577,11 +592,12 @@ export default function Models() {
</
AlertDialog
.
Root
>
</
AlertDialog
.
Root
>
<
XinyuanModal
<
XinyuanModal
footer=
{
<><
Button
variant=
"secondary"
onPress=
{
()
=>
setIsModelDialogOpen
(
false
)
}
>
取消
</
Button
><
Button
variant=
"primary"
onPress=
{
handleSaveModel
}
>
保存
</
Button
></>
}
footer=
{
<><
Button
isDisabled=
{
modelSaving
}
variant=
"secondary"
onPress=
{
()
=>
setIsModelDialogOpen
(
false
)
}
>
取消
</
Button
><
Button
isDisabled=
{
modelSaving
}
isPending=
{
modelSaving
}
variant=
"primary"
onPress=
{
handleSaveModel
}
>
保存
</
Button
></>
}
isDismissable=
{
!
modelSaving
}
isOpen=
{
isModelDialogOpen
}
isOpen=
{
isModelDialogOpen
}
size=
"2xl"
size=
"2xl"
title=
"编辑模型归档信息"
title=
"编辑模型归档信息"
onOpenChange=
{
setIsModelDialogOpen
}
onOpenChange=
{
(
nextOpen
)
=>
{
if
(
!
modelSaving
)
setIsModelDialogOpen
(
nextOpen
);
}
}
>
>
<
div
className=
"space-y-4"
>
<
div
className=
"space-y-4"
>
<
div
className=
"grid gap-4 sm:grid-cols-2"
><
TextField
fullWidth
isDisabled
value=
{
editingModel
?.
modelName
||
''
}
><
FieldLabel
>
模型名称
</
FieldLabel
><
Input
fullWidth
variant=
"secondary"
/></
TextField
><
TextField
fullWidth
isDisabled
value=
{
editingModel
?.
modelCode
||
''
}
><
FieldLabel
>
模型编码
</
FieldLabel
><
Input
fullWidth
variant=
"secondary"
/></
TextField
></
div
>
<
div
className=
"grid gap-4 sm:grid-cols-2"
><
TextField
fullWidth
isDisabled
value=
{
editingModel
?.
modelName
||
''
}
><
FieldLabel
>
模型名称
</
FieldLabel
><
Input
fullWidth
variant=
"secondary"
/></
TextField
><
TextField
fullWidth
isDisabled
value=
{
editingModel
?.
modelCode
||
''
}
><
FieldLabel
>
模型编码
</
FieldLabel
><
Input
fullWidth
variant=
"secondary"
/></
TextField
></
div
>
...
...
src/pages/project_manager_xy/model-monitor.business-flow.md
View file @
07133902
...
@@ -118,7 +118,7 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
...
@@ -118,7 +118,7 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
3.
含子分组或模型的分组不得删除。
3.
含子分组或模型的分组不得删除。
4.
模型只允许编辑说明、分组和标签;外部模型编码、名称、主键及数据 Subject 不可编辑。
4.
模型只允许编辑说明、分组和标签;外部模型编码、名称、主键及数据 Subject 不可编辑。
分组保存流程:从分组操作打开编辑弹窗,修改分组名称或父级后保存;名称不能为空,父级不能选择当前分组或其后代。创建时调用
`POST /record?preview=false`
,携带
`subject: model_monitor_group`
;编辑时调用
`PUT /record/:name`
,更新
`title`
、
`metadata.group_name`
和
`metadata.parent_group_id`
等可写字段,顶层不传只读
`subject`
。统一更新 service 同样处理模型、批次和审批请求,兼容已有内部或外部对象。
保存成功后重新加载分组和模型列表并关闭弹窗,显示成功提示;保存失败时保留表单并显示接口错误,不回退本地存储
。
分组保存流程:从分组操作打开编辑弹窗,修改分组名称或父级后保存;名称不能为空,父级不能选择当前分组或其后代。创建时调用
`POST /record?preview=false`
,携带
`subject: model_monitor_group`
;编辑时调用
`PUT /record/:name`
,更新
`title`
、
`metadata.group_name`
和
`metadata.parent_group_id`
等可写字段,顶层不传只读
`subject`
。统一更新 service 同样处理模型、批次和审批请求,兼容已有内部或外部对象。
分组保存成功后重新加载分组和模型列表并关闭弹窗,显示成功提示;保存失败时保留表单并显示接口错误,不回退本地存储。模型详情保存流程及列表刷新异常见“操作提示与关闭”
。
### 4.5 查看批次与数据
### 4.5 查看批次与数据
...
@@ -175,7 +175,10 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
...
@@ -175,7 +175,10 @@ MySQL 联调数据可通过 `scripts/mysql/model-monitor-source-fixtures.sql`
-
登录、站点配置、模型加载、分组/模型保存、同步、隐藏/恢复、转发、填写和审批失败时,统一显示 HeroUI Toast 浮层;用户查询失败同时在人员选择器内显示加载错误,保留当前表单和选中记录。
-
登录、站点配置、模型加载、分组/模型保存、同步、隐藏/恢复、转发、填写和审批失败时,统一显示 HeroUI Toast 浮层;用户查询失败同时在人员选择器内显示加载错误,保留当前表单和选中记录。
-
提示挂载在应用根部,通过 Portal 呈现在视口顶部,层级高于业务 Modal、删除确认和移动侧栏;弹窗打开时也能看到并关闭提示,提示出现不抢走表单焦点。
-
提示挂载在应用根部,通过 Portal 呈现在视口顶部,层级高于业务 Modal、删除确认和移动侧栏;弹窗打开时也能看到并关闭提示,提示出现不抢走表单焦点。
-
错误和通知处理异常提示默认 8 秒后消失,成功提示默认 4 秒后消失;悬停或键盘聚焦时暂停计时。关闭按钮始终可见,移动端也可直接点击,无需先悬停。
-
错误和通知处理异常提示默认 8 秒后消失,成功提示默认 4 秒后消失;悬停或键盘聚焦时暂停计时。沿用问题整改
`bfc5862`
,当前提示的关闭图标始终可见,位于正文右侧,使用 HeroUI
`Button`
(
`ghost`
、
`sm`
、
`isIconOnly`
)和
`AppIcons.Close`
,点击或键盘激活通过官方
`slot="close"`
关闭;后排堆叠提示隐藏关闭按钮,避免误关闭。按钮随正文布局,不使用绝对定位、放大边框或正文右侧占位。
-
“全部模型 → 编辑模型归档信息 → 保存”调用现有
`saveModelDetails`
(
`PUT /record/:name`
)更新
`model_monitor_model`
的说明、分组与标签等字段。写入成功后关闭编辑弹窗并立即显示“模型详情已保存”,再刷新模型/分组列表;列表刷新失败时另外显示“已保存,但列表刷新失败”的 warning,不把已完成写入误报为保存失败。写入失败保留弹窗与草稿,仅显示错误;保存中显示 pending,禁用重复提交、取消及弹窗关闭。
-
“转发审批 → 填写下发数据 → 保存修改”通过现有
`saveForwardEditableRecords`
创建或更新
`model_monitor_forward_item`
;只有全部回填写入成功后才显示“填写数据已保存”,弹窗保持打开并展示保存结果。任一写入失败保留填写草稿,仅显示错误。加载中、保存中、无记录或当前用户无填写权限时不提交、不显示成功提示;保存中禁用关闭与重复操作,不改变完成填写及审批状态。
-
模型详情重新打开或再次保存时清理上次保存成功/刷新警告;回填弹窗重新打开、继续修改字段、再次保存或完成填写时清理上次保存成功提示。成功提示由页面级状态管理,关闭弹窗不会立即清除刚产生的成功提示,仍按默认计时或手动关闭。
-
再次提交发生同样错误时重新弹出提示,并替换同一来源的上一条提示。清空错误或离开对应页面时清理该来源的提示;关闭提示只影响展示,不把失败状态改为成功、不清空表单、不触发保存或再次同步。
-
再次提交发生同样错误时重新弹出提示,并替换同一来源的上一条提示。清空错误或离开对应页面时清理该来源的提示;关闭提示只影响展示,不把失败状态改为成功、不清空表单、不触发保存或再次同步。
-
模型总览、模型列表、批次、批次数据和转发审批加载失败时显示可重新加载的空状态,详细原因由浮层提示;重新加载只读取页面数据,不重新同步批次。通知中心的重试方式见对应流程文档。
-
模型总览、模型列表、批次、批次数据和转发审批加载失败时显示可重新加载的空状态,详细原因由浮层提示;重新加载只读取页面数据,不重新同步批次。通知中心的重试方式见对应流程文档。
-
单批次同步返回部分写入失败时显示异常提示,不显示完整成功;批次继续保留错误摘要和不可再次同步限制。
-
单批次同步返回部分写入失败时显示异常提示,不显示完整成功;批次继续保留错误摘要和不可再次同步限制。
...
...
src/pages/project_manager_xy/notifications/notifications.business-flow.md
View file @
07133902
...
@@ -35,6 +35,6 @@
...
@@ -35,6 +35,6 @@
-
所有请求继续使用现有登录 Token、请求拦截器和退出处理,不新增通知专用鉴权。
-
所有请求继续使用现有登录 Token、请求拦截器和退出处理,不新增通知专用鉴权。
-
接口 401/403、网络失败或动态 action 配置缺失时不得伪造成功状态或未读数量。
-
接口 401/403、网络失败或动态 action 配置缺失时不得伪造成功状态或未读数量。
-
加载或操作错误使用应用统一 HeroUI Toast 浮层,覆盖在弹窗和侧栏上方;
关闭按钮始终可见,加载错误默认 8 秒自动消失,操作错误
默认 4 秒自动消失,悬停/聚焦暂停计时。列表加载失败时保留“重试”空状态,关闭提示不清除失败状态,点击重试后重新请求列表。
-
加载或操作错误使用应用统一 HeroUI Toast 浮层,覆盖在弹窗和侧栏上方;
当前提示右侧使用始终可见的小号透明关闭按钮,通过 Toast 的
`close`
slot 关闭,后排堆叠提示隐藏关闭按钮。错误/警告默认 8 秒、成功
默认 4 秒自动消失,悬停/聚焦暂停计时。列表加载失败时保留“重试”空状态,关闭提示不清除失败状态,点击重试后重新请求列表。
-
通知中心不读取或修改模型监控 Subject、Record、审批字段及用户
`applications`
授权结构。
-
通知中心不读取或修改模型监控 Subject、Record、审批字段及用户
`applications`
授权结构。
-
本应用不在五项完整功能白名单中,不实现
`shortTitle`
、Subject View 权限或审计材料中心文件工作流。
-
本应用不在五项完整功能白名单中,不实现
`shortTitle`
、Subject View 权限或审计材料中心文件工作流。
src/tailwind.css
View file @
07133902
...
@@ -37,18 +37,10 @@ html {
...
@@ -37,18 +37,10 @@ html {
top
:
max
(
1rem
,
env
(
safe-area-inset-top
));
top
:
max
(
1rem
,
env
(
safe-area-inset-top
));
}
}
.app-feedback-toast.toast
{
/* Only the front toast is interactive; its ghost close button stays visible without hover. */
gap
:
0.5rem
;
.app-feedback-toast
:not
([
data-frontmost
=
"true"
])
.app-feedback-close
{
padding-inline-end
:
3.5rem
;
visibility
:
hidden
;
}
pointer-events
:
none
;
.app-feedback-toast
[
data-frontmost
=
"true"
]
.app-feedback-close
{
top
:
0.25rem
;
right
:
0.25rem
;
width
:
2.75rem
;
height
:
2.75rem
;
opacity
:
1
;
pointer-events
:
auto
;
}
}
body
{
body
{
...
...
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