<template> <view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear"> <uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" /> <uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap"> <view class="uni-popup__wrapper-box" @click.stop="clear"> <slot /> </view> </uni-transition> </view> </template> <script> import uniTransition from '../uni-transition/uni-transition.vue' /** * PopUp 弹出层 * @description 弹出层组件,为了解决遮罩弹层的问题 * @tutorial https://ext.dcloud.net.cn/plugin?id=329 * @property {String} type = [top|center|bottom] 弹出方式 * @value top 顶部弹出 * @value center 中间弹出 * @value bottom 底部弹出 * @property {Boolean} animation = [ture|false] 是否开启动画 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗 * @event {Function} change 打开关闭弹窗触发,e={show: false} */ export default { name: 'UniPopup', components: { uniTransition }, props: { // 开启动画 animation: { type: Boolean, default: true }, // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层 type: { type: String, default: 'center' }, // maskClick maskClick: { type: Boolean, default: true } }, data() { return { duration: 300, ani: [], showPopup: false, showTrans: false, maskClass: { 'position': 'fixed', 'bottom': 0, 'top': 0, 'left': 0, 'right': 0, 'backgroundColor': 'rgba(0, 0, 0, 0.4)' }, transClass: { 'position': 'fixed', 'left': 0, 'right': 0, } } }, watch: { type: { handler: function(newVal) { switch (this.type) { case 'top': this.ani = ['slide-top'] this.transClass = { 'position': 'fixed', 'left': 0, 'right': 0, } break case 'bottom': this.ani = ['slide-bottom'] this.transClass = { 'position': 'fixed', 'left': 0, 'right': 0, 'bottom': 0 } break case 'center': this.ani = ['zoom-out', 'fade'] this.transClass = { 'position': 'fixed', /* #ifndef APP-NVUE */ 'display': 'flex', 'flexDirection': 'column', /* #endif */ 'bottom': 0, 'left': 0, 'right': 0, 'top': 0, 'justifyContent': 'center', 'alignItems': 'center' } break } }, immediate: true } }, created() { if (this.animation) { this.duration = 300 } else { this.duration = 0 } }, methods: { clear(e) { // TODO nvue 取消冒泡 e.stopPropagation() }, open() { this.showPopup = true this.$nextTick(() => { clearTimeout(this.timer) this.timer = setTimeout(() => { this.showTrans = true }, 50); }) this.$emit('change', { show: true }) }, close(type) { this.showTrans = false this.$nextTick(() => { clearTimeout(this.timer) this.timer = setTimeout(() => { this.$emit('change', { show: false }) this.showPopup = false }, 300) }) }, onTap() { if (!this.maskClick) return this.close() } } } </script> <style lang="scss" scoped> .uni-popup { position: fixed; /* #ifdef H5 */ top: var(--window-top); /* #endif */ /* #ifndef H5 */ top: 0; /* #endif */ bottom: 0; left: 0; right: 0; /* #ifndef APP-NVUE */ z-index: 99; /* #endif */ } .uni-popup__mask { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: $uni-bg-color-mask; opacity: 0; } .mask-ani { transition-property: opacity; transition-duration: 0.2s; } .uni-top-mask { opacity: 1; } .uni-bottom-mask { opacity: 1; } .uni-center-mask { opacity: 1; } .uni-popup__wrapper { /* #ifndef APP-NVUE */ display: block; /* #endif */ position: absolute; } .top { top: 0; left: 0; right: 0; transform: translateY(-500px); } .bottom { bottom: 0; left: 0; right: 0; transform: translateY(500px); } .center { /* #ifndef APP-NVUE */ display: flex; flex-direction: column; /* #endif */ bottom: 0; left: 0; right: 0; top: 0; justify-content: center; align-items: center; transform: scale(1.2); opacity: 0; } .uni-popup__wrapper-box { /* #ifndef APP-NVUE */ display: block; /* #endif */ position: relative; } .content-ani { // transition: transform 0.3s; transition-property: transform, opacity; transition-duration: 0.2s; } .uni-top-content { transform: translateY(0); } .uni-bottom-content { transform: translateY(0); } .uni-center-content { transform: scale(1); opacity: 1; } </style>