@ -1,7 +1,14 @@
< script setup lang = "ts" >
import type { CSSProperties } from 'vue' ;
import { computed , ref , watchEffect } from 'vue' ;
import {
computed ,
onBeforeUnmount ,
onMounted ,
onUpdated ,
ref ,
watchEffect ,
} from 'vue' ;
import { VbenTooltip } from '@vben-core/shadcn-ui' ;
@ -33,6 +40,16 @@ interface Props {
* @ default true
* /
tooltip ? : boolean ;
/ * *
* 是否只在文本被截断时显示提示框
* @ default false
* /
tooltipWhenEllipsis ? : boolean ;
/ * *
* 文本截断检测的像素差异阈值 , 越大则判断越严格
* @ default 3
* /
ellipsisThreshold ? : number ;
/ * *
* 提示框背景颜色 , 优先级高于 overlayStyle
* /
@ -62,12 +79,15 @@ const props = withDefaults(defineProps<Props>(), {
maxWidth : '100%' ,
placement : 'top' ,
tooltip : true ,
tooltipWhenEllipsis : false ,
ellipsisThreshold : 3 ,
tooltipBackgroundColor : '' ,
tooltipColor : '' ,
tooltipFontSize : 14 ,
tooltipMaxWidth : undefined ,
tooltipOverlayStyle : ( ) => ( { textAlign : 'justify' } ) ,
} ) ;
const emit = defineEmits < { expandChange : [ boolean ] } > ( ) ;
const textMaxWidth = computed ( ( ) => {
@ -79,9 +99,67 @@ const textMaxWidth = computed(() => {
const ellipsis = ref ( ) ;
const isExpand = ref ( false ) ;
const defaultTooltipMaxWidth = ref ( ) ;
const isEllipsis = ref ( false ) ;
const { width : eleWidth } = useElementSize ( ellipsis ) ;
/ / 检 测 文 本 是 否 被 截 断
const checkEllipsis = ( ) => {
if ( ! ellipsis . value || ! props . tooltipWhenEllipsis ) return ;
const element = ellipsis . value ;
const originalText = element . textContent || '' ;
const originalTrimmed = originalText . trim ( ) ;
/ / 对 于 空 文 本 直 接 返 回 f a l s e
if ( ! originalTrimmed ) {
isEllipsis . value = false ;
return ;
}
const widthDiff = element . scrollWidth - element . clientWidth ;
const heightDiff = element . scrollHeight - element . clientHeight ;
/ / 使 用 足 够 大 的 差 异 阈 值 确 保 只 有 真 正 被 截 断 的 文 本 才 会 显 示 t o o l t i p
isEllipsis . value =
props . line === 1
? widthDiff > props . ellipsisThreshold
: heightDiff > props . ellipsisThreshold ;
} ;
/ / 使 用 R e s i z e O b s e r v e r 监 听 尺 寸 变 化
let resizeObserver : null | ResizeObserver = null ;
onMounted ( ( ) => {
if ( typeof ResizeObserver !== 'undefined' && props . tooltipWhenEllipsis ) {
resizeObserver = new ResizeObserver ( ( ) => {
checkEllipsis ( ) ;
} ) ;
if ( ellipsis . value ) {
resizeObserver . observe ( ellipsis . value ) ;
}
}
/ / 初 始 检 测
checkEllipsis ( ) ;
} ) ;
/ / 使 用 o n U p d a t e d 钩 子 检 测 内 容 变 化
onUpdated ( ( ) => {
if ( props . tooltipWhenEllipsis ) {
checkEllipsis ( ) ;
}
} ) ;
onBeforeUnmount ( ( ) => {
if ( resizeObserver ) {
resizeObserver . disconnect ( ) ;
resizeObserver = null ;
}
} ) ;
watchEffect (
( ) => {
if ( props . tooltip && eleWidth . value ) {
@ -91,9 +169,13 @@ watchEffect(
} ,
{ flush : 'post' } ,
) ;
function onExpand ( ) {
isExpand . value = ! isExpand . value ;
emit ( 'expandChange' , isExpand . value ) ;
if ( props . tooltipWhenEllipsis ) {
checkEllipsis ( ) ;
}
}
function handleExpand ( ) {
@ -110,7 +192,9 @@ function handleExpand() {
color : tooltipColor ,
backgroundColor : tooltipBackgroundColor ,
} "
: disabled = "!props.tooltip || isExpand"
: disabled = "
! props . tooltip || isExpand || ( props . tooltipWhenEllipsis && ! isEllipsis )
"
: side = "placement"
>
< slot name = "tooltip" >