mirror of https://github.com/ElemeFE/element
Table: improve performance (#9426)
* improve table render time in some condition * Update table.vuepull/9566/head
parent
b27f340561
commit
9b9384214b
|
@ -0,0 +1,68 @@
|
|||
export default {
|
||||
created() {
|
||||
this.tableLayout.addObserver(this);
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.tableLayout.removeObserver(this);
|
||||
},
|
||||
|
||||
computed: {
|
||||
tableLayout() {
|
||||
let layout = this.layout;
|
||||
if (!layout && this.table) {
|
||||
layout = this.table.layout;
|
||||
}
|
||||
if (!layout) {
|
||||
throw new Error('Can not find table layout.');
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.onColumnsChange(this.tableLayout);
|
||||
this.onScrollableChange(this.tableLayout);
|
||||
},
|
||||
|
||||
updated() {
|
||||
if (this.__updated__) return;
|
||||
this.onColumnsChange(this.tableLayout);
|
||||
this.onScrollableChange(this.tableLayout);
|
||||
this.__updated__ = true;
|
||||
},
|
||||
|
||||
methods: {
|
||||
onColumnsChange() {
|
||||
const cols = this.$el.querySelectorAll('colgroup > col');
|
||||
if (!cols.length) return;
|
||||
const flattenColumns = this.tableLayout.getFlattenColumns();
|
||||
const columnsMap = {};
|
||||
flattenColumns.forEach((column) => {
|
||||
columnsMap[column.id] = column;
|
||||
});
|
||||
for (let i = 0, j = cols.length; i < j; i++) {
|
||||
const col = cols[i];
|
||||
const name = col.getAttribute('name');
|
||||
const column = columnsMap[name];
|
||||
if (column) {
|
||||
col.setAttribute('width', column.realWidth || column.width);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onScrollableChange(layout) {
|
||||
const cols = this.$el.querySelectorAll('colgroup > col[name=gutter]');
|
||||
for (let i = 0, j = cols.length; i < j; i++) {
|
||||
const col = cols[i];
|
||||
col.setAttribute('width', layout.scrollY ? layout.gutterWidth : '0');
|
||||
}
|
||||
const ths = this.$el.querySelectorAll('th.gutter');
|
||||
for (let i = 0, j = ths.length; i < j; i++) {
|
||||
const th = ths[i];
|
||||
th.style.width = layout.scrollY ? layout.gutterWidth + 'px' : '0';
|
||||
th.style.display = layout.scrollY ? '' : 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -3,8 +3,13 @@ import { hasClass, addClass, removeClass } from 'element-ui/src/utils/dom';
|
|||
import ElCheckbox from 'element-ui/packages/checkbox';
|
||||
import ElTooltip from 'element-ui/packages/tooltip';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import LayoutObserver from './layout-observer';
|
||||
|
||||
export default {
|
||||
name: 'ElTableBody',
|
||||
|
||||
mixins: [LayoutObserver],
|
||||
|
||||
components: {
|
||||
ElCheckbox,
|
||||
ElTooltip
|
||||
|
@ -16,9 +21,6 @@ export default {
|
|||
},
|
||||
stripe: Boolean,
|
||||
context: {},
|
||||
layout: {
|
||||
required: true
|
||||
},
|
||||
rowClassName: [String, Function],
|
||||
rowStyle: [Object, Function],
|
||||
fixed: String,
|
||||
|
@ -35,11 +37,7 @@ export default {
|
|||
border="0">
|
||||
<colgroup>
|
||||
{
|
||||
this._l(this.columns, column =>
|
||||
<col
|
||||
name={ column.id }
|
||||
width={ column.realWidth || column.width }
|
||||
/>)
|
||||
this._l(this.columns, column => <col name={ column.id } />)
|
||||
}
|
||||
</colgroup>
|
||||
<tbody>
|
||||
|
@ -112,9 +110,6 @@ export default {
|
|||
}
|
||||
})
|
||||
}
|
||||
{
|
||||
!this.fixed && this.layout.scrollY && this.layout.gutterWidth ? <td class="gutter" /> : ''
|
||||
}
|
||||
</tr>,
|
||||
this.store.isRowExpanded(row)
|
||||
? (<tr>
|
||||
|
@ -344,7 +339,7 @@ export default {
|
|||
|
||||
if (hasClass(cellChild, 'el-tooltip') && cellChild.scrollWidth > cellChild.offsetWidth && this.$refs.tooltip) {
|
||||
const tooltip = this.$refs.tooltip;
|
||||
|
||||
// TODO 会引起整个 Table 的重新渲染,需要优化
|
||||
this.tooltipContent = cell.textContent || cell.innerText;
|
||||
tooltip.referenceElm = cell;
|
||||
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none');
|
||||
|
@ -363,7 +358,7 @@ export default {
|
|||
const cell = getCell(event);
|
||||
if (!cell) return;
|
||||
|
||||
const oldHoverState = this.table.hoverState;
|
||||
const oldHoverState = this.table.hoverState || {};
|
||||
this.table.$emit('cell-mouse-leave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event);
|
||||
},
|
||||
|
||||
|
|
|
@ -116,6 +116,26 @@ const DEFAULT_RENDER_CELL = function(h, { row, column }) {
|
|||
return value;
|
||||
};
|
||||
|
||||
const parseWidth = (width) => {
|
||||
if (width !== undefined) {
|
||||
width = parseInt(width, 10);
|
||||
if (isNaN(width)) {
|
||||
width = null;
|
||||
}
|
||||
}
|
||||
return width;
|
||||
};
|
||||
|
||||
const parseMinWidth = (minWidth) => {
|
||||
if (minWidth !== undefined) {
|
||||
minWidth = parseInt(minWidth, 10);
|
||||
if (isNaN(minWidth)) {
|
||||
minWidth = 80;
|
||||
}
|
||||
}
|
||||
return minWidth;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'ElTableColumn',
|
||||
|
||||
|
@ -205,25 +225,12 @@ export default {
|
|||
let parent = this.columnOrTableParent;
|
||||
let owner = this.owner;
|
||||
this.isSubColumn = owner !== parent;
|
||||
this.columnId = (parent.tableId || (parent.columnId + '_')) + 'column_' + columnIdSeed++;
|
||||
this.columnId = (parent.tableId || parent.columnId) + '_column_' + columnIdSeed++;
|
||||
|
||||
let type = this.type;
|
||||
|
||||
let width = this.width;
|
||||
if (width !== undefined) {
|
||||
width = parseInt(width, 10);
|
||||
if (isNaN(width)) {
|
||||
width = null;
|
||||
}
|
||||
}
|
||||
|
||||
let minWidth = this.minWidth;
|
||||
if (minWidth !== undefined) {
|
||||
minWidth = parseInt(minWidth, 10);
|
||||
if (isNaN(minWidth)) {
|
||||
minWidth = 80;
|
||||
}
|
||||
}
|
||||
const width = parseWidth(this.width);
|
||||
const minWidth = parseMinWidth(this.minWidth);
|
||||
|
||||
let isColumnGroup = false;
|
||||
|
||||
|
@ -353,14 +360,14 @@ export default {
|
|||
|
||||
width(newVal) {
|
||||
if (this.columnConfig) {
|
||||
this.columnConfig.width = newVal;
|
||||
this.columnConfig.width = parseWidth(newVal);
|
||||
this.owner.store.scheduleLayout();
|
||||
}
|
||||
},
|
||||
|
||||
minWidth(newVal) {
|
||||
if (this.columnConfig) {
|
||||
this.columnConfig.minWidth = newVal;
|
||||
this.columnConfig.minWidth = parseMinWidth(newVal);
|
||||
this.owner.store.scheduleLayout();
|
||||
}
|
||||
},
|
||||
|
@ -368,7 +375,7 @@ export default {
|
|||
fixed(newVal) {
|
||||
if (this.columnConfig) {
|
||||
this.columnConfig.fixed = newVal;
|
||||
this.owner.store.scheduleLayout();
|
||||
this.owner.store.scheduleLayout(true);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import LayoutObserver from './layout-observer';
|
||||
|
||||
export default {
|
||||
name: 'ElTableFooter',
|
||||
|
||||
mixins: [LayoutObserver],
|
||||
|
||||
render(h) {
|
||||
const sums = [];
|
||||
this.columns.forEach((column, index) => {
|
||||
|
@ -41,16 +45,10 @@ export default {
|
|||
border="0">
|
||||
<colgroup>
|
||||
{
|
||||
this._l(this.columns, column =>
|
||||
<col
|
||||
name={ column.id }
|
||||
width={ column.realWidth || column.width }
|
||||
/>)
|
||||
this._l(this.columns, column => <col name={ column.id } />)
|
||||
}
|
||||
{
|
||||
!this.fixed && this.layout.gutterWidth
|
||||
? <col name="gutter" width={ this.layout.scrollY ? this.layout.gutterWidth : '' }></col>
|
||||
: ''
|
||||
this.hasGutter ? <col name="gutter" /> : ''
|
||||
}
|
||||
</colgroup>
|
||||
<tbody class={ [{ 'has-gutter': this.hasGutter }] }>
|
||||
|
@ -70,9 +68,7 @@ export default {
|
|||
)
|
||||
}
|
||||
{
|
||||
this.hasGutter
|
||||
? <td class="gutter" style={{ width: this.layout.scrollY ? this.layout.gutterWidth + 'px' : '0' }}></td>
|
||||
: ''
|
||||
this.hasGutter ? <th class="gutter"></th> : ''
|
||||
}
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -85,9 +81,6 @@ export default {
|
|||
store: {
|
||||
required: true
|
||||
},
|
||||
layout: {
|
||||
required: true
|
||||
},
|
||||
summaryMethod: Function,
|
||||
sumText: String,
|
||||
border: Boolean,
|
||||
|
@ -103,6 +96,10 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
table() {
|
||||
return this.$parent;
|
||||
},
|
||||
|
||||
isAllSelected() {
|
||||
return this.store.states.isAllSelected;
|
||||
},
|
||||
|
@ -124,7 +121,7 @@ export default {
|
|||
},
|
||||
|
||||
hasGutter() {
|
||||
return !this.fixed && this.layout.gutterWidth;
|
||||
return !this.fixed && this.tableLayout.gutterWidth;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import ElCheckbox from 'element-ui/packages/checkbox';
|
|||
import ElTag from 'element-ui/packages/tag';
|
||||
import Vue from 'vue';
|
||||
import FilterPanel from './filter-panel.vue';
|
||||
import LayoutObserver from './layout-observer';
|
||||
|
||||
const getAllColumns = (columns) => {
|
||||
const result = [];
|
||||
|
@ -65,13 +66,14 @@ const convertToRows = (originColumns) => {
|
|||
export default {
|
||||
name: 'ElTableHeader',
|
||||
|
||||
mixins: [LayoutObserver],
|
||||
|
||||
render(h) {
|
||||
const originColumns = this.store.states.originColumns;
|
||||
const columnRows = convertToRows(originColumns, this.columns);
|
||||
// 是否拥有多级表头
|
||||
const isGroup = columnRows.length > 1;
|
||||
if (isGroup) this.$parent.isGroup = true;
|
||||
|
||||
return (
|
||||
<table
|
||||
class="el-table__header"
|
||||
|
@ -80,16 +82,10 @@ export default {
|
|||
border="0">
|
||||
<colgroup>
|
||||
{
|
||||
this._l(this.columns, column =>
|
||||
<col
|
||||
name={ column.id }
|
||||
width={ column.realWidth || column.width }
|
||||
/>)
|
||||
this._l(this.columns, column => <col name={ column.id } />)
|
||||
}
|
||||
{
|
||||
!this.fixed && this.layout.gutterWidth
|
||||
? <col name="gutter" width={ this.layout.scrollY ? this.layout.gutterWidth : '' }></col>
|
||||
: ''
|
||||
this.hasGutter ? <col name="gutter" /> : ''
|
||||
}
|
||||
</colgroup>
|
||||
<thead class={ [{ 'is-group': isGroup, 'has-gutter': this.hasGutter }] }>
|
||||
|
@ -137,12 +133,7 @@ export default {
|
|||
)
|
||||
}
|
||||
{
|
||||
this.hasGutter
|
||||
? <th class="gutter" style={{
|
||||
width: this.layout.scrollY ? this.layout.gutterWidth + 'px' : '0',
|
||||
display: this.layout.scrollY ? '' : 'none'
|
||||
}}></th>
|
||||
: ''
|
||||
this.hasGutter ? <th class="gutter"></th> : ''
|
||||
}
|
||||
</tr>
|
||||
)
|
||||
|
@ -157,9 +148,6 @@ export default {
|
|||
store: {
|
||||
required: true
|
||||
},
|
||||
layout: {
|
||||
required: true
|
||||
},
|
||||
border: Boolean,
|
||||
defaultSort: {
|
||||
type: Object,
|
||||
|
@ -211,7 +199,7 @@ export default {
|
|||
},
|
||||
|
||||
hasGutter() {
|
||||
return !this.fixed && this.layout.gutterWidth;
|
||||
return !this.fixed && this.tableLayout.gutterWidth;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import scrollbarWidth from 'element-ui/src/utils/scrollbar-width';
|
||||
import Vue from 'vue';
|
||||
|
||||
class TableLayout {
|
||||
constructor(options) {
|
||||
this.observers = [];
|
||||
this.table = null;
|
||||
this.store = null;
|
||||
this.columns = null;
|
||||
|
@ -43,7 +45,7 @@ class TableLayout {
|
|||
const bodyWrapper = this.table.bodyWrapper;
|
||||
if (this.table.$el && bodyWrapper) {
|
||||
const body = bodyWrapper.querySelector('.el-table__body');
|
||||
this.scrollY = body.offsetHeight > bodyWrapper.offsetHeight;
|
||||
this.scrollY = body.offsetHeight > this.bodyHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,19 +54,19 @@ class TableLayout {
|
|||
if (typeof value === 'string' && /^\d+$/.test(value)) {
|
||||
value = Number(value);
|
||||
}
|
||||
|
||||
this.height = value;
|
||||
|
||||
if (!el) return;
|
||||
if (!el && value) return Vue.nextTick(() => this.setHeight(value, prop));
|
||||
|
||||
if (typeof value === 'number') {
|
||||
el.style[prop] = value + 'px';
|
||||
|
||||
this.updateHeight();
|
||||
this.updateElsHeight();
|
||||
} else if (typeof value === 'string') {
|
||||
if (value === '') {
|
||||
el.style[prop] = '';
|
||||
}
|
||||
this.updateHeight();
|
||||
this.updateElsHeight();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,37 +74,33 @@ class TableLayout {
|
|||
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;
|
||||
updateElsHeight() {
|
||||
if (!this.table.$ready) return Vue.nextTick(() => this.updateElsHeight());
|
||||
const { headerWrapper, appendWrapper, footerWrapper } = this.table.$refs;
|
||||
const footerHeight = this.footerHeight = footerWrapper ? footerWrapper.offsetHeight : 0;
|
||||
this.appendHeight = appendWrapper ? appendWrapper.offsetHeight : 0;
|
||||
|
||||
if (this.showHeader && !headerWrapper) return;
|
||||
if (!this.showHeader) {
|
||||
this.headerHeight = 0;
|
||||
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
|
||||
this.bodyHeight = height - footerHeight + (footerWrapper ? 1 : 0);
|
||||
}
|
||||
this.fixedBodyHeight = this.scrollX ? height - this.gutterWidth : height;
|
||||
} else {
|
||||
const headerHeight = this.headerHeight = headerWrapper.offsetHeight;
|
||||
const bodyHeight = height - headerHeight - footerHeight + (footerWrapper ? 1 : 0);
|
||||
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
|
||||
this.bodyHeight = bodyHeight;
|
||||
}
|
||||
this.fixedBodyHeight = this.scrollX ? bodyHeight - this.gutterWidth : bodyHeight;
|
||||
const headerHeight = this.headerHeight = !this.showHeader ? 0 : headerWrapper.offsetHeight;
|
||||
if (this.showHeader && headerWrapper.offsetWidth > 0 && headerHeight < 2) {
|
||||
return Vue.nextTick(() => this.updateElsHeight());
|
||||
}
|
||||
this.viewportHeight = this.scrollX ? height - (noData ? 0 : this.gutterWidth) : height;
|
||||
const tableHeight = this.tableHeight = this.table.$el.clientHeight;
|
||||
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
|
||||
const footerHeight = this.footerHeight = footerWrapper ? footerWrapper.offsetHeight : 0;
|
||||
this.bodyHeight = tableHeight - headerHeight - footerHeight + (footerWrapper ? 1 : 0);
|
||||
}
|
||||
this.fixedBodyHeight = this.scrollX ? this.bodyHeight - this.gutterWidth : this.bodyHeight;
|
||||
|
||||
const noData = !this.table.data || this.table.data.length === 0;
|
||||
this.viewportHeight = this.scrollX ? tableHeight - (noData ? 0 : this.gutterWidth) : tableHeight;
|
||||
|
||||
this.updateScrollY();
|
||||
this.notifyObservers('scrollable');
|
||||
}
|
||||
|
||||
update() {
|
||||
const fit = this.fit;
|
||||
const columns = this.table.columns;
|
||||
const bodyWidth = this.table.$el.clientWidth;
|
||||
let bodyMinWidth = 0;
|
||||
|
||||
getFlattenColumns() {
|
||||
const flattenColumns = [];
|
||||
const columns = this.table.columns;
|
||||
columns.forEach((column) => {
|
||||
if (column.isColumnGroup) {
|
||||
flattenColumns.push.apply(flattenColumns, column.columns);
|
||||
|
@ -111,8 +109,21 @@ class TableLayout {
|
|||
}
|
||||
});
|
||||
|
||||
return flattenColumns;
|
||||
}
|
||||
|
||||
updateColumnsWidth() {
|
||||
const fit = this.fit;
|
||||
const bodyWidth = this.table.$el.clientWidth;
|
||||
let bodyMinWidth = 0;
|
||||
|
||||
const flattenColumns = this.getFlattenColumns();
|
||||
let flexColumns = flattenColumns.filter((column) => typeof column.width !== 'number');
|
||||
|
||||
flattenColumns.forEach((column) => { // Clean those columns whose width changed from flex to unflex
|
||||
if (typeof column.width === 'number' && column.realWidth) column.realWidth = null;
|
||||
});
|
||||
|
||||
if (flexColumns.length > 0 && fit) {
|
||||
flattenColumns.forEach((column) => {
|
||||
bodyMinWidth += column.width || column.minWidth || 80;
|
||||
|
@ -169,7 +180,7 @@ class TableLayout {
|
|||
if (fixedColumns.length > 0) {
|
||||
let fixedWidth = 0;
|
||||
fixedColumns.forEach(function(column) {
|
||||
fixedWidth += column.realWidth;
|
||||
fixedWidth += column.realWidth || column.width;
|
||||
});
|
||||
|
||||
this.fixedWidth = fixedWidth;
|
||||
|
@ -179,11 +190,40 @@ class TableLayout {
|
|||
if (rightFixedColumns.length > 0) {
|
||||
let rightFixedWidth = 0;
|
||||
rightFixedColumns.forEach(function(column) {
|
||||
rightFixedWidth += column.realWidth;
|
||||
rightFixedWidth += column.realWidth || column.width;
|
||||
});
|
||||
|
||||
this.rightFixedWidth = rightFixedWidth;
|
||||
}
|
||||
|
||||
this.notifyObservers('columns');
|
||||
}
|
||||
|
||||
addObserver(observer) {
|
||||
this.observers.push(observer);
|
||||
}
|
||||
|
||||
removeObserver(observer) {
|
||||
const index = this.observers.indexOf(observer);
|
||||
if (index !== -1) {
|
||||
this.observers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
notifyObservers(event) {
|
||||
const observers = this.observers;
|
||||
observers.forEach((observer) => {
|
||||
switch (event) {
|
||||
case 'columns':
|
||||
observer.onColumnsChange(this);
|
||||
break;
|
||||
case 'scrollable':
|
||||
observer.onScrollableChange(this);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Table Layout don't have event ${event}.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,6 @@ const TableStore = function(table, initialState = {}) {
|
|||
fixedLeafColumnsLength: 0,
|
||||
rightFixedLeafColumnsLength: 0,
|
||||
isComplex: false,
|
||||
_data: null,
|
||||
filteredData: null,
|
||||
data: null,
|
||||
sortingColumn: null,
|
||||
|
@ -137,15 +136,6 @@ TableStore.prototype.mutations = {
|
|||
states.filteredData = data;
|
||||
states.data = sortData((data || []), states);
|
||||
|
||||
// states.data.forEach((item) => {
|
||||
// if (!item.$extra) {
|
||||
// Object.defineProperty(item, '$extra', {
|
||||
// value: {},
|
||||
// enumerable: false
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
this.updateCurrentRow();
|
||||
|
||||
if (!states.reserveSelection) {
|
||||
|
@ -252,8 +242,10 @@ TableStore.prototype.mutations = {
|
|||
states.reserveSelection = column.reserveSelection;
|
||||
}
|
||||
|
||||
this.updateColumns(); // hack for dynamics insert column
|
||||
this.scheduleLayout();
|
||||
if (this.table.$ready) {
|
||||
this.updateColumns(); // hack for dynamics insert column
|
||||
this.scheduleLayout();
|
||||
}
|
||||
},
|
||||
|
||||
removeColumn(states, column, parent) {
|
||||
|
@ -266,8 +258,10 @@ TableStore.prototype.mutations = {
|
|||
array.splice(array.indexOf(column), 1);
|
||||
}
|
||||
|
||||
this.updateColumns(); // hack for dynamics remove column
|
||||
this.scheduleLayout();
|
||||
if (this.table.$ready) {
|
||||
this.updateColumns(); // hack for dynamics remove column
|
||||
this.scheduleLayout();
|
||||
}
|
||||
},
|
||||
|
||||
setHoverRow(states, row) {
|
||||
|
@ -370,7 +364,9 @@ TableStore.prototype.clearSelection = function() {
|
|||
const states = this.states;
|
||||
states.isAllSelected = false;
|
||||
const oldSelection = states.selection;
|
||||
states.selection = [];
|
||||
if (states.selection.length) {
|
||||
states.selection = [];
|
||||
}
|
||||
if (oldSelection.length > 0) {
|
||||
this.table.$emit('selection-change', states.selection ? states.selection.slice() : []);
|
||||
}
|
||||
|
@ -531,8 +527,11 @@ TableStore.prototype.updateAllSelected = function() {
|
|||
states.isAllSelected = isAllSelected;
|
||||
};
|
||||
|
||||
TableStore.prototype.scheduleLayout = function() {
|
||||
this.table.debouncedLayout();
|
||||
TableStore.prototype.scheduleLayout = function(updateColumns) {
|
||||
if (updateColumns) {
|
||||
this.updateColumns();
|
||||
}
|
||||
this.table.debouncedUpdateLayout();
|
||||
};
|
||||
|
||||
TableStore.prototype.setCurrentRowKey = function(key) {
|
||||
|
|
|
@ -7,154 +7,213 @@
|
|||
'el-table--hidden': isHidden,
|
||||
'el-table--group': isGroup,
|
||||
'el-table--fluid-height': maxHeight,
|
||||
'el-table--scrollable-x': layout.scrollX,
|
||||
'el-table--scrollable-y': layout.scrollY,
|
||||
'el-table--enable-row-hover': !store.states.isComplex,
|
||||
'el-table--enable-row-transition': (store.states.data || []).length !== 0 && (store.states.data || []).length < 100
|
||||
}, tableSize ? `el-table--${ tableSize }` : '']"
|
||||
@mouseleave="handleMouseLeave($event)">
|
||||
<div class="hidden-columns" ref="hiddenColumns"><slot></slot></div>
|
||||
<div class="el-table__header-wrapper" ref="headerWrapper" v-if="showHeader" v-mousewheel="handleHeaderFooterMousewheel">
|
||||
<div
|
||||
v-if="showHeader"
|
||||
v-mousewheel="handleHeaderFooterMousewheel"
|
||||
class="el-table__header-wrapper"
|
||||
ref="headerWrapper">
|
||||
<table-header
|
||||
ref="tableHeader"
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:border="border"
|
||||
:default-sort="defaultSort"
|
||||
:style="{ width: layout.bodyWidth ? layout.bodyWidth + 'px' : '' }">
|
||||
:style="{
|
||||
width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
|
||||
}">
|
||||
</table-header>
|
||||
</div>
|
||||
<div
|
||||
class="el-table__body-wrapper"
|
||||
ref="bodyWrapper"
|
||||
:class="[layout.scrollX ? `is-scroll-${scrollPosition}` : 'is-scroll-none']"
|
||||
:class="[layout.scrollX ? `is-scrolling-${scrollPosition}` : 'is-scrolling-none']"
|
||||
:style="[bodyHeight]">
|
||||
<table-body
|
||||
:context="context"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:layout="layout"
|
||||
:row-class-name="rowClassName"
|
||||
:row-style="rowStyle"
|
||||
:highlight="highlightCurrentRow"
|
||||
:style="{ width: bodyWidth }">
|
||||
:style="{
|
||||
width: bodyWidth
|
||||
}">
|
||||
</table-body>
|
||||
<div :style="{ width: bodyWidth }" class="el-table__empty-block" v-if="!data || data.length === 0">
|
||||
<span class="el-table__empty-text"><slot name="empty">{{ emptyText || t('el.table.emptyText') }}</slot></span>
|
||||
<div
|
||||
v-if="!data || data.length === 0"
|
||||
class="el-table__empty-block"
|
||||
ref="emptyBlock"
|
||||
:style="{
|
||||
width: bodyWidth
|
||||
}">
|
||||
<span class="el-table__empty-text">
|
||||
<slot name="empty">{{ emptyText || t('el.table.emptyText') }}</slot>
|
||||
</span>
|
||||
</div>
|
||||
<div class="el-table__append-wrapper" ref="appendWrapper" v-if="$slots.append">
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
class="el-table__append-wrapper"
|
||||
ref="appendWrapper">
|
||||
<slot name="append"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-table__footer-wrapper" ref="footerWrapper" v-if="showSummary" v-show="data && data.length > 0" v-mousewheel="handleHeaderFooterMousewheel">
|
||||
<div
|
||||
v-if="showSummary"
|
||||
v-show="data && data.length > 0"
|
||||
v-mousewheel="handleHeaderFooterMousewheel"
|
||||
class="el-table__footer-wrapper"
|
||||
ref="footerWrapper">
|
||||
<table-footer
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:border="border"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:summary-method="summaryMethod"
|
||||
:default-sort="defaultSort"
|
||||
:style="{ width: layout.bodyWidth ? layout.bodyWidth + 'px' : '' }">
|
||||
:style="{
|
||||
width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
|
||||
}">
|
||||
</table-footer>
|
||||
</div>
|
||||
<div class="el-table__fixed" ref="fixedWrapper"
|
||||
<div
|
||||
v-if="fixedColumns.length > 0"
|
||||
v-mousewheel="handleFixedMousewheel"
|
||||
:style="[
|
||||
{ width: layout.fixedWidth ? layout.fixedWidth + 'px' : '' },
|
||||
fixedHeight
|
||||
]">
|
||||
<div class="el-table__fixed-header-wrapper" ref="fixedHeaderWrapper" v-if="showHeader">
|
||||
class="el-table__fixed"
|
||||
ref="fixedWrapper"
|
||||
:style="[{
|
||||
width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''
|
||||
},
|
||||
fixedHeight]">
|
||||
<div
|
||||
v-if="showHeader"
|
||||
class="el-table__fixed-header-wrapper"
|
||||
ref="fixedHeaderWrapper" >
|
||||
<table-header
|
||||
ref="fixedTableHeader"
|
||||
fixed="left"
|
||||
:border="border"
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:style="{ width: layout.fixedWidth ? layout.fixedWidth + 'px' : '' }"></table-header>
|
||||
:style="{
|
||||
width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''
|
||||
}"></table-header>
|
||||
</div>
|
||||
<div
|
||||
class="el-table__fixed-body-wrapper"
|
||||
ref="fixedBodyWrapper"
|
||||
:style="[
|
||||
{ top: layout.headerHeight + 'px' },
|
||||
fixedBodyHeight
|
||||
]">
|
||||
:style="[{
|
||||
top: layout.headerHeight + 'px'
|
||||
},
|
||||
fixedBodyHeight]">
|
||||
<table-body
|
||||
fixed="left"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:layout="layout"
|
||||
:highlight="highlightCurrentRow"
|
||||
:row-class-name="rowClassName"
|
||||
:row-style="rowStyle"
|
||||
:style="{ width: layout.fixedWidth ? layout.fixedWidth + 'px' : '' }">
|
||||
:style="{
|
||||
width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''
|
||||
}">
|
||||
</table-body>
|
||||
<div class="el-table__append-gutter" :style="{ height: layout.appendHeight + 'px' }" v-if="$slots.append"></div>
|
||||
<div
|
||||
v-if="$slots.append"
|
||||
class="el-table__append-gutter"
|
||||
:style="{
|
||||
height: layout.appendHeight + 'px'
|
||||
}"></div>
|
||||
</div>
|
||||
<div class="el-table__fixed-footer-wrapper" ref="fixedFooterWrapper" v-if="showSummary" v-show="data && data.length > 0">
|
||||
<div
|
||||
v-if="showSummary"
|
||||
v-show="data && data.length > 0"
|
||||
class="el-table__fixed-footer-wrapper"
|
||||
ref="fixedFooterWrapper">
|
||||
<table-footer
|
||||
fixed="left"
|
||||
:border="border"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:summary-method="summaryMethod"
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:style="{ width: layout.fixedWidth ? layout.fixedWidth + 'px' : '' }"></table-footer>
|
||||
:style="{
|
||||
width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''
|
||||
}"></table-footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-table__fixed-right" ref="rightFixedWrapper"
|
||||
<div
|
||||
v-if="rightFixedColumns.length > 0"
|
||||
v-mousewheel="handleFixedMousewheel"
|
||||
:style="[
|
||||
{ width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '' },
|
||||
{ right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : '' },
|
||||
fixedHeight
|
||||
]">
|
||||
<div class="el-table__fixed-header-wrapper" ref="rightFixedHeaderWrapper" v-if="showHeader">
|
||||
class="el-table__fixed-right"
|
||||
ref="rightFixedWrapper"
|
||||
:style="[{
|
||||
width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '',
|
||||
right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : ''
|
||||
},
|
||||
fixedHeight]">
|
||||
<div v-if="showHeader"
|
||||
class="el-table__fixed-header-wrapper"
|
||||
ref="rightFixedHeaderWrapper">
|
||||
<table-header
|
||||
ref="rightFixedTableHeader"
|
||||
fixed="right"
|
||||
:border="border"
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:style="{ width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '' }"></table-header>
|
||||
:style="{
|
||||
width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : ''
|
||||
}"></table-header>
|
||||
</div>
|
||||
<div class="el-table__fixed-body-wrapper" ref="rightFixedBodyWrapper"
|
||||
:style="[
|
||||
{ top: layout.headerHeight + 'px' },
|
||||
fixedBodyHeight
|
||||
]">
|
||||
<div
|
||||
class="el-table__fixed-body-wrapper"
|
||||
ref="rightFixedBodyWrapper"
|
||||
:style="[{
|
||||
top: layout.headerHeight + 'px'
|
||||
},
|
||||
fixedBodyHeight]">
|
||||
<table-body
|
||||
fixed="right"
|
||||
:store="store"
|
||||
:stripe="stripe"
|
||||
:layout="layout"
|
||||
:row-class-name="rowClassName"
|
||||
:row-style="rowStyle"
|
||||
:highlight="highlightCurrentRow"
|
||||
:style="{ width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '' }">
|
||||
:style="{
|
||||
width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : ''
|
||||
}">
|
||||
</table-body>
|
||||
</div>
|
||||
<div class="el-table__fixed-footer-wrapper" ref="rightFixedFooterWrapper" v-if="showSummary" v-show="data && data.length > 0">
|
||||
<div
|
||||
v-if="showSummary"
|
||||
v-show="data && data.length > 0"
|
||||
class="el-table__fixed-footer-wrapper"
|
||||
ref="rightFixedFooterWrapper">
|
||||
<table-footer
|
||||
fixed="right"
|
||||
:border="border"
|
||||
:sum-text="sumText || t('el.table.sumText')"
|
||||
:summary-method="summaryMethod"
|
||||
:store="store"
|
||||
:layout="layout"
|
||||
:style="{ width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '' }"></table-footer>
|
||||
:style="{
|
||||
width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : ''
|
||||
}"></table-footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-table__fixed-right-patch"
|
||||
<div
|
||||
v-if="rightFixedColumns.length > 0"
|
||||
:style="{ width: layout.scrollY ? layout.gutterWidth + 'px' : '0', height: layout.headerHeight + 'px' }"></div>
|
||||
class="el-table__fixed-right-patch"
|
||||
ref="rightFixedPatch"
|
||||
:style="{
|
||||
width: layout.scrollY ? layout.gutterWidth + 'px' : '0',
|
||||
height: layout.headerHeight + 'px'
|
||||
}"></div>
|
||||
<div class="el-table__column-resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/babel">
|
||||
import ElCheckbox from 'element-ui/packages/checkbox';
|
||||
import throttle from 'throttle-debounce/throttle';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
|
||||
import Mousewheel from 'element-ui/src/directives/mousewheel';
|
||||
|
@ -346,32 +405,44 @@
|
|||
});
|
||||
|
||||
if (this.fit) {
|
||||
this.windowResizeListener = throttle(50, () => {
|
||||
if (this.$ready) this.doLayout();
|
||||
});
|
||||
addResizeListener(this.$el, this.windowResizeListener);
|
||||
addResizeListener(this.$el, this.resizeListener);
|
||||
}
|
||||
},
|
||||
|
||||
resizeListener() {
|
||||
if (!this.$ready) return;
|
||||
let shouldUpdateLayout = false;
|
||||
const el = this.$el;
|
||||
const { width: oldWidth, height: oldHeight } = this.resizeState;
|
||||
|
||||
const width = el.offsetWidth;
|
||||
if (oldWidth !== width) {
|
||||
shouldUpdateLayout = true;
|
||||
}
|
||||
|
||||
const height = el.offsetHeight;
|
||||
if (this.height && oldHeight !== height) {
|
||||
shouldUpdateLayout = true;
|
||||
}
|
||||
|
||||
if (shouldUpdateLayout) {
|
||||
this.resizeState.width = width;
|
||||
this.resizeState.height = height;
|
||||
this.doLayout();
|
||||
}
|
||||
},
|
||||
|
||||
doLayout() {
|
||||
this.store.updateColumns();
|
||||
this.updateScrollY();
|
||||
this.layout.update();
|
||||
this.$nextTick(() => {
|
||||
if (this.height) {
|
||||
this.layout.setHeight(this.height);
|
||||
} else if (this.maxHeight) {
|
||||
this.layout.setMaxHeight(this.maxHeight);
|
||||
} else if (this.shouldUpdateHeight) {
|
||||
this.layout.updateHeight();
|
||||
}
|
||||
});
|
||||
if (this.shouldUpdateHeight) {
|
||||
this.layout.updateElsHeight();
|
||||
}
|
||||
this.layout.updateColumnsWidth();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.tableId = 'el-table_' + tableIdSeed + '_';
|
||||
this.debouncedLayout = debounce(50, () => this.doLayout());
|
||||
this.tableId = 'el-table_' + tableIdSeed++;
|
||||
this.debouncedUpdateLayout = debounce(50, () => this.doLayout());
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -384,7 +455,7 @@
|
|||
},
|
||||
|
||||
shouldUpdateHeight() {
|
||||
return typeof this.height === 'number' ||
|
||||
return this.height ||
|
||||
this.fixedColumns.length > 0 ||
|
||||
this.rightFixedColumns.length > 0;
|
||||
},
|
||||
|
@ -409,34 +480,29 @@
|
|||
return this.store.states.rightFixedColumns;
|
||||
},
|
||||
|
||||
bodyHeight() {
|
||||
let style = {};
|
||||
|
||||
if (this.height) {
|
||||
style = {
|
||||
height: this.layout.bodyHeight ? this.layout.bodyHeight + 'px' : ''
|
||||
};
|
||||
} else if (this.maxHeight) {
|
||||
style = {
|
||||
'max-height': (this.showHeader
|
||||
? this.maxHeight - this.layout.headerHeight - this.layout.footerHeight
|
||||
: this.maxHeight - this.layout.footerHeight) + 'px'
|
||||
};
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
|
||||
bodyWidth() {
|
||||
const { bodyWidth, scrollY, gutterWidth } = this.layout;
|
||||
return bodyWidth ? bodyWidth - (scrollY ? gutterWidth : 0) + 'px' : '';
|
||||
},
|
||||
|
||||
fixedBodyHeight() {
|
||||
let style = {};
|
||||
|
||||
bodyHeight() {
|
||||
if (this.height) {
|
||||
style = {
|
||||
return {
|
||||
height: this.layout.bodyHeight ? this.layout.bodyHeight + 'px' : ''
|
||||
};
|
||||
} else if (this.maxHeight) {
|
||||
return {
|
||||
'max-height': (this.showHeader
|
||||
? this.maxHeight - this.layout.headerHeight - this.layout.footerHeight
|
||||
: this.maxHeight - this.layout.footerHeight) + 'px'
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
fixedBodyHeight() {
|
||||
if (this.height) {
|
||||
return {
|
||||
height: this.layout.fixedBodyHeight ? this.layout.fixedBodyHeight + 'px' : ''
|
||||
};
|
||||
} else if (this.maxHeight) {
|
||||
|
@ -448,38 +514,40 @@
|
|||
|
||||
maxHeight -= this.layout.footerHeight;
|
||||
|
||||
style = {
|
||||
return {
|
||||
'max-height': maxHeight + 'px'
|
||||
};
|
||||
}
|
||||
|
||||
return style;
|
||||
return {};
|
||||
},
|
||||
|
||||
fixedHeight() {
|
||||
let style = {};
|
||||
|
||||
if (this.maxHeight) {
|
||||
style = {
|
||||
return {
|
||||
bottom: (this.layout.scrollX && this.data.length) ? this.layout.gutterWidth + 'px' : ''
|
||||
};
|
||||
} else {
|
||||
style = {
|
||||
return {
|
||||
height: this.layout.viewportHeight ? this.layout.viewportHeight + 'px' : ''
|
||||
};
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
height(value) {
|
||||
this.layout.setHeight(value);
|
||||
height: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
this.layout.setHeight(value);
|
||||
}
|
||||
},
|
||||
|
||||
maxHeight(value) {
|
||||
this.layout.setMaxHeight(value);
|
||||
maxHeight: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
this.layout.setMaxHeight(value);
|
||||
}
|
||||
},
|
||||
|
||||
currentRowKey(newVal) {
|
||||
|
@ -488,8 +556,8 @@
|
|||
|
||||
data: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.store.commit('setData', val);
|
||||
handler(value) {
|
||||
this.store.commit('setData', value);
|
||||
if (this.$ready) {
|
||||
this.$nextTick(() => {
|
||||
this.doLayout();
|
||||
|
@ -509,13 +577,19 @@
|
|||
},
|
||||
|
||||
destroyed() {
|
||||
if (this.windowResizeListener) removeResizeListener(this.$el, this.windowResizeListener);
|
||||
if (this.resizeListener) removeResizeListener(this.$el, this.resizeListener);
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.bindEvents();
|
||||
this.store.updateColumns();
|
||||
this.doLayout();
|
||||
|
||||
this.resizeState = {
|
||||
width: this.$el.offsetWidth,
|
||||
height: this.$el.offsetHeight
|
||||
};
|
||||
|
||||
// init filters
|
||||
this.store.states.columns.forEach(column => {
|
||||
if (column.filteredValue && column.filteredValue.length) {
|
||||
|
@ -542,11 +616,15 @@
|
|||
showHeader: this.showHeader
|
||||
});
|
||||
return {
|
||||
store,
|
||||
layout,
|
||||
store,
|
||||
isHidden: false,
|
||||
renderExpanded: null,
|
||||
resizeProxyVisible: false,
|
||||
resizeState: {
|
||||
width: null,
|
||||
height: null
|
||||
},
|
||||
// 是否拥有多级表头
|
||||
isGroup: false,
|
||||
scrollPosition: 'left'
|
||||
|
|
|
@ -83,6 +83,18 @@
|
|||
}
|
||||
}
|
||||
|
||||
@include m(scrollable-x) {
|
||||
.el-table__body-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@include m(scrollable-y) {
|
||||
.el-table__body-wrapper {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
color: $--table-header-color;
|
||||
font-weight: 500;
|
||||
|
@ -296,6 +308,7 @@
|
|||
top: 0;
|
||||
left: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
box-shadow: $--table-fixed-box-shadow;
|
||||
|
||||
&::before {
|
||||
|
@ -372,6 +385,7 @@
|
|||
|
||||
@include e((header, body, footer)) {
|
||||
table-layout: fixed;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
@include e((header-wrapper, footer-wrapper)) {
|
||||
|
@ -384,36 +398,36 @@
|
|||
}
|
||||
|
||||
@include e(body-wrapper) {
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
@include when(scroll-none) {
|
||||
@include when(scrolling-none) {
|
||||
~ .el-table__fixed,
|
||||
~ .el-table__fixed-right {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scroll-left) {
|
||||
@include when(scrolling-left) {
|
||||
~ .el-table__fixed {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scroll-right) {
|
||||
@include when(scrolling-right) {
|
||||
~ .el-table__fixed-right {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table--border {
|
||||
@include when(scroll-right) {
|
||||
@include when(scrolling-right) {
|
||||
~ .el-table__fixed-right {
|
||||
border-left: $--table-border;
|
||||
}
|
||||
}
|
||||
|
||||
@include when(scroll-left) {
|
||||
@include when(scrolling-left) {
|
||||
~ .el-table__fixed {
|
||||
border-right: $--table-border;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue