el-table配置show-overflow-tooltip属性后,如果文字超长页面就会抖动

问题概述

1、触发场景:el-table 配置 show-overflow-tooltip="true",当单元格文字超长(触发 text-overflow)时。

2、具体现象

  • 鼠标 hover 超长文本单元格,tooltip 反复显示 / 隐藏,不展示或闪跳(抖动)。

  • 最终 tooltip 无法稳定展示,甚至完全不弹出。

  • 附图

问题原因分析

1、源码关键逻辑拆解(el-table 内置 tooltip 触发机制)

// 1. 鼠标进入单元格(mouseenter)触发逻辑
handleCellMouseEnter(event, row) {
  // 步骤1:判断单元格是否存在 text-overflow(通过 rangeWidth+padding 或 scrollWidth 对比 offsetWidth)
  const cellChild = event.target.querySelector('.cell');
  if (!(hasClass(cellChild, 'pc-tooltip') && cellChild.childNodes.length)) return;
  
  // 步骤2:若文字超长,渲染 tooltip 内容并激活显示
  if (/* 超长判断成立 */ && this.$refs.tooltip) {
    this.tooltipContent = cell.innerText || cell.textContent; // 赋值会触发表格重渲染(源码 TODO 标注待优化)
    tooltip.referenceElm = cell; // 绑定 tooltip 参考元素为当前单元格
    tooltip.activateTooltip(tooltip); // 显示 tooltip
  }
}

// 2. 鼠标离开单元格(mouseleave)触发逻辑
handleCellMouseLeave(event) {
  // 步骤:若 tooltip 存在,直接关闭 tooltip
  const tooltip = this.$refs.tooltip;
  if (tooltip) {
    tooltip.setExpectedState(false);
    tooltip.handleClosePopper(); // 隐藏 tooltip
  }
}

2、循环触发逻辑(不展示或抖动根源)
  1. 触发 mouseenter → 显示 tooltip。

  2. tooltip 弹出后 覆盖了原单元格(默认定位可能与单元格重叠) → 触发 mouseleave → 关闭 tooltip。

  3. tooltip 关闭后,鼠标重新回到单元格 → 再次触发 mouseenter → 重复步骤 1-2,形成无限循环。

  4. 最终表现:tooltip 不展示或闪跳(抖动),无法稳定停留。

解决方法

  1. 替换为 Element UI 原生 Tooltip 组件(推荐)

