一、组件目录
二、组件样式、方法、参数定义
<template>
<div class="modal-bg" v-show="state.visibled" @click="onCancel()">
<div class="modal-content" @click.stop="">
<div class="modal-title">{{ config.title }}</div>
<div class="modal-message">{{ config.message }}</div>
<div class="modal-btn">
<div class="btn cancel" v-if="config.showCancel" @click="onCancel()">
{{ config.cancelText }}
</div>
<div class="line" v-if="config.showCancel"></div>
<div class="btn confirm" @click="onConfirm()">
{{ config.confirmText }}
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs } from "vue";
const emit = defineEmits(["cancel", "confirm"]);
const state = reactive({
visibled: false,
});
const props = defineProps({
config: {
// 消息配置项
type: Object,
default: () => {},
},
remove: {
// 取消挂载回调
type: Function,
default: () => {},
},
cancel: {
type: Function,
default: () => {},
},
confirm: {
type: Function,
default: () => {},
},
});
// 打开弹框
const onOpen = (config: Object) => {
setTimeout(() => {
state.visibled = true;
}, 10);
};
// 关闭弹框
const onClose = () => {
state.visibled = false;
setTimeout(() => {
props.remove();
}, 200);
};
const onCancel = () => {
props.cancel();
onClose();
};
const onConfirm = () => {
props.confirm();
onClose();
};
onOpen(props.config);
</script>
<style lang="less" scoped>
.modal-bg {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
.modal-content {
width: 72%;
border-radius: 8px;
background: #fff;
.modal-title {
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.9);
text-align: center;
margin: 20px 0 8px;
}
.modal-message {
font-size: 14px;
color: rgba(0, 0, 0, 0.4);
text-align: center;
padding: 0 16px;
box-sizing: border-box;
}
.modal-btn {
display: flex;
margin-top: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
line-height: 46px;
text-align: center;
.line {
width: 1px;
height: 46px;
background: rgba(0, 0, 0, 0.1);
}
.btn {
width: 50%;
font-size: 16px;
}
.cancel {
color: rgba(0, 0, 0, 0.3);
}
.confirm {
color: #e6c173;
}
}
}
}
</style>
三、组件实例操作
import { createApp } from "vue";
import Modal from "./modal.vue";
/**
* Modal 实例操作
* @param {Object} cfg 实例配置
* @param {Function} callback 回调函数
*/
const createInstance = (cfg, callback) => {
const config = cfg || {};
// 创建包裹容器
let modalNode = document.createElement("div");
let attr = document.createAttribute("class");
attr.value = "modal-content-my-que";
modalNode.setAttributeNode(attr);
const modaltList = document.getElementsByClassName("modal-content-my-que");
// 只显示一条数据
if (modaltList.length >= 1) {
return false;
}
// 挂载实例到body
const app = createApp(Modal, {
config,
remove() {
handleRemove(); //移除元素,关闭后从DOM上取消挂载并移除
},
cancel() {
// 取消回调
return callback(false);
},
confirm() {
// 确认回调
return callback(true);
},
});
app.vm = app.mount(modalNode);
document.body.appendChild(modalNode);
app.close = () => {
handleRemove();
};
// 取消挂载
const handleRemove = () => {
app.unmount(modalNode);
document.body.removeChild(modalNode);
};
return app;
};
export default createInstance;
四、读取配置并渲染modal,暴露API
import createInstance from "./instance";
/**
* 读取配置并渲染modal
* @param {Object/String} cfg 自定义配置
*/
function renderModal(cfg = "", callback) {
// 允许直接传入消息内容,因此要判断传入的 cfg 类型
const isContent = typeof cfg === "string";
// 整合自定义配置
cfg = isContent
? {
message: cfg,
}
: cfg;
const config = Object.assign({}, cfg); //合并配置
const {
title = "温馨提示",
message = "", //消息内容共
showCancel = true, //是否显示取消按钮
cancelText = "取消", //取消按钮文字
confirmText = "确认", //确认按钮文字
} = config;
// 创建实例
return createInstance(
{
title,
message,
showCancel,
cancelText,
confirmText,
},
callback
);
}
// 暴露text,success,error 等API
export default {
show(cfg, callback) {
return renderModal(cfg, callback);
},
};
五、组件调用
import Modal from "@/components/modal/modal.js";
Modal.show(modalInfo, (isConfirm) => {
if (isConfirm) {
isBack.value = true;
router.back();
} else isBack.value = false;
});
嗯,这只是简单的模拟modal的组件,有更好的方法可以一起探讨学习。