最近公司需要用到弹窗,但是element-puls的弹窗组件达不到想要的效果,于是自己动手写了个,顺便学习一下vue3.0如何使用方法调用组件显示,写的比较简陋,主要是表达实现方式,学习为主。
组件显示效果:
使用方法如下:(支持promise)
// 自制弹窗组件
showMessageBox({
title: '配置条件已超过3条,将无法继续新增',
desc: '现场景配置仅支持至多3条公式内的计算',
cancelButtonText:'我知道了',
type:'confirm',
closeOnClickModal: false
})
主要实现方法如下:
import { h, render } from 'vue';
//需要引入弹窗组件
import MessageBox from './index.vue';
// 单例模式
let vnode = null;
// 显示弹窗组件
let showMessageBox = option => {
if (vnode) {
return;
}
// vue的props
let options = {
...option,
onVanish: () => {
vnode=null
render(null, container);
// document.body.removeChild(container);
},
};
// 组件挂载的容器
let container =document.createElement('div');
// 生成vnode
vnode = h(MessageBox, options);
// 渲染到页面
render(vnode, container);
//document.body.appendChild(container);
document.body.appendChild(container.firstElementChild);
// vnode对应的组件
let component = vnode.component;
// vue实例
let vm = component.proxy;
vm.visible = true;
let vmProps = Object.keys(option);
// props列表不存在则移至到vm自身属性
vmProps.forEach(key => {
if (!propsList.includes(key)) {
vm[key]=option[key]
}
});
};
// 配置的props数组
export const propsList = [
'closeOnClickModal',
'update:visible',
'desc',
'title',
'cancelButtonText',
'type',
'saveButtonText',
'onSave',
'onCancel',
];
showMessageBox.confirm = (title, desc, option) => {
return new Promise((reslove, reject) => {
const vm = showMessageBox({
title,
desc,
type: 'confirm',
...option,
onSave: () => {
reslove(vm);
},
onCancel: () => {
reject();
},
});
});
};
showMessageBox.alert = (title, desc, option) => {
return new Promise((reslove, reject) => {
const vm = showMessageBox({
title,
desc,
type: 'alert',
...option,
onSave: () => {
reslove(vm);
},
onCancel: () => {
reject();
},
});
});
};
export default showMessageBox;
vue组件部分
<template>
<div class="el-overlay is-message-box message-main" @click="closeOnClickModal ? emits('vanish') : null" v-show="visible">
<div class="el-message-box message-box" @click.stop>
<div class="message-title"><img src="../image/warn.png" class="message-info" />{{ title }}</div>
<div class="message-desc">{{ desc }}</div>
<div class="meesage-btns" v-if="type == 'alert'">
<el-button type="primary" class="btn" @click="onVanish">{{ cancelButtonText || '我知道了' }}</el-button>
</div>
<div class="meesage-btns" v-else-if="type == 'confirm'">
<el-button class="btn" @click="cancel">{{ cancelButtonText || '取消' }}</el-button>
<el-button type="primary" class="btn" @click="save">{{ saveButtonText || '保存' }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, defineProps,defineEmits } from 'vue';
import { ElButton } from 'element-plus';
// 配置外部传来的事件
const emits = defineEmits(['vanish'])
// 可见状态
const visible = ref(false);
// props
const props = defineProps([
//'onVanish',
'closeOnClickModal',
'update:visible',
'desc',
'title',
'cancelButtonText',
'type',
'saveButtonText',
'onSave',
'onCancel'
]);
const cancel = () => {
emits('vanish')
props.onCancel?.()
};
const save = () => {
emits('vanish')
props.onSave?.()
};
</script>
<style lang="scss" scoped>
//@import url(); 引入公共css类
.message-box {
background: #ffffff;
box-shadow: 0px 2px 16px rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 32px;
width: 560px;
height: 218px;
box-sizing: border-box;
z-index: 9999;
}
.message-main {
z-index: 888;
}
.message-title {
font-weight: 500;
font-size: 20px;
line-height: 29px;
display: flex;
align-items: center;
color: #000000;
margin-bottom: 12px;
}
.message-info {
width: 24px;
height: 24px;
margin-right: 12px;
}
.message-desc {
padding-left: 36px;
font-weight: 400;
font-size: 16px;
line-height: 23px;
display: flex;
align-items: center;
color: #a5a7ae;
padding-bottom: 32px;
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
margin-bottom: 16px;
}
.meesage-btns {
text-align: right;
.btn {
display: inline-block;
border-radius: 4px;
width: 96px;
height: 40px;
margin-left: 16px;
font-weight: 400;
font-size: 14px;
}
.btn[type='primary'] {
background: #426fe3;
color: #fff;
border: 1px solid #426fe3;
}
}
</style>