CustomTooltip.vue 3.4 KB

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