【vue系列】封装公共弹窗组件的正确方式

您所在的位置:网站首页 vue项目中封装过哪些组件 【vue系列】封装公共弹窗组件的正确方式

【vue系列】封装公共弹窗组件的正确方式

#【vue系列】封装公共弹窗组件的正确方式| 来源: 网络整理| 查看: 265

最近一个项目向Vue框架搭建的新项目迁移,但是项目中没有使用vue ui库,也还没有封装公用的弹窗组件。于是我就实现了一个简单的弹窗组件。在开发的之前考虑到以下几点:

组件标题,按钮文案,按钮个数、弹窗内容均可定制化;

弹窗垂直水平居中 考虑实际在微信环境头部不可用,ios微信环境中底部返回按钮的空间占用;

遮罩层和弹窗内容分离,点击遮罩层关闭弹窗;

多个弹窗同时出现时弹窗的z-index要不之前的要高;

点击遮罩层关闭弹窗和处理弹窗底部的页面内容不可滚动.

其中包含了要实现的主要功能,以及要处理的问题。

实现的步骤 完成页面结构和样式以及过渡动画 定制弹窗标题、按钮和主题内容 组件开关 z-index处理 点击遮罩层关闭弹窗 处理弹窗底部的页面内容不可滚动 1. 完成页面结构和样式

先创建一个弹窗组件vue文件,实现基本的结构与样式。

临时标题 临时内容 确定 export default { data(){ return {} } } // 弹窗动画 .dialog-enter-active, .dialog-leave-active { transition: opacity .5s; } .dialog-enter, .dialog-leave-to { opacity: 0; } // 最外层 设置position定位 // 遮罩 设置背景层,z-index值要足够大确保能覆盖,高度 宽度设置满 做到全屏遮罩 .dialog { position: fixed; top: 0; right: 0; width: 100%; height: 100%; // 内容层 z-index要比遮罩大,否则会被遮盖 .dialog-mark { position: absolute; top: 0; height: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, .6); } .dialog-sprite { // 移动端使用felx布局 position: absolute; top: 10%; left: 15%; right: 15%; bottom: 25%; display: flex; flex-direction: column; max-height: 75%; min-height: 180px; overflow: hidden; z-index: 23456765435; background: #fff; border-radius: 8px; .header { padding: 15px; text-align: center; font-size: 18px; font-weight: 700; color: #333; } .dialog-body { flex: 1; overflow-x: hidden; overflow-y: scroll; padding: 0 15px 20px 15px; } .dialog-footer { position: relative; display: flex; width: 100%; // flex-shrink: 1; &::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 1px; background: #ddd; transform: scaleY(.5); } .btn { flex: 1; text-align: center; padding: 15px; font-size: 17px; &:nth-child(2) { position: relative; &::after { content: ''; position: absolute; left: 0; top: 0; width: 1px; height: 100%; background: #ddd; transform: scaleX(.5); } } } .btn-confirm { color: #43ac43; } } } } 2. 定制弹窗标题、按钮和主题内容

省略样式代码,我们将标题设置为可定制化传入,且为必传属性。

按钮默认显示一个确认按钮,可以定制化确认按钮的文案,以及可以显示取消按钮,并且可定制化取消按钮的文案,以及它们的点击事件的处理。

主题内容建议使用slot插槽处理。不清楚的可以到vue官网学习slot。

{{ title }} {{cancelText}} {{confirmText}} export default { props: { title: String, showCancel: { typs: Boolean, default: false, required: false, }, cancelText: { type: String, default: '取消', required: false, }, confirmText: { type: String, default: '确定', required: false, }, }, data() { return { name: 'dialog', } }, ... methods: { /** 取消按钮操作 */ cancel() { this.$emit('cancel', false); }, /** 确认按钮操作 */ confirm() { this.$emit('confirm', false) }, } } 3. 组件开关

弹窗组件的开关由外部控制,但是没有直接使用show来直接控制。而是对show进行监听,赋值给组件内部变量showSelf。

这样处理也会方便组件内部控制弹窗的隐藏。下文中的点击遮罩层关闭弹窗就是基于这点来处理的。

// 只展示了开关相关代码 export default { props: { //弹窗组件是否显示 默认不显示 必传属性 show: { type: Boolean, default: false, required: true, }, }, data() { return { showSelf: false, } }, watch: { show(val) { if (!val) { this.closeMyself() } else { this.showSelf = val } } }, created() { this.showSelf = this.show; }, } 4. z-index处理

首先我们要保证弹窗组件的层级z-inde足够高,其次要确保弹窗内容的层级比弹窗遮罩层的层级高。

后弹出的弹窗比早弹出的弹窗层级高。(没有完全确保实现)

... export default { data() { return { zIndex: this.getZIndex(), } }, methods: { /** 每次获取之后 zindex 自动增加 */ getZIndex() { let zIndexInit = 20190315; return zIndexInit++ }, } } 5. 点击遮罩层关闭弹窗和处理弹窗底部的页面内容不可滚动

