在 Vue 2 项目中实现 el-table 无数据时空组件左右居中,并且不跟随表格横向滚动条滚动

先说一说我遇到的问题

elementui的el-table组件在没有数据时会展示一个空数据组件,我们可以使用empty-text来控制空数据时展示的文本,也可以用#empty插槽来展示自定义的空数据组件

针对空数据组件的位置,element-ui的设计是将空数据组件在表格内部宽度居中显示,而不是依据table整体的宽度进行居中,所以有个很尴尬的点就是...表格出现横向滚动条时,滚动表格,空数据组件也会跟随滚动... image.png 如上图,不是居中看着就很难受,那产品和UI看了就不满意,提需求了,改!不仅要改,还要全局改!(悄咪咪吐槽一下,我也是才来的,怎么之前不知道要改,项目都三四年了堆了这么多史突然要说改)

我一看项目代码好家伙,el-table没封组件,项目中所有用到表格的都是直接用el-table,不仅如此,空状态组件也是五花八门,有文本的,有图片的,有图片加按钮的,这意味着没办法通过常规CSS全局改动样式来实现居中,总不可能每个表格改一下吧...

image.png

单独改是不可能单独改的,下面就说说我的思路吧

需求分析

我们需要实现以下功能:

  1. 当表格数据为空时,将空样式居中显示。
  2. 空组件样式不随横向滚动条滚动。
  3. 将功能全局化,自动应用到项目中的所有 el-table,而无需在每个组件中手动引入。

实现思路

  1. 编写一个mixin,用js动态控制table的空组件。

    el-table的空组件是放在el-table__body-wrapper下的,所以才会受滚动条影响,不能将原组件设置绝对定位,因为高度会塌陷。

    我的思路是通过js克隆空组件的dom元素,将克隆的元素放到原位置用于占位,将原有的dom拿出来放到table下面,用绝对定位固定到中间,这一步替换操作是为了让居中的元素依然保留之前的交互,复制的元素是不保留绑定事件的

  2. 使用vue的Vue.extend方法拓展el-table组件,再全局注册覆盖el-table

实现步骤

1. 编写功能逻辑的 mixin

以下是一个实现上述功能的 mixin完整代码,主要逻辑包括:

  • 监听 tableData 的变化。
  • 当表格数据为空时,克隆空组件dom,将原组件移到table下,克隆的组件移到原位置
  • 修改空样式的 DOM 结构和样式,实现居中。
  • 清理旧的空样式节点,避免性能问题。
export default {
  data() {
    return {
      tableEmptyDomClone: null,
      tableEmptyDom: null
    };
  },
  mounted() {
    this.$watch(
      "tableData",
      (newVal) => {
        if (newVal && newVal.length === 0) {
          this.handleTableEmpty();
        } else {
          if (this.tableEmptyDom) {
            if (this.tableEmptyDom.parentNode.contains(this.tableEmptyDom)) {
              this.tableEmptyDom.parentNode.removeChild(this.tableEmptyDom);
            }
            this.tableEmptyDom = null;
          }
        }
      },
      { deep: true }
    );
  },
  methods: {
    // 处理 table 数据为空时的样式
    handleTableEmpty() {
      const tableDom = this.$el;
      const tableEmptyBlock = tableDom.querySelector(".el-table__empty-block");

      // 移除旧的 empty 元素
      if (this.tableEmptyDomClone && tableEmptyBlock.contains(this.tableEmptyDomClone)) {
        tableEmptyBlock.removeChild(this.tableEmptyDomClone);
      }
      if (this.tableEmptyDom && tableDom.contains(this.tableEmptyDom)) {
        tableDom.removeChild(this.tableEmptyDom);
      }

      // 获取空元素并克隆
      this.tableEmptyDom = this.$el.querySelector(".el-table__empty-text");
      this.tableEmptyDomClone = this.tableEmptyDom.cloneNode(true);

      // 将 tableEmptyDomClone 放到原有位置中占位
      if (tableEmptyBlock.contains(this.tableEmptyDom)) {
        tableEmptyBlock.removeChild(this.tableEmptyDom);
      }
      tableEmptyBlock.appendChild(this.tableEmptyDomClone);

      const tableHeaderDom = this.$el.querySelector(".el-table__header-wrapper");
      const tableHeaderRect = tableHeaderDom.getBoundingClientRect();

      // 设置空样式的样式属性
      this.tableEmptyDom.style.position = "absolute";
      this.tableEmptyDom.style.top = `${tableHeaderRect.height}px`;
      this.tableEmptyDom.style.left = "50%";
      this.tableEmptyDom.style.transform = "translateX(-50%)";
      this.tableEmptyDom.style.backgroundColor = "#fff";
      this.tableEmptyDom.style.zIndex = "5";
      this.tableEmptyDom.style.width = "100%";
      this.tableEmptyDom.style.textAlign = 'center'
      this.tableEmptyDom.classList.add("table-empty-mixin");

      // 插入到 tableDom 中
      tableDom.appendChild(this.tableEmptyDom);
    }
  }
};

2. 使用全局扩展组件

使用 Vue 的 Vue.extend 方法对 el-table 组件进行扩展,并全局注册修改后的组件。

import Vue from "vue";
import { Table } from "element-ui";
import tableMixin from "./path/to/your/mixin"; // 引入上面的 mixin

// 扩展 el-table 组件
const ExtendedTable = Vue.extend(Table);

// 将功能逻辑混入到 el-table
ExtendedTable.mixin(tableMixin);

// 全局注册扩展后的 el-table
Vue.component("el-table", ExtendedTable);

以上代码将在项目中所有使用 el-table 的地方自动应用 tableMixin 的功能,无需手动引入。


效果展示

拖动滚动条也会一直居中,点击按钮也会正常触发事件

image.png

image.png


结语

本人菜鸟,技术还不成熟,简单分享一下希望能帮到大家,各位如果有更好的方案或者对我的方案有改进建议的欢迎提出。年前放假前一天在公司摸鱼写点博客哈哈~祝大家过个好年! image.png