Table: improve performance (#9426)

* improve table render time in some condition

* Update table.vue
pull/9566/head
FuryBean 2018-01-30 20:37:23 +08:00 committed by 杨奕
parent b27f340561
commit 9b9384214b
9 changed files with 415 additions and 229 deletions

View File

@ -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';
}
}
}
};

View File

@ -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);
},

View File

@ -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);
}
},

View File

@ -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;
}
},

View File

@ -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;
}
},

View File

@ -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}.`);
}
});
}
}

View File

@ -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) {

View File

@ -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'

View File

@ -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;
}