main.ts中
//引入并注册
import {
createApp } from "vue";
import SubDialog from '@/components/sub.ts'
let instance: any = null;
instance = createApp(App);
instance.use(SubDialog);
components下的sub.ts文件
import {
h, render } from 'vue'
import iLawDialog from '#/components/iLawDialog.vue'
let mountNode
let app
// doubleDialog 是否需要二级弹窗
let createMount = (opts) => {
if (mountNode && !opts.doubleDialog) {
//确保只存在一个弹窗
document.body.removeChild(mountNode)
mountNode = null
}
mountNode = document.createElement('div')
document.body.appendChild(mountNode)
const vnode = h(iLawDialog, {
options: {
...opts,
remove() {
//传入remove函数,组件内可移除dom 组件内通过props接收x
document.body.removeChild(mountNode)
mountNode = null
}
},
})
vnode.appContext = app
render(vnode, mountNode);
}
const V3Popup = (options = {
id: Number }) => {
options.id = options.id || 'v3popup_' + 1
createMount(options)
}
export default {
install(Vue: any) {
/**
* @description: 将公共的工具类 注册在vue 实例上
* @param {*}
* @return {*}
*/
app = Vue._context
Vue.config.globalProperties.$subDialog = V3Popup;
},
};
components下的iLawDialog.vue
<template>
<el-dialog
v-model="dialogVisible"
:custom-class="`subDialog ${props.options.hideHeader}`"
v-bind="props.options"
>
<template #header>
<component
:is="props.options?.headerDom"
v-bind="props.options.contentComponent.attrs"
@onClick="_handleToolbarClick($event)"
></component>
</template>
<template #default>
<p
v-html="props.options.contentText"
v-if="props.options.contentText"
></p>
<div v-if="props.options.contentComponent">
<component
:is="componentId"
v-bind="props.options.contentComponent.attrs"
@onClick="_handleToolbarClick($event)"
></component>
</div>
</template>
<template #footer v-if="is_toolbar">
<div class="dialog-footer">
<iLawButtons
:toolbar="props.options.toolbar"
@onClick="_handleToolbarClick($event)"
></iLawButtons>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import iLawButtons from "@/components/iLawButtons.vue";
import {
ref, Ref, defineProps, watch, computed } from "vue";
/**
* @description: 获取 函数 实例
* @return {*}
*/
// 注册 绑定 父组件 v-model change 事件
// 获取 父组件传值 v-model
const props = defineProps({
options: {
type: Object,
default: () => {
},
},
});
// 定义 dialog v-model 传值
let dialogVisible: Ref<boolean> = ref(true);
/**
* @description: 计算属性判断 底部按钮是否默认还是没有
* @return {*}
*/
const is_toolbar = computed(() => {
return props.options?.toolbar ? true : false;
});
const componentId = computed(() => {
return props.options.contentComponent.is;
});
// 监听 dialogVisible
watch(
() => dialogVisible.value,
(val) => {
}
);
/**
* 处理按钮回调
* @param item
* @param data
* @param $event
*/
const _handleToolbarClick = (item?: any, $event?: any) => {
if (item.click == "cancel") {
props.options.remove();
return;
}
// 将 移除dom 的当 callback 传出
props.options[item.click](props.options.remove, item.options);
};
/**
* @description: 将属性方法首字母大写
* @param {*} str
* @return {*}
*/
const titleCase = (str) => {
let newStr = str.slice(0, 1).toUpperCase() + str.slice(1).toLowerCase();
return newStr;
};
</script>
<style lang="scss">
.subDialog {
&.el-dialog {
z-index: 99;
background-color: #fff;
}
.el-dialog__header {
border-bottom: 1px solid rgb(0 0 0 / 6%);
padding: 0 25px;
width: 100%;
height: 50px;
text-align: left;
line-height: 50px;
box-sizing: border-box;
}
.el-dialog__title {
font-size: 18px;
font-family: "PingFangSC-Medium", "PingFang SC";
font-weight: 500;
color: rgb(0 0 0 / 85%);
}
.el-dialog__footer {
display: flex;
justify-content: center;
align-items: center;
margin-right: 20px;
border-top: 0;
padding: 0;
height: 60px;
line-height: 60px;
.el-button {
border-radius: 22px;
padding: 0;
width: 120px;
height: 32px;
line-height: 32px;
}
.color-info {
border-color: #999;
background: #999 !important;
}
}
}
.hideHeader .el-dialog__header {
display: none;
}
</style>
components下的 iLawButtons.vue
<template>
<div :class="`iLaw-buttons-${align}`">
<component
:is="componentName"
v-for="(item, index) of props.toolbar"
:key="index"
v-bind="item"
@click.stop="handleToolbarClick(item, $event)"
>
<template v-if="item.text">{
{
item.text }}</template>
<template v-else>{
{
item.content }}</template>
</component>
</div>
</template>
<script setup lang="ts">
import {
ElButton } from "element-plus";
import {
defineProps,
ref,
Ref,
computed,
getCurrentInstance,
defineEmits,
} from "vue";
const {
proxy }: any = getCurrentInstance();
/**
* @description: 获取 函数 实例
* @return {*}
*/
const emits = defineEmits(["onClick"]);
// 按钮组
// @group Z.私有组件
const Types = {
button: ElButton,
text: "ge-textButton",
};
const props = defineProps({
// 按钮对齐方式
align: {
type: String,
default: (): string => "center",
validator: (value: string) => ["left", "center", "right"].includes(value),
},
// 按钮数组
toolbar: {
// type: Array,
default: (): any => [{
}],
required: true,
},
// 按钮类型
type: {
type: String,
default: (): string => "button",
// validator: (value: string) => Types[value],
},
});
let timer: Ref<any> = ref(null);
const componentName = computed(() => {
return Types[props.type] || Types.button;
});
const handleToolbarClick = (item, event) => {
timer.value && clearTimeout(timer.value);
const {
click, ...rest } = item;
timer.value = setTimeout(() => {
if (typeof click === "function") {
click(rest, event);
} else {
const func = proxy.$attrs[click] ? click : "onClick";
emits(func, item);
}
}, 300);
};
</script>
<style lang="scss" scoped>
.iLaw-buttons {
&-left {
text-align: left;
}
&-center {
text-align: center;
}
&-right {
text-align: right;
}
}
</style>
创建组件
<template>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">确认</el-button>
</template>
<script setup lang="ts">
import {
defineProps, ref, defineEmits } from "vue";
const props: any = defineProps({
data: {
type: Object,
default: () => {
},
},
});
const emits = defineEmits(["onClick"]);
const form = ref('');
const cancel = () => {
emits("onClick", {
click: "cancel",
});
};
const submit = () => {
emits("onClick", {
click: "confirm",
options: form.value,
});
};
</script>
<style scoped lang="scss">
</style>
如何使用
import {
getCurrentInstance, markRaw } from 'vue'
const {
proxy}:any = getCurrentInstance()
//执行
proxy.$subDialog({
width: 383,
hideHeader: "hideHeader",
"show-close": false,
contentComponent: {
is: markRaw(DownloadContent),//创建的自定义的组件
attrs: {
data: '',//传值给自定义组件
toolbar: [
{
text: "取消", click: "cancel" },
{
text: "确认", type: "primary", click: "confirm" },
],
},
},
confirm: (remove, form) => {
// 自定义组件传回来的值 form
remove();//弹框
},
});