uni-popup.vue 6.2 KB

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