• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

设计一个简单的Vue模态组件

开发技术 开发技术 5个月前 (05-12) 126次浏览

在使用ElementUI组件库中,MessageBox组件(包括注入原型链中的$confirm方法,实际上就是MessageBox实例)和Dialog组件本质上都是模态,但ElementUI并未提供一个统一的模态组件,虽然Dialog组件可以充当这一角色,但是在使用时容易踩坑,比如我之前的一篇文章 深入了解el-dialog组件中插槽的生命周期 中说到的子组件生命周期的问题,又或者没有title时样式错位问题等,遂自己写造轮子,写了一个模态组件。

设计

一个模态框的整体设计如下: 界面:包括6个部分:遮罩层shadow,标题title,关闭按钮close,内容body,页脚footer; 操作:鼠标点击关闭按钮关闭模态,鼠标点击遮罩层关闭模态; 灵活性:提供标题title,页脚footer和内容body的插槽slot,也可以通过title属性和body属性传入。

页面结构如下

考虑点击遮罩层关闭的效果,采用 点击遮罩层非内容区域关闭遮罩层的三种思路四种方案 中的方案二。

<template> <!-- 模态框 --> <div v-if="selfVisible"> <div @click="handleClickShadow"></div> <div :style="{ width: width || '500px' }"> <i v-if="showClose" @click="close"></i> <div :class="{ 'modal-header': title || $slots.title }"> <slot name="title">{{title}}</slot> </div> <div> <slot>{{body}}</slot> </div> <div :class="{ 'modal-footer': $slots.footer }"> <slot name="footer"></slot> </div> </div> </div> </template> 复制代码

组件内部逻辑

主要包含三部分:props,data和method,其中需要监听visible属性修改模态内部selfVisible状态。

<script> export default { name: 'Modal', props: { // 标题 title:{ type: String, default: '' }, // 内容 body: { type: String, default: '' }, // 是否显示模态框 visible: { type: Boolean, default: true }, // 展示关闭按钮 showClose: { type: Boolean, default: true }, // 弹框宽度 width: { type: String, default: '500px' }, // 点击遮罩层关闭模态 closeOnClickShadow: { type: Boolean, default: false } }, data () { return { selfVisible: false } }, watch: { visible: { handler (newVal) { if (this.visible) { this.open() } else { this.close() } }, immediate: true } }, methods: { // 打开 open () { this.selfVisible = true this.$emit('opened') }, // 关闭 close () { this.selfVisible = false this.$emit('closed') this.$emit('update:visible', false) }, // 点击遮罩层 handleClickShadow () { if (this.closeOnClickShadow) { this.close() } }, } } </script> 复制代码

样式

包裹元素和遮罩层fixed定位,内容区域absolute定位,其余样式主要就是美化。

<style lang="less"> .modal-wrap { position: fixed; top: 0; left: 0; z-index: 9999; .modal-shadow { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0,0,0,.5); width: 100%; height: 100%; z-index: 10000; } .modal-content { background-color: #fff; box-shadow: 2px 2px 20px 1px; overflow-x: auto; border-radius: 16px; position: absolute; transform: translate(50%, 50%); z-index: 10001; .modal-content-icon__close { cursor: pointer; position: absolute; top: 8px; right: 8px; line-height: 20px; height: 20px; z-index: 10001; font-size: 18px; } .modal-content-icon__close:hover { color: #66b1ff; animation: rotate360 0.5s; @keyframes rotate360 { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } } .modal-header { border-bottom: 1px solid #eee; color: #313131; padding: 15px; font-size: 18px; text-align: left; } .modal-body { position: relative; padding: 20px; font-size: 16px; text-align: center; } .modal-footer { border-top: 1px solid #eee; padding: 15px; display: flex; justify-content: flex-end; align-items: center; } } } </style> 复制代码


喜欢 (0)