ellipsis-text.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <script setup lang="ts">
  2. import { computed, type CSSProperties, ref, watchEffect } from 'vue';
  3. import { VbenTooltip } from '@vben-core/shadcn-ui';
  4. interface Props {
  5. /**
  6. * 是否启用点击文本展开全部
  7. * @default false
  8. */
  9. expand?: boolean;
  10. /**
  11. * 文本最大行数
  12. * @default 1
  13. */
  14. line?: number;
  15. /**
  16. * 文本最大宽度
  17. * @default '100%'
  18. */
  19. maxWidth?: number | string;
  20. /**
  21. * 提示框位置
  22. * @default 'top'
  23. */
  24. placement?: 'bottom' | 'left' | 'right' | 'top';
  25. /**
  26. * 是否启用文本提示框
  27. * @default true
  28. */
  29. tooltip?: boolean;
  30. /**
  31. * 提示框背景颜色,优先级高于 overlayStyle
  32. */
  33. tooltipBackgroundColor?: string;
  34. /**
  35. * 提示文本字体颜色,优先级高于 overlayStyle
  36. */
  37. tooltipColor?: string;
  38. /**
  39. * 提示文本字体大小,单位px,优先级高于 overlayStyle
  40. */
  41. tooltipFontSize?: number;
  42. /**
  43. * 提示框内容最大宽度,单位px,默认不设置时,提示文本内容自动与展示文本宽度保持一致
  44. */
  45. tooltipMaxWidth?: number;
  46. /**
  47. * 提示框内容区域样式
  48. * @default { textAlign: 'justify' }
  49. */
  50. tooltipOverlayStyle?: CSSProperties;
  51. }
  52. const props = withDefaults(defineProps<Props>(), {
  53. expand: false,
  54. line: 1,
  55. maxWidth: '100%',
  56. placement: 'top',
  57. tooltip: true,
  58. tooltipBackgroundColor: '',
  59. tooltipColor: '',
  60. tooltipFontSize: 14,
  61. tooltipMaxWidth: undefined,
  62. tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
  63. });
  64. const emit = defineEmits<{ expandChange: [boolean] }>();
  65. const textMaxWidth = computed(() => {
  66. if (typeof props.maxWidth === 'number') {
  67. return `${props.maxWidth}px`;
  68. }
  69. return props.maxWidth;
  70. });
  71. const ellipsis = ref();
  72. const isExpand = ref(false);
  73. const defaultTooltipMaxWidth = ref();
  74. watchEffect(
  75. () => {
  76. if (props.tooltip && ellipsis.value) {
  77. defaultTooltipMaxWidth.value =
  78. props.tooltipMaxWidth ?? ellipsis.value.offsetWidth + 24;
  79. }
  80. },
  81. { flush: 'post' },
  82. );
  83. function onExpand() {
  84. isExpand.value = !isExpand.value;
  85. emit('expandChange', isExpand.value);
  86. }
  87. function handleExpand() {
  88. props.expand && onExpand();
  89. }
  90. </script>
  91. <template>
  92. <VbenTooltip
  93. :content-style="{
  94. ...tooltipOverlayStyle,
  95. maxWidth: `${defaultTooltipMaxWidth}px`,
  96. fontSize: `${tooltipFontSize}px`,
  97. color: tooltipColor,
  98. backgroundColor: tooltipBackgroundColor,
  99. }"
  100. :disabled="!props.tooltip || isExpand"
  101. :side="placement"
  102. >
  103. <slot name="tooltip">
  104. <slot></slot>
  105. </slot>
  106. <template #trigger>
  107. <div
  108. ref="ellipsis"
  109. :class="{
  110. '!cursor-pointer': expand,
  111. ['inline-block truncate']: line === 1,
  112. [$style.ellipsisMultiLine]: line > 1,
  113. }"
  114. :style="{
  115. '-webkit-line-clamp': isExpand ? '' : line,
  116. 'max-width': textMaxWidth,
  117. }"
  118. class="cursor-text overflow-hidden"
  119. @click="handleExpand"
  120. v-bind="$attrs"
  121. >
  122. <slot></slot>
  123. </div>
  124. </template>
  125. </VbenTooltip>
  126. </template>
  127. <style module>
  128. .ellipsisMultiLine {
  129. display: -webkit-box;
  130. -webkit-box-orient: vertical;
  131. }
  132. </style>