tree-table-vue-extend
Version:
A table (with tree-grid) component for Vue.js 2.0 and add some features based on team's own need. (Its style extends iView)
340 lines (330 loc) • 15 kB
JavaScript
import Checkbox from '../Checkbox/Checkbox'; // eslint-disable-line
// import Radio from '../Radio/Radio'; // eslint-disable-line
import { mixins } from './utils';
import { Radio } from 'view-design'; // eslint-disable-line
/* eslint-disable no-underscore-dangle */
export default {
name: 'TreeTable__body',
mixins: [mixins],
components: { Radio },
data() {
return {
radioSelectedIndex: -1
};
},
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];
// 更新折叠状态
if (type === 'Fold') {
this.table.foldStatus[target[this.table.idProp]].status = typeof value === 'undefined' ? !row[`_is${type}`] : value;
}
this.table.bodyData.splice(rowIndex, 1, {
...target,
[`_is${type}`]: typeof value === 'undefined' ? !row[`_is${type}`] : value,
});
},
handleEvent($event, type, data, others) {
const certainType = this.validateType(type, ['cell', 'row', 'checkbox', 'icon', 'radio', 'edit'], '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);
this.table.checkedData.push(row);
if (row._childrenLen > 0) {
const childrenIndex = this.table.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);
}
// Radio
if (certainType.radio) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
// Tree's icon
if (certainType.icon) {
$event.stopPropagation();
this.toggleStatus('Fold', row, rowIndex);
const childrenIndex = this.table.getChildrenIndex(row._level, rowIndex);
for (let i = 0; i < childrenIndex.length; i++) {
this.toggleStatus('Hide', latestData[childrenIndex[i]], childrenIndex[i]);
}
var isExpanded = $event.target.className.indexOf('fa-plus-square-o') > 0;
return this.table.$emit('tree-icon-click', latestData[rowIndex], column, columnIndex, $event, isExpanded);
}
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;
if (hover && $event.currentTarget.className.indexOf(`${this.prefixCls}--row-hover`) == -1) {
$event.currentTarget.className = `${this.prefixCls}--row-hover ` + $event.currentTarget.className;
} else {
$event.currentTarget.className = $event.currentTarget.className.replace(`${this.prefixCls}--row-hover `, "");
}
}
if (certainType.row && others && others.clickRow) {
this.radioSelectedIndex = rowIndex;
return this.table.$emit('radio-click', { row, rowIndex, column, columnIndex, $event });
}
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');
const style = this.table[`${type}Style`];
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 (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`);
}
}
if (column && column.className) {
column.className.forEach((item,idx) => classList.push(item));
}
}
return classList.join(' ');
}
function renderTitle(row, rowIndex, column, columnIndex) {
return row[column.key];
}
// 渲染自定义行
function renderCustomRow(row, rowIndex, tmpData) {
return this.table.$scopedSlots['$createdata']({row, rowIndex, tmpData});
}
// 根据type渲染单元格Cell
function renderCell(row, rowIndex, column, columnIndex) {
// ExpandType
if (this.isExpandCell(this.table, columnIndex)) {
return <i class='fa fa-angle-right'></i>;
}
// SelectionType's Checkbox
if (this.isSelectionCell(this.table, columnIndex)) {
let res = null;
if (this.table.selectType === 'checkbox') {
let allCheck;
let childrenIndex;
const hasChildren = row._childrenLen > 0;
let allDisabled = false;
if (hasChildren) {
childrenIndex = this.table.getChildrenIndex(row._level, rowIndex, false);
allCheck = true;
for (let i = 0; i < childrenIndex.length; i++) {
if (!this.table.bodyData[childrenIndex[i]]._isChecked) {
allCheck = false;
break;
}
}
allDisabled = true;
for (let i = 0; i < childrenIndex.length; i++) {
if (!this.table.bodyData[childrenIndex[i]]._isDisabled) {
allDisabled = false;
break;
}
}
} else {
allDisabled = row._isDisabled;
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;
}
}
}
res = <Checkbox
indeterminate={ indeterminate }
value={ allCheck }
disabled = { allDisabled }
on-on-change={ isChecked => this.handleEvent(null, 'checkbox', { row, rowIndex, column, columnIndex }, { isChecked }) }>
</Checkbox>;
} else {
res = <Radio value={this.radioSelectedIndex === rowIndex} on-on-change={ () => this.handleEvent(null, 'radio', { row, rowIndex, column, columnIndex }) }></Radio>;
}
return res;
}
// Tree's firstProp
if (this.table.treeType && this.table.firstProp === column.key) {
return <span
class={ `${this.prefixCls}--level-${row._level}-cell` }
style={{
marginLeft: `${(row._level - 1) * 24}px`,
paddingLeft: (row[this.table.forceExpandIconProp] != undefined && !row[this.table.forceExpandIconProp]) || row._childrenLen > 0 ? '' : '20px',
}}>
{ (!this.table.treeLoading[row[this.table.idProp]] && (row._childrenLen > 0 || (row[this.table.forceExpandIconProp] != undefined && !row[this.table.forceExpandIconProp]))) &&
<i
class={ `${this.prefixCls}--tree-icon fa fa-${row._isFold ? 'plus' : 'minus'}-square-o`}
on-click={ $event => this.handleEvent($event, 'icon', { row, rowIndex, column, columnIndex }, { isFold: row._isFold }) }></i>
}
{ (this.table.treeLoading[row[this.table.idProp]]) &&
<i class={ `${this.prefixCls}--tree-icon fa fa-spinner fa-spin fa-fw`}></i>
}
<span>
{
column.type === 'template' ? (this.table.$scopedSlots[column.template]
? this.table.$scopedSlots[column.template]({ row, rowIndex, column, columnIndex })
: row[column.key]) : row[column.key]
}
</span>
</span>;
}
// TreeType children's index
if (this.table.showIndex && this.table.treeType && column.key === '_normalIndex' && row._level > 1) {
return '';
}
if (column.type === undefined || column.type === 'custom') {
return <span>{row[column.key]}</span>;
} else if (column.type === 'template') {
return this.table.$scopedSlots[column.template]
? this.table.$scopedSlots[column.template]({ row, rowIndex, column, columnIndex })
: '';
}
return '';
}
var $table = this.table;
var $idProp = this.table.idProp;
// Template
return (
<table cellspacing="0" cellpadding="0" border="0" class={ `${this.prefixCls}__body` }>
<colgroup>
{ this.table.tableColumns.map(column =>
<col width={ column.computedWidth || column.minWidth || column.width }></col>)
}
</colgroup>
<tbody>
<tr><td colspan={this.table.tableColumns.length}><div class={ `${this.prefixCls}__body-tmpdata` }>{this.table.$scopedSlots['$createdata'] ? renderCustomRow.call(this, {}, -1, $table.tmpData['$firstdata']) : ''}</div></td></tr>
{ this.table.bodyData.length > 0
? this.table.bodyData.map((row, rowIndex) =>
[
<tr
v-show={ !row._isHide }
key={ `table_row_${rowIndex}` }
style={ getStyle.call(this, 'row', row, rowIndex) }
class={ getClassName.call(this, 'row', row, rowIndex) }
on-click={ $event => this.handleEvent($event, 'row', { row, rowIndex }, { clickRow: true }) }
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
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 title={ renderTitle.call(this, row, rowIndex, column, columnIndex)} 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><td colspan="999"><div class={ `${this.prefixCls}__body-tmpdata` }>{this.table.$scopedSlots['$createdata'] ? renderCustomRow.call(this, row, rowIndex, $table.tmpData[row[$idProp]]) : ''}</div></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>
);
},
};