先说一说我遇到的问题
elementui的el-table组件在没有数据时会展示一个空数据组件,我们可以使用empty-text来控制空数据时展示的文本,也可以用#empty插槽来展示自定义的空数据组件
针对空数据组件的位置,element-ui的设计是将空数据组件在表格内部宽度居中显示,而不是依据table整体的宽度进行居中,所以有个很尴尬的点就是...表格出现横向滚动条时,滚动表格,空数据组件也会跟随滚动...
如上图,不是居中看着就很难受,那产品和UI看了就不满意,提需求了,改!不仅要改,还要全局改!(悄咪咪吐槽一下,我也是才来的,怎么之前不知道要改,项目都三四年了堆了这么多史突然要说改)
我一看项目代码好家伙,el-table没封组件,项目中所有用到表格的都是直接用el-table,不仅如此,空状态组件也是五花八门,有文本的,有图片的,有图片加按钮的,这意味着没办法通过常规CSS全局改动样式来实现居中,总不可能每个表格改一下吧...
单独改是不可能单独改的,下面就说说我的思路吧
需求分析
我们需要实现以下功能:
- 当表格数据为空时,将空样式居中显示。
- 空组件样式不随横向滚动条滚动。
- 将功能全局化,自动应用到项目中的所有
el-table,而无需在每个组件中手动引入。
实现思路
-
编写一个mixin,用js动态控制table的空组件。
el-table的空组件是放在el-table__body-wrapper下的,所以才会受滚动条影响,不能将原组件设置绝对定位,因为高度会塌陷。我的思路是通过js克隆空组件的dom元素,将克隆的元素放到原位置用于占位,将原有的dom拿出来放到table下面,用绝对定位固定到中间,这一步替换操作是为了让居中的元素依然保留之前的交互,复制的元素是不保留绑定事件的
-
使用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 的功能,无需手动引入。
效果展示
拖动滚动条也会一直居中,点击按钮也会正常触发事件
结语
本人菜鸟,技术还不成熟,简单分享一下希望能帮到大家,各位如果有更好的方案或者对我的方案有改进建议的欢迎提出。年前放假前一天在公司摸鱼写点博客哈哈~祝大家过个好年!