Element Table内容的溢出隐藏显示tooltip功能实现

需求的产生:在最近做的项目中,产品希望我们在表格的展示中,将超出 n(根据项目的需求,希望可以对设置超出1/2/3/4行的文本进行处理) 行的文本内容进行自动的溢出隐藏显示省略号,并且鼠标移入显示tooltip。正常文本内容正常显示。 那么问题来了,通过阅读Element表格方面的文档,发现确实是有关于内容溢出隐藏显示tooltip提示的,但是具备一些缺陷,如:

  1. 仅支持超出一行溢出隐藏
  2. 鼠标移入tooltip气泡立即消失,导致用户无法进行复制操作
  3. 无法控制显示隐藏时间等

因此,由于以上原因,我们需要自己自定义方法去实现这个功能了 效果截图:

image.png

image.png

ps:这里主要讲思路,所以就直接用我项目中封装的table表格(项目一般会封装一下表格,处理表格的公共样式与方法)做示范了,且项目基于Vue3+typescript,不过对于vue2什么的也是一样的。如果是vue2的话,只需要改造一下下边组件的语法即可直接使用

这里我先分享一下实现原理以及思路(原理是利用JS与CSS的配合实现),过程中会说一下当前方式的缺点,可能未来会有改进,但目前也已经满足项目需求了。下边有组件完整代码!

1.判断内容是否需要溢出隐藏

首先最重要的是判断内容究竟是否需要溢出隐藏啦。这里涉及到的逻辑如下:

/**
 * @description: 获取 实际宽度、每行至多字符、最多几行
 * @param {*}
 * @return {*}
 */
const getOverFlowData = (width: string) => {
  // 实际装文字的宽度,table cell有总共20的内边距  通过查看元素 发现还有 1px 需要减去
  const realWidth = parseInt(width) - 21
  // 一行至多存在字符数 一个字符 6 px  (文字font-size为12px)
  const rowChar = Math.floor(realWidth / 6)
  // 获取 至多 显示 几行(这个是由父组件传值过来的)
  const row = props.overflowRow

  return {
    rowChar,
    row
  }
}
/**
 * @description: 是否显示tooltip
 * @param {*}
 * @return {*}
 */
const computeOverflow = (val: string, width: string): boolean => {
  if (typeof val !== 'string' || !width) return false
  // 内容字符长度
  let len = 0
  for (let i = 0; i < val.length; i++) {
    const code = val.charCodeAt(i)
    len++
    // 中文算两个字符
    if (code > 255) {
      len++
    }
  }
  // 调用 上边的getOverFlowData方法
  const { rowChar, row } = getOverFlowData(width)
  // console.log(len, rowChar, row * rowChar)
  return row * rowChar < len
}
复制代码

上边的核心思想就是通过计算一行能容纳多少字符,从而得到最多可容纳多少字符,超出则溢出隐藏且显示tooltip 由于项目表格的文字font-size为12px,可获得一个中文字符占约12px的宽度,由于一个中文占两个字符长度,因此一个字符就占6px的宽度。

另外你会注意到,上边两个方法都需要width参数,这个width其实就是我们给el-table-column所设置的width固定宽度。没错,这就是这个功能的一个小缺点,必须给el-table-column设置width固定宽度,不能是min-width或其他,因为我们需要切实获得他的宽度。目前所尝试的其他方式获取width宽度会遇见许多问题(如通过dom获取,且这样就需要考虑resize事件了,也就是说屏幕宽度一变都要重新获取内容宽度,感觉非常耗费性能),看以后是否能优化。但是你不能给表格的所有列都设置固定宽度,至少得有一个自己撑开,不然可能会出现所有的宽度相加为未占满屏幕的问题

2.判断出需要溢出隐藏时,对文本添加css类控制省略号

通过设置样式的方式,设置溢出隐藏。class名称格式为tooltip-text-x,在template中动态设置类名即可。涉及代码如下:

css核心代码

.tooltip-text-1,
.tooltip-text-2,
.tooltip-text-3,
.tooltip-text-4 {
  display: inline-block;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-all;
}

.tooltip-text-1 {
  -webkit-line-clamp: 1;
}

.tooltip-text-2 {
  -webkit-line-clamp: 2;
}
.tooltip-text-3 {
  -webkit-line-clamp: 3;
}
.tooltip-text-4 {
  -webkit-line-clamp: 4;
}
复制代码

template模板部分核心代码,overflowRow表示超出几行溢出,父组件传过来

<span :class="[`tooltip-text-${overflowRow}`]">{{ value }}</span>
复制代码

