ellipsis-text.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <script setup lang="ts">
  2. import { computed, type CSSProperties, nextTick, 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 showTooltip = ref(false);
  72. const ellipsis = ref();
  73. const defaultTooltipMaxWidth = ref();
  74. watchEffect(() => {
  75. showTooltip.value = props.tooltip;
  76. });
  77. watchEffect(
  78. () => {
  79. if (props.tooltip && ellipsis.value) {
  80. defaultTooltipMaxWidth.value =
  81. props.tooltipMaxWidth ?? ellipsis.value.offsetWidth + 24;
  82. }
  83. },
  84. { flush: 'post' },
  85. );
  86. function onExpand() {
  87. const { style } = ellipsis.value;
  88. const isExpanded = !style['-webkit-line-clamp'];
  89. if (props.tooltip) {
  90. showTooltip.value = !isExpanded;
  91. }
  92. nextTick(() => {
  93. style['-webkit-line-clamp'] = isExpanded ? props.line : '';
  94. });
  95. emit('expandChange', !isExpanded);
  96. }
  97. function handleExpand() {
  98. if (props.expand) {
  99. onExpand();
  100. } else {
  101. return false;
  102. }
  103. }
  104. </script>
  105. <template>
  106. <VbenTooltip
  107. :content-style="{
  108. ...tooltipOverlayStyle,
  109. maxWidth: `${defaultTooltipMaxWidth}px`,
  110. fontSize: `${tooltipFontSize}px`,
  111. color: tooltipColor,
  112. backgroundColor: tooltipBackgroundColor,
  113. }"
  114. :disabled="!showTooltip"
  115. :side="placement"
  116. >
  117. <slot name="tooltip">
  118. <slot></slot>
  119. </slot>
  120. <template #trigger>
  121. <div
  122. ref="ellipsis"
  123. :class="{
  124. '!cursor-pointer': expand,
  125. ['inline-block truncate']: line === 1,
  126. [$style.ellipsisMultiLine]: line > 1,
  127. }"
  128. :style="`-webkit-line-clamp: ${line}; max-width: ${textMaxWidth};`"
  129. class="cursor-text overflow-hidden"
  130. @click="handleExpand"
  131. v-bind="$attrs"
  132. >
  133. <slot></slot>
  134. </div>
  135. </template>
  136. </VbenTooltip>
  137. </template>
  138. <style module>
  139. .ellipsisMultiLine {
  140. display: -webkit-box;
  141. -webkit-box-orient: vertical;
  142. }
  143. </style>