第一步:构建Dialog组件
提供了标题,内容插槽,加载loading,自定义按钮等一些基础功能
<template>
<div>
<div
v-if="props.show"
:class="$style['dialog']"
>
<div
v-if="props.title"
:class="$style['title']"
>
{{ props.title }}
</div>
<div :class="$style['mainer']">
<slot />
</div>
<div :class="$style['footer']">
<Button
v-if="props.showCancelButton"
:disabled="cancelLoading"
:loading="cancelLoading"
:class="$style['btn']"
plain
@click="handleCancel"
>{{ props.cancelButtonText || '取消' }}</Button
>
<Button
:disabled="confirmLoading"
:loading="confirmLoading"
:class="$style['btn']"
type="primary"
plain
@click="handleConfirm"
>{{ props.confirmButtonText || '确认' }}</Button
>
</div>
</div>
<div
v-if="props.show"
:class="$style['overlay']"
></div>
</div>
</template>
<script setup lang="ts">
import { Button } from 'vant';
import { Props } from './type.ts';
import { shallowRef } from 'vue';
const props = withDefaults(defineProps<Props>(), {
cancelButtonText: '',
confirmButtonText: '',
show: false,
showCancelButton: false,
});
const confirmLoading = shallowRef(false);
const cancelLoading = shallowRef(false);
const handleConfirm = async () => {
const result = props.confirm?.();
if (result instanceof Promise) {
confirmLoading.value = true;
result.finally(() => {
confirmLoading.value = false;
});
}
};
const handleCancel = async () => {
const result = props.cancel?.();
if (result instanceof Promise) {
cancelLoading.value = true;
result.finally(() => {
cancelLoading.value = false;
});
}
};
</script>
<style lang="scss" module>
.footer {
display: flex;
align-items: center;
padding: 5px;
width: 100%;
.btn {
flex: 1;
border: none;
}
}
.dialog {
width: 80%;
position: fixed;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px 20px 0 20px;
z-index: 2;
overflow: hidden;
transition: 0.3s;
border-radius: 16px;
transition-property: transform, opacity;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1;
}
.title {
font-weight: 400;
font-size: 18px;
color: #202020;
line-height: 22px;
text-align: center;
}
.mainer {
margin-top: 5px;
}
</style>
第二步:构建showDialog函数
基于第一步的Dialog组件,封装函数,更便于使用
import Dialog from './index.vue';
import { render, ref, h, VNode } from 'vue';
import { ToastWrapperInstance } from 'vant';
export { Dialog };
interface Props {
show?: boolean;
title?: string;
cancelButtonText?: string | boolean;
confirmButtonText?: string | boolean;
showCancelButton?: boolean;
cancel?: () => void | Promise<void | ToastWrapperInstance>;
confirm?: () => void | Promise<void | ToastWrapperInstance>;
slotContent?: VNode | VNode[];
}
export function showDialog({
show,
showCancelButton,
title,
confirmButtonText,
cancelButtonText,
confirm,
cancel,
slotContent,
}: Props) {
const showRef = ref(show);
const handleConfirm = async () => {
try {
confirm?.();
} catch (error) {
} finally {
closeDialog();
}
};
const handleCancel = async () => {
try {
cancel?.();
} catch (error) {
} finally {
closeDialog();
}
};
const closeDialog = () => {
showRef.value = false;
setTimeout(() => {
render(null, container);
}, 200); // Ensure any closing animations can complete
};
// const vnode = createVNode(Dialog, { //createVNode和h同效果
const vnode = h(
Dialog,
{
show: showRef.value,
title: title,
confirmButtonText,
cancelButtonText,
showCancelButton: showCancelButton,
confirm: handleConfirm,
cancel: handleCancel,
},
{
default: () => {
return slotContent ? slotContent : null;
},
},
);
const container = document.createElement('div');
document.body.appendChild(container);
render(vnode, container);
return {
closeDialog,
};
}
export function showCancelDialog(options: Props) {
return showDialog({
...options,
showCancelButton: true,
});
}
第三步:使用
//第一种使用方式
<Dialog
title="提示"
:show="show"
:show-cancel-button="true"
:confirm="submit"
:cancel="
() => {
show = false;
}
"
>
<div>是否确认,请填写理由</div>
</Dialog>
//第二种使用方式
showDialog({
show: true,
title: '标题',
confirmButtonText: '确认通过',
confirm: handleConfirm,
onCancel: handleCancel,
slotContent: [h('div', [h('div','测试1'),h('div',测试2)])]
});