Commit b36da2f9 authored by furybean's avatar furybean Committed by cinwell.li

Improve Table:

1. Add TableStore & TableLayout.
2. Remove customCriteria & customBackgroundColors.
3. Remove fixedColumnCount.
4. Add fixed prop for TableColumn.
5. Add selectable prop for TableColumn[type="selection"].
parent 815bdd47
...@@ -15,10 +15,15 @@ ...@@ -15,10 +15,15 @@
- 修复 Loading 关闭后有几率滚动失效的问题 - 修复 Loading 关闭后有几率滚动失效的问题
- 修复 远程搜索的 Select 不能正确渲染默认初始值的问题 - 修复 远程搜索的 Select 不能正确渲染默认初始值的问题
- 修复 Switch 的 width 属性无效的问题 - 修复 Switch 的 width 属性无效的问题
- 优化 Table 性能,优化 Table 代码结构
- Table 增加属性 rowClassName
- TableColumn 增加 fixed 属性,可选值:true, false, left, right
- TableColumn[type="selection"] 增加 selectable 属性
#### 非兼容性更新 #### 非兼容性更新
- 全屏 Loading 现在默认不再锁定屏幕滚动。如果需要的话,可添加 `lock` 修饰符 - 全屏 Loading 现在默认不再锁定屏幕滚动。如果需要的话,可添加 `lock` 修饰符
- Table 删除属性 fixedColumnCount, customCriteria, customBackgroundColors
### 1.0.0-rc.7 ### 1.0.0-rc.7
......
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
"repository": "https://github.com/element-component/element/tree/master/packages/dialog", "repository": "https://github.com/element-component/element/tree/master/packages/dialog",
"author": "elemefe", "author": "elemefe",
"license": "MIT", "license": "MIT",
"devDependencies": { "dependencies": {
"throttle-debounce": "^1.0.1" "throttle-debounce": "^1.0.1"
} }
} }
const getColumnById = function(grid, columnId) { import { getValueByPath, getCell } from './util';
const getColumnById = function(table, columnId) {
let column = null; let column = null;
grid.columns.forEach(function(item) { table.columns.forEach(function(item) {
if (item.id === columnId) { if (item.id === columnId) {
column = item; column = item;
} }
...@@ -8,26 +10,24 @@ const getColumnById = function(grid, columnId) { ...@@ -8,26 +10,24 @@ const getColumnById = function(grid, columnId) {
return column; return column;
}; };
const getColumnByCell = function(grid, cell) { const getColumnByCell = function(table, cell) {
const matches = (cell.className || '').match(/grid_[^\s]+/gm); const matches = (cell.className || '').match(/table_[^\s]+/gm);
if (matches) { if (matches) {
return getColumnById(grid, matches[0]); return getColumnById(table, matches[0]);
} }
return null; return null;
}; };
import { getValueByPath, getCell, orderBy, getChild } from './util';
export default { export default {
props: { props: {
columns: {}, store: {
data: {}, required: true
fixed: {}, },
selection: { layout: {
default() { required: true
return []; },
} rowClassName: [String, Function],
} fixed: String
}, },
render(h) { render(h) {
...@@ -37,17 +37,20 @@ export default { ...@@ -37,17 +37,20 @@ export default {
cellspacing="0" cellspacing="0"
cellpadding="0" cellpadding="0"
border="0"> border="0">
{
this._l(this.columns, column =>
<colgroup
name={ column.id }
width={ column.realWidth || column.width }
/>)
}
<tbody> <tbody>
{ {
this._l(this.data, (row, $index) => this._l(this.data, (row, $index) =>
<tr <tr
on-click={ ($event) => this.handleClick($event, row) } on-click={ ($event) => this.handleClick($event, row) }
on-mouseenter={ _ => this.handleMouseEnter($index) } on-mouseenter={ _ => this.handleMouseEnter($index) }
style={ this.getCustomStyle(row) } class={ this.getRowClass(row, $index) }>
class={{
'current-row': row === this.$parent.selected,
'hover': this.$parent.$parent.hoverRowIndex === $index
}}>
{ {
this._l(this.columns, (column) => this._l(this.columns, (column) =>
<td <td
...@@ -57,11 +60,14 @@ export default { ...@@ -57,11 +60,14 @@ export default {
on-mouseleave={ this.handleCellMouseLeave }> on-mouseleave={ this.handleCellMouseLeave }>
{ {
column.template column.template
? column.template.call(this._renderProxy, h, { row, column, $index, _self: this.$parent.$vnode.context }) ? column.template.call(this._renderProxy, h, { row, column, $index, store: this.store, _self: this.$parent.$vnode.context })
: <div class="cell">{ this.$getPropertyText(row, column.property, column.id) }</div> : <div class="cell">{ this.getCellContent(row, column.property, column.id) }</div>
} }
</td> </td>
).concat(this.fixed ? <td class="gutter" /> : '') )
}
{
!this.fixed && this.layout.scrollY && this.layout.gutterWidth ? <td class="gutter" /> : ''
} }
</tr> </tr>
) )
...@@ -71,93 +77,104 @@ export default { ...@@ -71,93 +77,104 @@ export default {
); );
}, },
computed: {
data() {
return this.store.states.data;
},
hoverRowIndex() {
return this.store.states.hoverRow;
},
currentRow() {
return this.store.states.currentRow;
},
columns() {
if (this.fixed === true || this.fixed === 'left') {
return this.store.states.fixedColumns;
} else if (this.fixed === 'right') {
return this.store.states.rightFixedColumns;
}
return this.store.states.columns;
}
},
data() { data() {
return { return {
criteria: this.$parent.customCriteria,
colors: this.$parent.customBackgroundColors,
tooltipDisabled: true tooltipDisabled: true
}; };
}, },
filters: {
orderBy
},
methods: { methods: {
getColumnWhiteSpaceStyle(column) { getRowClass(row, index) {
return column.showTooltipWhenOverflow ? { 'white-space': 'nowrap' } : {}; const classes = [];
}, if (row === this.currentRow) {
classes.push('current-row');
}
if (this.hoverRowIndex === index) {
classes.push('hover-row');
}
checkProperty(row) { const rowClassName = this.rowClassName;
if (this.criteria && this.criteria.length > 0) { if (typeof rowClassName === 'string') {
for (let i = 0, len = this.criteria.length; i < len; i++) { classes.push(rowClassName);
if (row[this.criteria[i]] === true) { } else if (typeof rowClassName === 'function') {
return i; classes.push(rowClassName.apply(null, [row, index]) || '');
}
}
} }
return -1;
return classes.join(' ');
}, },
getCustomStyle(row) { getColumnWhiteSpaceStyle(column) {
if (!this.criteria || !this.colors || this.criteria.length !== this.colors.length) { return column.showTooltipWhenOverflow ? { 'white-space': 'nowrap' } : {};
return {};
}
let criterionIndex = this.checkProperty(row);
return criterionIndex > -1 ? { 'background-color': this.colors[criterionIndex] } : {};
}, },
handleCellMouseEnter(event, row) { handleCellMouseEnter(event, row) {
let grid = this.$parent; const table = this.$parent;
const cell = getCell(event); const cell = getCell(event);
if (cell) { if (cell) {
const column = getColumnByCell(grid, cell); const column = getColumnByCell(table, cell);
const hoverState = grid.hoverState = { cell: cell, column: column, row: row }; const hoverState = table.hoverState = { cell, column, row };
grid.$emit('cellmouseenter', hoverState.row, hoverState.column, hoverState.cell, event); table.$emit('cellmouseenter', hoverState.row, hoverState.column, hoverState.cell, event);
} }
// 判断是否text-overflow, 如果是就显示tooltip // 判断是否text-overflow, 如果是就显示tooltip
const cellChild = getChild(event); const cellChild = event.target.querySelector('.cell');
this.tooltipDisabled = cellChild.scrollWidth <= cellChild.offsetWidth; this.tooltipDisabled = cellChild.scrollWidth <= cellChild.offsetWidth;
}, },
handleCellMouseLeave(event) { handleCellMouseLeave(event) {
let grid = this.$parent;
const cell = getCell(event); const cell = getCell(event);
if (cell) { if (cell) {
const oldHoverState = grid.hoverState; const table = this.$parent;
grid.$emit('cellmouseleave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event); const oldHoverState = table.hoverState;
table.$emit('cellmouseleave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event);
} }
}, },
handleMouseEnter(index) { handleMouseEnter(index) {
this.$parent.hoverRowIndex = index; this.store.commit('setHoverRow', index);
}, },
handleClick(event, row) { handleClick(event, row) {
let grid = this.$parent; const table = this.$parent;
const cell = getCell(event); const cell = getCell(event);
if (cell) { if (cell) {
const column = getColumnByCell(grid, cell); const column = getColumnByCell(table, cell);
if (column) { if (column) {
grid.$emit('cellclick', row, column, cell, event); table.$emit('cellclick', row, column, cell, event);
} }
} }
if (grid.selectionMode === 'single') { this.store.commit('setSelectedRow', row);
grid.selected = row;
}
grid.$emit('rowclick', row, event); table.$emit('rowclick', row, event);
}, },
$getPropertyText(row, property, columnId) { getCellContent(row, property, columnId) {
let grid = this.$parent; const column = getColumnById(this.$parent, columnId);
const column = getColumnById(grid, columnId);
if (column && column.formatter) { if (column && column.formatter) {
return column.formatter(row, column); return column.formatter(row, column);
} }
......
...@@ -28,20 +28,39 @@ const defaults = { ...@@ -28,20 +28,39 @@ const defaults = {
const forced = { const forced = {
selection: { selection: {
headerTemplate: function(h) { return <div><el-checkbox nativeOn-click={ this.toggleAllSelection } domProps-value={ this.allSelected } on-input={ ($event) => this.$emit('allselectedchange', $event) } /></div>; }, headerTemplate: function(h) {
template: function(h, { row }) { return <el-checkbox domProps-value={ row.$selected } on-input={ ($event) => {row.$selected = $event;} } />; }, return <div><el-checkbox
nativeOn-click={ this.toggleAllSelection }
domProps-value={ this.isAllSelected }
on-input={ (value) => { this.$emit('allselectedchange', value); } } />
</div>;
},
template: function(h, { row, column, store, $index }) {
return <el-checkbox
domProps-value={ row.$selected }
disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false }
on-input={ (value) => { row.$selected = value; store.commit('rowSelectedChanged', row); } } />;
},
sortable: false, sortable: false,
resizable: false resizable: false
}, },
index: { index: {
// headerTemplate: function(h) { return <div>#</div>; }, // headerTemplate: function(h) { return <div>#</div>; },
headerTemplate: function(h, label) { return <div>{ label || '#' }</div>; }, headerTemplate: function(h, label) {
template: function(h, { row, $index }) { return <div>{ $index + 1 }</div>; }, return <div>{ label || '#' }</div>;
},
template: function(h, { row, $index }) {
return <div>{ $index + 1 }</div>;
},
sortable: false sortable: false
}, },
filter: { filter: {
headerTemplate: function(h) { return <div>#</div>; }, headerTemplate: function(h) {
template: function(h, { row, column }) { return <el-tag type="primary" style="height: 16px; line-height: 16px; min-width: 40px; text-align: center">{ row[column.property] }</el-tag>; }, return <div>#</div>;
},
template: function(h, { row, column }) {
return <el-tag type="primary" style="height: 16px; line-height: 16px; min-width: 40px; text-align: center">{ row[column.property] }</el-tag>;
},
resizable: false resizable: false
} }
}; };
...@@ -60,6 +79,12 @@ const getDefaultColumn = function(type, options) { ...@@ -60,6 +79,12 @@ const getDefaultColumn = function(type, options) {
} }
} }
if (!column.minWidth) {
column.minWidth = 80;
}
column.realWidth = column.width || column.minWidth;
return column; return column;
}; };
...@@ -73,6 +98,7 @@ export default { ...@@ -73,6 +98,7 @@ export default {
}, },
label: String, label: String,
property: String, property: String,
prop: String,
width: {}, width: {},
minWidth: {}, minWidth: {},
template: String, template: String,
...@@ -89,7 +115,9 @@ export default { ...@@ -89,7 +115,9 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
formatter: Function fixed: [Boolean, String],
formatter: Function,
selectable: Function
}, },
render() {}, render() {},
...@@ -112,16 +140,25 @@ export default { ...@@ -112,16 +140,25 @@ export default {
ElTag ElTag
}, },
computed: {
owner() {
let parent = this.$parent;
while (parent && !parent.tableId) {
parent = parent.$parent;
}
return parent;
}
},
created() { created() {
this.customRender = this.$options.render; this.customRender = this.$options.render;
this.$options.render = (h) => h('div'); this.$options.render = (h) => h('div');
let columnId = this.columnId = (this.$parent.gridId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++; let columnId = this.columnId = (this.$parent.tableId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++;
let parent = this.$parent; let parent = this.$parent;
if (!parent.gridId) { let owner = this.owner;
this.isChildColumn = true; this.isChildColumn = owner !== parent;
}
let type = this.type; let type = this.type;
...@@ -139,17 +176,15 @@ export default { ...@@ -139,17 +176,15 @@ export default {
if (isNaN(minWidth)) { if (isNaN(minWidth)) {
minWidth = 80; minWidth = 80;
} }
} else {
minWidth = 80;
} }
let isColumnGroup = false; let isColumnGroup = false;
let template; let template;
let property = this.property; let property = this.prop || this.property;
if (property) { if (property) {
template = function(h, { row }, parent) { template = function(h, { row }, parent) {
return <span>{ parent.$getPropertyText(row, property, columnId) }</span>; return <span>{ parent.getCellContent(row, property, columnId) }</span>;
}; };
} }
...@@ -163,11 +198,12 @@ export default { ...@@ -163,11 +198,12 @@ export default {
width, width,
isColumnGroup, isColumnGroup,
align: this.align ? 'is-' + this.align : null, align: this.align ? 'is-' + this.align : null,
realWidth: width || minWidth,
sortable: this.sortable, sortable: this.sortable,
resizable: this.resizable, resizable: this.resizable,
showTooltipWhenOverflow: this.showTooltipWhenOverflow, showTooltipWhenOverflow: this.showTooltipWhenOverflow,
formatter: this.formatter formatter: this.formatter,
selectable: this.selectable,
fixed: this.fixed
}); });
objectAssign(column, forced[type] || {}); objectAssign(column, forced[type] || {});
...@@ -186,7 +222,7 @@ export default { ...@@ -186,7 +222,7 @@ export default {
return _self.customRender.call(data); return _self.customRender.call(data);
}; };
}; }
return _self.showTooltipWhenOverflow return _self.showTooltipWhenOverflow
? <el-tooltip ? <el-tooltip
...@@ -203,31 +239,8 @@ export default { ...@@ -203,31 +239,8 @@ export default {
}, },
destroyed() { destroyed() {
if (!this.$parent) { if (!this.$parent) return;
return; this.owner.store.commit('removeColumn', this.columnConfig);
}
let columns = this.$parent.columns;
if (columns) {
let columnId = this.columnId;
for (let i = 0, j = columns.length; i < j; i++) {
let column = columns[i];
if (column.id === columnId) {
columns.splice(i, 1);
break;
}
}
}
if (this.isChildColumn) {
if (this.$parent.$parent.$ready) {
this.$parent.$parent.debouncedReRender();
}
} else {
if (this.$parent.$ready) {
this.$parent.debouncedReRender();
}
}
}, },
watch: { watch: {
...@@ -237,6 +250,12 @@ export default { ...@@ -237,6 +250,12 @@ export default {
} }
}, },
prop(newVal) {
if (this.columnConfig) {
this.columnConfig.property = newVal;
}
},
property(newVal) { property(newVal) {
if (this.columnConfig) { if (this.columnConfig) {
this.columnConfig.property = newVal; this.columnConfig.property = newVal;
...@@ -245,8 +264,8 @@ export default { ...@@ -245,8 +264,8 @@ export default {
}, },
mounted() { mounted() {
let parent = this.$parent; const owner = this.owner;
let columnConfig = this.columnConfig; const parent = this.$parent;
let columnIndex; let columnIndex;
if (!this.isChildColumn) { if (!this.isChildColumn) {
...@@ -255,18 +274,6 @@ export default { ...@@ -255,18 +274,6 @@ export default {
columnIndex = [].indexOf.call(parent.$el.children, this.$el); columnIndex = [].indexOf.call(parent.$el.children, this.$el);
} }
parent.columns.splice(columnIndex, 0, columnConfig); owner.store.commit('insertColumn', this.columnConfig, columnIndex);
if (this.isChildColumn) {
parent.columnConfig.columns = parent.columns;
if (parent.$parent.$ready) {
parent.$parent.debouncedReRender();
}
} else {
if (parent.$ready) {
parent.debouncedReRender();
}
}
} }
}; };
...@@ -16,47 +16,58 @@ export default { ...@@ -16,47 +16,58 @@ export default {
<colgroup <colgroup
name={ column.id } name={ column.id }
width={ column.realWidth || column.width } width={ column.realWidth || column.width }
/>).concat( />)
<thead> }
<tr> {
!this.fixed && this.layout.gutterWidth
? <colgroup name="gutter" width={ this.layout.scrollY ? this.layout.gutterWidth : '' }></colgroup>
: ''
}
<thead>
<tr>
{
this._l(this.columns, column =>
<th
on-mousemove={ ($event) => this.handleMouseMove($event, column) }
on-mouseout={ this.handleMouseOut }
on-mousedown={ ($event) => this.handleMouseDown($event, column) }
on-click={ ($event) => this.handleHeaderClick($event, column) }
class={ [column.id, column.direction, column.align] }>
{ {
this._l(this.columns, column => [
<th column.headerTemplate
on-mousemove={ ($event) => this.handleMouseMove($event, column) } ? column.headerTemplate.call(this._renderProxy, h, column.label)
on-mouseout={ this.handleMouseOut } : <div>{ column.label }</div>,
on-mousedown={ ($event) => this.handleMouseDown($event, column) } column.sortable
on-click={ ($event) => this.handleHeaderClick($event, column) } ? <div class="caret-wrapper">
class={ [column.id, column.direction, column.align] }> <i class="sort-caret ascending"></i>
{ <i class="sort-caret descending"></i>
[ </div>
column.headerTemplate : ''
? column.headerTemplate.call(this._renderProxy, h, column.label) ]
: <div>{ column.label }</div>,
column.sortable
? <div class="caret-wrapper">
<i class="sort-caret ascending"></i>
<i class="sort-caret descending"></i>
</div>
: ''
]
}
</th>
).concat(this.$parent.showVScrollBar && this.$parent.currentGutterWidth ? <th class="gutter"
style={{ width: this.$parent.currentGutterWidth + 'px' }}></th> : '')
} }
</tr> </th>
</thead> )
) }
} {
!this.fixed && this.layout.gutterWidth
? <th class="gutter" style={{ width: this.layout.scrollY ? this.layout.gutterWidth + 'px' : '0' }}></th>
: ''
}
</tr>
</thead>
</table> </table>
); );
}, },
props: { props: {
columns: {}, columns: {},
fixed: Boolean, fixed: String,
allSelected: { store: {
default: Boolean required: true
},
layout: {
required: true
}, },
border: Boolean border: Boolean
}, },
...@@ -66,9 +77,24 @@ export default { ...@@ -66,9 +77,24 @@ export default {
ElTag ElTag
}, },
computed: {
isAllSelected() {
return this.store.states.isAllSelected;
},
columns() {
if (this.fixed === true || this.fixed === 'left') {
return this.store.states.fixedColumns;
} else if (this.fixed === 'right') {
return this.store.states.rightFixedColumns;
}
return this.store.states.columns;
}
},
methods: { methods: {
toggleAllSelection($event) { toggleAllSelection() {
this.$parent.toggleAllSelection($event); this.store.commit('toggleAllSelection');
}, },
handleMouseDown(event, column) { handleMouseDown(event, column) {
...@@ -77,19 +103,19 @@ export default { ...@@ -77,19 +103,19 @@ export default {
this.$parent.resizeProxyVisible = true; this.$parent.resizeProxyVisible = true;
const gridEl = this.$parent.$el; const tableEl = this.$parent.$el;
const gridLeft = gridEl.getBoundingClientRect().left; const tableLeft = tableEl.getBoundingClientRect().left;
const columnEl = this.$el.querySelector(`th.${column.id}`); const columnEl = this.$el.querySelector(`th.${column.id}`);
const columnRect = columnEl.getBoundingClientRect(); const columnRect = columnEl.getBoundingClientRect();
const minLeft = columnRect.left - gridLeft + 30; const minLeft = columnRect.left - tableLeft + 30;
columnEl.classList.add('noclick'); columnEl.classList.add('noclick');
this.dragState = { this.dragState = {
startMouseLeft: event.clientX, startMouseLeft: event.clientX,
startLeft: columnRect.right - gridLeft, startLeft: columnRect.right - tableLeft,
startColumnLeft: columnRect.left - gridLeft, startColumnLeft: columnRect.left - tableLeft,
gridLeft: gridLeft tableLeft
}; };
const resizeProxy = this.$parent.$refs.resizeProxy; const resizeProxy = this.$parent.$refs.resizeProxy;
...@@ -98,22 +124,20 @@ export default { ...@@ -98,22 +124,20 @@ export default {
document.onselectstart = function() { return false; }; document.onselectstart = function() { return false; };
document.ondragstart = function() { return false; }; document.ondragstart = function() { return false; };
const mousemove = (event) => { const handleMouseMove = (event) => {
const deltaLeft = event.clientX - this.dragState.startMouseLeft; const deltaLeft = event.clientX - this.dragState.startMouseLeft;
const proxyLeft = this.dragState.startLeft + deltaLeft; const proxyLeft = this.dragState.startLeft + deltaLeft;
resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px'; resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px';
}; };
const mouseup = () => { const handleMouseUp = () => {
if (this.dragging) { if (this.dragging) {
const finalLeft = parseInt(resizeProxy.style.left, 10); const finalLeft = parseInt(resizeProxy.style.left, 10);
const columnWidth = finalLeft - this.dragState.startColumnLeft; const columnWidth = finalLeft - this.dragState.startColumnLeft;
column.width = column.realWidth = columnWidth; column.width = column.realWidth = columnWidth;
this.$nextTick(() => { this.store.scheduleLayout();
this.$parent.$calcColumns();
});
document.body.style.cursor = ''; document.body.style.cursor = '';
this.dragging = false; this.dragging = false;
...@@ -123,8 +147,8 @@ export default { ...@@ -123,8 +147,8 @@ export default {
this.$parent.resizeProxyVisible = false; this.$parent.resizeProxyVisible = false;
} }
document.removeEventListener('mousemove', mousemove); document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', mouseup); document.removeEventListener('mouseup', handleMouseUp);
document.onselectstart = null; document.onselectstart = null;
document.ondragstart = null; document.ondragstart = null;
...@@ -133,8 +157,8 @@ export default { ...@@ -133,8 +157,8 @@ export default {
}, 0); }, 0);
}; };
document.addEventListener('mousemove', mousemove); document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', mouseup); document.addEventListener('mouseup', handleMouseUp);
} }
}, },
...@@ -146,13 +170,14 @@ export default { ...@@ -146,13 +170,14 @@ export default {
if (!this.dragging && this.border) { if (!this.dragging && this.border) {
let rect = target.getBoundingClientRect(); let rect = target.getBoundingClientRect();
var bodyStyle = document.body.style;
if (rect.width > 12 && rect.right - event.pageX < 8) { if (rect.width > 12 && rect.right - event.pageX < 8) {
document.body.style.cursor = 'col-resize'; bodyStyle.cursor = 'col-resize';
this.draggingColumn = column; this.draggingColumn = column;
} else if (!this.dragging) { } else if (!this.dragging) {
document.body.style.cursor = ''; bodyStyle.cursor = '';
this.draggingColumn = null; this.draggingColumn = null;
if (column.sortable) document.body.style.cursor = 'pointer'; if (column.sortable) bodyStyle.cursor = 'pointer';
} }
} }
}, },
...@@ -176,14 +201,14 @@ export default { ...@@ -176,14 +201,14 @@ export default {
if (!column.sortable) return; if (!column.sortable) return;
const grid = this.$parent; const sortCondition = this.store.states.sortCondition;
if (grid.sortingColumn !== column) { if (sortCondition.column !== column) {
if (grid.sortingColumn) { if (sortCondition.column) {
grid.sortingColumn.direction = ''; sortCondition.column.direction = '';
} }
grid.sortingColumn = column; sortCondition.column = column;
grid.sortingProperty = column.property; sortCondition.property = column.property;
} }
if (!column.direction) { if (!column.direction) {
...@@ -192,25 +217,12 @@ export default { ...@@ -192,25 +217,12 @@ export default {
column.direction = 'descending'; column.direction = 'descending';
} else { } else {
column.direction = ''; column.direction = '';
grid.sortingColumn = null; sortCondition.column = null;
grid.sortingProperty = null; sortCondition.property = null;
} }
sortCondition.direction = column.direction === 'descending' ? -1 : 1;
grid.sortingDirection = column.direction === 'descending' ? -1 : 1; this.store.commit('changeSortCondition');
},
$setVisibleFilter(property) {
if (this.visibleFilter) {
this.visibleFilter = null;
} else {
this.visibleFilter = property;
}
}
},
watch: {
visibleFilter(val) {
this.$parent.visibleFilter = val;
} }
}, },
...@@ -218,9 +230,7 @@ export default { ...@@ -218,9 +230,7 @@ export default {
return { return {
draggingColumn: null, draggingColumn: null,
dragging: false, dragging: false,
dragState: {}, dragState: {}
columnsMap: null,
visibleFilter: null
}; };
} }
}; };
import { getScrollBarWidth } from './util';
import Vue from 'vue';
let GUTTER_WIDTH;
class TableLayout {
constructor(options) {
this.table = null;
this.store = null;
this.columns = null;
this.fit = true;
this.scrollX = false;
this.scrollY = false;
this.bodyWidth = null;
this.fixedWidth = null;
this.rightFixedWidth = null;
this.tableHeight = null;
this.headerHeight = 44; // Table Header Height
this.viewportHeight = null; // Table Height - Scroll Bar Height
this.bodyHeight = null; // Table Height - Table Header Height
this.fixedBodyHeight = null; // Table Height - Table Header Height - Scroll Bar Height
if (GUTTER_WIDTH === undefined) {
GUTTER_WIDTH = getScrollBarWidth();
}
this.gutterWidth = GUTTER_WIDTH;
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');
}
}
syncHeight() {
Vue.nextTick(() => {
const { bodyWrapper, fixedBodyWrapper } = this.table.$refs;
// 若非固定列中的某行内容被撑高, 需要固定列中对应行高度与其保持一致
const bodyHeight = bodyWrapper.offsetHeight;
const fixedBodyHeight = fixedBodyWrapper.offsetHeight;
if (bodyHeight !== fixedBodyHeight) {
const rows = bodyWrapper.querySelectorAll('tr');
const fixedRows = fixedBodyWrapper.querySelectorAll('tr');
[].forEach.call(rows, (row, i) => {
const fixedRow = fixedRows[i];
const rowHeight = row.offsetHeight;
const fixedRowHeight = fixedRow.offsetHeight;
if (rowHeight !== fixedRowHeight) {
fixedRow.style.height = rowHeight + 'px';
}
});
}
});
}
updateScrollY() {
const bodyWrapper = this.table.$refs.bodyWrapper;
if (this.table.$el && bodyWrapper) {
const body = bodyWrapper.querySelector('.el-table__body');
this.scrollY = body.offsetHeight > bodyWrapper.offsetHeight;
}
}
setHeight(height) {
if (typeof height === 'string' && /^\d+$/.test(height)) {
height = Number(height);
}
const el = this.table.$el;
if (!isNaN(height) && el) {
el.style.height = height + 'px';
this.updateHeight();
}
}
updateHeight() {
const height = this.tableHeight = this.table.$el.clientHeight;
const { headerWrapper } = this.table.$refs;
if (!headerWrapper) return;
const headerHeight = this.headerHeight = headerWrapper.offsetHeight;
const bodyHeight = this.bodyHeight = height - headerHeight;
this.fixedBodyHeight = this.scrollX ? bodyHeight - this.gutterWidth : bodyHeight;
this.viewportHeight = this.scrollX ? height - 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) => {
if (!column.width && !column.minWidth) {
column.realWidth = 80;
}
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;
import Vue from 'vue';
import debounce from 'throttle-debounce/debounce';
import { orderBy } from './util';
const TableStore = function(table, initialState = {}) {
if (!table) {
throw new Error('Table is required.');
}
this.table = table;
this.states = {
_columns: [],
columns: [],
fixedColumns: [],
rightFixedColumns: [],
_data: null,
data: null,
sortCondition: {
column: null,
property: null,
direction: null
},
isAllSelected: false,
selection: null,
allowNoSelection: false,
selectionMode: 'none',
selectable: null,
currentRow: null,
hoverRow: null
};
for (let prop in initialState) {
if (initialState.hasOwnProperty(prop) && this.states.hasOwnProperty(prop)) {
this.states[prop] = initialState[prop];
}
}
};
TableStore.prototype.mutations = {
setData(states, data) {
states._data = data;
if (data && data[0] && typeof data[0].$selected === 'undefined') {
data.forEach((item) => Vue.set(item, '$selected', false));
}
states.data = orderBy((data || []), states.sortCondition.property, states.sortCondition.direction);
this.updateSelectedRow();
if (states.fixedColumns.length > 0 || states.rightFixedColumns.length > 0) Vue.nextTick(() => this.table.syncHeight());
Vue.nextTick(() => this.table.updateScrollY());
},
changeSortCondition(states) {
states.data = orderBy((states._data || []), states.sortCondition.property, states.sortCondition.direction);
if (states.fixedColumns.length > 0 || states.rightFixedColumns.length > 0) Vue.nextTick(() => this.table.syncHeight());
Vue.nextTick(() => this.table.updateScrollY());
},
insertColumn(states, column, index) {
let _columns = states._columns;
if (typeof index !== 'undefined') {
_columns.splice(index, 0, column);
} else {
_columns.push(column);
}
if (column.type === 'selection') {
states.selectable = column.selectable;
}
this.scheduleLayout();
},
removeColumn(states, column) {
let _columns = states._columns;
if (_columns) {
_columns.splice(_columns.indexOf(column), 1);
}
this.scheduleLayout();
},
setHoverRow(states, row) {
states.hoverRow = row;
},
rowSelectedChanged(states) {
let isAllSelected = true;
const data = states.data || [];
for (let i = 0, j = data.length; i < j; i++) {
const item = data[i];
if (states.selectable) {
if (states.selectable.call(null, item, i) && !item.$selected) {
isAllSelected = false;
break;
}
} else {
if (!item.$selected) {
isAllSelected = false;
break;
}
}
}
states.isAllSelected = isAllSelected;
},
toggleAllSelection: debounce(10, function(states) {
const data = states.data || [];
const value = !states.isAllSelected;
data.forEach((item, index) => {
if (states.selectable) {
if (states.selectable.call(null, item, index)) {
item.$selected = value;
}
} else {
item.$selected = value;
}
});
states.isAllSelected = value;
}),
setSelectedRow(states, row) {
if (states.selectionMode === 'single') {
states.currentRow = row;
}
}
};
TableStore.prototype.updateColumns = function() {
const states = this.states;
const _columns = states._columns || [];
states.fixedColumns = _columns.filter((column) => column.fixed === true || column.fixed === 'left');
states.rightFixedColumns = _columns.filter((column) => column.fixed === 'right');
if (states.fixedColumns.length > 0 && _columns[0] && _columns[0].type === 'selection' && !_columns[0].fixed) {
_columns[0].fixed = true;
states.fixedColumns.unshift(_columns[0]);
}
states.columns = [].concat(states.fixedColumns).concat(_columns.filter((column) => !column.fixed)).concat(states.rightFixedColumns);
};
TableStore.prototype.updateSelectedRow = function() {
const states = this.states;
const table = this.table;
const data = states.data || [];
if (states.selectionMode === 'single') {
const oldSelectedRow = states.currentRow;
if (oldSelectedRow === null && !states.allowNoSelection) {
states.currentRow = data[0];
if (states.currentRow !== oldSelectedRow) {
table.$emit('selectionchange', states.currentRow);
}
} else if (data.indexOf(oldSelectedRow) === -1) {
if (!states.allowNoSelection) {
states.currentRow = data[0];
} else {
states.currentRow = null;
}
if (states.currentRow !== oldSelectedRow) {
table.$emit('selectionchange', states.currentRow);
}
}
}
};
TableStore.prototype.scheduleLayout = function() {
this.table.debouncedLayout();
};
TableStore.prototype.commit = function(name, ...args) {
const mutations = this.mutations;
if (mutations[name]) {
mutations[name].apply(this, [this.states].concat(args));
}
};
export default TableStore;
This diff is collapsed.
var scrollBarWidth; let scrollBarWidth;
export const getScrollBarWidth = () => { export const getScrollBarWidth = () => {
if (scrollBarWidth !== undefined) return scrollBarWidth; if (scrollBarWidth !== undefined) return scrollBarWidth;
...@@ -75,7 +75,3 @@ export const orderBy = function(array, sortKey, reverse) { ...@@ -75,7 +75,3 @@ export const orderBy = function(array, sortKey, reverse) {
return a === b ? 0 : a > b ? order : -order; return a === b ? 0 : a > b ? order : -order;
}); });
}; };
export const getChild = function(event) {
return event.target.querySelector('.cell');
};
...@@ -105,7 +105,7 @@ ...@@ -105,7 +105,7 @@
box-sizing: border-box; box-sizing: border-box;
} }
@e fixed { @e fixed, fixed-right {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
...@@ -123,6 +123,14 @@ ...@@ -123,6 +123,14 @@
} }
} }
@e fixed-right {
top: 0;
left: auto;
right: 0;
box-shadow: -1px 0 8px #d3d4d6;
}
@e fixed-header-wrapper { @e fixed-header-wrapper {
position: absolute; position: absolute;
left: 0; left: 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment