wd-toast.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <template>
  2. <wd-overlay v-if="cover" :z-index="zIndex" lock-scroll :show="show" custom-style="background-color: transparent;pointer-events: auto;"></wd-overlay>
  3. <wd-transition name="fade" :show="show" :custom-style="transitionStyle" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave">
  4. <view :class="rootClass">
  5. <!--iconName优先级更高-->
  6. <wd-loading
  7. v-if="iconName === 'loading'"
  8. :type="loadingType"
  9. :color="loadingColor"
  10. :size="loadingSize"
  11. :custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
  12. />
  13. <view
  14. :class="`wd-toast__iconWrap wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
  15. v-else-if="iconName === 'success' || iconName === 'warning' || iconName === 'info' || iconName === 'error'"
  16. >
  17. <view class="wd-toast__iconBox">
  18. <view class="wd-toast__iconSvg" :style="svgStyle"></view>
  19. </view>
  20. </view>
  21. <wd-icon
  22. v-else-if="iconClass"
  23. :custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
  24. :size="iconSize"
  25. :class-prefix="classPrefix"
  26. :name="iconClass"
  27. ></wd-icon>
  28. <!--文本-->
  29. <view v-if="msg" class="wd-toast__msg">{{ msg }}</view>
  30. </view>
  31. </wd-transition>
  32. </template>
  33. <script lang="ts">
  34. export default {
  35. name: 'wd-toast',
  36. options: {
  37. addGlobalClass: true,
  38. virtualHost: true,
  39. styleIsolation: 'shared'
  40. }
  41. }
  42. </script>
  43. <script lang="ts" setup>
  44. import wdIcon from '../wd-icon/wd-icon.vue'
  45. import wdLoading from '../wd-loading/wd-loading.vue'
  46. import wdOverlay from '../wd-overlay/wd-overlay.vue'
  47. import wdTransition from '../wd-transition/wd-transition.vue'
  48. import { computed, inject, onBeforeMount, ref, watch, type CSSProperties } from 'vue'
  49. import base64 from '../common/base64'
  50. import { defaultOptions, getToastOptionKey, toastIcon } from '.'
  51. import { toastProps, type ToastDirection, type ToastLoadingType, type ToastOptions, type ToastProps } from './types'
  52. import { addUnit, isDef, isFunction, objToStyle } from '../common/util'
  53. const props = defineProps(toastProps)
  54. const iconName = ref<string>('') // 图标类型
  55. const msg = ref<string>('') // 消息内容
  56. const position = ref<string>('middle')
  57. const show = ref<boolean>(false)
  58. const zIndex = ref<number>(100)
  59. const loadingType = ref<ToastLoadingType>('outline')
  60. const loadingColor = ref<string>('#4D80F0')
  61. const iconSize = ref<string>() // 图标大小
  62. const loadingSize = ref<string>() // loading大小
  63. const svgStr = ref<string>('') // 图标
  64. const cover = ref<boolean>(false) // 是否存在遮罩层
  65. const classPrefix = ref<string>('wd-icon') // 图标前缀
  66. const iconClass = ref<string>('') // 图标类名
  67. const direction = ref<ToastDirection>('horizontal') // toast布局方向
  68. let opened: (() => void) | null = null
  69. let closed: (() => void) | null = null
  70. const toastOptionKey = getToastOptionKey(props.selector)
  71. const toastOption = inject(toastOptionKey, ref<ToastOptions>(defaultOptions)) // toast选项
  72. // 监听options变化展示
  73. watch(
  74. () => toastOption.value,
  75. (newVal: ToastOptions) => {
  76. reset(newVal)
  77. },
  78. {
  79. deep: true,
  80. immediate: true
  81. }
  82. )
  83. // 监听options变化展示
  84. watch(
  85. () => iconName.value,
  86. () => {
  87. buildSvg()
  88. },
  89. {
  90. deep: true,
  91. immediate: true
  92. }
  93. )
  94. /**
  95. * 动画自定义样式
  96. */
  97. const transitionStyle = computed(() => {
  98. const style: CSSProperties = {
  99. 'z-index': zIndex.value,
  100. position: 'fixed',
  101. top: '50%',
  102. left: 0,
  103. width: '100%',
  104. transform: 'translate(0, -50%)',
  105. 'text-align': 'center',
  106. 'pointer-events': 'none'
  107. }
  108. return objToStyle(style)
  109. })
  110. const rootClass = computed(() => {
  111. return `wd-toast ${props.customClass} wd-toast--${position.value} ${
  112. (iconName.value !== 'loading' || msg.value) && (iconName.value || iconClass.value) ? 'wd-toast--with-icon' : ''
  113. } ${iconName.value === 'loading' && !msg.value ? 'wd-toast--loading' : ''} ${direction.value === 'vertical' ? 'is-vertical' : ''}`
  114. })
  115. const svgStyle = computed(() => {
  116. const style: CSSProperties = {
  117. backgroundImage: `url(${svgStr.value})`
  118. }
  119. if (isDef(iconSize.value)) {
  120. style.width = iconSize.value
  121. style.height = iconSize.value
  122. }
  123. return objToStyle(style)
  124. })
  125. onBeforeMount(() => {
  126. buildSvg()
  127. })
  128. function handleAfterEnter() {
  129. if (isFunction(opened)) {
  130. opened()
  131. }
  132. }
  133. function handleAfterLeave() {
  134. if (isFunction(closed)) {
  135. closed()
  136. }
  137. }
  138. function buildSvg() {
  139. if (iconName.value !== 'success' && iconName.value !== 'warning' && iconName.value !== 'info' && iconName.value !== 'error') return
  140. const iconSvg = toastIcon[iconName.value]()
  141. const iconSvgStr = `"data:image/svg+xml;base64,${base64(iconSvg)}"`
  142. svgStr.value = iconSvgStr
  143. }
  144. /**
  145. * 重置toast选项值
  146. * @param option toast选项值
  147. */
  148. function reset(option: ToastOptions) {
  149. show.value = isDef(option.show) ? option.show : false
  150. if (show.value) {
  151. mergeOptionsWithProps(option, props)
  152. }
  153. }
  154. function mergeOptionsWithProps(option: ToastOptions, props: ToastProps) {
  155. iconName.value = isDef(option.iconName!) ? option.iconName! : props.iconName
  156. iconClass.value = isDef(option.iconClass!) ? option.iconClass! : props.iconClass
  157. msg.value = isDef(option.msg!) ? option.msg! : props.msg
  158. position.value = isDef(option.position!) ? option.position! : props.position
  159. zIndex.value = isDef(option.zIndex!) ? option.zIndex! : props.zIndex
  160. loadingType.value = isDef(option.loadingType!) ? option.loadingType! : props.loadingType
  161. loadingColor.value = isDef(option.loadingColor!) ? option.loadingColor! : props.loadingColor
  162. iconSize.value = isDef(option.iconSize) ? addUnit(option.iconSize) : isDef(props.iconSize) ? addUnit(props.iconSize) : undefined
  163. loadingSize.value = isDef(option.loadingSize) ? addUnit(option.loadingSize) : isDef(props.loadingSize) ? addUnit(props.loadingSize) : undefined
  164. cover.value = isDef(option.cover!) ? option.cover! : props.cover
  165. classPrefix.value = isDef(option.classPrefix) ? option.classPrefix : props.classPrefix
  166. direction.value = isDef(option.direction) ? option.direction : props.direction
  167. closed = isFunction(option.closed) ? option.closed : isFunction(props.closed) ? props.closed : null
  168. opened = isFunction(option.opened) ? option.opened : isFunction(props.opened) ? props.opened : null
  169. }
  170. </script>
  171. <style lang="scss" scoped>
  172. @import './index.scss';
  173. </style>