Vue3 + elementPlus dialog 纯js调用弹窗

3,802 阅读1分钟

基于Vue3 + elementPlus dialog封装,可用js调用弹窗,支持异步操作

定义dialog.vue文件

<template>
    <el-dialog :title="title" v-model="visible">
        <slot>
            <!-- 渲染纯js窗体 -->
            <div v-html="contentBody" v-if="contentBody"></div>
        </slot>
        <template #footer>
            <span class="dialog-footer">
                <el-button @click="handleCancel" :disabled="confirmLoading">
                {{cancelText}}</el-button>
                <el-button
                type="primary"
                :loading="confirmLoading"
                @click="$emit('confirm')"
                >{{ confirmText }}</el-button>
            </span>
        </template>
    </el-dialog>
</template>

<script setup>
import { computed, defineProps, defineEmits, ref } from "vue";
// 纯js调用时必须,不然解析不出<el-dialog></el-dialog>、<el-button />
import { ElDialog, ElButton } from "element-plus";
const props = defineProps({
    modelValue: false,
    title: "",
    cancelText: {
        type: String,
        default: "取消"
    },
    confirmText: {
        type: String,
        default: "确定"
    },
    loading: false,
    contentBody: null
});

const emits = defineEmits(["update:modelValue", "cancel", "close"]);
// 纯js实例调用改变loading属性
const _loading = ref(false);
const confirmLoading = computed(() => {
    return props.loading || _loading.value;
});

const visible = computed({
    get () {
        return props.modelValue;
    },
    set (val) {
        emits("update:modelValue", val);
    }
});

// methods
const handleCancel = () => {
    emits("cancel");
    visible.value = false;
};

const showLoading = () => {
    _loading.value = true;
};

const hideLoading = () => {
    _loading.value = false;
};
// 暴露给纯js实例调用,可在实例中使用instance._instance.exposed查询到
defineExpose({ showLoading, hideLoading });
</script>

定义dialog.js文件

import { createApp } from 'vue'
import Dialog from "./dialog.vue";

export default({ title, content, onCancel, onConfirm }) => {
    const mountNode = document.createElement('div')
    const instance = createApp(Dialog, {
        title,
        modelValue: true,
        contentBody: content,
        onCancel: () => {
            instance.unmount(mountNode);
            document.body.removeChild(mountNode);
            onCancel && onCancel()
        },
        onConfirm: async () => {
            if (onConfirm) {
                await onConfirm()
            }
            instance.unmount(mountNode);
            document.body.removeChild(mountNode);
        }
    })

    document.body.appendChild(mountNode)
    instance.mount(mountNode)
    // 显示dialog loading
    instance.showLoading = () => {
        instance._instance.exposed.showLoading()
    };
    // 关闭dialog loading
    instance.hideLoading = () => {
        instance._instance.exposed.hideLoading()
    }
    return instance
}

标签组件使用

<template>
    <Dialog
    title="标题"
    :loading="confirmLoading"
    confirmText="保存"
    @confirm="handleConfirm"
    >
        <div>我是内容</div>
    </Dialog>
</template>
<script setup>
import {ref} from "vue";
import Dialog from "@/components/dialog.vue";

const confirmLoading = ref(false);
// methods
const handleConfirm = () => {
    confirmLoading.value = true;
    setTimeout(()=>{
        confirmLoading.value = false;
    },1500)
};

</script>

纯js调用

<template>
    <el-button @click="handleOpenDialog">open</el-button>
</template>

<script setup>
import DialogHandle from "@/components/dialog.js";

const handleOpenDialog = () => {
    const dialog = DialogHandle({
        title: "操作确认",
        content: "<div>是否确定删除数据?</div>",
        onConfirm: () => {
            return new Promise(async (resolve) => {
                dialog.showLoading();
                setTimeout(() => {
                    dialog.hideLoading();
                    resolve();
                }, 1500);
            });
        },
    });
};
</script>