通过继承实现el-table-column列宽根据内容自适应
安装
已封装到npm, 可直接安装使用:
$ npm install ex-table-column --save
$ yarn add ex-table-column
github地址:
https://github.com/mnm1001/ex-table-column
需求
项目中使用element-ui
的el-table
与el-table-column
,
需求上需要实现列宽根据内容自适应.
实现方案
element-ui 的table组件本身有着强大的功能, 所以我们期望保留其现有功能, 然后进行扩展. 基于这样的前提, 继承el-table-column
组件进行扩展是一个比较合适的方案.
Vue组件继承
vue 提供 extends 以扩展已有组件, 所以我们很容易实现一个扩展于 el-table-column
的组件 ex-table-column
:
import { TableColumn } from 'element-ui';
export default {
name: 'ExTableColumn',
extends: TableColumn, // 指定继承组件
};
介入el-table-column
width计算
在 element-ui
的 table-column.js 源码中, 我们发现以下一些内容和width计算有关.
computed: {
realWidth() {
return parseWidth(this.width);
},
realMinWidth() {
return parseMinWidth(this.minWidth);
},
},
methods: {
setColumnWidth(column) {
if (this.realWidth) {
column.width = this.realWidth;
}
if (this.realMinWidth) {
column.minWidth = this.realMinWidth;
}
if (!column.minWidth) {
column.minWidth = 80;
}
column.realWidth = column.width === undefined ? column.minWidth : column.width;
return column;
},
},
created() {
const chains = compose(this.setColumnRenders, this.setColumnWidth, this.setColumnForcedProps);
column = chains(column);
this.columnConfig = column;
},
我们的实现思路是Table先渲染, 计算当列内容的宽度中的最大值, 再将最大值赋予列宽然后刷新. 所以:
- 无法在
created
中介入, 因为此时Table还没有mouted, - 若在
methods
中介入的话, 我们还需要手动刷新一次组件以触发新的width
, 也不合适, - 所以修改
computed
是相对合适的, 因为computed
所依赖的值发生变化以后会自动触发组件的更新.
计算内容宽度并更新列宽
在Table
组件mounted
后遍历当列内容, 获取最大值. 然后将此值存入data
的autoWidth
中, 并使autoWidth
介入到realMinWidth
的computed
计算中, 这样就简单的实现了自动列宽的功能.
data() {
return {
autoWidth: 0,
};
},
computed: {
realMinWidth() {
return this.autoWidth;
},
},
mounted() {
let cells = window.document.querySelectorAll(`td.${this.columnId} .cell`);
const autoMinWidth = max(map(cells, item => item.getBoundingClientRect().width));
this.autoWidth = autoMinWidth;
},
完善
至此我们已基本实现自动列宽功能, 然而还有一些问题需要完善:
兼容
我们其实期望不去改变el-table-column
的原有特性, 而这里直接覆盖realMinWidth
的值稍显粗暴, 所以我们可以通过传入一个名为autoFix
的prop
来决定是否启用自动内容列宽功能, 若autoFix
为false
, 则保留el-table-column
原有的realMinWidth
计算逻辑:
props: {
autoFit: {
type: Boolean,
default: false,
},
},
data() {
return {
autoWidth: 0,
};
},
computed: {
realMinWidth() {
if (this.autoFit) {
return parseMinWidth(max([this.minWidth, this.autoWidth]));
}
return parseMinWidth(this.minWidth);
},
},
灵活性
若el-table-column
有复杂的solt
内容, 我们可能期望指定某一个element
来计算列宽, 所以可以传入一个fitByClass
的属性来指定计算列宽所依赖的element
:
props: {
fitByClass: {
type: String,
default: 'cell',
},
},
mounted() {
let cells = window.document.querySelectorAll(`td.${this.columnId} .${this.fitByClass}`);
if (isEmpty(cells)) {
cells = window.document.querySelectorAll(`td.${this.columnId} .cell`);
}
const autoMinWidth = max(map(cells, item => item.getBoundingClientRect().width));
this.autoWidth = autoMinWidth;
},
分页更新
当Table带有分页时, 切换分页不会重新触发mounted
, 但Table的列内容的最大宽度可能需要重新计算, 所以需要在updated
中重新计算autoWidth
:
methods: {
updateAutoWidth() {
let cells = window.document.querySelectorAll(`td.${this.columnId} .${this.fitByClass}`);
if (isEmpty(cells)) {
cells = window.document.querySelectorAll(`td.${this.columnId} .cell`);
}
const autoMinWidth = max(map(cells, item => item.getBoundingClientRect().width));
if (this.autoWidth !== autoMinWidth) {
this.autoWidth = autoMinWidth;
}
},
},
updated() {
this.updateAutoWidth();
},
mounted() {
this.updateAutoWidth
},
最后
中间代码有所精简, 完整源码可以参考 ExTableColumn.js .
已封装到npm, 可通过npm install ex-table-column --save
安装使用.