You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

264 lines
4.9 KiB

  1. <template>
  2. <view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
  3. <uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
  4. <uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
  5. <view class="uni-popup__wrapper-box" @click.stop="clear">
  6. <slot />
  7. </view>
  8. </uni-transition>
  9. </view>
  10. </template>
  11. <script>
  12. import uniTransition from '../uni-transition/uni-transition.vue'
  13. /**
  14. * PopUp 弹出层
  15. * @description 弹出层组件,为了解决遮罩弹层的问题
  16. * @tutorial https://ext.dcloud.net.cn/plugin?id=329
  17. * @property {String} type = [top|center|bottom] 弹出方式
  18. * @value top 顶部弹出
  19. * @value center 中间弹出
  20. * @value bottom 底部弹出
  21. * @property {Boolean} animation = [ture|false] 是否开启动画
  22. * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
  23. * @event {Function} change 打开关闭弹窗触发,e={show: false}
  24. */
  25. export default {
  26. name: 'UniPopup',
  27. components: {
  28. uniTransition
  29. },
  30. props: {
  31. // 开启动画
  32. animation: {
  33. type: Boolean,
  34. default: true
  35. },
  36. // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
  37. type: {
  38. type: String,
  39. default: 'center'
  40. },
  41. // maskClick
  42. maskClick: {
  43. type: Boolean,
  44. default: true
  45. }
  46. },
  47. data() {
  48. return {
  49. duration: 300,
  50. ani: [],
  51. showPopup: false,
  52. showTrans: false,
  53. maskClass: {
  54. 'position': 'fixed',
  55. 'bottom': 0,
  56. 'top': 0,
  57. 'left': 0,
  58. 'right': 0,
  59. 'backgroundColor': 'rgba(0, 0, 0, 0.4)'
  60. },
  61. transClass: {
  62. 'position': 'fixed',
  63. 'left': 0,
  64. 'right': 0,
  65. }
  66. }
  67. },
  68. watch: {
  69. type: {
  70. handler: function(newVal) {
  71. switch (this.type) {
  72. case 'top':
  73. this.ani = ['slide-top']
  74. this.transClass = {
  75. 'position': 'fixed',
  76. 'left': 0,
  77. 'right': 0,
  78. }
  79. break
  80. case 'bottom':
  81. this.ani = ['slide-bottom']
  82. this.transClass = {
  83. 'position': 'fixed',
  84. 'left': 0,
  85. 'right': 0,
  86. 'bottom': 0
  87. }
  88. break
  89. case 'center':
  90. this.ani = ['zoom-out', 'fade']
  91. this.transClass = {
  92. 'position': 'fixed',
  93. /* #ifndef APP-NVUE */
  94. 'display': 'flex',
  95. 'flexDirection': 'column',
  96. /* #endif */
  97. 'bottom': 0,
  98. 'left': 0,
  99. 'right': 0,
  100. 'top': 0,
  101. 'justifyContent': 'center',
  102. 'alignItems': 'center'
  103. }
  104. break
  105. }
  106. },
  107. immediate: true
  108. }
  109. },
  110. created() {
  111. if (this.animation) {
  112. this.duration = 300
  113. } else {
  114. this.duration = 0
  115. }
  116. },
  117. methods: {
  118. clear(e) {
  119. // TODO nvue 取消冒泡
  120. e.stopPropagation()
  121. },
  122. open() {
  123. this.showPopup = true
  124. this.$nextTick(() => {
  125. clearTimeout(this.timer)
  126. this.timer = setTimeout(() => {
  127. this.showTrans = true
  128. }, 50);
  129. })
  130. this.$emit('change', {
  131. show: true
  132. })
  133. },
  134. close(type) {
  135. this.showTrans = false
  136. this.$nextTick(() => {
  137. clearTimeout(this.timer)
  138. this.timer = setTimeout(() => {
  139. this.$emit('change', {
  140. show: false
  141. })
  142. this.showPopup = false
  143. }, 300)
  144. })
  145. },
  146. onTap() {
  147. if (!this.maskClick) return
  148. this.close()
  149. }
  150. }
  151. }
  152. </script>
  153. <style lang="scss" scoped>
  154. .uni-popup {
  155. position: fixed;
  156. /* #ifdef H5 */
  157. top: var(--window-top);
  158. /* #endif */
  159. /* #ifndef H5 */
  160. top: 0;
  161. /* #endif */
  162. bottom: 0;
  163. left: 0;
  164. right: 0;
  165. /* #ifndef APP-NVUE */
  166. z-index: 99;
  167. /* #endif */
  168. }
  169. .uni-popup__mask {
  170. position: absolute;
  171. top: 0;
  172. bottom: 0;
  173. left: 0;
  174. right: 0;
  175. background-color: $uni-bg-color-mask;
  176. opacity: 0;
  177. }
  178. .mask-ani {
  179. transition-property: opacity;
  180. transition-duration: 0.2s;
  181. }
  182. .uni-top-mask {
  183. opacity: 1;
  184. }
  185. .uni-bottom-mask {
  186. opacity: 1;
  187. }
  188. .uni-center-mask {
  189. opacity: 1;
  190. }
  191. .uni-popup__wrapper {
  192. /* #ifndef APP-NVUE */
  193. display: block;
  194. /* #endif */
  195. position: absolute;
  196. }
  197. .top {
  198. top: 0;
  199. left: 0;
  200. right: 0;
  201. transform: translateY(-500px);
  202. }
  203. .bottom {
  204. bottom: 0;
  205. left: 0;
  206. right: 0;
  207. transform: translateY(500px);
  208. }
  209. .center {
  210. /* #ifndef APP-NVUE */
  211. display: flex;
  212. flex-direction: column;
  213. /* #endif */
  214. bottom: 0;
  215. left: 0;
  216. right: 0;
  217. top: 0;
  218. justify-content: center;
  219. align-items: center;
  220. transform: scale(1.2);
  221. opacity: 0;
  222. }
  223. .uni-popup__wrapper-box {
  224. /* #ifndef APP-NVUE */
  225. display: block;
  226. /* #endif */
  227. position: relative;
  228. }
  229. .content-ani {
  230. // transition: transform 0.3s;
  231. transition-property: transform, opacity;
  232. transition-duration: 0.2s;
  233. }
  234. .uni-top-content {
  235. transform: translateY(0);
  236. }
  237. .uni-bottom-content {
  238. transform: translateY(0);
  239. }
  240. .uni-center-content {
  241. transform: scale(1);
  242. opacity: 1;
  243. }
  244. </style>