cwg-tooltip.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <template>
  2. <view class="cwg-tooltip" @mouseenter="showTooltip" @mouseleave="hideTooltip">
  3. <!-- 触发元素插槽 -->
  4. <slot></slot>
  5. <!-- 弹出层 -->
  6. <view
  7. v-if="content || $slots.content"
  8. class="cwg-tooltip-popup"
  9. :class="[`placement-${placement}`, { show: isVisible }]"
  10. :style="popupStyle"
  11. >
  12. <!-- 内容插槽 -->
  13. <slot name="content">
  14. {{ content }}
  15. </slot>
  16. </view>
  17. </view>
  18. </template>
  19. <script setup>
  20. import { ref, watch, computed } from 'vue'
  21. const props = defineProps({
  22. content: {
  23. type: String,
  24. default: ''
  25. },
  26. placement: {
  27. type: String,
  28. default: 'bottom',
  29. validator: (value) => ['left', 'right', 'top', 'bottom'].includes(value)
  30. },
  31. manual: {
  32. type: Boolean,
  33. default: false
  34. },
  35. visible: {
  36. type: Boolean,
  37. default: false
  38. },
  39. // 添加间距控制
  40. spacing: {
  41. type: String,
  42. default: '12rpx'
  43. },
  44. // 添加最大宽度控制
  45. maxWidth: {
  46. type: String,
  47. default: ''
  48. }
  49. })
  50. const emit = defineEmits(['update:visible', 'show', 'hide'])
  51. // 内部显示状态
  52. const isVisible = ref(props.manual ? props.visible : false)
  53. // 监听visible变化
  54. watch(() => props.visible, (val) => {
  55. if (props.manual) {
  56. isVisible.value = val
  57. }
  58. })
  59. // 弹窗样式
  60. const popupStyle = computed(() => {
  61. const style = {}
  62. if (props.maxWidth) {
  63. style.maxWidth = props.maxWidth
  64. }
  65. return style
  66. })
  67. // 显示弹窗
  68. const showTooltip = () => {
  69. if (!props.manual) {
  70. isVisible.value = true
  71. emit('show')
  72. }
  73. }
  74. // 隐藏弹窗
  75. const hideTooltip = () => {
  76. if (!props.manual) {
  77. isVisible.value = false
  78. emit('hide')
  79. }
  80. }
  81. // 切换显示状态(手动模式)
  82. const toggle = () => {
  83. if (props.manual) {
  84. isVisible.value = !isVisible.value
  85. emit('update:visible', isVisible.value)
  86. }
  87. }
  88. // 显示(手动模式)
  89. const show = () => {
  90. if (props.manual) {
  91. isVisible.value = true
  92. emit('update:visible', true)
  93. emit('show')
  94. }
  95. }
  96. // 隐藏(手动模式)
  97. const hide = () => {
  98. if (props.manual) {
  99. isVisible.value = false
  100. emit('update:visible', false)
  101. emit('hide')
  102. }
  103. }
  104. // 暴露方法给父组件
  105. defineExpose({
  106. toggle,
  107. show,
  108. hide
  109. })
  110. </script>
  111. <style scoped lang="scss">
  112. .cwg-tooltip {
  113. position: relative;
  114. display: inline-block;
  115. cursor: pointer;
  116. }
  117. /* 弹窗基础样式 */
  118. .cwg-tooltip-popup {
  119. position: absolute;
  120. z-index: 1000;
  121. text-align: center;
  122. line-height: 1.5;
  123. word-break: break-word;
  124. white-space: nowrap;
  125. display: none;
  126. padding: px2rpx(16) px2rpx(24);
  127. font-size: px2rpx(16);
  128. color: #333;
  129. /* 你的样式要求 */
  130. background-color: var(--color-slate-100) !important;
  131. border-radius: px2rpx(8);
  132. /* 让箭头可见 */
  133. overflow: visible;
  134. /* 默认箭头样式(右侧定位) */
  135. &::after {
  136. content: "";
  137. position: absolute;
  138. top: 50%;
  139. left: -12px;
  140. transform: translateY(-50%);
  141. width: 0;
  142. height: 0;
  143. border-width: px2rpx(6);
  144. border-style: solid;
  145. border-color: transparent var(--color-slate-100) transparent transparent;
  146. pointer-events: none;
  147. z-index: 100001;
  148. }
  149. }
  150. /* 显示状态 */
  151. .cwg-tooltip-popup.show {
  152. display: block;
  153. }
  154. /* 不同位置的定位 - 箭头方向也相应调整 */
  155. /* 左侧定位 */
  156. .cwg-tooltip-popup.placement-left {
  157. top: 50%;
  158. right: 100%;
  159. transform: translateY(-50%);
  160. margin-right: v-bind('props.spacing');
  161. &::after {
  162. left: auto;
  163. right: -12px;
  164. border-color: transparent transparent transparent var(--color-slate-100);
  165. }
  166. }
  167. /* 右侧定位 */
  168. .cwg-tooltip-popup.placement-right {
  169. top: 50%;
  170. left: 100%;
  171. transform: translateY(-50%);
  172. margin-left: v-bind('props.spacing');
  173. &::after {
  174. left: -12px;
  175. border-color: transparent var(--color-slate-100) transparent transparent;
  176. }
  177. }
  178. /* 上方定位 */
  179. .cwg-tooltip-popup.placement-top {
  180. bottom: 100%;
  181. left: 50%;
  182. transform: translateX(-50%);
  183. margin-bottom: v-bind('props.spacing');
  184. &::after {
  185. top: auto;
  186. bottom: -12px;
  187. left: 50%;
  188. transform: translateX(-50%);
  189. border-color: var(--color-slate-100) transparent transparent transparent;
  190. }
  191. }
  192. /* 下方定位 */
  193. .cwg-tooltip-popup.placement-bottom {
  194. top: 100%;
  195. left: 50%;
  196. transform: translateX(-50%);
  197. margin-top: v-bind('props.spacing');
  198. &::after {
  199. top: -12px;
  200. left: 50%;
  201. transform: translateX(-50%);
  202. border-color: transparent transparent var(--color-slate-100) transparent;
  203. }
  204. }
  205. /* 长内容换行 - 当设置了最大宽度时 */
  206. .cwg-tooltip-popup[style*="max-width"] {
  207. white-space: normal;
  208. }
  209. </style>