<!-- vp-tooltip.vue -->
<template>
<div class="vp-tooltip"
@mouseenter="handleCellMouseEnter"
@mouseleave="handleCellMouseLeave"
>
<div :class="classObj"
:style="styleObj"
>
<slot />
</div>
<el-tooltip ref="tooltip"
placement="top"
effect="dark"
:content="tooltipContent"
>
<template v-if="$slots.content"
#content
>
<slot name="content" />
</template>
</el-tooltip>
</div>
</template>
<script>
import debounce from 'throttle-debounce/debounce'
import { hasClass } from 'element-ui/src/utils/dom'
/**
* 显隐可控的tooltip组件
* @desc 基于element-ui抽离,组件内部自动计算文本是否超宽来决定tooltip调用与否
*/
export default {
name: 'VpTooltip',
componentName: 'VpTooltip',
props: {
/**
* 限定超宽范围
*/
width: {
type: String,
default: '200px'
},
/**
* tooltip显示的内容
*/
content: {
type: String,
default: ''
},
/**
* cell自定义样式
*/
cellStyle: {
type: Object,
default: () => ({
width: '50%'
})
},
/**
* 保留属性,意义不大,纯粹为了与源码结构保持一致
*/
showOverflowTooltip: {
type: Boolean,
default: true
}
},
data() {
return {
tooltipContent: ''
}
},
computed: {
classObj() {
return {
cell: true,
'el-tooltip': this.showOverflowTooltip
}
},
styleObj() {
return {
...this.cellStyle,
width: this.width
}
}
},
created() {
// 防抖函数保护,避免鼠标键入后n秒内多次调用
this.activateTooltip = debounce(50, (tooltip) =>
tooltip.handleShowPopper()
)
},
methods: {
// 鼠标键入显示tooltip
handleCellMouseEnter(event) {
// 无提示内容直接返回
if (!this.content && !this.$slots.content) {
return
}
const cell = event.target
// 判断是否text-overflow, 如果是就显示tooltip
const cellChild = event.target.querySelector('.cell')
if (!(hasClass(cellChild, 'el-tooltip') && cellChild.childNodes.length)) {
return
}
// use range width instead of scrollWidth to determine whether the text is overflowing
// to address a potential FireFox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1074543#c3
const range = document.createRange()
range.setStart(cellChild, 0)
range.setEnd(cellChild, 1)
const rangeHeight = range.getBoundingClientRect().height
// 判断文本 + padding的宽度是否超过容器宽度
if ((rangeHeight > cellChild.offsetHeight ||
cellChild.scrollHeight > cellChild.offsetHeight) &&
this.$refs.tooltip) {
const tooltip = this.$refs.tooltip
// TODO 会引起整个 Table 的重新渲染,需要优化
this.tooltipContent = this.content // 这里为啥写这行类似1=1的代码呢,纯粹为了与源码结构保持一致
tooltip.referenceElm = cell
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none')
tooltip.doDestroy()
tooltip.setExpectedState(true)
this.activateTooltip(tooltip)
}
},
// 鼠标移出关闭tooltip
handleCellMouseLeave() {
const tooltip = this.$refs.tooltip
if (tooltip) {
tooltip.setExpectedState(false)
tooltip.handleClosePopper()
}
}
}
}
</script>
<style lang="scss">
.el-tooltip__popper{
font-size: 14px;
max-width:50vh;
}
</style>
<style scoped lang="scss">
.vp-tooltip .cell {
box-sizing: border-box;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 1;
display: -webkit-box;
-webkit-box-orient: vertical;
white-space: normal;
}
</style>
import Vue from 'vue'
组件封装完毕后,在main.js中进行引入
import VpToolTip from '@/components/VpTooltip'
挂载全局
Vue.component('VpToolTip', VpToolTip)
1.在vue文件中的使用
2.在el-tree树型结构中的使用
renderContent(h, { node }) {
return (
<VpToolTip content={node.label}
style="font-size: 14px;"
width="100%"
>
<span>{ node.label }</span>
</VpToolTip>
)
},