uni-popup.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <template>
  2. <view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
  3. <uni-transition v-if="maskShow" class="uni-mask--hook" :mode-class="['fade']" :styles="maskClass"
  4. :duration="duration" :show="showTrans" @click="onTap" :style="{backgroundColor: maskBg}" />
  5. <uni-transition :mode-class="ani" :styles="transClass" :style="{backgroundColor:maskBg}" :duration="duration"
  6. :show="showTrans" @click="onTap">
  7. <view :style="{ backgroundColor: backgroundColor }" class="uni-popup__wrapper-box" @click.stop="clear">
  8. <slot />
  9. </view>
  10. </uni-transition>
  11. </view>
  12. </template>
  13. <script>
  14. import uniTransition from '../uni-transition/uni-transition.vue'
  15. import popup from './popup.js'
  16. /**
  17. * PopUp 弹出层
  18. * @description 弹出层组件,为了解决遮罩弹层的问题
  19. * @tutorial https://ext.dcloud.net.cn/plugin?id=329
  20. * @property {String} type = [top|center|bottom] 弹出方式
  21. * @value top 顶部弹出
  22. * @value center 中间弹出
  23. * @value bottom 底部弹出
  24. * @value message 消息提示
  25. * @value dialog 对话框
  26. * @value share 底部分享示例
  27. * @property {Boolean} animation = [ture|false] 是否开启动画
  28. * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
  29. * @event {Function} change 打开关闭弹窗触发,e={show: false}
  30. */
  31. export default {
  32. name: 'UniPopup',
  33. components: {
  34. uniTransition
  35. },
  36. props: {
  37. maskBg: {
  38. type: String,
  39. default: 'rgba(0, 0, 0, 0.4)'
  40. },
  41. // 开启动画
  42. animation: {
  43. type: Boolean,
  44. default: true
  45. },
  46. // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
  47. // message: 消息提示 ; dialog : 对话框
  48. type: {
  49. type: String,
  50. default: 'center'
  51. },
  52. // maskClick
  53. maskClick: {
  54. type: Boolean,
  55. default: true
  56. },
  57. backgroundColor: {
  58. type: String,
  59. default: 'transparent'
  60. }
  61. },
  62. provide() {
  63. return {
  64. popup: this
  65. }
  66. },
  67. mixins: [popup],
  68. watch: {
  69. /**
  70. * 监听type类型
  71. */
  72. type: {
  73. handler: function(newVal) {
  74. this[this.config[newVal]]()
  75. },
  76. immediate: true
  77. },
  78. /**
  79. * 监听遮罩是否可点击
  80. * @param {Object} val
  81. */
  82. maskClick: {
  83. handler: function(val) {
  84. this.mkclick = val
  85. },
  86. immediate: true
  87. }
  88. },
  89. data() {
  90. return {
  91. duration: 300,
  92. ani: [],
  93. showPopup: false,
  94. showTrans: false,
  95. maskClass: {
  96. 'position': 'fixed',
  97. 'bottom': 0,
  98. 'top': 0,
  99. 'left': 0,
  100. 'right': 0,
  101. 'backgroundColor': 'rgba(0, 0, 0, 0.4)'
  102. },
  103. transClass: {
  104. 'position': 'fixed',
  105. 'left': 0,
  106. 'right': 0,
  107. },
  108. maskShow: true,
  109. mkclick: true,
  110. popupstyle: 'top'
  111. }
  112. },
  113. created() {
  114. this.mkclick = this.maskClick
  115. if (this.animation) {
  116. this.duration = 300
  117. } else {
  118. this.duration = 0
  119. }
  120. },
  121. methods: {
  122. clear(e) {
  123. // TODO nvue 取消冒泡
  124. e.stopPropagation()
  125. },
  126. open() {
  127. this.showPopup = true
  128. this.$nextTick(() => {
  129. new Promise(resolve => {
  130. clearTimeout(this.timer)
  131. this.timer = setTimeout(() => {
  132. this.showTrans = true
  133. // fixed by mehaotian 兼容 app 端
  134. this.$nextTick(() => {
  135. resolve();
  136. })
  137. }, 50);
  138. }).then(res => {
  139. // 自定义打开事件
  140. clearTimeout(this.msgtimer)
  141. this.msgtimer = setTimeout(() => {
  142. this.customOpen && this.customOpen()
  143. }, 100)
  144. this.$emit('change', {
  145. show: true,
  146. type: this.type
  147. })
  148. })
  149. })
  150. },
  151. close(type) {
  152. this.showTrans = false
  153. this.$nextTick(() => {
  154. this.$emit('change', {
  155. show: false,
  156. type: this.type
  157. })
  158. clearTimeout(this.timer)
  159. // 自定义关闭事件
  160. this.customOpen && this.customClose()
  161. this.timer = setTimeout(() => {
  162. this.showPopup = false
  163. }, 300)
  164. })
  165. },
  166. onTap() {
  167. if (!this.mkclick) return
  168. this.close()
  169. },
  170. /**
  171. * 顶部弹出样式处理
  172. */
  173. top() {
  174. this.popupstyle = 'top'
  175. this.ani = ['slide-top']
  176. this.transClass = {
  177. 'position': 'fixed',
  178. 'left': 0,
  179. 'right': 0,
  180. }
  181. },
  182. /**
  183. * 底部弹出样式处理
  184. */
  185. bottom() {
  186. this.popupstyle = 'bottom'
  187. this.ani = ['slide-bottom']
  188. this.transClass = {
  189. 'position': 'fixed',
  190. 'left': 0,
  191. 'right': 0,
  192. 'bottom': 0
  193. }
  194. },
  195. /**
  196. * 中间弹出样式处理
  197. */
  198. center() {
  199. this.popupstyle = 'center'
  200. this.ani = ['zoom-out', 'fade']
  201. this.transClass = {
  202. 'position': 'fixed',
  203. /* #ifndef APP-NVUE */
  204. 'display': 'flex',
  205. 'flexDirection': 'column',
  206. /* #endif */
  207. 'bottom': 0,
  208. 'left': 0,
  209. 'right': 0,
  210. 'top': 0,
  211. 'justifyContent': 'center',
  212. 'alignItems': 'center'
  213. }
  214. }
  215. }
  216. }
  217. </script>
  218. <style scoped>
  219. @charset "UTF-8";
  220. .uni-popup {
  221. position: fixed;
  222. /* #ifndef APP-NVUE */
  223. z-index: 99;
  224. /* #endif */
  225. }
  226. .uni-popup__mask {
  227. position: absolute;
  228. top: 0;
  229. bottom: 0;
  230. left: 0;
  231. right: 0;
  232. background-color: rgba(0, 0, 0, 0.4);
  233. opacity: 0;
  234. }
  235. .mask-ani {
  236. transition-property: opacity;
  237. transition-duration: 0.2s;
  238. }
  239. .uni-top-mask {
  240. opacity: 1;
  241. }
  242. .uni-bottom-mask {
  243. opacity: 1;
  244. }
  245. .uni-center-mask {
  246. opacity: 1;
  247. }
  248. .uni-popup__wrapper {
  249. /* #ifndef APP-NVUE */
  250. display: block;
  251. /* #endif */
  252. position: absolute;
  253. }
  254. .top {
  255. /* #ifdef H5 */
  256. top: var(--window-top);
  257. /* #endif */
  258. /* #ifndef H5 */
  259. top: 0;
  260. /* #endif */
  261. }
  262. .bottom {
  263. bottom: 0;
  264. }
  265. .uni-popup__wrapper-box {
  266. /* #ifndef APP-NVUE */
  267. display: block;
  268. /* #endif */
  269. position: relative;
  270. /* iphonex 等安全区设置,底部安全区适配 */
  271. /* #ifndef APP-NVUE */
  272. /* padding-bottom: constant(safe-area-inset-bottom);
  273. padding-bottom: env(safe-area-inset-bottom); */
  274. /* #endif */
  275. background-color: #ffffff;
  276. }
  277. .content-ani {
  278. transition-property: transform, opacity;
  279. transition-duration: 0.2s;
  280. }
  281. .uni-top-content {
  282. transform: translateY(0);
  283. }
  284. .uni-bottom-content {
  285. transform: translateY(0);
  286. }
  287. .uni-center-content {
  288. transform: scale(1);
  289. opacity: 1;
  290. }
  291. </style>