UNPKG

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