这里我们需要注意的地方是,当组件挂载完成之后,通过给body设置overflow为hidden,来防止滑动弹窗时,弹窗下的页面滚动。

当点击遮罩层层时,我们在组件内部就可以将弹窗组件隐藏。v-if隐藏时也是该组件的销毁。

export default { data() { return { zIndex: this.getZIndex(), } }, mounted() { this.forbidScroll() }, methods: { /** 禁止页面滚动 */ forbidScroll() { this.bodyOverflow = document.body.style.overflow document.body.style.overflow = 'hidden' }, /** 点击遮罩关闭弹窗 */ closeMyself(event) { this.showSelf = false; this.solveBodyOverflow() }, /** 恢复页面的滚动 */ solveBodyOverflow() { document.body.style.overflow = this.bodyOverflow; }, } } 组件最后实现的效果

1.jpeg

2.jpeg

最终的完整组件代码如下:

{{ title }} {{cancelText}} {{confirmText}} export default { props: { //弹窗组件是否显示 默认不显示 必传属性 show: { type: Boolean, default: false, required: true, }, title: { type: String, required: true, }, showCancel: { typs: Boolean, default: false, required: false, }, cancelText: { type: String, default: '取消', required: false, }, confirmText: { type: String, default: '确定', required: false, }, }, data() { return { name: 'dialog', showSelf: false, zIndex: this.getZIndex(), bodyOverflow: '' } }, watch: { show(val) { if (!val) { this.closeMyself() } else { this.showSelf = val } } }, created() { this.showSelf = this.show; }, mounted() { this.forbidScroll() }, methods: { /** 禁止页面滚动 */ forbidScroll() { this.bodyOverflow = document.body.style.overflow document.body.style.overflow = 'hidden' }, /** 每次获取之后 zindex 自动增加 */ getZIndex() { let zIndexInit = 20190315; return zIndexInit++ }, /** 取消按钮操作 */ cancel() { this.$emit('cancel', false); }, /** 确认按钮操作 */ confirm() { this.$emit('confirm', false) }, /** 点击遮罩关闭弹窗 */ closeMyself(event) { this.showSelf = false; this.sloveBodyOverflow() }, /** 恢复页面的滚动 */ sloveBodyOverflow() { document.body.style.overflow = this.bodyOverflow; }, } } // 弹窗动画 .dialog-enter-active, .dialog-leave-active { transition: opacity .5s; } .dialog-enter, .dialog-leave-to { opacity: 0; } // 最外层 设置position定位 // 遮罩 设置背景层,z-index值要足够大确保能覆盖,高度 宽度设置满 做到全屏遮罩 .dialog { position: fixed; top: 0; right: 0; width: 100%; height: 100%; // 内容层 z-index要比遮罩大,否则会被遮盖 .dialog-mark { position: absolute; top: 0; height: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, .6); } } .dialog-sprite { // 移动端使用felx布局 position: absolute; top: 10%; left: 15%; right: 15%; bottom: 25%; display: flex; flex-direction: column; max-height: 75%; min-height: 180px; overflow: hidden; z-index: 23456765435; background: #fff; border-radius: 8px; .header { padding: 15px; text-align: center; font-size: 18px; font-weight: 700; color: #333; } .dialog-body { flex: 1; overflow-x: hidden; overflow-y: scroll; padding: 0 15px 20px 15px; } .dialog-footer { position: relative; display: flex; width: 100%; // flex-shrink: 1; &::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 1px; background: #ddd; transform: scaleY(.5); } .btn { flex: 1; text-align: center; padding: 15px; font-size: 17px; &:nth-child(2) { position: relative; &::after { content: ''; position: absolute; left: 0; top: 0; width: 1px; height: 100%; background: #ddd; transform: scaleX(.5); } } } .btn-confirm { color: #43ac43; } } } 使用方式 在父组件中将弹窗组件引入 import TheDialog from './component/TheDialog' 组件中components注册 components: { TheDialog } 在template中使用 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 主题内容 export default { data() { return { // 控制两个弹窗组件的初始显示与隐藏 showDialog: true, showDialog2: true, } }, methods: { cancel(show) { this.showDialog = show }, confirm(show) { this.showDialog = show }, cancel2(show) { this.showDialog2 = show }, confirm2(show) { this.showDialog2 = show; }, } }

此文简单记录了一个简单弹窗组件的实现步骤。主要使用了vue的slot插槽接受父组件传来的弹窗内容;通过props接收从父组件传过来的弹窗定制化设置以及控制弹窗的显示与隐藏;子组件通过$emit监听事件传送到父组件去进行逻辑处理。

其它

不看后悔的Vue系列,在这里:juejin.im/post/684490…

很多学习 Vue 的小伙伴知识碎片化严重,我整理出系统化的一套关于Vue的学习系列博客。在自我成长的道路上,也希望能够帮助更多人进步。戳 链接



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3