Vue3.x 函数式(命令式)弹窗使用方案

180 阅读1分钟

解决在全局使用同一类弹框如登录弹框,在认证过期后需要重新弹出弹框

使用hooks

import { createVNode, render, getCurrentInstance } from "vue";
// 处理插入元素
const getAppendToElement = (props) => {
    let appendTo = document.body;
    if (props.appendTo) {
        if (typeof props.appendTo === "string") {
            appendTo = document.querySelector(props.appendTo);
        }
        if (props.appendTo instanceof HTMLElement) {
            appendTo = props.appendTo;
        }
        if (!(appendTo instanceof HTMLElement)) {
            appendTo = document.body;
        }
    }
    return appendTo;
};
// 初始化使用createVNode 创建虚拟Dom,render函数渲染,挂载Dom,
const initInstance = (Component, props, container, appContext = null) => {
    const vNode = createVNode(Component, props);
    vNode.appContext = appContext;
    render(vNode, container);
    getAppendToElement(props).appendChild(container);
    return vNode;
};
export const useCommandComponent = (Component) => {
    var _a, _b;
    const appContext = (_a = getCurrentInstance()) === null || _a === void 0 ? void 0 : _a.appContext;
    if (appContext) {
        const currentProvides = (_b = getCurrentInstance()) === null || _b === void 0 ? void 0 : _b.provides;
        Reflect.set(appContext, "provides"Object.assign(Object.assign({}, appContext.provides), currentProvides));
    }
    const container = document.createElement("div");
    const close = () => {
        var _a;
        render(null, container);
        (_a = container.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(container);
    };
    const CommandComponent = (options) => {
        var _a;
        if (!Reflect.has(options, "visible")) {
            options.visible = true;
        }
        if (typeof options.onClose !== "function") {
            options.onClose = close;
        }
        else {
            const originOnclose = options.onClose;
            options.onClose = () => {
                originOnclose();
                close();
            };
        }
        const vNode = initInstance(Component, options, container, appContext);
        const vm = (_a = vNode.component) === null || _a === void 0 ? void 0 : _a.proxy;
        for (const prop in options) {
            if (Reflect.has(options, prop) && !Reflect.has(vm.$props, prop)) {
                vm[prop] = options[prop];
            }
        }
        return vNode;
    };
    CommandComponent.close = close;
    return CommandComponent;
};
export default useCommandComponent;

简单的dialog

<script setup lang="ts">
import { computed } from "vue";
import { ElDialog } from "element-plus";
const props = defineProps<{
  visible: boolean;
  title?: string;
}>();

const emits = defineEmits<{
  (event: "update:visible", visible: boolean): void;
  (event: "close"): void;
}>();

const visible = computed<boolean>({
  get() {
    return props.visible;
  },
  set(visible) {
    emits("update:visible", visible);
    if (!visible) {
      emits("close");
    }
  }
});
</script>

<template>
  <ElDialog v-model="visible" :title="title" width="30%">
 <span>This is a message</span>
    <template #footer>
      <span>
        <el-button @click="visible = false">Cancel</el-button>
        <el-button type="primary" @click="visible = false"> Confirm </el-button>
      </span>
    </template>
  </ElDialog>
</template>

使用

const myDialog = useCommandComponent(MyDialog);
const handleDialog = () => {
  myDialog({
    title: "父组件弹窗",
    dialogVisible: true,
    onSubmit: () => myDialog.close(),
    onCancel: () => myDialog.close()
  });
};