通过区分类名来实现不同行的溢出隐藏,缺点是如果要5行以上的溢出隐藏,都要手动多写,也就是说上边的css仅支持1/2/3/4行溢出隐藏的情况。且有兼容性问题,不过项目已经用的vue3了,所以问题不大。另外值得注意的是,即便是一行溢出隐藏,也不要使用原本的那种溢出隐藏的方式(overflow: hidden;text-overflow: ellipsis;white-space: nowrap;这样会导致tooltip出现位置不正确,可通过审查元素发现原因,这里就不详细说明了)

下边是实现这个功能的核心组件。在项目中首先我将这部分逻辑封装成了组件,下边分享下,该功能完整的逻辑(注意这里使用的vue3+ts开发, 不要盲目复制哟!):

组件名称为:TooltipColumn.vue

<!-- 介绍:表格文字溢出隐藏组件封装,在BaseTable(这里指项目封装的表格组件)中以使用,如果使用表格组件中有列需要自定义,也可以通过引入该组件方便的实现 文字溢出隐藏。 -->
<!-- 1.溢出隐藏的行数可以设定为 1234 满足项目需求,默认为 超出2行溢出(设定其他无意义) -->
<!-- 2.使用时必传 value列值, width对应列的宽度,不然无意义(注意这里的width是拿来计算是否显示tooltip的,而不是拿来设置列宽度的,如果该组件在自定义列中使用时,记得该值得设置成与列宽一致,不然计算不正确),行数默认为2行 -->
<template>
  <template v-if="computeOverflow(value, width)">
    <el-tooltip placement="top-start" effect="light" :show-after="200">
      <template #content>
        <div class="tooltip-box">{{ value }}</div>
      </template>
      <span :class="[`tooltip-text-${overflowRow}`]">{{ value }}</span>
    </el-tooltip>
  </template>
  <span v-else>{{ value }}</span>
</template>
<script lang="ts" setup>
const props = withDefaults(defineProps<{
  value: any;
  width: string | number;
  overflowRow?: number;
}>(), {
  value: '',
  width: '',
  overflowRow: 2
})
/**
 * @description: 获取 实际宽度、每行至多字符、最多几行
 * @param {*}
 * @return {*}
 */
const getOverFlowData = (width: string | number) => {
  // 实际装文字的宽度,table cell有总共20的内边距  通过查看元素 发现还有 1px 需要减去
  const realWidth = parseInt(width as string) - 21
  // 一行至多存在字符数 一个字符 6 px  (文字font-size为12px)
  const rowChar = Math.floor(realWidth / 6)
  // 获取 至多 显示 几行
  const row = props.overflowRow

  return {
    rowChar,
    row
  }
}
/**
 * @description: 是否显示tooltip
 * @param {*}
 * @return {*}
 */
const computeOverflow = (val: string, width: string | number): boolean => {
  if (typeof val !== 'string' || !width) return false
  // 内容字符长度
  let len = 0
  for (let i = 0; i < val.length; i++) {
    const code = val.charCodeAt(i)
    len++
    // 中文算两个字符
    if (code > 255) {
      len++
    }
  }
  const { rowChar, row } = getOverFlowData(width)
  // console.log(len, rowChar, row * rowChar)
  return row * rowChar < len
}
</script>
<style scoped lang="scss">
//tooltip样式
.tooltip-box {
  max-width: 600px;
  white-space: pre-wrap;
}
.tooltip-text-1,
.tooltip-text-2,
.tooltip-text-3,
.tooltip-text-4 {
  display: inline-block;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-all;
}

.tooltip-text-1 {
  -webkit-line-clamp: 1;
}

.tooltip-text-2 {
  -webkit-line-clamp: 2;
}
.tooltip-text-3 {
  -webkit-line-clamp: 3;
}
.tooltip-text-4 {
  -webkit-line-clamp: 4;
}
</style>
复制代码

该组件接收三个prop:value,width,overflowRow分别代表 值,宽度,几行溢出隐藏。可以看到模板中,如果判断出需要溢出隐藏,则渲染tooltip组件,否则正常显示文本内容

接下来是该组件如何在表格中使用的情况

首先是在项目中封装的表格中如何使用,封装的表格el-table-column是v-for遍历生成的,也就是说只需要在:

image.png 这里边放进去即可。其中需接收一个overflowRow,表示溢出几行提示,默认为 2行溢出

//接收父组件传值
props: {
  overflowRow: {
    type: Number,
    default: 2
  }
}
复制代码

如果纯粹使用element表格没有封装,直接这样使用即可

<el-table-column prop="xxx" label="表格项" width="140">
   <template #default="{ row }">
      <TooltipColumn :value="row.xxx" width="140" />
   </template>
</el-table-column>
复制代码

这样就大功告成啦

Guess you like

Origin juejin.im/post/7035275070444404772