CustomTooltip.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <script lang="ts" setup>
  2. interface Props {
  3. visible?: boolean
  4. autoHide?: boolean
  5. autoHideDelay?: number
  6. icon?: string
  7. iconSize?: string
  8. message?: string
  9. highlightText1?: string
  10. highlightText2?: string
  11. width?: string
  12. }
  13. const props = withDefaults(defineProps<Props>(), {
  14. visible: false,
  15. autoHide: true,
  16. autoHideDelay: 10000,
  17. icon: 'shop',
  18. iconSize: '32rpx',
  19. message: 'Full refund for unsuccessful group purchase $99.99 Receive red envelope rewards $1.8',
  20. highlightText1: '$99.99',
  21. highlightText2: '$1.8',
  22. width: '600rpx',
  23. })
  24. const emit = defineEmits<{
  25. 'update:visible': [value: boolean]
  26. 'hide': []
  27. }>()
  28. const internalVisible = ref(props.visible)
  29. let hideTimer: number | null = null
  30. // 监听 visible prop 变化
  31. watch(() => props.visible, (newVal) => {
  32. internalVisible.value = newVal
  33. if (newVal && props.autoHide) {
  34. startAutoHideTimer()
  35. }
  36. else {
  37. clearAutoHideTimer()
  38. }
  39. })
  40. // 监听内部 visible 变化
  41. watch(internalVisible, (newVal) => {
  42. emit('update:visible', newVal)
  43. if (!newVal) {
  44. emit('hide')
  45. clearAutoHideTimer()
  46. }
  47. })
  48. // 启动自动隐藏定时器
  49. function startAutoHideTimer() {
  50. clearAutoHideTimer()
  51. if (props.autoHide && props.autoHideDelay > 0) {
  52. hideTimer = setTimeout(() => {
  53. hide()
  54. }, props.autoHideDelay)
  55. }
  56. }
  57. // 清除自动隐藏定时器
  58. function clearAutoHideTimer() {
  59. if (hideTimer) {
  60. clearTimeout(hideTimer)
  61. hideTimer = null
  62. }
  63. }
  64. // 显示提示框
  65. function show() {
  66. internalVisible.value = true
  67. if (props.autoHide) {
  68. startAutoHideTimer()
  69. }
  70. }
  71. // 隐藏提示框
  72. function hide() {
  73. internalVisible.value = false
  74. }
  75. // 切换显示状态
  76. function toggle() {
  77. if (internalVisible.value) {
  78. hide()
  79. }
  80. else {
  81. show()
  82. }
  83. }
  84. // 处理消息文本,高亮特定文本
  85. function formatMessage() {
  86. let formattedMessage = props.message
  87. if (props.highlightText1) {
  88. formattedMessage = formattedMessage.replace(
  89. props.highlightText1,
  90. `<text class="text-[var(--wot-color-theme)]">${props.highlightText1}</text>`,
  91. )
  92. }
  93. if (props.highlightText2) {
  94. formattedMessage = formattedMessage.replace(
  95. props.highlightText2,
  96. `<text class="text-[var(--wot-color-theme)]">${props.highlightText2}</text>`,
  97. )
  98. }
  99. return formattedMessage
  100. }
  101. // 组件卸载时清除定时器
  102. onUnmounted(() => {
  103. clearAutoHideTimer()
  104. })
  105. // 暴露方法给父组件
  106. defineExpose({
  107. show,
  108. hide,
  109. toggle,
  110. })
  111. </script>
  112. <template>
  113. <view
  114. v-if="internalVisible"
  115. class="absolute bottom-full left-1/2 mb-20rpx transform rounded-full bg-black bg-opacity-80 px-20rpx py-12rpx text-24rpx text-white -translate-x-1/2"
  116. :style="{ width }"
  117. >
  118. <view class="flex items-center">
  119. <wd-icon
  120. v-if="icon"
  121. :name="icon"
  122. :size="iconSize"
  123. class="mr-12rpx flex-shrink-0"
  124. />
  125. <view class="text-center">
  126. <text>Full refund for unsuccessful group purchase </text>
  127. <text class="text-[var(--wot-color-theme)]">
  128. {{ highlightText1 }}
  129. </text>
  130. <text> Receive red envelope rewards </text>
  131. <text class="text-[var(--wot-color-theme)]">
  132. {{ highlightText2 }}
  133. </text>
  134. </view>
  135. </view>
  136. <!-- 倒三角箭头 -->
  137. <view
  138. class="absolute left-1/2 top-full h-0 w-0 -translate-x-1/2"
  139. style="border-left: 16rpx solid transparent; border-right: 16rpx solid transparent; border-top: 16rpx solid rgba(0, 0, 0, 0.8);"
  140. />
  141. </view>
  142. </template>