安裝
已封裝到npm, 可直接安裝使用:
$ npm install ex-table-column --save
$ yarn add ex-table-column
github地址:
https://github.com/mnm1001/ex-table-column
需求
項(xiàng)目中使用element-ui的el-table與el-table-column,
需求上需要實(shí)現(xiàn)列寬根據(jù)內(nèi)容自適應(yīng).
實(shí)現(xiàn)方案
element-ui 的table組件本身有著強(qiáng)大的功能, 所以我們期望保留其現(xiàn)有功能, 然后進(jìn)行擴(kuò)展. 基于這樣的前提, 繼承el-table-column組件進(jìn)行擴(kuò)展是一個(gè)比較合適的方案.
Vue組件繼承
vue 提供 extends 以擴(kuò)展已有組件, 所以我們很容易實(shí)現(xiàn)一個(gè)擴(kuò)展于 el-table-column 的組件 ex-table-column:
import { TableColumn } from 'element-ui';
export default {
name: 'ExTableColumn',
extends: TableColumn, // 指定繼承組件
};
介入el-table-columnwidth計(jì)算
在 element-ui 的 table-column.js 源碼中, 我們發(fā)現(xiàn)以下一些內(nèi)容和width計(jì)算有關(guān).
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;
},
我們的實(shí)現(xiàn)思路是Table先渲染, 計(jì)算當(dāng)列內(nèi)容的寬度中的最大值, 再將最大值賦予列寬然后刷新. 所以:
- 無(wú)法在
created中介入, 因?yàn)榇藭r(shí)Table還沒(méi)有mouted, - 若在
methods中介入的話, 我們還需要手動(dòng)刷新一次組件以觸發(fā)新的width, 也不合適, - 所以修改
computed是相對(duì)合適的, 因?yàn)?code>computed所依賴(lài)的值發(fā)生變化以后會(huì)自動(dòng)觸發(fā)組件的更新.
計(jì)算內(nèi)容寬度并更新列寬
在Table組件mounted后遍歷當(dāng)列內(nèi)容, 獲取最大值. 然后將此值存入data的autoWidth中, 并使autoWidth介入到realMinWidth的computed計(jì)算中, 這樣就簡(jiǎn)單的實(shí)現(xiàn)了自動(dòng)列寬的功能.
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;
},
完善
至此我們已基本實(shí)現(xiàn)自動(dòng)列寬功能, 然而還有一些問(wèn)題需要完善:
兼容
我們其實(shí)期望不去改變el-table-column的原有特性, 而這里直接覆蓋realMinWidth的值稍顯粗暴, 所以我們可以通過(guò)傳入一個(gè)名為autoFix的prop來(lái)決定是否啟用自動(dòng)內(nèi)容列寬功能, 若autoFix為false, 則保留el-table-column原有的realMinWidth計(jì)算邏輯:
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有復(fù)雜的solt內(nèi)容, 我們可能期望指定某一個(gè)element來(lái)計(jì)算列寬, 所以可以傳入一個(gè)fitByClass的屬性來(lái)指定計(jì)算列寬所依賴(lài)的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;
},
分頁(yè)更新
當(dāng)Table帶有分頁(yè)時(shí), 切換分頁(yè)不會(huì)重新觸發(fā)mounted, 但Table的列內(nèi)容的最大寬度可能需要重新計(jì)算, 所以需要在updated中重新計(jì)算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
},
最后
中間代碼有所精簡(jiǎn), 完整源碼可以參考 ExTableColumn.js .
已封裝到npm, 可通過(guò)npm install ex-table-column --save安裝使用.