说明
table-layout
是对整个表格宽高等布局进行修改的。
源码解读
import scrollbarWidth from 'element-ui/src/utils/scrollbar-width';
class TableLayout {
constructor(options) {
this.table = null;
this.store = null;
this.columns = null;
this.fit = true;
this.showHeader = true;
this.height = null;
this.scrollX = false;
this.scrollY = false;
this.bodyWidth = null;
this.fixedWidth = null;
this.rightFixedWidth = null;
this.tableHeight = null;
this.headerHeight = 44; // 表头高度
this.viewportHeight = null; // 表格高度 - 滚动条的高度
this.bodyHeight = null; // 表格高度 - 表头高度
this.fixedBodyHeight = null; // 表格高度 - 表头高度 - 滚动条高度
this.gutterWidth = scrollbarWidth(); // 滚动条宽度
for (let name in options) {
if (options.hasOwnProperty(name)) {
this[name] = options[name];
}
}
if (!this.table) {
throw new Error('table is required for Table Layout');
}
if (!this.store) {
throw new Error('store is required for Table Layout');
}
}
/**
* 更新 scrollY
*/
updateScrollY() {
const height = this.height;
if (typeof height !== 'string' && typeof height !== 'number') return;
const bodyWrapper = this.table.bodyWrapper;
if (this.table.$el && bodyWrapper) {
const body = bodyWrapper.querySelector('.el-table__body');
this.scrollY = body.offsetHeight > bodyWrapper.offsetHeight;
}
}
/**
* 设置高度
* @param value 高度值
* @param prop 设置的属性名
*/
setHeight(value, prop = 'height') {
const el = this.table.$el;
if (typeof value === 'string' && /^\d+$/.test(value)) {
value = Number(value);
}
this.height = value;
if (!el) return; // 没有挂载的的 DOM
if (typeof value === 'number') {
el.style[prop] = value + 'px';
this.updateHeight();
} else if (typeof value === 'string') {
this.updateHeight();
}
}
/**
* 设置最高高度
* @param value 高度值
* @returns {*}
*/
setMaxHeight(value) {
return this.setHeight(value, 'max-height');
}
/**
* 更新高度
*/
updateHeight() {
const height = this.tableHeight = this.table.$el.clientHeight;
const noData = !this.table.data || this.table.data.length === 0;
const { headerWrapper } = this.table.$refs;
if (this.showHeader && !headerWrapper) return; // 如果要显示 header,却没 headerWrapper
if (!this.showHeader) { // 如果不显示表头
this.headerHeight = 0; // 表头高度为 0
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
this.bodyHeight = height; // 说明表的主体部分充斥了整个表格
}
// 固定表体的高度
this.fixedBodyHeight = this.scrollX ? height - this.gutterWidth : height;
} else { // 如果显示表头
const headerHeight = this.headerHeight = headerWrapper.offsetHeight;
const bodyHeight = height - headerHeight;
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
this.bodyHeight = bodyHeight;
}
this.fixedBodyHeight = this.scrollX ? bodyHeight - this.gutterWidth : bodyHeight;
}
this.viewportHeight = this.scrollX ? height - (noData ? 0 : this.gutterWidth) : height;
}
/**
* 更新
*/
update() {
const fit = this.fit;
const columns = this.table.columns;
const bodyWidth = this.table.$el.clientWidth;
let bodyMinWidth = 0;
const flattenColumns = [];
columns.forEach((column) => {
if (column.isColumnGroup) { // 如果是列组,就放入其包含的列
flattenColumns.push.apply(flattenColumns, column.columns);
} else {
flattenColumns.push(column);
}
});
// 自适应的列
let flexColumns = flattenColumns.filter((column) => typeof column.width !== 'number');
// 如果有自适应的列,并且规定了自适应
if (flexColumns.length > 0 && fit) {
flattenColumns.forEach((column) => {
bodyMinWidth += column.width || column.minWidth || 80;
});
if (bodyMinWidth < bodyWidth - this.gutterWidth) { // DON'T HAVE SCROLL BAR 没有滚动条
this.scrollX = false; // 没有横向滚动条
const totalFlexWidth = bodyWidth - this.gutterWidth - bodyMinWidth; // 剩余可分配的宽度
if (flexColumns.length === 1) { // 如果自适应的列只有一个
flexColumns[0].realWidth = (flexColumns[0].minWidth || 80) + totalFlexWidth;
} else {
// 总宽度
const allColumnsWidth = flexColumns.reduce((prev, column) => prev + (column.minWidth || 80), 0);
// 平分
const flexWidthPerPixel = totalFlexWidth / allColumnsWidth;
let noneFirstWidth = 0;
// 按比例平分宽度
flexColumns.forEach((column, index) => {
if (index === 0) return;
const flexWidth = Math.floor((column.minWidth || 80) * flexWidthPerPixel);
noneFirstWidth += flexWidth;
column.realWidth = (column.minWidth || 80) + flexWidth;
});
// 剩余宽度都放到第一列
flexColumns[0].realWidth = (flexColumns[0].minWidth || 80) + totalFlexWidth - noneFirstWidth;
}
} else { // HAVE HORIZONTAL SCROLL BAR 如果有垂直滚动条
this.scrollX = true;
flexColumns.forEach(function(column) {
column.realWidth = column.minWidth;
});
}
this.bodyWidth = Math.max(bodyMinWidth, bodyWidth);
} else { // 如果没有自适应的
flattenColumns.forEach((column) => {
// 根据 width、minWidth、80 的顺序设置高度
if (!column.width && !column.minWidth) {
column.realWidth = 80;
} else {
column.realWidth = column.width || column.minWidth;
}
bodyMinWidth += column.realWidth;
});
// 设置是否有横向的滚动
this.scrollX = bodyMinWidth > bodyWidth;
this.bodyWidth = bodyMinWidth;
}
const fixedColumns = this.store.states.fixedColumns;
// 如果有左侧固定列
if (fixedColumns.length > 0) {
// 计算固定列的宽度
let fixedWidth = 0;
fixedColumns.forEach(function(column) {
fixedWidth += column.realWidth;
});
this.fixedWidth = fixedWidth;
}
// 计算右侧固定咧的宽度
const rightFixedColumns = this.store.states.rightFixedColumns;
if (rightFixedColumns.length > 0) {
let rightFixedWidth = 0;
rightFixedColumns.forEach(function(column) {
rightFixedWidth += column.realWidth;
});
this.rightFixedWidth = rightFixedWidth;
}
}
}
export default TableLayout;