<template> <div class="mod-config"> <el-form id="myForm" :inline="true" :model="dataForm" ref="dataForm" @keyup.enter.native="onSubmit(0)" style="margin-bottom:10px;height:42px;"> <el-form-item class="myInput" label="心跳时间"> <el-date-picker v-model="dataForm.starttime" type="datetime" placeholder="选择起始时间" align="right" :picker-options="pickerOptions" clearable> </el-date-picker> 至 <el-date-picker v-model="dataForm.endtime" type="datetime" placeholder="选择结束时间" default-time="00:00:00" clearable> </el-date-picker> </el-form-item> <el-form-item class="myInput"> <el-button v-if="isAuth('maya:mybatteryinfo:list')" @click="onSubmit(0)" class="set-other-btn" icon="el-icon-search" size="mini">查询</el-button> <el-button :disabled="exportLoading" :type="exportLoading ? 'info' : 'primary'" @click="list2Export"> {{ exportLoading ? '导出中......' : '导出' }} </el-button> </el-form-item> </el-form> <el-table id="myTableType" :height="tableHeightRun" :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" highlight-current-row style="width: 100%;margin-top:-0.1rem;height:630px;"> <el-table-column label="序号" type="index" align="center" show-overflow-tooltip width="50"> </el-table-column> <el-table-column sortable prop="uploadTime" header-align="center" align="center" label="心跳时间" width="260"> </el-table-column> <el-table-column prop="dataType" header-align="center" align="center" label="数据类型" width="160" :formatter="formatDataType" show-overflow-tooltip> </el-table-column> <el-table-column sortable prop="batteryWorkModeValue" header-align="center" align="center" label="工作状态" show-overflow-tooltip width="100"> </el-table-column> <el-table-column prop="longitudeDirection,longitude,latitudeDirection,latitude" header-align="center" align="center" label="经纬度" width="200" show-overflow-tooltip> <template slot-scope="scope"> <div style="cursor:pointer;font-weight: bold;" @click="mapPositionHandleMy(scope.row.batteryId,scope.row.uploadTime,scope.row.longitudeDirection,scope.row.longitude,scope.row.latitudeDirection,scope.row.latitude)"> {{scope.row.longitudeDirection}}{{scope.row.longitude}}-{{scope.row.latitudeDirection}}{{scope.row.latitude}} </div> </template> </el-table-column> <el-table-column sortable prop="batterySoc" header-align="center" align="center" width="130" label="SOC(%)"> </el-table-column> <el-table-column sortable prop="batteryVoltage" header-align="center" align="center" label="电池包电压(mV)" width="140"> </el-table-column> <el-table-column sortable prop="cellVoltage" header-align="center" align="center" label="电芯电压(mV)" width="140" show-overflow-tooltip> </el-table-column> <el-table-column sortable prop="batteryCurrent" header-align="center" align="center" label="电池包电流(mA)" width="140"> </el-table-column> <el-table-column sortable prop="batteryVoltageMax" header-align="center" align="center" label="电芯最高电压(mV)" width="150"> </el-table-column> <el-table-column sortable prop="batteryVoltageMin" header-align="center" align="center" label="电芯最低电压(mV)" width="150"> </el-table-column> <el-table-column sortable prop="totalChargeEnergy" header-align="center" align="center" label="历史充电总能量(kwh)" width="170"> </el-table-column> <el-table-column sortable prop="chargeCyclesHistory" header-align="center" align="center" label="历史充电次数" width="150"> </el-table-column> <el-table-column sortable prop="chargeCapacityHistory" header-align="center" align="center" label="历史充电容量" width="150"> </el-table-column> <el-table-column sortable prop="dischargeCapacityHistory" header-align="center" align="center" label="历史放电容量" width="150"> </el-table-column> <el-table-column sortable prop="ntc1" header-align="center" align="center" label="NTC1(℃)" width="150"> </el-table-column> <el-table-column sortable prop="ntc2" header-align="center" align="center" label="NTC2(℃)" width="150"> </el-table-column> <el-table-column sortable prop="ntc3" header-align="center" align="center" label="NTC3(℃)" width="150"> </el-table-column> <el-table-column sortable prop="ntc4" header-align="center" align="center" label="NTC4(℃)" width="150"> </el-table-column> <el-table-column sortable prop="trackerCommunicationStatus" header-align="center" align="center" label="tracker通讯状态" width="150"> </el-table-column> <el-table-column sortable prop="batteryTemperatureMax" header-align="center" align="center" label="最高温度(℃)" width="120"> </el-table-column> <el-table-column sortable prop="batteryTemperatureMin" header-align="center" align="center" label="最低温度(℃)" width="120"> </el-table-column> <el-table-column sortable prop="mosTemp" header-align="center" align="center" label="MOS温度(℃)" width="130"> </el-table-column> <el-table-column sortable prop="batteryCycleTimes" header-align="center" align="center" width="130" label="循环次数"> </el-table-column> <el-table-column prop="batteryHardVersion" header-align="center" align="center" label="电池硬件版本" width="160"> </el-table-column> <el-table-column prop="batterySoftVersion" header-align="center" align="center" label="电池软件版本" width="160"> </el-table-column> <el-table-column prop="batteryErrorCodeValue" header-align="center" align="center" label="告警信息" show-overflow-tooltip width="120"> </el-table-column> <el-table-column prop="batteryErrorCode" header-align="center" align="center" label="故障码" show-overflow-tooltip width="120"> </el-table-column> <el-table-column prop="batteryErrorCodeDetails" header-align="center" align="center" label="故障值" show-overflow-tooltip width="120"> </el-table-column> <el-table-column prop="locationModeValue" header-align="center" align="center" label="定位模式" show-overflow-tooltip width="100"> </el-table-column> <el-table-column prop="gpsSignal" header-align="center" align="center" label="4G信号强度" width="120"> </el-table-column> <el-table-column prop="gpsSpeed" header-align="center" align="center" label="速度(Km/h)" width="120"> </el-table-column> <el-table-column prop="gpsCog" header-align="center" align="center" label="对地航向角(°)" width="120"> </el-table-column> <el-table-column prop="sn" header-align="center" align="center" label="sn码" show-overflow-tooltip> </el-table-column> <el-table-column prop="soh" header-align="center" align="center" label="SOH"> </el-table-column> <el-table-column prop="batteryTemperatureAvg" header-align="center" align="center" label="平均温度(℃)" width="100"> </el-table-column> <el-table-column prop="chargeMode" header-align="center" align="center" label="充电模式" > </el-table-column> <el-table-column prop="lowBatteryWarn" header-align="center" align="center" label="低电量提示" width="100" > </el-table-column> <el-table-column prop="chargeCurrentMax" header-align="center" align="center" label="最大充电电流(mA)" width="160"> </el-table-column> <el-table-column prop="dischargeCurrentMax" header-align="center" align="center" label="最大放电电流(mA)" width="160"> </el-table-column> <el-table-column prop="chargeCurrentAvg" header-align="center" align="center" label="平均充电电流(mA)" width="160"> </el-table-column> <el-table-column prop="dischargeCurrentAvg" header-align="center" align="center" label="平均放电电流(mA)" width="160"> </el-table-column> <el-table-column prop="trackerSoftwareVersion" header-align="center" align="center" label="tracker软件版本" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="fcc" header-align="center" align="center" label="FCC(mah)" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="actualSoh" header-align="center" align="center" label="真实SOH" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="emptyCount" header-align="center" align="center" label="未满充计数" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="dischargingSop" header-align="center" align="center" label="放电SOP(mA)" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="chargingSop" header-align="center" align="center" label="充电SOP(mA)" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="bsoc" header-align="center" align="center" label="BSOC" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="tsoc" header-align="center" align="center" label="TSOC" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="dsoc" header-align="center" align="center" label="DSOC" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="correctionRate" header-align="center" align="center" label="修正速率" width="160" show-overflow-tooltip> </el-table-column> <el-table-column prop="msgDelayTime" header-align="center" align="center" label="延时(s)" width="160" show-overflow-tooltip> </el-table-column> </el-table> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100, 500]" :page-size="pageSize" :total="totalPage" layout="total, sizes, prev, pager, next, jumper"> </el-pagination> <!-- 弹窗 地图定位 --> <map-position-my v-if="mapPositionVisibleMy" :parentmsg="curhwid" ref="mapPositionMy"></map-position-my> </div> </template> <script> import MapPositionMy from './mybatteryinfoheart-mapmy' import { debounce } from '@/utils' // 表格滚动 export default { props: ['detailsparentmsg'], data () { return { dotCount: 0 ,// 控制省略号数量 exportLoading: false, // 新增的导出加载状态 curhwid: this.detailsparentmsg, mapPositionVisibleMy: false, clientHeightRun: document.documentElement.clientHeight, tableHeightRun:630,// 定义table高度 // 1:设备主动请求或上报 2:平台主动推送或下指令 3:平台指令反馈的结果 flagIds:[ { value: '1', label: '设备主动请求或上报' }, { value: '3', label: '平台指令反馈的结果' }], dataForm: { starttime: '', endtime: '', flag: '', deviceId: '', key: '' }, dataList: [], pageIndex: 1, pageSize: 20, totalPage: 0, dataListLoading: false, dataListSelections: [], addOrUpdateVisible: false, updateVisible: false, resultShowVisible: false, pickerOptions: { shortcuts: [{ text: '今天', onClick(picker) { picker.$emit('pick', new Date()); } }, { text: '昨天', onClick(picker) { const date = new Date(); date.setTime(date.getTime() - 3600 * 1000 * 24); picker.$emit('pick', date); } }, { text: '一周前', onClick(picker) { const date = new Date(); date.setTime(date.getTime() - 3600 * 1000 * 24 * 7); picker.$emit('pick', date); } }, { text: '一个月前', onClick(picker) { const date = new Date(); date.setTime(date.getTime() - 3600 * 1000 * 24 * 30); picker.$emit('pick', date); } }, { text: '三个月前', onClick(picker) { const date = new Date(); date.setTime(date.getTime() - 3600 * 1000 * 24 * 90); picker.$emit('pick', date); } }] } } }, components: { MapPositionMy }, activated () { this.getDataList() }, watch: { // 监听高度变化 clientHeightRun(newValue, oldValue) { if(newValue) { this.getTableHeightRun() } } }, created() { // 初始值,自己定义 this.tableHeightRun = this.clientHeightRun - 240 }, mounted(){ this.getDataList() // 函数调用 // 重置浏览器高度 window.onresize = () => { this.clientHeightRun = document.documentElement.clientHeight; } }, methods: { // 防抖动 getTableHeightRun: debounce(function() { // 给table高度赋值 this.tableHeightRun = this.clientHeightRun - 240 }, 500), // 打开地图定位弹框 mapPositionHandleMy (batteryId,uploadTime,longitudeDirection,longitude,latitudeDirection,latitude) { this.mapPositionVisibleMy = true this.$nextTick(() => { this.$refs.mapPositionMy.init(batteryId,uploadTime,longitudeDirection,longitude,latitudeDirection,latitude) }) }, formatJson(filterVal, jsonData) { return jsonData.map(v => filterVal.map(j => v[j])) }, formatDataType(row, column, cellValue) { if (cellValue === 0) { return '动态'; } else if (cellValue === 1) { return '静态'; } else { return '-'; } }, list2Export() { try { // 如果没有选择开始时间或结束时间,则默认最近一周 let starttime = this.dataForm.starttime ? this.dataForm.starttime : new Date(new Date().getTime() - 3600 * 1000 * 24 * 2); let endtime = this.dataForm.endtime ? this.dataForm.endtime : new Date(); this.$confirm(`确定对数据进行导出操作?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.exportLoading = true; // 设置导出加载状态 this.$http({ url: this.$http.adornUrl('/maya/mybatteryinfo/list2Export'), method: 'get', timeout: 600000, params: this.$http.adornParams({ 'deviceId': this.detailsparentmsg, 'starttime': starttime, 'endtime': endtime, 'flag': 1 }) }).then(({ data }) => { if (data && data.code === 0) { console.log('正在导出数据到Excel文件...'); this.exportList = data.data; require.ensure([], () => { const { export_json_to_excel } = require('../../../vendor/Export2Excel'); const tHeader = ['心跳时间', '电池编号', '工作状态', '电池硬件版本', '电池软件版本', '电池包电压(mV)', '电芯电压(mV)', '电池包电流(mA)', '电芯最高电压(mV)', '电芯最低电压(mV)', '历史充电总能量(kwh)', '历史充电次数', '历史充电容量', '历史放电容量', 'NTC1', 'NTC2', 'NTC3', 'NTC4', 'traker通讯状态', '最高温度(℃)', '最低温度(℃)', 'MOS温度(℃)', '循环次数', 'SOC(%)', '故障状态', '故障码', '故障值', '定位模式', '经度标识', '经度', '纬度标识', '纬度', 'GPS信号强度', '速度(Km/h)', '对地航向角(°)', 'sn码', 'SOH', '平均温度(℃)', '充电模式', '低电量提示', '最大充电电流(mA)', '最大放电电流(mA)', '平均充电电流(mA)', '平均放电电流(mA)', 'tracker软件版本', '原始数据', 'FCC(mah)', '真实SOH', '未满充计数', '放电SOP(mA)', '充电SOP(mA)', 'BSOC', 'TSOC', 'DSOC', '修正速率', '延时(s)', '数据类型']; const filterVal = ['uploadTime', 'batteryId', 'batteryWorkModeValue', 'batteryHardVersion', 'batterySoftVersion', 'batteryVoltage', 'cellVoltage', 'batteryCurrent', 'batteryVoltageMax', 'batteryVoltageMin', 'totalChargeEnergy', 'chargeCyclesHistory', 'chargeCapacityHistory', 'dischargeCapacityHistory', 'ntc1', 'ntc2', 'ntc3', 'ntc4', 'trackerCommunicationStatus', 'batteryTemperatureMax', 'batteryTemperatureMin', 'mosTemp', 'batteryCycleTimes', 'batterySoc', 'batteryErrorCodeValue', 'batteryErrorCode', 'batteryErrorCodeDetails', 'locationModeValue', 'longitudeDirection', 'longitude', 'latitudeDirection', 'latitude', 'gpsSignal', 'gpsSpeed', 'gpsCog', 'sn', 'soh', 'batteryTemperatureAvg', 'chargeMode', 'lowBatteryWarn', 'chargeCurrentMax', 'dischargeCurrentMax', 'chargeCurrentAvg', 'dischargeCurrentAvg', 'trackerSoftwareVersion', 'payload', 'fcc', 'actualSoh', 'emptyCount', 'dischargingSop', 'chargingSop', 'bsoc', 'tsoc', 'dsoc', 'correctionRate', 'msgDelayTime', 'dataType']; const list = this.exportList; const dataExcel = this.formatJson(filterVal, list); export_json_to_excel(tHeader, dataExcel, '心跳数据导出-(' + this.detailsparentmsg + ')'); // 导出完成后提示用户 this.$message({ message: '心跳数据导出完成', type: 'success' }); }); } else { this.$message.error(data.msg); } this.exportLoading = false; // 导出完成后关闭加载状态 }); }); } catch (err) { if (err === 'cencel') { return; } if (err.code === 'ECONNABORTED' || /timeout/.test(err.message)) { this.$message.error('数据过大,导出超时!'); } else { this.$message.error(err.message); } } finally { this.exportLoading = false; // 导出失败后关闭加载状态 } }, todoNowFormat (row, column) { console.log(row.todoNow) if (row.todoNow === 0) { return '否' } else if (row.todoNow === 1) { return '是' } }, handledFormat (row, column) { console.log(row.handled) if (row.handled === 0) { return '待处理' } else if (row.handled === 1) { return '已处理' //(发送Mqtt) } else if (row.handled === 2) { return '撤销' // (清空指令) } }, flagFormat (row, column) { console.log(row.flag) if (row.flag === 1) { return '设备上报' } else if (row.flag === 2) { return '平台指令' } }, reset(){ this.dataForm = { key: undefined, deviceId: undefined, starttime: undefined, endtime: undefined, flag: undefined }; this.$refs["dataForm"].resetFields(); this.onSubmit(0) }, querySearchAsync(queryString, cb) { clearTimeout(1000); var results = [] if (queryString == '') { cb(results); } else { //掉接口需要的参数 this.$http({ url: this.$http.adornUrl('/mqtt/mqtttriad/getDeviceIds'), method: 'get', params: this.$http.adornParams({ 'deviceId': queryString, 'projectParameter': this.dataForm.projectParameter }) }).then(({data}) => { if (data && data.code === 0) { var result = data.data //循环放到一个远程搜索需要的数组 for (let i = 0; i < result.length; i++) { const element = result[i]; results.push({ value: element, id: i }) } cb(results); } else { console.log('没有数据的显示') results = [] cb(results); } }) } }, //点击出现搜索后点击的每一项 handleSelect(item) { this.id = item.id this.name = item.value }, onSubmit(pgindex) { // 添加参数判断,防止总是跳到第一页 if (pgindex==1) { this.getDataList() }else{ this.pageIndex = 1 this.getDataList() } }, // 获取数据列表 getDataList () { this.dataListLoading = true this.$http({ url: this.$http.adornUrl('/maya/mybatteryinfo/list'), method: 'get', params: this.$http.adornParams({ 'page': this.pageIndex, 'limit': this.pageSize, 'deviceId': this.detailsparentmsg, 'starttime': this.dataForm.starttime, 'endtime': this.dataForm.endtime, 'flag': 1 //this.dataForm.flag }) }).then(({data}) => { if (data && data.code === 0) { console.log('datalist:',data) this.dataList = data.page.list this.totalPage = data.page.totalCount } else { this.dataList = [] this.totalPage = 0 } this.dataListLoading = false }) }, // 每页数 sizeChangeHandle (val) { this.pageSize = val this.pageIndex = 1 this.getDataList() }, // 当前页 currentChangeHandle (val) { this.pageIndex = val this.getDataList() }, // 多选 selectionChangeHandle (val) { this.dataListSelections = val }, // 新增 / 修改 addOrUpdateHandle (id) { this.addOrUpdateVisible = true this.$nextTick(() => { this.$refs.addOrUpdate.init(id) }) }, // 删除 deleteHandle (id) { var ids = id ? [id] : this.dataListSelections.map(item => { return item.id }) this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.$http({ url: this.$http.adornUrl('/maya/mybatteryinfo/delete'), method: 'post', data: this.$http.adornData(ids, false) }).then(({data}) => { if (data && data.code === 0) { this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => { this.getDataList() } }) } else { this.$message.error(data.msg) } }) }) } } } </script> <style> .el-table__body tr.current-row > td { background-color: #6eadff !important; color: #fff; } /* #909399 #f57878 606266*/ #myTableType .el-table__body tr.current-row>td { background: #606266 !important; } #ai-tab-change .el-radio-button__inner { width: 142px; } #myTableType { font-size: 13px; } #myTableType td, #myTableType th { padding: 5px 0; min-width: 0; -webkit-box-sizing: border-box; box-sizing: border-box; text-overflow: ellipsis; vertical-align: middle; position: relative; } /* #myForm{ height: 50px; } */ #myForm .myInput .el-input__inner{ height: 30px; } </style> ,分析页面代码,导出的时候设置持续按钮的动态效果
最新发布
12-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值