Skip to content

Commit b6aceba

Browse files
committed
FmModal 组件增加 beforeClose 回调函数,优化关闭逻辑并支持异步处理
1 parent 7b373ed commit b6aceba

File tree

4 files changed

+128
-23
lines changed

4 files changed

+128
-23
lines changed

src/ui/components/FmModal/dialog/DialogContent.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { computed } from 'vue'
1313
import { cn } from '@/utils'
1414
1515
const props = defineProps<DialogContentProps & {
16+
modalId: string
1617
class?: HTMLAttributes['class']
1718
open?: boolean
1819
maximize?: boolean
@@ -54,8 +55,6 @@ watch(showOverlay, (val) => {
5455
isLocked.value = false
5556
}
5657
})
57-
58-
const id = inject('ModalId')
5958
</script>
6059

6160
<template>
@@ -73,7 +72,7 @@ const id = inject('ModalId')
7372
>
7473
<div
7574
v-if="showOverlay"
76-
:data-modal-id="id"
75+
:data-modal-id="props.modalId"
7776
:class="cn('fixed inset-0 z-2000 data-[state=closed]:animate-out data-[state=open]:animate-in bg-black/50 data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0', {
7877
'backdrop-blur-sm': props.overlayBlur,
7978
})"

src/ui/components/FmModal/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export interface ModalProps {
2222
cancelButtonText?: string
2323
confirmButtonDisabled?: boolean
2424
confirmButtonLoading?: boolean
25+
beforeClose?: (
26+
action: 'confirm' | 'cancel' | 'close',
27+
done: () => void
28+
) => void
2529
header?: boolean
2630
footer?: boolean
2731
closeOnClickOverlay?: boolean
@@ -43,13 +47,13 @@ export interface ModalEmits {
4347

4448
type alertOptions = Pick<ModalProps, 'title' | 'description' | 'icon' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass'> & {
4549
content: string
46-
onConfirm?: () => any
50+
onConfirm?: () => void
4751
}
4852

49-
type confirmOptions = Pick<ModalProps, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass'> & {
53+
type confirmOptions = Pick<ModalProps, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'beforeClose' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass'> & {
5054
content: string
51-
onConfirm?: () => any
52-
onCancel?: () => any
55+
onConfirm?: () => void
56+
onCancel?: () => void
5357
}
5458

