diy-message-box(开箱即用)
模仿element-plus里的ElMessage.confirm实现自定义消息确认弹窗
技术栈:vue3+js
使用方式:
import DiyMessageBox from "./index.js";
DiyMessageBox.confirm("确认操作", "你确定要执行此操作吗?")
.then(() => {
console.log("用户确认操作");
})
.catch((error) => {
console.log(error.message);
});
主要代码:
index.js
import { createVNode, render } from "vue";
import DiyMessageBoxComp from "./DiyMessageBox.vue";
// 创建一个单例的容器元素
const container = document.createElement("div");
document.body.appendChild(container); //挂载组件到 body
const DiyMessageBox = {
confirm(title = "提示", message = "", options = {}) {
return new Promise((resolve, reject) => {
let isCleaned = false;
// 清除弹窗
const cleanUp = () => {
if (isCleaned) return;
isCleaned = true;
render(null, container);
};
const handleError = (action, errorMessage, rejectFn) => {
try {
rejectFn();
} catch (error) {
console.error(`${action}处理异常:`, error);
} finally {
cleanUp();
}
};
try {
const vnode = createVNode(DiyMessageBoxComp, {
title,
message,
...options,
onConfirm: () => {
try {
console.log("confirm");
resolve();
} catch (error) {
console.error("确认按钮处理函数异常:", error);
} finally {
cleanUp();
}
},
onCancel: () =>
handleError("取消按钮", "用户取消操作", () =>
reject(new Error("用户取消操作"))
),
onClose: () =>
handleError("关闭按钮", "用户关闭弹窗", () =>
reject(new Error("用户关闭弹窗"))
),
});
render(vnode, container);
} catch (error) {
console.error("创建或渲染虚拟节点时出错:", error);
cleanUp();
reject(error);
}
});
},
};
export default DiyMessageBox;
DiyMessageBox.vue(该组件可以自由发挥,实现任何样式)
<template>
<div class="diy-message-box" @click="close">
<div class="diy-message-box__wrapper" @click.stop>
<div class="diy-message-box__header">
<el-icon class="header-icon"><WarningFilled /></el-icon>
<span class="header-text">{
{ props.title }}</span>
<el-icon class="header-icon close" @click="close"
><CloseBold
/></el-icon>
</div>
<div class="diy-message-box__content">
<p>{
{ props.message }}</p>
</div>
<div class="diy-message-box__footer">
<button class="confirm" @click="handleConfirm">
{
{ props.confirmButtonText }}
</button>
<button class="cancel" @click="handleCancel">
{
{ props.cancelButtonText }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, onMounted, onUnmounted } from "vue";
import { CloseBold, WarningFilled } from "@element-plus/icons-vue";
// 定义组件的属性
const props = defineProps({
title: {
type: String,
default: "提示",
},
message: {
type: String,
default: "",
},
confirmButtonText: {
type: String,
default: "确认",
},
cancelButtonText: {
type: String,
default: "取消",
},
});
// 定义组件的事件
const emits = defineEmits(["confirm", "cancel", "close"]);
// 处理确认按钮点击事件
const handleConfirm = () => {
console.log("handleConfirm is called");
emits("confirm");
};
// 处理取消按钮点击事件
const handleCancel = () => {
emits("cancel");
};
// 处理关闭事件
const close = () => {
emits("close");
};
// 处理键盘按下事件,当按下 Esc 键时关闭弹窗
const handleKeydown = (e) => {
if (e.key === "Escape") close();
};
onMounted(() => window.addEventListener("keydown", handleKeydown));
onUnmounted(() => window.removeEventListener("keydown", handleKeydown));
// 导出组件的方法
defineExpose({});
</script>
<style scoped lang="less">
.diy-message-box {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1024;
.diy-message-box__wrapper {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 316px;
height: 164px;
background: linear-gradient(
270deg,
rgba(38, 51, 67, 0.7) 16%,
rgba(16, 29, 47, 0.8) 100%
);
.diy-message-box__header {
display: grid;
grid-template-columns: 20px auto 20px;
column-gap: 4px;
align-items: center;
padding: 5px 8px;
background: linear-gradient(
89deg,
#152a35 0%,
rgba(62, 123, 213, 0.8) 21%,
rgba(32, 39, 68, 0) 72%,
rgba(32, 39, 68, 0) 92%
);
border-radius: 0px 0px 0px 0px;
border: 1px solid;
border-image: linear-gradient(
88deg,
rgba(213, 242, 253, 0.3),
rgba(62, 123, 213, 1),
rgba(213, 242, 253, 0.2),
rgba(213, 242, 253, 0)
)
1 1;
.header-text {
font-weight: bold;
font-size: 14px;
color: #ffffff;
line-height: 20px;
}
.header-icon {
font-size: 16px;
}
.header-icon.close {
cursor: pointer;
}
}
.diy-message-box__content {
text-align: center;
font-weight: bold;
font-size: 16px;
color: #ffffff;
min-height: 20px;
max-height: 200px;
overflow-y: auto;
padding: 30px 0;
}
.diy-message-box__footer {
display: flex;
justify-content: center;
gap: 30px;
padding-bottom: 20px;
button {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-sizing: border-box;
width: 84px;
height: 30px;
font-weight: 500;
font-size: 14px;
white-space: nowrap;
color: #ffffff;
line-height: 20px;
border-radius: 4px 4px 4px 4px;
}
.confirm {
background: var(--button-highlight-bg);
box-shadow: inset 0px 0px 16px 0px #67a2ff;
}
.cancel {
background: rgba(1, 26, 68, 0.6);
border: 1px solid rgba(255, 255, 255, 0.6);
}
}
}
}
</style>
效果图:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理