@@ -1,9 +1,13 @@
< template >
< view v-if = "showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']" @touchmove.stop.prevent="clear" >
< view @touchstart ="touchstart" >
< uni-transition key = "1" v-if = "maskShow" name="mask" mode-class="fade" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" / >
< uni-transition key = "2" :mode-class = "ani" name = "content" :styles = "transClass" :duration = "duration" :show = "showTrans" @click ="onTap" >
< view class= "u ni-popup__wrapper" : style = "{ backgroundColor: bg }" :class = "[popupstyle]" @click ="clear" > < slot / > < / view >
< view v-if = "showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']" >
< view @touchstart ="touchstart" >
< uni -transition key = "1" v-if = "maskShow" name="mask" mode-class="fade" :styles="maskClass"
:duration = "duration" :show = "showTrans" @click ="onTap" / >
< uni-transition key = "2" :mode- class= "a ni" name = "content" :styles = "transClass" :duration = "duration"
:show = "showTrans" @click ="onTap" >
< view class = "uni-popup__wrapper" : style = "{ backgroundColor: bg }" :class = "[popupstyle]" @click ="clear" >
< slot / >
< / view >
< / uni-transition >
< / view >
<!-- # ifdef H5 -- >
@@ -13,391 +17,458 @@
< / template >
< script >
// #ifdef H5
import keypress from './keypress.js'
// #endif
// #ifdef H5
import keypress from './keypress.js'
// #endif
/**
* PopUp 弹出层
* @description 弹出层组件,为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value left 左侧弹出
* @value right 右侧弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [tu re|false] 是否开启动画
* @property {Boolean} maskClick = [tu re|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发, e={show: false}
* @event {Function} maskClick 点击遮罩触发
*/
/**
* PopUp 弹出层
* @description 弹出层组件,为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value left 左侧弹出
* @value right 右侧弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [tru e|false] 是否开启动画
* @property {Boolean} maskClick = [tru e|false] 蒙版点击是否关闭弹窗(废弃)
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {String} maskBackgroundColor 蒙版颜色
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发, e={show: false}
* @event {Function} maskClick 点击遮罩触发
*/
export default {
name : 'uniPopup' ,
components : {
// #ifdef H5
keypress
// #endif
} ,
emits : [ 'change' , 'maskClick' ] ,
props : {
// 开启动画
animation : {
type : Boolean ,
default : true
} ,
// 弹出层类型, 可选值, top: 顶部弹出层; bottom: 底部弹出层; center: 全屏弹出层
// message: 消息提示 ; dialog : 对话框
type : {
type : String ,
default : 'center'
} ,
// maskClick
maskClick : {
type : Boolean ,
default : true
} ,
backgroundColor : {
type : String ,
default : 'none'
} ,
safeArea : {
type : Boolean ,
default : true
}
} ,
watch : {
/**
* 监听type类型
*/
type : {
handler : function ( type ) {
if ( ! this . config [ type ] ) return
this [ this . config [ type ] ] ( true )
} ,
immediate : true
} ,
isDesktop : {
handler : function ( newVal ) {
if ( ! this . config [ newVal ] ) return
this [ this . config [ this . type ] ] ( true )
} ,
immediate : true
} ,
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick : {
handler : function ( val ) {
this . mkclick = val
} ,
immediate : true
}
} ,
data ( ) {
return {
duration : 300 ,
ani : [ ] ,
showPopup : false ,
showTrans : false ,
popupWidth : 0 ,
popupHeight : 0 ,
config : {
top : 'top' ,
bottom : 'bottom' ,
center : 'center' ,
left : 'left' ,
right : 'right' ,
message : 'top' ,
dialog : 'center' ,
share : 'bottom'
} ,
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
} ,
maskShow : true ,
mkclick : true ,
popupstyle : this . isDesktop ? 'fixforpc-top' : 'top'
}
} ,
computed : {
isDesktop ( ) {
return this . popupWidth >= 500 && this . popupHeight >= 500
} ,
bg ( ) {
if ( this . backgroundColor === '' || this . backgroundColor === 'none' ) {
return 'transparent'
}
return this . backgroundColor
}
} ,
mounted ( ) {
const fixSize = ( ) => {
const { windowWidth , windowHeight , windowTop , safeAreaInsets } = uni . getSystemInfoSync ( )
this . popupWidth = windowWidth
this . popupHeight = windowHeight + windowTop
// 是否适配底部安全区
if ( this . safeArea ) {
this . safeAreaInsets = safeAreaInsets
} else {
this . safeAreaInsets = 0
}
}
fixSize ( )
// #ifdef H5
// window.addEventListener('resize', fixSize)
// this.$once('hook:beforeDestroy', () => {
// window.removeEventListener('resize', fixSize)
// })
// #endif
} ,
created ( ) {
this . mkclick = this . maskClick
if ( this . animation ) {
this . duration = 300
} else {
this . duration = 0
}
// TODO 处理 message 组件生命周期异常的问题
this . messageChild = null
// TODO 解决头条冒泡的问题
this . clearPropagation = false
} ,
methods : {
/**
* 公用方法,不显示遮罩层
*/
closeMask ( ) {
this . maskShow = false
} ,
/**
* 公用方法,遮罩层禁止点击
*/
disableMask ( ) {
this . mkclick = false
} ,
// TODO nvue 取消冒泡
clear ( e ) {
// #ifndef APP-NVUE
e . stopPropagation ( )
export default {
name : 'uniPopup' ,
components : {
// #ifdef H5
keypress
// #endif
this . clearPropagation = true
} ,
emits : [ 'change' , 'maskClick' ] ,
props : {
// 开启动画
animation : {
type : Boolean ,
default : true
} ,
// 弹出层类型, 可选值, top: 顶部弹出层; bottom: 底部弹出层; center: 全屏弹出层
// message: 消息提示 ; dialog : 对话框
type : {
type : String ,
default : 'center'
} ,
// maskClick
isMaskClick : {
type : Boolean ,
default : null
} ,
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
maskClick : {
type : Boolean ,
default : null
} ,
backgroundColor : {
type : String ,
default : 'none'
} ,
safeArea : {
type : Boolean ,
default : true
} ,
maskBackgroundColor : {
type : String ,
default : 'rgba(0, 0, 0, 0.4)'
} ,
} ,
open ( direction ) {
let innerType = [ 'top' , 'center' , 'bottom' , 'left' , 'right' , 'message' , 'dialog' , 'share' ]
if ( ! ( direction && innerType . indexOf ( direction ) !== - 1 ) ) {
direction = this . type
watch : {
/**
* 监听type类型
*/
type : {
handler : function ( type ) {
if ( ! this . config [ type ] ) return
this [ this . config [ type ] ] ( true )
} ,
immediate : true
} ,
isDesktop : {
handler : function ( newVal ) {
if ( ! this . config [ newVal ] ) return
this [ this . config [ this . type ] ] ( true )
} ,
immediate : true
} ,
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick : {
handler : function ( val ) {
this . mkclick = val
} ,
immediate : true
} ,
isMaskClick : {
handler : function ( val ) {
this . mkclick = val
} ,
immediate : true
} ,
// H5 下禁止底部滚动
showPopup ( show ) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document . getElementsByTagName ( 'body' ) [ 0 ] . style . overflow = show ? 'hidden' : 'visible'
// #endif
}
if ( ! this . config [ direction ] ) {
console . error ( '缺少类型:' , direction )
return
} ,
data ( ) {
return {
duration : 300 ,
ani : [ ] ,
showPopup : false ,
showTrans : false ,
popupWidth : 0 ,
popupHeight : 0 ,
config : {
top : 'top' ,
bottom : 'bottom' ,
center : 'center' ,
left : 'left' ,
right : 'right' ,
message : 'top' ,
dialog : 'center' ,
share : 'bottom'
} ,
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
} ,
maskShow : true ,
mkclick : true ,
popupstyle : this . isDesktop ? 'fixforpc-top' : 'top'
}
this [ this . config [ direction ] ] ( )
this . $emit ( 'change' , {
show : true ,
type : direction
} )
} ,
close ( type ) {
this . showTrans = false
this . $emit ( 'change' , {
show : false ,
type : this . type
} )
clearTimeout ( this . timer )
// // 自定义关闭事件
// this.customOpen && this.customClose()
this . timer = setTimeout ( ( ) => {
this . showPopup = false
} , 300 )
} ,
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
touchstart ( ) {
this . clearPropagation = false
} ,
onTap ( ) {
if ( this . clearPropagation ) {
// fix by mehaotian 兼容 nvue
this . clearPropagation = false
return
}
this . $emit ( 'maskClick' )
if ( ! this . mkclick ) return
this . close ( )
} ,
/**
* 顶部弹出样式处理
*/
top ( type ) {
this . popupstyle = this . isDesktop ? 'fixforpc-top' : 'top'
this . ani = [ 'slide-top' ]
this . transClass = {
position : 'fixed' ,
left : 0 ,
right : 0 ,
backgroundColor : this . bg
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
this . $nextTick ( ( ) => {
if ( this . messageChild && this . type === 'message' ) {
this . messageChild . timerClose ( )
computed : {
isDesktop ( ) {
return this . popupWidth >= 500 && this . popupHeight >= 500
} ,
bg ( ) {
if ( this . backgroundColor === '' || this . backgroundColor === 'none' ) {
return 'transparent'
}
} )
return this . backgroundColor
}
} ,
/**
* 底部弹出样式处理
*/
bottom ( type ) {
this . popupstyle = 'bottom'
this . ani = [ 'slide-bottom' ]
mounted ( ) {
const fixSize = ( ) => {
const {
windowWidth ,
windowHeight ,
windowTop ,
safeArea ,
screenHeight ,
safeAreaInsets
} = uni . getSystemInfoSync ( )
this . popupWidth = windowWidth
this . popupHeight = windowHeight + ( windowTop || 0 )
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
if ( safeArea && this . safeArea ) {
// #ifdef MP-WEIXIN
this . safeAreaInsets = screenHeight - safeArea . bottom
// #endif
// #ifndef MP-WEIXIN
this . safeAreaInsets = safeAreaInsets . bottom
// #endif
} else {
this . safeAreaInsets = 0
}
}
fixSize ( )
// #ifdef H5
// window.addEventListener('resize', fixSize)
// this.$once('hook:beforeDestroy', () => {
// window.removeEventListener('resize', fixSize)
// })
// #endif
} ,
// #ifndef VUE3
// TODO vue2
destroyed ( ) {
this . setH5Visible ( )
} ,
// #endif
// #ifdef VUE3
// TODO vue3
unmounted ( ) {
this . setH5Visible ( )
} ,
// #endif
created ( ) {
// this.mkclick = this.isMaskClick || this.maskClick
if ( this . isMaskClick === null && this . maskClick === null ) {
this . mkclick = true
} else {
this . mkclick = this . isMaskClick !== null ? this . isMaskClick : this . maskClick
}
if ( this . animation ) {
this . duration = 300
} else {
this . duration = 0
}
// TODO 处理 message 组件生命周期异常的问题
this . messageChild = null
// TODO 解决头条冒泡的问题
this . clearPropagation = false
this . maskClass . backgroundColor = this . maskBackgroundColor
} ,
methods : {
setH5Visible ( ) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
document . getElementsByTagName ( 'body' ) [ 0 ] . style . overflow = 'visible'
// #endif
} ,
/**
* 公用方法,不显示遮罩层
*/
closeMask ( ) {
this . maskShow = false
} ,
/**
* 公用方法,遮罩层禁止点击
*/
disableMask ( ) {
this . mkclick = false
} ,
// TODO nvue 取消冒泡
clear ( e ) {
// #ifndef APP-NVUE
e . stopPropagation ( )
// #endif
this . clearPropagation = true
} ,
this . transClass = {
position : 'fixed' ,
left : 0 ,
right : 0 ,
bottom : 0 ,
paddingBottom : ( this . safeAreaInsets && this . safeAreaInsets . bottom ) || 0 ,
backgroundColor : this . bg
open ( direction ) {
// fix by mehaotian 处理快速打开关闭的情况
if ( this . showPopup ) {
clearTimeout ( this . timer )
this . showPopup = false
}
let innerType = [ 'top' , 'center' , 'bottom' , 'left' , 'right' , 'message' , 'dialog' , 'share' ]
if ( ! ( direction && innerType . indexOf ( direction ) !== - 1 ) ) {
direction = this . type
}
if ( ! this . config [ direction ] ) {
console . error ( '缺少类型:' , direction )
return
}
this [ this . config [ direction ] ] ( )
this . $emit ( 'change' , {
show : true ,
type : direction
} )
} ,
close ( type ) {
this . showTrans = false
this . $emit ( 'change' , {
show : false ,
type : this . type
} )
clearTimeout ( this . timer )
// // 自定义关闭事件
// this.customOpen && this.customClose()
this . timer = setTimeout ( ( ) => {
this . showPopup = false
} , 300 )
} ,
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
touchstart ( ) {
this . clearPropagation = false
} ,
onTap ( ) {
if ( this . clearPropagation ) {
// fix by mehaotian 兼容 nvue
this . clearPropagation = false
return
}
this . $emit ( 'maskClick' )
if ( ! this . mkclick ) return
this . close ( )
} ,
/**
* 顶部弹出样式处理
*/
top ( type ) {
this . popupstyle = this . isDesktop ? 'fixforpc-top' : 'top'
this . ani = [ 'slide-top' ]
this . transClass = {
position : 'fixed' ,
left : 0 ,
right : 0 ,
backgroundColor : this . bg
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
this . $nextTick ( ( ) => {
if ( this . messageChild && this . type === 'message' ) {
this . messageChild . timerClose ( )
}
} )
} ,
/**
* 底部弹出样式处理
*/
bottom ( type ) {
this . popupstyle = 'bottom'
this . ani = [ 'slide-bottom' ]
this . transClass = {
position : 'fixed' ,
left : 0 ,
right : 0 ,
bottom : 0 ,
paddingBottom : this . safeAreaInsets + 'px' ,
backgroundColor : this . bg
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
/**
* 中间弹出样式处理
*/
center ( type ) {
this . popupstyle = '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'
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
left ( type ) {
this . popupstyle = 'left'
this . ani = [ 'slide-left' ]
this . transClass = {
position : 'fixed' ,
left : 0 ,
bottom : 0 ,
top : 0 ,
backgroundColor : this . bg ,
/* #ifndef APP-NVUE */
display : 'flex' ,
flexDirection : 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
right ( type ) {
this . popupstyle = 'right'
this . ani = [ 'slide-right' ]
this . transClass = {
position : 'fixed' ,
bottom : 0 ,
right : 0 ,
top : 0 ,
backgroundColor : this . bg ,
/* #ifndef APP-NVUE */
display : 'flex' ,
flexDirection : 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
/**
* 中间弹出样式处理
*/
center ( type ) {
this . popupstyle = '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'
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
left ( type ) {
this . popupstyle = 'left'
this . ani = [ 'slide-left' ]
this . transClass = {
position : 'fixed' ,
left : 0 ,
bottom : 0 ,
top : 0 ,
backgroundColor : this . bg ,
/* #ifndef APP-NVUE */
display : 'flex' ,
flexDirection : 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
} ,
right ( type ) {
this . popupstyle = 'right'
this . ani = [ 'slide-right' ]
this . transClass = {
position : 'fixed' ,
bottom : 0 ,
right : 0 ,
top : 0 ,
backgroundColor : this . bg ,
/* #ifndef APP-NVUE */
display : 'flex' ,
flexDirection : 'column'
/* #endif */
}
// TODO 兼容 type 属性 ,后续会废弃
if ( type ) return
this . showPopup = true
this . showTrans = true
}
}
}
< / script >
< style lang = "scss" scoped >
. uni - popup {
position : fixed ;
/* #ifndef APP-NVUE */
z - index : 99 ;
/* #endif */
& . top ,
& . left ,
& . right {
/* #ifdef H5 */
top : var ( -- window - top ) ;
/* #endif */
/* #ifndef H5 */
top : 0 ;
/* #endif */
}
. uni - popup _ _wrapper {
< style lang = "scss" >
. uni - popup {
position : fixed ;
/* #ifndef APP-NVUE */
display : block ;
/* #endif */
position : relative ;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
z - index : 99 ;
/* #endif */
& . top ,
& . left ,
& . right {
/* #ifdef H5 */
padding - top: var ( -- window - top ) ;
top : var ( -- window - top ) ;
/* #endif */
/* #ifndef H5 */
padding - top: 0 ;
top : 0 ;
/* #endif */
flex : 1 ;
}
. uni - popup _ _wrapper {
/* #ifndef APP-NVUE */
display : block ;
/* #endif */
position : relative ;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
// padding-bottom: constant(safe-area-inset-bottom);
// padding-bottom: env(safe-area-inset-bottom);
/* #endif */
& . left ,
& . right {
/* #ifdef H5 */
padding - top : var ( -- window - top ) ;
/* #endif */
/* #ifndef H5 */
padding - top : 0 ;
/* #endif */
flex : 1 ;
}
}
}
}
. fixforpc - z - index {
/* #ifndef APP-NVUE */
z - index : 999 ;
/* #endif */
}
. fixforpc - z - index {
/* #ifndef APP-NVUE */
z - index : 999 ;
/* #endif */
}
. fixforpc - top {
top : 0 ;
}
. fixforpc - top {
top : 0 ;
}
< / style >