table-tree-grid
Version:
A table (with tree-grid) component for Vue.js 2.0. (Its style extends iView)
318 lines (310 loc) • 13.1 kB
JavaScript
import Checkbox from '../Checkbox/Checkbox'; // eslint-disable-line
import { mixins } from './utils';
/* eslint-disable no-underscore-dangle */
export default {
name: 'zk-table__body',
mixins: [mixins],
data() {
return {
};
},
computed: {
table() {
return this.$parent;
},
},
methods: {
toggleStatus(type, row, rowIndex, value) {
this.validateType(type, ['Expanded', 'Checked', 'Hide', 'Fold'], 'toggleStatus', false);
const target = this.table.bodyData[rowIndex];
this.table.bodyData.splice(rowIndex, 1, {
...target,
[`_is${type}`]: typeof value === 'undefined' ? !row[`_is${type}`] : value,
});
},
getChildrenIndex(parentLevel, parentIndex, careFold = true) {
const data = this.table.bodyData;
let childrenIndex = [];
for (let i = parentIndex + 1; i < data.length; i++) {
if (data[i]._level <= parentLevel) break;
if (data[i]._level - 1 === parentLevel) {
childrenIndex.push(i);
}
}
const len = childrenIndex.length; // important!!!
if (len > 0) {
for (let i = 0; i < len; i++) {
const childData = data[childrenIndex[i]];
if (
childData._childrenLen &&
(!careFold || (careFold && !childData._isFold))
) {
childrenIndex = childrenIndex.concat(
this.getChildrenIndex(childData._level, childrenIndex[i], careFold));
}
}
}
return childrenIndex;
},
handleEvent($event, type, data, others) {
const certainType = this.validateType(type, ['cell', 'row', 'checkbox', 'icon'], 'handleEvent');
const eventType = $event ? $event.type : '';
const { row, rowIndex, column, columnIndex } = data;
const latestData = this.table.bodyData;
// Checkbox
if (certainType.checkbox) {
const { isChecked } = others;
this.toggleStatus('Checked', row, rowIndex, isChecked);
if (row._childrenLen > 0) {
const childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Checked', latestData[childrenIndex[i]], childrenIndex[i], isChecked);
}
}
return this.table.$emit('checkbox-click', latestData[rowIndex], column, columnIndex, $event);
}
// Tree's icon
if (certainType.icon) {
$event.stopPropagation();
this.toggleStatus('Fold', row, rowIndex);
const childrenIndex = this.getChildrenIndex(row._level, rowIndex);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Hide', latestData[childrenIndex[i]], childrenIndex[i]);
}
return this.table.$emit('tree-icon-click', latestData[rowIndex], column, columnIndex, $event);
}
if (certainType.cell && eventType === 'click') {
// 点击扩展单元格
if (this.isExpandCell(this.table, columnIndex)) {
this.toggleStatus('Expanded', row, rowIndex);
return this.table.$emit('expand-cell-click', latestData[rowIndex], column, columnIndex, $event);
}
}
// 行:Hover
if (certainType.row && (eventType === 'mouseenter' || eventType === 'mouseleave')) {
const { hover } = others;
const target = latestData[rowIndex];
latestData.splice(rowIndex, 1, {
...target,
_isHover: hover,
});
}
if (certainType.cell) {
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, column, columnIndex, $event);
}
return this.table.$emit(`${type}-${eventType}`, latestData[rowIndex], rowIndex, $event);
},
},
render() {
// key
function getKey(row, rowIndex) {
const rowKey = this.table.rowKey;
if (rowKey) {
return rowKey.call(null, row, rowIndex);
}
return rowIndex;
}
// style
function getStyle(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row'], 'getStyle');
let style = this.table[`${type}Style`];
if (this.isSelectionCell(this.table, columnIndex)) {
style = 'width: 50px';
}
if (typeof style === 'function') {
if (certainType.row) {
return style.call(null, row, rowIndex);
}
if (certainType.cell) {
return style.call(null, row, rowIndex, column, columnIndex);
}
}
return style;
}
// className
function getClassName(type, row, rowIndex, column, columnIndex) {
const certainType = this.validateType(type, ['cell', 'row', 'inner'], 'getClassName');
const classList = [];
if (certainType.row || certainType.cell) {
const className = this.table[`${type}ClassName`];
if (typeof className === 'string') {
classList.push(className);
} else if (typeof className === 'function') {
if (certainType.row) {
classList.push(className.call(null, row, rowIndex) || '');
}
if (certainType.cell) {
classList.push(className.call(null, row, rowIndex, column, columnIndex) || '');
}
}
if (certainType.row) {
classList.push(`${this.prefixCls}__body-row`);
if (this.table.stripe && rowIndex % 2 !== 0) {
classList.push(`${this.prefixCls}--stripe-row`);
}
if (this.table.showRowHover && row._isHover) {
classList.push(`${this.prefixCls}--row-hover`);
}
}
if (certainType.cell) {
classList.push(`${this.prefixCls}__body-cell`);
if (this.table.border) {
classList.push(`${this.prefixCls}--border-cell`);
}
const align = column.align;
if (['center', 'right'].indexOf(align) > -1) {
classList.push(`${this.prefixCls}--${align}-cell`);
}
}
}
if (certainType.inner) {
classList.push(`${this.prefixCls}__cell-inner`);
if (this.isExpandCell(this.table, columnIndex)) {
classList.push(`${this.prefixCls}--expand-inner`);
if (row._isExpanded) {
classList.push(`${this.prefixCls}--expanded-inner`);
}
}
}
return classList.join(' ');
}
// 根据type渲染单元格Cell
function renderCell(row, rowIndex, column, columnIndex) {
// ExpandType
if (this.isExpandCell(this.table, columnIndex)) {
return <i class='zk-icon zk-icon-angle-right'></i>;
}
// SelectionType's Checkbox
if (this.isSelectionCell(this.table, columnIndex)) {
let disableCheckboxBy = this.table.disableCheckboxBy;
let disabled = false;
if(disableCheckboxBy) {
if(typeof disableCheckboxBy === "string") {
disabled = row[disableCheckboxBy];
} else if(typeof disableCheckboxBy === "function") {
disabled = disableCheckboxBy(row);
} else {
// No logic
}
}
let allCheck;
let childrenIndex;
const hasChildren = row._childrenLen > 0;
if (hasChildren) {
childrenIndex = this.getChildrenIndex(row._level, rowIndex, false);
allCheck = true;
for (let i = 0; i < childrenIndex.length; i++) {
if (!this.table.bodyData[childrenIndex[i]]._isChecked || disabled) {
allCheck = false;
break;
}
}
} else {
allCheck = row._isChecked;
}
let indeterminate = false;
if (hasChildren && !allCheck) {
for (let i = 0; i < childrenIndex.length; i++) {
if (this.table.bodyData[childrenIndex[i]]._isChecked) {
indeterminate = true;
break;
}
}
}
return <Checkbox
indeterminate={ indeterminate }
value={ allCheck }
disabled={ disabled }
onOn-change={ isChecked => this.handleEvent(null, 'checkbox', { row, rowIndex, column, columnIndex }, { isChecked }) }>
</Checkbox>;
}
// Tree's firstProp
if (this.table.treeType && this.table.firstProp === column.prop) {
return <span
class={ `${this.prefixCls}--level-${row._level}-cell` }
style={{
marginLeft: `${(row._level - 1) * 24}px`,
paddingLeft: row._childrenLen === 0 ? '20px' : '',
}}>
{ row._childrenLen > 0 &&
<i
class={ `${this.prefixCls}--tree-icon zk-icon zk-icon-${row._isFold ? 'plus' : 'minus'}-square-o`}
on-click={ $event => this.handleEvent($event, 'icon', { row, rowIndex, column, columnIndex }, { isFold: row._isFold }) }></i>
}
{ row[column.prop] ? row[column.prop] : '' }
</span>;
}
// TreeType children's index
if (this.table.showIndex && this.table.treeType && column.prop === '_normalIndex' && row._level > 1) {
return '';
}
if (column.type === undefined || column.type === 'custom') {
return row[column.prop];
} else if (column.type === 'template') {
return this.table.$scopedSlots[column.template]
? this.table.$scopedSlots[column.template]({ row, rowIndex, column, columnIndex })
: '';
}
return '';
}
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={ `${this.prefixCls}__body` }>
<tbody>
{ this.table.bodyData.length > 0
? this.table.bodyData.map((row, rowIndex) =>
[
<tr
v-show={ !row._isHide }
key={ this.table.rowKey ? getKey(row, rowIndex) : rowIndex }
style={ getStyle.call(this, 'row', row, rowIndex) }
class={ getClassName.call(this, 'row', row, rowIndex) }
on-click={ $event => this.handleEvent($event, 'row', { row, rowIndex }) }
on-dblclick={ $event => this.handleEvent($event, 'row', { row, rowIndex }) }
on-contextmenu={ $event => this.handleEvent($event, 'row', { row, rowIndex }) }
on-mouseenter={ $event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: true }) }
on-mouseleave={ $event => this.handleEvent($event, 'row', { row, rowIndex }, { hover: false }) }>
{ this.table.tableColumns.map((column, columnIndex) =>
<td
v-show={ column.visible }
style={ getStyle.call(this, 'cell', row, rowIndex, column, columnIndex) }
class={ getClassName.call(this, 'cell', row, rowIndex, column, columnIndex) }
on-click={ $event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex }) }
on-dblclick={ $event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex }) }
on-contextmenu={ $event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex }) }
on-mouseenter={ $event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex }) }
on-mouseleave={ $event => this.handleEvent($event, 'cell', { row, rowIndex, column, columnIndex }) }>
<div class={ getClassName.call(this, 'inner', row, rowIndex, column, columnIndex) }>
{ renderCell.call(this, row, rowIndex, column, columnIndex) }
</div>
</td>)
}
</tr>,
this.table.expandType && row._isExpanded &&
<tr
key={ rowIndex }
class={ `${this.prefixCls}__body-row ${this.prefixCls}--expand-row` }>
<td
class={ `${this.prefixCls}--expand-content` }
colspan={ this.table.tableColumns.length }>
{ this.table.$scopedSlots.$expand
? this.table.$scopedSlots.$expand({ row, rowIndex })
: ''
}
</td>
</tr>,
])
: <tr
class={ `${this.prefixCls}--empty-row` }>
<td
class={ `${this.prefixCls}__body-cell ${this.prefixCls}--empty-content` }
colspan={ this.table.tableColumns.length }>
{ this.table.emptyText }
</td>
</tr>
}
</tbody>
</table>
);
},
};