wd-popup.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <template>
  2. <wd-root-portal v-if="rootPortal">
  3. <view class="wd-popup-wrapper">
  4. <wd-overlay
  5. v-if="modal"
  6. :show="modelValue"
  7. :z-index="zIndex"
  8. :lock-scroll="lockScroll"
  9. :duration="duration"
  10. :custom-style="modalStyle"
  11. @click="handleClickModal"
  12. @touchmove="noop"
  13. />
  14. <wd-transition
  15. :lazy-render="lazyRender"
  16. :custom-class="rootClass"
  17. :custom-style="style"
  18. :duration="duration"
  19. :show="modelValue"
  20. :name="transitionName"
  21. :destroy="hideWhenClose"
  22. @before-enter="emit('before-enter')"
  23. @enter="emit('enter')"
  24. @after-enter="emit('after-enter')"
  25. @before-leave="emit('before-leave')"
  26. @leave="emit('leave')"
  27. @after-leave="emit('after-leave')"
  28. >
  29. <slot />
  30. <wd-icon v-if="closable" custom-class="wd-popup__close" name="add" @click="close" />
  31. </wd-transition>
  32. </view>
  33. </wd-root-portal>
  34. <!-- 非传送模式 -->
  35. <view v-else class="wd-popup-wrapper">
  36. <wd-overlay
  37. v-if="modal"
  38. :show="modelValue"
  39. :z-index="zIndex"
  40. :lock-scroll="lockScroll"
  41. :duration="duration"
  42. :custom-style="modalStyle"
  43. @click="handleClickModal"
  44. @touchmove="noop"
  45. />
  46. <wd-transition
  47. :lazy-render="lazyRender"
  48. :custom-class="rootClass"
  49. :custom-style="style"
  50. :duration="duration"
  51. :show="modelValue"
  52. :name="transitionName"
  53. :destroy="hideWhenClose"
  54. @before-enter="emit('before-enter')"
  55. @enter="emit('enter')"
  56. @after-enter="emit('after-enter')"
  57. @before-leave="emit('before-leave')"
  58. @leave="emit('leave')"
  59. @after-leave="emit('after-leave')"
  60. >
  61. <slot />
  62. <wd-icon v-if="closable" custom-class="wd-popup__close" name="add" @click="close" />
  63. </wd-transition>
  64. </view>
  65. </template>
  66. <script lang="ts">
  67. export default {
  68. name: 'wd-popup',
  69. options: {
  70. virtualHost: true,
  71. addGlobalClass: true,
  72. styleIsolation: 'shared'
  73. }
  74. }
  75. </script>
  76. <script lang="ts" setup>
  77. import { computed, onBeforeMount, ref } from 'vue'
  78. import wdIcon from '../wd-icon/wd-icon.vue'
  79. import wdOverlay from '../wd-overlay/wd-overlay.vue'
  80. import wdTransition from '../wd-transition/wd-transition.vue'
  81. import wdRootPortal from '../wd-root-portal/wd-root-portal.vue'
  82. import { popupProps } from './types'
  83. import type { TransitionName } from '../wd-transition/types'
  84. const props = defineProps(popupProps)
  85. const emit = defineEmits([
  86. 'update:modelValue',
  87. 'before-enter',
  88. 'enter',
  89. 'before-leave',
  90. 'leave',
  91. 'after-leave',
  92. 'after-enter',
  93. 'click-modal',
  94. 'close'
  95. ])
  96. /**
  97. * 弹出位置
  98. */
  99. const transitionName = computed<TransitionName | TransitionName[]>(() => {
  100. if (props.transition) {
  101. return props.transition
  102. }
  103. if (props.position === 'center') {
  104. return ['zoom-in', 'fade']
  105. }
  106. if (props.position === 'left') {
  107. return 'slide-left'
  108. }
  109. if (props.position === 'right') {
  110. return 'slide-right'
  111. }
  112. if (props.position === 'bottom') {
  113. return 'slide-up'
  114. }
  115. if (props.position === 'top') {
  116. return 'slide-down'
  117. }
  118. return 'slide-up'
  119. })
  120. const safeBottom = ref<number>(0)
  121. const style = computed(() => {
  122. return `z-index:${props.zIndex}; padding-bottom: ${safeBottom.value}px;${props.customStyle}`
  123. })
  124. const rootClass = computed(() => {
  125. return `wd-popup wd-popup--${props.position} ${!props.transition && props.position === 'center' ? 'is-deep' : ''} ${props.customClass || ''}`
  126. })
  127. onBeforeMount(() => {
  128. if (props.safeAreaInsetBottom) {
  129. const { safeArea, screenHeight, safeAreaInsets } = uni.getSystemInfoSync()
  130. if (safeArea) {
  131. // #ifdef MP-WEIXIN
  132. safeBottom.value = screenHeight - (safeArea!.bottom || 0)
  133. // #endif
  134. // #ifndef MP-WEIXIN
  135. safeBottom.value = safeAreaInsets ? safeAreaInsets.bottom : 0
  136. // #endif
  137. } else {
  138. safeBottom.value = 0
  139. }
  140. }
  141. })
  142. function handleClickModal() {
  143. emit('click-modal')
  144. if (props.closeOnClickModal) {
  145. close()
  146. }
  147. }
  148. function close() {
  149. emit('close')
  150. emit('update:modelValue', false)
  151. }
  152. function noop() {}
  153. </script>
  154. <style lang="scss" scoped>
  155. @import './index.scss';
  156. </style>