文章目录
- 引言
- 为什么需要二次封装?
- 封装思路
- 代码实现
- 1. 基础封装组件 (Dialog.vue)
- 2. Vue中引入使用示例
- 封装后的优势
- 进阶优化建议
- 总结
引言
在 Vue 项目中,Element UI 的 el-dialog
是一个非常实用的对话框组件。但在实际开发中,我们经常会遇到需要重复设置对话框属性、处理相同逻辑的情况。通过二次封装,我们可以创建一个更灵活、更易用的对话框组件,提高开发效率并保持代码一致性。
为什么需要二次封装?
- 减少重复代码:多个对话框可能需要相同的逻辑(如关闭确认、表单重置等)
- 统一风格:确保所有对话框的视觉和交互行为一致
- 简化使用:通过默认值和封装方法,减少每次使用时需要编写的代码
- 增强功能:添加常用功能如加载状态、国际化支持等
封装思路
我们将创建一个 Dialog
组件,它封装了那些扩展?:
- 封装了
el-dialog
的常用属性和事件 - 动态按钮大小,按钮标题动态展示,按钮的type类型
- 弹窗的动态宽度
- 是否显示关闭,取消按钮
- 添加了确认关闭逻辑
- 支持插槽内容(内容根据项目随意调整)
- 提供统一的关闭方法
- 批量按钮的动态添加
- 窗口的响应式
代码实现
1. 基础封装组件 (Dialog.vue)
<template><div><el-dialogclass="cust-dialog":model-value="show":title="title":width="dialogWidth":top="top + 'px'"@close="close":draggable="true":show-close="showClose":modal-class="'dialog-fade'"><divclass="dialog-body"><slot></slot></div><template #footer><span class="dialog-footer"><slot name="footer"><!-- 默认footer内容 --><el-button type="danger" @click="close" v-if="showCancel">取消</el-button><el-buttonv-for="item in buttons"@click="item.click":type="item.type || primary":size="item.size">{{ item.text }}</el-button></slot></span></template></el-dialog></div>
</template><script setup lang="ts">
import { computed, watch, onMounted, onUnmounted, ref } from "vue";
const props = defineProps<{show: { type: Boolean; default: false };title: { type: String; default: "提示" };width: { type: String; default: "30%" };top: { type: Number; default: 50 };padding: { type: Number; default: 15 };showClose: { type: Boolean; default: true };showCancel: { type: Boolean; default: true };buttons: {type: Array<{text: string;click: () => void;type?: "primary" | "success" | "warning" | "danger" | "info";size?: "large" | "small" | "default";}>;default: () => [];};
}>();const dialogWidth = ref(props.width);
// 监听窗口大小变化,根据窗口宽度动态设置dialog宽度
onMounted(() => {const handleResize = () => {if (window.innerWidth <= 768) {dialogWidth.value = "50%";} else {dialogWidth.value = props.width;}};window.addEventListener("resize", handleResize);
});onUnmounted(() => {window.removeEventListener("resize", handleResize);
});const emit = defineEmits(["close"]);
const close = () => {emit("close");
};
</script><style lang="scss">
@keyframes fade {from {opacity: 0;}to {opacity: 1;}
}.dialog-fade {animation: fade 0.3s;
}.cust-dialog {margin: 30px auto 10px !important;.el-dialog__body {padding: 0px;}.dialog-body {border-top: 1px solid #ddd;border-bottom: 1px solid #ddd;min-height: 80px;overflow: auto;/* 自定义滚动条 */&::-webkit-scrollbar {width: 3px;height: 8px;}&::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 4px;}&::-webkit-scrollbar-thumb {background: #c1c1c1;border-radius: 4px;transition: background 0.3s;&:hover {background: #a8a8a8;}}}.dialog-footer {text-align: right;padding: 5px 20px;}
}
</style>
2. Vue中引入使用示例
<template><div><Dialog:show="dialogConfig.show":title="dialogConfig.title":width="dialogConfig.width":top="dialogConfig.top":padding="dialogConfig.padding":showClose="dialogConfig.showClose":showCancel="dialogConfig.showCancel":buttons="dialogConfig.buttons"@close="dialogConfig.show = false"><el-form :model="form" label-width="auto" style="max-width: 600px"><el-form-item label="Activity name"><el-input v-model="form.name" /></el-form-item><el-form-item label="Activity zone"><el-selectv-model="form.region"placeholder="please select your zone"><el-option label="Zone one" value="shanghai" /><el-option label="Zone two" value="beijing" /></el-select></el-form-item><el-form-item label="Activity time"><el-col :span="11"><el-date-pickerv-model="form.date1"type="date"placeholder="Pick a date"style="width: 100%"/></el-col><el-col :span="2" class="text-center"><span class="text-gray-500">-</span></el-col><el-col :span="11"><el-time-pickerv-model="form.date2"placeholder="Pick a time"style="width: 100%"/></el-col></el-form-item><el-form-item label="Instant delivery"><el-switch v-model="form.delivery" /></el-form-item><el-form-item label="Activity type"><el-checkbox-group v-model="form.type"><el-checkbox value="Online activities" name="type">Online activities</el-checkbox><el-checkbox value="Promotion activities" name="type">Promotion activities</el-checkbox><el-checkbox value="Offline activities" name="type">Offline activities</el-checkbox><el-checkbox value="Simple brand exposure" name="type">Simple brand exposure</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="Resources"><el-radio-group v-model="form.resource"><el-radio value="Sponsor">Sponsor</el-radio><el-radio value="Venue">Venue</el-radio></el-radio-group></el-form-item><el-form-item label="Activity form"><el-input v-model="form.desc" type="textarea" /></el-form-item><el-form-item><el-button type="primary" @click="onSubmit">Create</el-button><el-button>Cancel</el-button></el-form-item></el-form></Dialog><el-button type="primary" @click="showDialog">点击</el-button></div>
</template><script setup lang="ts">
import Dialog from "@/components/Dialog.vue";
import { ref, reactive } from "vue";
const dialogConfig = ref({show: false, // 控制弹窗显示title: "测试弹窗", // 标题width: "80%", // 宽度top: 50, // 距离顶部高度padding: 15, // 内容内边距showClose: true, // 是否显示关闭按钮showCancel: true, // 是否显示取消按钮buttons: [{text: "确定",click: () => console.log("确定"),type: "success",size: "default",},],
});const showDialog = () => {dialogConfig.value.show = true;
};const form = reactive({name: "",region: "",date1: "",date2: "",delivery: false,type: [],resource: "",desc: "",
});const onSubmit = () => {console.log("submit!");
};
</script><style lang="scss" scoped></style>
效果:
封装后的优势
-
统一配置:所有对话框共享相同的配置项,如关闭行为、按钮文本等
-
简化使用:只需关注对话框内容,无需重复设置相同属性
-
增强功能:
- 内置加载状态
- 统一的确认/取消逻辑
- 可选的关闭前确认
-
更好的可维护性:所有对话框逻辑集中在一个组件中
-
灵活性:
- 保留
el-dialog
的所有属性和事件(通过$attrs
透传) - 支持插槽自定义内容
- 支持自定义底部按钮
- 保留
进阶优化建议
- 国际化支持:将按钮文本等可配置项提取到语言包中
- 主题定制:通过 CSS 变量或 props 允许自定义样式
- 动画效果:添加自定义进入/离开动画
- 表单验证集成:如果主要用于表单,可以集成表单验证逻辑
- 响应式宽度:根据内容自动调整宽度
总结
通过对 el-dialog
的二次封装,我们创建了一个更强大、更易用的对话框组件。这不仅减少了重复代码,还提高了开发效率和代码一致性。在实际项目中,可以根据团队需求进一步扩展这个基础组件,添加更多实用功能。
希望这个封装方案能为你的 Vue 项目开发带来便利!如果你有任何改进建议或使用中的问题,欢迎在评论区交流。