5559
export function useFmModal() {

src/ui/components/FmModal/index.vue

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,34 +53,79 @@ defineExpose({
5353
areaRef: dialogAreaRef,
5454
})
5555
56-
const id = useId()
57-
provide('ModalId', id)
58-
56+
const modalId = useId()
5957
const isOpen = ref(props.modelValue)
6058
6159
watch(() => props.modelValue, (newValue) => {
6260
isOpen.value = newValue
6361
})
6462
65-
function updateOpen(value: boolean) {
66-
isOpen.value = value
67-
emits('update:modelValue', value)
68-
if (value) {
63+
watch(isOpen, (val) => {
64+
emits('update:modelValue', val)
65+
if (val) {
6966
emits('open')
7067
}
7168
else {
7269
emits('close')
7370
}
71+
})
72+
73+
async function updateOpen(value: boolean) {
74+
if (value) {
75+
isOpen.value = value
76+
emits('open')
77+
}
78+
else {
79+
if (props.beforeClose) {
80+
await props.beforeClose(
81+
'close',
82+
() => {
83+
isOpen.value = value
84+
emits('close')
85+
},
86+
)
87+
}
88+
else {
89+
isOpen.value = value
90+
emits('close')
91+
}
92+
}
7493
}
7594
76-
function onConfirm() {
77-
updateOpen(false)
78-
emits('confirm')
95+
const isConfirmButtonLoading = ref(false)
96+
97+
async function onConfirm() {
98+
if (props.beforeClose) {
99+
isConfirmButtonLoading.value = true
100+
await props.beforeClose(
101+
'confirm',
102+
() => {
103+
isOpen.value = false
104+
emits('confirm')
105+
},
106+
)
107+
isConfirmButtonLoading.value = false
108+
}
109+
else {
110+
isOpen.value = false
111+
emits('confirm')
112+
}
79113
}
80114
81-
function onCancel() {
82-
updateOpen(false)
83-
emits('cancel')
115+
async function onCancel() {
116+
if (props.beforeClose) {
117+
await props.beforeClose(
118+
'cancel',
119+
() => {
120+
isOpen.value = false
121+
emits('cancel')
122+
},
123+
)
124+
}
125+
else {
126+
isOpen.value = false
127+
emits('cancel')
128+
}
84129
}
85130
86131
function handleFocusOutside(e: Event) {
@@ -89,7 +134,7 @@ function handleFocusOutside(e: Event) {
89134
}
90135
91136
function handleClickOutside(e: Event) {
92-
if (!props.closeOnClickOverlay || (e.target as HTMLElement).dataset.modalId !== id) {
137+
if (!props.closeOnClickOverlay || (e.target as HTMLElement).dataset.modalId !== modalId) {
93138
e.preventDefault()
94139
e.stopPropagation()
95140
}
@@ -115,6 +160,7 @@ function handleAnimationEnd() {
115160
<template>
116161
<Dialog :modal="false" :open="isOpen" @update:open="updateOpen">
117162
<DialogContent
163+
:modal-id="modalId"
118164
:open="isOpen"
119165
:closable="props.closable"
120166
:overlay="props.overlay"
@@ -176,7 +222,7 @@ function handleAnimationEnd() {
176222
<FmButton v-if="showCancelButton" variant="outline" class="w-full" @click="onCancel">
177223
{{ cancelButtonText }}
178224
</FmButton>
179-
<FmButton v-if="showConfirmButton" :disabled="confirmButtonDisabled" :loading="confirmButtonLoading" class="w-full" @click="onConfirm">
225+
<FmButton v-if="showConfirmButton" :disabled="confirmButtonDisabled" :loading="confirmButtonLoading || isConfirmButtonLoading" class="w-full" @click="onConfirm">
180226
{{ confirmButtonText }}
181227
</FmButton>
182228
</slot>

src/views/feature/component/built_in/modal/index.vue

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script setup lang="ts">
2+
import { toast } from 'vue-sonner'
23
import { useFmModal } from '@/ui/components/FmModal'
34
45
definePage({
@@ -24,34 +25,86 @@ watch(() => modalInfo.value.loading, (loading) => {
2425
}
2526
})
2627
28+
function handleBeforeClose(action: 'confirm' | 'cancel' | 'close', done: () => void) {
29+
if (action === 'close') {
30+
useFmModal().confirm({
31+
title: '提示',
32+
content: '确定要关闭吗?',
33+
onConfirm: () => {
34+
done()
35+
},
36+
})
37+
}
38+
else {
39+
done()
40+
}
41+
}
42+
2743
function showModalInfo() {
2844
useFmModal().info({
2945
title: '温馨提醒',
3046
content: '这是 info 弹窗',
47+
onConfirm: () => {
48+
toast.info('你点了确定')
49+
},
3150
})
3251
}
3352
function showModalSuccess() {
3453
useFmModal().success({
3554
title: '温馨提醒',
3655
content: '这是 success 弹窗',
56+
onConfirm: () => {
57+
toast.info('你点了确定')
58+
},
3759
})
3860
}
3961
function showModalWarning() {
4062
useFmModal().warning({
4163
title: '温馨提醒',
4264
content: '这是 warning 弹窗',
65+
onConfirm: () => {
66+
toast.info('你点了确定')
67+
},
4368
})
4469
}
4570
function showModalError() {
4671
useFmModal().error({
4772
title: '温馨提醒',
4873
content: '这是 confirm 弹窗',
74+
onConfirm: () => {
75+
toast.info('你点了确定')
76+
},
4977
})
5078
}
5179
function showModalConfirm() {
5280
useFmModal().confirm({
5381
title: '温馨提醒',
5482
content: '这是 confirm 弹窗',
83+
onConfirm: () => {
84+
toast.info('你点了确定')
85+
},
86+
})
87+
}
88+
function showModalPromiseConfirm() {
89+
useFmModal().confirm({
90+
title: '温馨提醒',
91+
content: '这是 confirm 弹窗',
92+
confirmButtonText: '确认(随机成功或失败)',
93+
beforeClose: async (action, done) => {
94+
if (action === 'confirm') {
95+
await new Promise(resolve => setTimeout(resolve, 1000))
96+
if (Math.random() > 0.5) {
97+
toast.success('成功了!')
98+
done()
99+
}
100+
else {
101+
toast.error('失败了!')
102+
}
103+
}
104+
else {
105+
done()
106+
}
107+
},
55108
})
56109
}
57110
</script>
@@ -63,7 +116,7 @@ function showModalConfirm() {
63116
<FmButton @click="modal = true">
64117
打开
65118
</FmButton>
66-
<FmModal v-model="modal" title="标题" description="这里是一段描述介绍" :closable="modalInfo.closable" :center="modalInfo.center" :loading="modalInfo.loading" :header="modalInfo.header" :footer="modalInfo.footer">
119+
<FmModal v-model="modal" title="标题" description="这里是一段描述介绍" :closable="modalInfo.closable" :center="modalInfo.center" :loading="modalInfo.loading" :header="modalInfo.header" :footer="modalInfo.footer" :before-close="handleBeforeClose">
67120
<div :class="modalInfo.contentHeight">
68121
<div class="flex-start-center flex-wrap gap-2">
69122
<FmButton :variant="modalInfo.closable ? 'default' : 'outline'" class="w-full" @click="modalInfo.closable = !modalInfo.closable">
@@ -105,6 +158,9 @@ function showModalConfirm() {
105158
<FmButton @click="showModalConfirm">
106159
Confirm
107160
</FmButton>
161+
<FmButton @click="showModalPromiseConfirm">
162+
Confirm with promise
163+
</FmButton>
108164
</div>
109165
</FmPageMain>
110166
</div>

0 commit comments

Comments
 (0)