mirror of
https://gitee.com/ElemeFE/element.git
synced 2024-12-03 04:39:09 +08:00
443 lines
13 KiB
Vue
443 lines
13 KiB
Vue
<template>
|
|
<div class="el-table" :class="{ 'el-table--fit': fit, 'el-table--striped': stripe, 'el-table--border': border }" @mouseleave="handleMouseLeave($event)">
|
|
<div class="hidden-columns" ref="hiddenColumns"><slot></slot></div>
|
|
<div class="el-table__header-wrapper">
|
|
<table-header :columns="columns" :all-selected="allSelected" @allselectedchange="handleAllSelectedChange" :selection="selection" :style="{ width: bodyWidth ? bodyWidth + 'px' : '' }" :border="border"></table-header>
|
|
</div>
|
|
<div class="el-table__body-wrapper">
|
|
<table-body :columns="columns" :selection="selection" :data="filterData" :style="{ width: bodyWidth ? bodyWidth - (showVScrollBar ? currentGutterWidth : 0 ) + 'px' : '' }"></table-body>
|
|
</div>
|
|
<div class="el-table__fixed" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" ref="fixed">
|
|
<div class="el-table__fixed-header-wrapper" v-if="fixedColumnCount > 0">
|
|
<table-header :columns="fixedColumns" :all-selected="allSelected" @allselectedchange="handleAllSelectedChange" :selection="selection" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" :border="border"></table-header>
|
|
</div>
|
|
<div class="el-table__fixed-body-wrapper" v-if="fixedColumnCount > 0" :style="{ top: headerHeight + 'px' }">
|
|
<table-body :columns="fixedColumns" fixed :selection="selection" :data="filterData" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }"></table-body>
|
|
</div>
|
|
</div>
|
|
<div class="el-table__column-resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div>
|
|
<slot name="bottom"></slot>
|
|
</div>
|
|
</template>
|
|
|
|
<script type="text/babel">
|
|
import throttle from 'throttle-debounce/throttle';
|
|
import debounce from 'throttle-debounce/debounce';
|
|
import { getScrollBarWidth, orderBy } from './util';
|
|
import objectAssign from 'object-assign';
|
|
|
|
let gridIdSeed = 1;
|
|
let GUTTER_WIDTH;
|
|
|
|
import TableBody from './table-body';
|
|
import TableHeader from './table-header';
|
|
|
|
export default {
|
|
name: 'el-table',
|
|
|
|
props: {
|
|
data: {
|
|
type: Array,
|
|
default: function() {
|
|
return [];
|
|
}
|
|
},
|
|
|
|
width: String,
|
|
|
|
height: String,
|
|
|
|
fit: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
|
|
stripe: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
|
|
border: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
|
|
fixedColumnCount: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
|
|
selectionMode: {
|
|
type: String,
|
|
default: 'none'
|
|
},
|
|
|
|
selection: {},
|
|
|
|
allowNoSelection: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
|
|
gutterWidth: {
|
|
default: 0
|
|
}
|
|
},
|
|
|
|
components: {
|
|
TableHeader,
|
|
TableBody
|
|
},
|
|
|
|
methods: {
|
|
handleAllSelectedChange(val) {
|
|
this.allSelected = val;
|
|
},
|
|
|
|
doOnDataChange(data) {
|
|
data = data || [];
|
|
|
|
if (this.selectionMode === 'single') {
|
|
const oldSelection = this.selected;
|
|
if (oldSelection === null) {
|
|
if (!this.allowNoSelection) {
|
|
this.selected = data[0];
|
|
if (this.selected !== oldSelection) {
|
|
this.$emit('selectionchange', this.selected);
|
|
}
|
|
}
|
|
} else if (data.indexOf(oldSelection) === -1) {
|
|
if (!this.allowNoSelection) {
|
|
this.selected = data[0];
|
|
} else {
|
|
this.selected = null;
|
|
}
|
|
if (this.selected !== oldSelection) {
|
|
this.$emit('selectionchange', this.selected);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
toggleAllSelection() {
|
|
setTimeout(() => {
|
|
this.tableData.forEach(item => {
|
|
item.$selected = this.allSelected;
|
|
});
|
|
}, 0);
|
|
},
|
|
|
|
$calcColumns() {
|
|
let fit = this.fit;
|
|
let columns = this.columns;
|
|
|
|
let bodyWidth = this.$el.clientWidth;
|
|
let bodyMinWidth = 0;
|
|
|
|
let flattenColumns = [];
|
|
|
|
columns.forEach((column) => {
|
|
if (column.isColumnGroup) {
|
|
flattenColumns.push.apply(flattenColumns, column.columns);
|
|
} else {
|
|
flattenColumns.push(column);
|
|
}
|
|
});
|
|
|
|
if (fit) {
|
|
let flexColumns = [];
|
|
let definedWidthColumnsWidth = 0;
|
|
let definedMinWidthSum = 0;
|
|
|
|
flattenColumns.forEach((column) => {
|
|
definedMinWidthSum += column.minWidth || 80;
|
|
bodyMinWidth += column.width || column.minWidth || 80;
|
|
|
|
if (typeof column.width === 'number') {
|
|
definedWidthColumnsWidth += column.width;
|
|
} else {
|
|
flexColumns.push(column);
|
|
}
|
|
});
|
|
|
|
if (bodyMinWidth < bodyWidth - this.currentGutterWidth) { // do not have scroll bar.
|
|
let flexWidthTotal = bodyWidth - this.currentGutterWidth - columns.length - bodyMinWidth;
|
|
let flexWidthPerColumn = Math.floor(flexWidthTotal / flexColumns.length);
|
|
let flexWidthFirstColumn = flexWidthTotal - flexWidthPerColumn * flexColumns.length + flexWidthPerColumn;
|
|
|
|
flexColumns.forEach((column, index) => {
|
|
if (index === 0) {
|
|
column.realWidth = (column.minWidth || 80) + flexWidthFirstColumn;
|
|
} else {
|
|
column.realWidth = (column.minWidth || 80) + flexWidthPerColumn;
|
|
}
|
|
});
|
|
} else { // need horizontal scroll bar.
|
|
this.showHScrollBar = true;
|
|
flexColumns.forEach(function(column) {
|
|
column.realWidth = column.minWidth;
|
|
});
|
|
}
|
|
|
|
this.bodyWidth = Math.max(bodyMinWidth, bodyWidth);
|
|
} else {
|
|
flattenColumns.forEach((column) => {
|
|
if (!column.width && !column.minWidth) {
|
|
column.realWidth = 80;
|
|
}
|
|
|
|
bodyMinWidth += column.realWidth;
|
|
});
|
|
this.showHScrollBar = bodyMinWidth > bodyWidth;
|
|
|
|
this.bodyWidth = bodyMinWidth;
|
|
}
|
|
|
|
if (this.styleNode) {
|
|
let styleSheet = this.styleNode.sheet;
|
|
|
|
if (!styleSheet) return;
|
|
for (let i = 0, j = styleSheet.cssRules.length; i < j; i++) {
|
|
styleSheet.deleteRule(0);
|
|
}
|
|
|
|
columns.forEach(function(column) {
|
|
const addRule = function(rule) {
|
|
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
|
};
|
|
|
|
if (column.isColumnGroup) {
|
|
let childColumns = column.columns;
|
|
let groupWidth = 0;
|
|
childColumns.forEach(function(childColumn) {
|
|
groupWidth += childColumn.realWidth;
|
|
addRule(`.${childColumn.id}, .${childColumn.id} > div { width: ${childColumn.realWidth}px; }`);
|
|
});
|
|
|
|
addRule(`.${column.id}, .${column.id} > div { width: ${groupWidth}px; }`);
|
|
} else {
|
|
addRule(`.${column.id}, .${column.id} > div { width: ${column.realWidth}px; }`);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (this.fixedColumnCount > 0) {
|
|
let fixedBodyWidth = 0;
|
|
let fixedColumnCount = this.fixedColumnCount;
|
|
|
|
columns.forEach(function(column, index) {
|
|
if (index < fixedColumnCount) {
|
|
fixedBodyWidth += column.realWidth;
|
|
}
|
|
});
|
|
|
|
this.fixedBodyWidth = fixedBodyWidth;
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
this.headerHeight = this.$el.querySelector('.el-table__header-wrapper').offsetHeight;
|
|
});
|
|
},
|
|
|
|
$calcHeight(height) {
|
|
if (typeof height === 'string' && /^\d+$/.test(height)) {
|
|
height = Number(height);
|
|
}
|
|
|
|
if (!isNaN(height) && this.$el) {
|
|
const headerHeight = this.headerHeight = this.$el.querySelector('.el-table__header-wrapper').offsetHeight;
|
|
const bodyHeight = (height - headerHeight);
|
|
const gridWrapper = this.$el.querySelector('.el-table__body-wrapper');
|
|
gridWrapper.style.height = bodyHeight + 'px';
|
|
|
|
this.$el.style.height = height + 'px';
|
|
this.$refs.fixed.style.height = height + 'px';
|
|
|
|
const fixedBodyWrapper = this.$el.querySelector('.el-table__fixed-body-wrapper');
|
|
if (fixedBodyWrapper) {
|
|
fixedBodyWrapper.style.height = (this.showHScrollBar ? gridWrapper.offsetHeight - this.currentGutterWidth : gridWrapper.offsetHeight) + 'px';
|
|
}
|
|
}
|
|
},
|
|
|
|
handleMouseLeave() {
|
|
this.hoverRowIndex = null;
|
|
const hoverState = this.hoverState;
|
|
if (hoverState) {
|
|
this.hoverState = null;
|
|
}
|
|
},
|
|
|
|
updateScrollInfo() {
|
|
this.$nextTick(() => {
|
|
if (this.$el) {
|
|
let gridBodyWrapper = this.$el.querySelector('.el-table__body-wrapper');
|
|
let gridBody = this.$el.querySelector('.el-table__body-wrapper .el-table__body');
|
|
|
|
this.showVScrollBar = gridBody.offsetHeight > gridBodyWrapper.offsetHeight;
|
|
}
|
|
});
|
|
},
|
|
|
|
doRender() {
|
|
let bodyWrapper = this.$el.querySelector('.el-table__body-wrapper');
|
|
let headerWrapper = this.$el.querySelector('.el-table__header-wrapper');
|
|
const el = this.$el;
|
|
|
|
if (!this.$ready) {
|
|
bodyWrapper.addEventListener('scroll', function() {
|
|
headerWrapper.scrollLeft = this.scrollLeft;
|
|
let fixedBodyWrapper = el.querySelector('.el-table__fixed-body-wrapper');
|
|
if (fixedBodyWrapper) {
|
|
fixedBodyWrapper.scrollTop = this.scrollTop;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.$calcColumns();
|
|
|
|
if (!this.$ready && this.fit) {
|
|
this.windowResizeListener = throttle(100, () => {
|
|
this.$calcColumns();
|
|
});
|
|
window.addEventListener('resize', this.windowResizeListener);
|
|
}
|
|
|
|
this.$nextTick(() => {
|
|
if (this.height) {
|
|
this.$calcHeight(this.height);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
created() {
|
|
this.gridId = 'grid_' + gridIdSeed + '_';
|
|
|
|
if (GUTTER_WIDTH === undefined) {
|
|
GUTTER_WIDTH = getScrollBarWidth();
|
|
}
|
|
this.currentGutterWidth = GUTTER_WIDTH;
|
|
|
|
this.debouncedReRender = debounce(50, () => {
|
|
this.doRender();
|
|
});
|
|
},
|
|
|
|
computed: {
|
|
selection() {
|
|
if (this.selectionMode === 'multiple') {
|
|
const data = this.tableData || [];
|
|
return data.filter(item => item.$selected === true);
|
|
} else if (this.selectionMode === 'single') {
|
|
return this.selected;
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
fixedColumns() {
|
|
const columns = this.columns || [];
|
|
const fixedColumnCount = this.fixedColumnCount;
|
|
return columns.filter(function(item, index) {
|
|
return index < fixedColumnCount;
|
|
});
|
|
},
|
|
|
|
filterData() {
|
|
return orderBy(this.tableData, this.sortingProperty, this.sortingDirection);
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
fixedColumnCount() {
|
|
this.debouncedReRender();
|
|
},
|
|
|
|
selection(val) {
|
|
this.$emit('selectionchange', val);
|
|
if (this.selectionMode === 'multiple') {
|
|
this.allSelected = val.length === this.tableData.length;
|
|
}
|
|
},
|
|
|
|
visibleFilter(val) {
|
|
this.$broadcast('toggleFilterPopup', val);
|
|
},
|
|
|
|
height(value) {
|
|
this.$calcHeight(value);
|
|
},
|
|
|
|
data(val) {
|
|
if (val && this.selectionMode === 'multiple') {
|
|
this.tableData = val.map(item => objectAssign({ '$selected': false }, item));
|
|
} else {
|
|
this.tableData = val;
|
|
}
|
|
},
|
|
|
|
tableData(newVal) {
|
|
this.doOnDataChange(newVal);
|
|
this.updateScrollInfo();
|
|
}
|
|
},
|
|
|
|
destroyed() {
|
|
if (this.styleNode) {
|
|
this.styleNode.parentNode.removeChild(this.styleNode);
|
|
}
|
|
|
|
if (this.windowResizeListener) {
|
|
window.removeEventListener('resize', this.windowResizeListener);
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
const styleNode = document.createElement('style');
|
|
styleNode.type = 'text/css';
|
|
styleNode.rel = 'stylesheet';
|
|
styleNode.title = 'Grid Column Style';
|
|
document.getElementsByTagName('head')[0].appendChild(styleNode);
|
|
|
|
this.styleNode = styleNode;
|
|
|
|
this.doRender();
|
|
|
|
this.$ready = true;
|
|
if (this.tableData) {
|
|
this.doOnDataChange(this.tableData);
|
|
}
|
|
this.updateScrollInfo();
|
|
if (this.fixedColumnCount > 0) {
|
|
this.$nextTick(() => {
|
|
const style = this.$refs.fixed.style;
|
|
if (!style) return;
|
|
style.height = this.$el.clientHeight + 'px';
|
|
});
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
tableData: this.data,
|
|
showHScrollBar: false,
|
|
showVScrollBar: false,
|
|
hoverRowIndex: null,
|
|
headerHeight: 35,
|
|
selected: null,
|
|
allSelected: false,
|
|
columns: [],
|
|
resizeProxyVisible: false,
|
|
bodyWidth: '',
|
|
fixedBodyWidth: '',
|
|
sortingColumn: null,
|
|
sortingProperty: null,
|
|
sortingDirection: 1,
|
|
visibleFilter: null,
|
|
currentGutterWidth: this.gutterWidth
|
|
};
|
|
}
|
|
};
|
|
</script>
|