组件代码:
<template><!-- 外层容器,使用相对定位 --><div class="relative"><!-- 文本容器,根据 expanded 状态决定是否应用 line-clamp-4 类 --><div :class="{ 'line-clamp-4': !expanded }" ref="textContainer">{{ text }}</div><!-- 展开/收起按钮,只有在文本需要折叠时才显示,使用绝对定位 --><buttonv-if="isCollapsible"@click="toggleExpand"class="text-blue-500 absolute -right-6 bottom-0">{{ expanded ? '收起' : '展开' }}</button></div>
</template><script setup>
import { ref, onMounted, nextTick } from 'vue'// 定义组件的 props
const props = defineProps({text: {type: String,required: true},fontSize: {type: String,required: false,default: '12px'}
})const expanded = ref(false) // 控制文本是否展开
const isCollapsible = ref(false) // 控制文本是否需要折叠
const textContainer = ref(null) // 引用文本容器// 切换展开/收起状态的函数
const toggleExpand = () => {expanded.value = !expanded.value
}// 检查文本是否需要折叠的函数
const checkCollapsible = () => {nextTick(() => {const width = textContainer.value.clientWidth // 获取文本容器的宽度const dummyElement = document.createElement('div')dummyElement.style.position = 'absolute'dummyElement.style.visibility = 'hidden'dummyElement.style.width = `${width}px`dummyElement.style.fontSize = props.fontSize // 设置字体大小dummyElement.style.lineHeight = getComputedStyle(textContainer.value).lineHeightdummyElement.innerText = props.textdocument.body.appendChild(dummyElement)const lineHeight = parseInt(getComputedStyle(dummyElement).lineHeight) // 获取行高const totalHeight = dummyElement.clientHeight // 获取文本总高度document.body.removeChild(dummyElement)const maxHeight = lineHeight * 4 + 5 // 设定最大高度(假设最多显示4行)isCollapsible.value = totalHeight > maxHeight // 判断是否需要折叠})
}// 组件挂载时执行的操作
onMounted(() => {checkCollapsible() // 检查文本是否需要折叠window.addEventListener('resize', checkCollapsible) // 监听窗口大小变化,重新检查
})
</script><style scoped>
</style>
效果图:
优化版本,展开全部文字 展示在文本域后面。需要做一个截取文本操作。
<template><div><div ref="textContainer" :class="{ 'line-clamp-4': !expanded }"><span v-if="!expanded">{{ truncatedText }}</span><span v-else>{{ text }}</span><button v-if="isCollapsible" @click="toggleExpand" class="text-blue-500 ml-2">{{ expanded ? '收起' : '展开全部' }}</button></div></div>
</template><script setup>
import { ref, onMounted, nextTick } from 'vue'// 定义组件的 props
const props = defineProps({text: {type: String,required: true},fontSize: {type: String,required: false,default: '12px'},targetLines: {type: Number,required: false,default: 4}
})// 定义响应式变量
const expanded = ref(false) // 控制文本是否展开
const isCollapsible = ref(false) // 控制文本是否需要折叠
const textContainer = ref(null) // 引用文本容器
const truncatedText = ref('') // 存储截断的文本// 切换展开/收起状态的函数
const toggleExpand = () => {expanded.value = !expanded.value
}// 检查文本是否需要折叠的函数
const checkCollapsible = () => {nextTick(() => {const width = textContainer.value.clientWidthconst dummyElement = document.createElement('div')dummyElement.style.position = 'absolute'dummyElement.style.visibility = 'hidden'dummyElement.style.width = `${width}px`dummyElement.style.fontSize = props.fontSize // 设置字体大小dummyElement.style.lineHeight = getComputedStyle(textContainer.value).lineHeightdummyElement.style.whiteSpace = 'pre-wrap'dummyElement.style.wordWrap = 'break-word'dummyElement.innerText = props.textdocument.body.appendChild(dummyElement)const lineHeight = parseInt(getComputedStyle(dummyElement).lineHeight) // 获取行高const maxHeight = lineHeight * props.targetLines // 设定最大高度isCollapsible.value = dummyElement.clientHeight > maxHeight // 判断是否需要折叠if (isCollapsible.value) {// 截断文本以适应最大高度let totalHeight = 0let truncated = ''const words = props.text.split(' ')for (let i = 0; i < words.length; i++) {const word = words[i]dummyElement.innerText = truncated + word + ' ' + '... 展示全部'if (dummyElement.clientHeight > maxHeight) breaktruncated += word + ' '}truncatedText.value = truncated.trimEnd() + '...'} else {truncatedText.value = props.text}document.body.removeChild(dummyElement)})
}// 组件挂载时执行的操作
onMounted(() => {checkCollapsible() // 检查文本是否需要折叠window.addEventListener('resize', checkCollapsible) // 监听窗口大小变化,重新检查
})
</script><style scoped></style>
效果图: