Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporateCulture-qd
Overview
Overview
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王立鹏
corporateCulture-qd
Commits
dffcec96
Commit
dffcec96
authored
Mar 11, 2026
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 20331】 perf: 优化 确认弹窗组件等
parent
c883880d
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
134 additions
and
28 deletions
+134
-28
index.vue
src/components/common/MessageBox/index.vue
+92
-22
types.ts
src/components/common/MessageBox/types.ts
+4
-1
useMessageBox.tsx
src/hooks/useMessageBox.tsx
+38
-5
No files found.
src/components/common/MessageBox/index.vue
View file @
dffcec96
<
template
>
<Teleport
to=
"body"
>
<Transition
name=
"confirm"
>
<Transition
name=
"confirm"
@
after-leave=
"emit('afterLeave')"
>
<div
v-if=
"visible"
class=
"fixed inset-0 z-3000 flex items-center justify-center p-4"
>
<!-- Backdrop -->
<div
class=
"confirm-backdrop absolute inset-0 bg-black/20"
/>
...
...
@@ -11,7 +11,7 @@
:style=
"
{ width: normalizedWidth }"
>
<!-- 右上角关闭按钮 -->
<div
class=
"absolute top-2 right-2 cursor-pointer"
@
click=
"close"
>
<div
class=
"absolute top-2 right-2 cursor-pointer"
@
click
.
stop
=
"close"
>
<el-icon><Close
/></el-icon>
</div>
<div
class=
"px-6 py-5"
>
...
...
@@ -55,7 +55,7 @@
<!-- Message -->
<p
v-if=
"message"
class=
"mt-1.5 text-center text-13px text-gray-400 leading-5"
>
{{
message
}}
<component
:is=
"normalizedMessage"
/>
</p>
<!-- Actions -->
...
...
@@ -64,7 +64,7 @@
v-if=
"showCancelButton"
type=
"button"
class=
"confirm-btn cancel-btn"
@
click=
"onCancel"
@
click
.
stop
=
"onCancel"
>
{{
cancelText
}}
</button>
...
...
@@ -74,7 +74,7 @@
class=
"confirm-btn primary-btn"
:class=
"
{ 'danger-btn': type === 'danger', 'warning-btn': type === 'warning' }"
:disabled="loading"
@click="onConfirm"
@click
.stop
="onConfirm"
>
<svg
v-if=
"loading"
...
...
@@ -119,16 +119,46 @@ const {
type
=
'primary'
,
showCancelButton
=
true
,
showConfirmButton
=
true
,
validate
=
null
,
mousePosition
,
}
=
defineProps
<
MessageBoxProps
>
()
const
normalizedWidth
=
computed
(()
=>
(
typeof
width
===
'number'
?
`
${
width
}
px`
:
width
))
const
visible
=
defineModel
<
boolean
>
({
default
:
false
})
const
normalizedMousePosition
=
computed
(()
=>
{
return
{
x
:
mousePosition
.
x
+
'px'
,
y
:
mousePosition
.
y
+
'px'
,
}
})
onMounted
(()
=>
{
console
.
log
(
'onMounted'
)
})
onUpdated
(()
=>
{
console
.
log
(
'onUpdated'
)
})
onUnmounted
(()
=>
{
console
.
log
(
'onUnmounted'
)
})
const
normalizedMessage
=
computed
(()
=>
{
if
(
!
message
)
return
''
if
(
typeof
message
===
'function'
)
{
return
message
}
else
{
return
()
=>
message
}
})
const
emit
=
defineEmits
<
{
confirm
:
[]
confirm
:
[
res
:
string
|
number
|
undefined
]
cancel
:
[]
close
:
[]
afterLeave
:
[]
}
>
()
const
close
=
()
=>
{
...
...
@@ -141,9 +171,16 @@ const onCancel = () => {
close
()
}
const
onConfirm
=
()
=>
{
emit
(
'confirm'
)
if
(
!
loading
)
close
()
const
onConfirm
=
async
()
=>
{
if
(
validate
)
{
const
res
=
await
validate
?.()
emit
(
'confirm'
,
res
as
string
|
number
)
close
()
}
else
{
emit
(
'confirm'
,
undefined
)
if
(
!
loading
)
close
()
}
}
const
open
=
()
=>
{
...
...
@@ -154,32 +191,65 @@ defineExpose({ open, close })
</
script
>
<
style
scoped
>
.confirm-enter-active
{
transition
:
opacity
0.22s
ease
;
}
/* 背景透明度过渡:打开时 0→1,关闭时 1→0 */
.confirm-enter-active
,
.confirm-leave-active
{
transition
:
opacity
0.
16
s
ease
;
transition
:
opacity
0.
2
s
ease
;
}
.confirm-enter-from
,
.confirm-
leave-to
{
.confirm-
enter-from
{
opacity
:
0
;
}
.confirm-enter-to
,
.confirm-leave-
from
{
opacity
:
1
;
.confirm-leave-
to
{
opacity
:
0
;
}
/* card 动画 */
.confirm-enter-active
.confirm-card
{
transition
:
transform
0.3s
cubic-bezier
(
0.22
,
1.2
,
0.36
,
1
);
transition
:
transform
0.2s
cubic-bezier
(
0.22
,
1.2
,
0.36
,
1
),
opacity
0.2s
ease
;
}
.confirm-leave-active
.confirm-card
{
transition
:
transform
0.16s
ease
;
transition
:
transform
0.2s
ease
,
opacity
0.2s
ease
;
}
/* 从点击点展开(先用固定坐标 200px,200px 测试动画是否生效) */
.confirm-enter-from
.confirm-card
{
transform
:
scale
(
0.82
);
/* transform: translate(calc(200px - 50vw), calc(200px - 50vh)) scale(0.1); */
transform
:
translate
(
calc
(
v-bind
(
'normalizedMousePosition.x'
)
-
50vw
),
calc
(
v-bind
(
'normalizedMousePosition.y'
)
-
50vh
)
)
scale
(
0.1
);
opacity
:
0
;
}
.confirm-enter-to
.confirm-card
{
transform
:
translate
(
0
,
0
)
scale
(
1
);
opacity
:
1
;
}
/* 关闭时缩回点击点 */
.confirm-leave-from
.confirm-card
{
transform
:
translate
(
0
,
0
)
scale
(
1
);
opacity
:
1
;
}
.confirm-leave-to
.confirm-card
{
transform
:
scale
(
0.96
);
transform
:
translate
(
calc
(
v-bind
(
'normalizedMousePosition.x'
)
-
50vw
),
calc
(
v-bind
(
'normalizedMousePosition.y'
)
-
50vh
)
)
scale
(
0.1
);
opacity
:
0
;
}
.confirm-card
{
...
...
src/components/common/MessageBox/types.ts
View file @
dffcec96
import
type
{
VNode
,
Component
}
from
'vue'
export
interface
MessageBoxProps
{
title
?:
string
message
?:
string
message
?:
string
|
(()
=>
VNode
)
|
Component
confirmText
?:
string
cancelText
?:
string
width
?:
string
|
number
...
...
@@ -8,4 +9,6 @@ export interface MessageBoxProps {
type
?:
'primary'
|
'warning'
|
'danger'
showCancelButton
?:
boolean
showConfirmButton
?:
boolean
validate
?:
()
=>
Promise
<
string
|
number
>
|
null
mousePosition
:
{
x
:
number
;
y
:
number
}
}
src/hooks/useMessageBox.tsx
View file @
dffcec96
import
MessageBox
from
'@/components/common/MessageBox/index.vue'
import
type
{
MessageBoxProps
}
from
'@/components/common/MessageBox/types'
import
type
{
FormInstance
}
from
'element-plus'
import
{
render
,
effect
}
from
'vue'
import
{
render
,
effect
,
stop
}
from
'vue'
interface
ConfirmProps
extends
MessageBoxProps
{
useLoading
?:
boolean
// 是否用到loading
...
...
@@ -10,10 +10,28 @@ interface PromptProps extends MessageBoxProps {
inputPattern
?:
RegExp
// 输入框的正则
inputErrorMessage
?:
string
// 输入框的错误信息
}
let
hasBindEvent
=
false
// 记录每次鼠标点击的位置 x y
const
mousePosition
=
ref
({
x
:
0
,
y
:
0
})
let
shouldUpdateMousePosition
=
true
const
handler
=
(
e
:
MouseEvent
)
=>
{
if
(
!
shouldUpdateMousePosition
)
return
mousePosition
.
value
=
{
x
:
e
.
clientX
,
y
:
e
.
clientY
}
}
if
(
!
hasBindEvent
)
{
window
.
addEventListener
(
'click'
,
handler
,
{
capture
:
true
,
})
hasBindEvent
=
true
}
export
const
useMessageBox
=
()
=>
{
const
confirm
=
(
props
:
ConfirmProps
)
=>
{
const
confirm
=
(
props
:
Omit
<
ConfirmProps
,
'mousePosition'
>
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
shouldUpdateMousePosition
=
false
const
visible
=
ref
(
false
)
const
loading
=
ref
(
false
)
const
onConfirm
=
()
=>
{
...
...
@@ -40,15 +58,21 @@ export const useMessageBox = () => {
reject
(
false
)
}
effect
(()
=>
{
const
effectRunner
=
effect
(()
=>
{
render
(
<
MessageBox
v
-
model=
{
visible
.
value
}
{
...
props
}
mousePosition=
{
mousePosition
.
value
}
loading=
{
loading
.
value
}
onConfirm=
{
onConfirm
}
onCancel=
{
onCancel
}
onClose=
{
onClose
}
onAfterLeave=
{
()
=>
{
stop
(
effectRunner
)
render
(
null
,
document
.
body
)
shouldUpdateMousePosition
=
true
}
}
/>,
document
.
body
,
)
...
...
@@ -58,8 +82,11 @@ export const useMessageBox = () => {
})
}
const
prompt
=
<
T
extends
string
|
number
=
string
>
(props: PromptProps): Promise
<
T
>
=
>
{
const
prompt
=
<
T
extends
string
|
number
=
string
>
(
props: Omit
<
PromptProps
,
'
mousePosition
'
>
,
): Promise
<
T
>
=
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
shouldUpdateMousePosition
=
false
const
visible
=
ref
(
false
)
const
loading
=
ref
(
false
)
const
rules
=
{
...
...
@@ -104,7 +131,7 @@ export const useMessageBox = () => {
reject
(
false
)
}
effect
(()
=>
{
const
effectRunner
=
effect
(()
=>
{
render
(
<
MessageBox
{
...
props
}
...
...
@@ -112,9 +139,15 @@ export const useMessageBox = () => {
validate=
{
validate
}
message=
{
messageFC
}
loading=
{
loading
.
value
}
mousePosition=
{
mousePosition
.
value
}
onConfirm=
{
(
res
:
string
|
number
|
undefined
)
=>
onConfirm
(
res
as
T
)
}
onCancel=
{
onCancel
}
onClose=
{
onClose
}
onAfterLeave=
{
()
=>
{
stop
(
effectRunner
)
render
(
null
,
document
.
body
)
shouldUpdateMousePosition
=
true
}
}
/>,
document
.
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