This commit is contained in:
BianLzhaoMin 2025-09-17 09:49:32 +08:00
parent 7260c70d2c
commit e5ba12c6f8
4 changed files with 361 additions and 228 deletions

View File

@ -0,0 +1,329 @@
<template>
<div
class="text-tip"
:style="{
'--text-tip-max-lines': maxLines,
}"
>
<div
class="text-tip-content"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
ref="textContent"
>
{{ content }}
</div>
<!-- 悬浮提示 -->
<div
v-if="showTooltip"
class="tooltip"
:style="tooltipStyle"
@mouseenter="handleTooltipMouseEnter"
@mouseleave="handleTooltipMouseLeave"
>
<div class="tooltip-content">
{{ content }}
</div>
<div
class="tooltip-arrow"
:data-position="tooltipStyle['--arrow-position'] || 'bottom'"
></div>
</div>
</div>
</template>
<script>
export default {
name: 'TextTip',
props: {
content: {
type: String,
default: '',
},
maxLines: {
type: Number,
default: 2,
},
},
data() {
return {
showTooltip: false,
tooltipStyle: {},
isTextOverflow: false,
hideTooltipTimer: null,
}
},
mounted() {
this.$nextTick(() => {
this.checkTextOverflow()
})
//
window.addEventListener('resize', this.handleResize)
},
watch: {
content() {
this.$nextTick(() => {
this.checkTextOverflow()
})
},
},
beforeDestroy() {
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
//
window.removeEventListener('resize', this.handleResize)
},
methods: {
//
handleMouseEnter(event) {
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
if (this.isTextOverflow) {
this.updateTooltipPosition(event)
this.showTooltip = true
}
},
//
handleMouseLeave() {
//
this.hideTooltipTimer = setTimeout(() => {
this.showTooltip = false
}, 200)
},
//
handleTooltipMouseEnter() {
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
},
//
handleTooltipMouseLeave() {
this.showTooltip = false
},
//
updateTooltipPosition(event) {
const rect = event.target.getBoundingClientRect()
const tooltipWidth = Math.min(500, window.innerWidth - 40) //
const tooltipHeight = Math.min(400, window.innerHeight - 40) //
//
let left = rect.left + rect.width / 2 - tooltipWidth / 2
let top = rect.top - tooltipHeight - 20
let arrowPosition = 'top' //
//
if (left < 20) {
left = 20
} else if (left + tooltipWidth > window.innerWidth - 20) {
left = window.innerWidth - tooltipWidth - 20
}
//
if (top < 20) {
//
top = rect.bottom + 20
arrowPosition = 'bottom'
}
//
if (top + tooltipHeight > window.innerHeight - 20) {
top = Math.max(20, (window.innerHeight - tooltipHeight) / 2)
arrowPosition = 'center'
}
this.tooltipStyle = {
left: `${left}px`,
top: `${top}px`,
width: `${tooltipWidth}px`,
maxHeight: `${tooltipHeight}px`,
'--arrow-position': arrowPosition,
}
},
//
handleResize() {
if (this.showTooltip) {
//
this.$nextTick(() => {
const element = this.$refs.textContent
if (element) {
const rect = element.getBoundingClientRect()
const event = { target: element }
this.updateTooltipPosition(event)
}
})
}
},
//
checkTextOverflow() {
this.$nextTick(() => {
const element = this.$refs.textContent
if (element && this.content) {
//
const tempElement = element.cloneNode(true)
tempElement.style.position = 'absolute'
tempElement.style.visibility = 'hidden'
tempElement.style.height = 'auto'
tempElement.style.webkitLineClamp = 'unset'
tempElement.style.display = 'block'
tempElement.style.webkitBoxOrient = 'unset'
tempElement.style.overflow = 'visible'
tempElement.style.width = element.offsetWidth + 'px'
document.body.appendChild(tempElement)
const fullHeight = tempElement.offsetHeight
const lineHeight = parseFloat(
getComputedStyle(element).lineHeight,
)
const maxHeight = lineHeight * this.maxLines
this.isTextOverflow = fullHeight > maxHeight
document.body.removeChild(tempElement)
}
})
},
},
}
</script>
<style scoped lang="scss">
.text-tip {
position: relative;
display: inline-block;
width: 100%;
height: 100%;
}
.text-tip-content {
font-size: 14px;
font-family: 'PingFang SC', sans-serif;
letter-spacing: 1px;
line-height: 1.8;
cursor: pointer;
transition: all 0.3s ease;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: var(--text-tip-max-lines) !important;
overflow: hidden;
word-wrap: break-word;
&:hover {
background-color: rgba(74, 144, 226, 0.05);
border-radius: 4px;
padding: 4px;
margin: -4px;
}
}
/* 悬浮提示样式 */
.tooltip {
position: fixed;
z-index: 99999; /* 提高层级,确保不被遮挡 */
animation: tooltipFadeIn 0.3s ease-out;
pointer-events: auto;
max-width: 90vw; /* 最大宽度为视口宽度的90% */
min-width: 200px;
}
.tooltip-content {
background: rgba(0, 0, 0, 0.95);
color: white;
padding: 20px 24px;
border-radius: 12px;
font-size: 14px;
line-height: 1.8;
font-family: 'PingFang SC', sans-serif;
letter-spacing: 0.5px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
backdrop-filter: blur(15px);
border: 1px solid rgba(255, 255, 255, 0.2);
word-wrap: break-word;
white-space: pre-wrap;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
/* 确保内容完整显示 */
min-height: 60px;
max-height: 70vh; /* 最大高度为视口高度的70% */
}
/* Webkit 浏览器滚动条样式 */
.tooltip-content::-webkit-scrollbar {
width: 6px;
}
.tooltip-content::-webkit-scrollbar-track {
background: transparent;
}
.tooltip-content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.tooltip-content::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.tooltip-arrow {
position: absolute;
width: 0;
height: 0;
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
/* 箭头在上方(悬浮框在下方时) */
.tooltip-arrow[data-position='top'] {
top: -10px;
left: 20px;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid rgba(0, 0, 0, 0.95);
}
/* 箭头在下方(悬浮框在上方时) */
.tooltip-arrow[data-position='bottom'] {
bottom: -10px;
left: 20px;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid rgba(0, 0, 0, 0.95);
}
/* 箭头在中央(悬浮框在屏幕中央时) */
.tooltip-arrow[data-position='center'] {
display: none; /* 中央位置不显示箭头 */
}
@keyframes tooltipFadeIn {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>

View File

@ -40,7 +40,11 @@
<div class="case-right-content">
<h3>{{ item.caseCompany }}</h3>
<div>{{ item.caseIntroduction }} </div>
<!-- <div>{{ item.caseIntroduction }} </div> -->
<TextTip
:maxLines="2"
:content="item.caseIntroduction"
/>
</div>
</div>
</div>
@ -65,8 +69,12 @@
</template>
<script>
import TextTip from '@/components/TextTip/index'
export default {
name: 'CaseContainer',
components: {
TextTip,
},
props: {
cardTitle: {
type: String,
@ -247,7 +255,7 @@ export default {
// -
.carousel-wrapper {
width: 100%;
overflow: hidden;
// overflow: hidden;
position: relative;
// padding: 0 10px;
//
@ -323,17 +331,17 @@ export default {
font-weight: 600;
color: #333333;
}
div {
// width: 100%;
// height: 90px;
font-size: 14px;
color: #333333;
line-height: 1.8;
display: -webkit-box; /* 将元素作为弹性伸缩盒子模型显示 */
-webkit-box-orient: vertical; /* 设置伸缩盒子的子元素排列方式为垂直排列 */
-webkit-line-clamp: 2; /* 限制显示的行数为4行 */
overflow: hidden; /* 超出部分隐藏 */
}
// div {
// // width: 100%;
// // height: 90px;
// font-size: 14px;
// color: #333333;
// line-height: 1.8;
// display: -webkit-box; /* */
// -webkit-box-orient: vertical; /* */
// -webkit-line-clamp: 2; /* 4 */
// overflow: hidden; /* */
// }
}
::v-deep .el-carousel__arrow {

View File

@ -30,7 +30,9 @@
<div class="case-right-content">
<h3>{{ item.caseCompany }}</h3>
<div>{{ item.caseIntroduction }} </div>
<!-- <div>{{ item.caseIntroduction }} </div> -->
<TextTip :maxLines="2" :content="item.caseIntroduction" />
</div>
</div>
</div>
@ -38,8 +40,12 @@
<script>
import { getProductCenterDetailAPI } from '@/api/publicService/productCenter'
import TextTip from '@/components/TextTip/index'
export default {
name: 'CaseContainer',
components: {
TextTip,
},
props: {
cardTitle: {
type: String,

View File

@ -41,27 +41,8 @@
访问演示
</span>
</div>
<div
class="product-info-content"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
ref="productInfoContent"
>
{{ productDetail.introduction }}
</div>
<!-- 悬浮提示 -->
<div
v-if="showTooltip"
class="tooltip"
:style="tooltipStyle"
@mouseenter="handleTooltipMouseEnter"
@mouseleave="handleTooltipMouseLeave"
>
<div class="tooltip-content">
{{ productDetail.introduction }}
</div>
<div class="tooltip-arrow"></div>
</div>
<TextTip :content="productDetail.introduction" :maxLines="4" />
</div>
</div>
@ -102,6 +83,7 @@
import CardContainer from './components/card-container'
import CaseContainer from './components/case-container'
import DialogModel from '@/components/DialogModel/index'
import TextTip from '@/components/TextTip/index'
import { getProductCenterDetailAPI } from '@/api/publicService/productCenter'
import { encryptCBCTime } from '@/utils/aes'
@ -113,6 +95,7 @@ export default {
CardContainer,
CaseContainer,
DialogModel,
TextTip,
},
data() {
return {
@ -143,18 +126,6 @@ export default {
this.productId = this.$route.params?.id
this.getProductCenterDetailInScreenFun(this.$route.params?.id)
},
mounted() {
this.$nextTick(() => {
this.checkTextOverflow()
})
},
beforeDestroy() {
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
},
methods: {
//
@ -190,11 +161,6 @@ export default {
this.productCases = list
this.productVideos = videoList
this.productBrochures = fileList
//
this.$nextTick(() => {
this.checkTextOverflow()
})
},
goBack() {
@ -270,87 +236,6 @@ export default {
console.log('this.iframeUrl', this.iframeUrl)
this.dialogConfig.outerVisible = true
},
//
checkTextOverflow() {
this.$nextTick(() => {
const element = this.$refs.productInfoContent
if (element && this.productDetail.introduction) {
//
const tempElement = element.cloneNode(true)
tempElement.style.position = 'absolute'
tempElement.style.visibility = 'hidden'
tempElement.style.height = 'auto'
tempElement.style.webkitLineClamp = 'unset'
tempElement.style.display = 'block'
tempElement.style.webkitBoxOrient = 'unset'
tempElement.style.overflow = 'visible'
document.body.appendChild(tempElement)
const fullHeight = tempElement.offsetHeight
const lineHeight = parseFloat(
getComputedStyle(element).lineHeight,
)
const maxHeight = lineHeight * 4
this.isTextOverflow = fullHeight > maxHeight
document.body.removeChild(tempElement)
console.log('文本溢出检查:', {
fullHeight,
maxHeight,
isOverflow: this.isTextOverflow,
text: this.productDetail.introduction,
})
}
})
},
//
handleMouseEnter(event) {
console.log('鼠标移入,溢出状态:', this.isTextOverflow)
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
if (this.isTextOverflow) {
const rect = event.target.getBoundingClientRect()
this.tooltipStyle = {
position: 'fixed',
left: rect.left + 'px',
top: rect.top - 10 + 'px',
zIndex: 9999,
}
this.showTooltip = true
console.log('显示悬浮提示')
}
},
//
handleMouseLeave() {
//
this.hideTooltipTimer = setTimeout(() => {
this.showTooltip = false
}, 200)
},
//
handleTooltipMouseEnter() {
//
if (this.hideTooltipTimer) {
clearTimeout(this.hideTooltipTimer)
this.hideTooltipTimer = null
}
},
//
handleTooltipMouseLeave() {
this.showTooltip = false
},
},
}
</script>
@ -1013,105 +898,10 @@ export default {
background-color: #ccc !important;
}
}
.product-info-content {
font-size: 14px;
font-family: 'PingFang SC', sans-serif;
letter-spacing: 1px;
line-height: 1.8;
cursor: pointer;
transition: all 0.3s ease;
display: -webkit-box; /* 将元素作为弹性伸缩盒子模型显示 */
-webkit-box-orient: vertical; /* 设置伸缩盒子的子元素排列方式为垂直排列 */
-webkit-line-clamp: 4; /* 限制显示的行数为4行 */
overflow: hidden; /* 超出部分隐藏 */
&:hover {
background-color: rgba(74, 144, 226, 0.05);
border-radius: 4px;
padding: 4px;
margin: -4px;
}
}
}
.preview-docs {
// width: 100%;
height: 79vh;
}
/* 悬浮提示样式 */
.tooltip {
position: fixed;
z-index: 9999;
max-width: 500px;
min-width: 200px;
animation: tooltipFadeIn 0.3s ease-out;
pointer-events: auto; /* 确保可以接收鼠标事件 */
right: 0;
top: 0;
}
.tooltip-content {
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 16px 20px;
border-radius: 12px;
font-size: 14px;
line-height: 1.8;
font-family: 'PingFang SC', sans-serif;
letter-spacing: 0.5px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
word-wrap: break-word;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
/* 自定义滚动条样式 */
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.3) transparent;
}
/* Webkit 浏览器滚动条样式 */
.tooltip-content::-webkit-scrollbar {
width: 6px;
}
.tooltip-content::-webkit-scrollbar-track {
background: transparent;
}
.tooltip-content::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
}
.tooltip-content::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.tooltip-arrow {
position: absolute;
top: 100%;
left: 20px;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid rgba(0, 0, 0, 0.9);
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
@keyframes tooltipFadeIn {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>