UNPKG

vue-easytable

Version:
943 lines (851 loc) 31.4 kB
import BodyTr from "./body-tr"; import BodyTrScrolling from "./body-tr-scrolling"; import ExpandTr from "./expand-tr"; import VueDomResizeObserver from "../../../src/comps/resize-observer"; import { getDomResizeObserverCompKey, getFixedTotalWidthByColumnKey, clsName, } from "../util"; import { getValByUnit } from "../../../src/utils/index.js"; import emitter from "../../../src/mixins/emitter"; import { COMPS_NAME, EMIT_EVENTS, COLUMN_TYPES, EXPAND_TRIGGER_TYPES, } from "../util/constant"; export default { name: COMPS_NAME.VE_TABLE_BODY, mixins: [emitter], props: { tableViewportWidth: { type: Number, default: 0, }, columnsOptionResetTime: { type: Number, default: 0, }, colgroups: { type: Array, required: true, }, actualRenderTableData: { type: Array, required: true, }, hasFixedColumn: { type: Boolean, default: false, }, allRowKeys: { type: Array, required: true, }, // expand row option expandOption: { type: Object, default: function () { return null; }, }, // checkbox option checkboxOption: { type: Object, default: function () { return null; }, }, // radio option radioOption: { type: Object, default: function () { return null; }, }, // virual scroll virtualScrollOption: { type: Object, default: null, }, // is virtual scroll isVirtualScroll: { type: Boolean, default: false, }, // is scrolling showVirtualScrollingPlaceholder: { type: Boolean, default: false, }, rowKeyFieldName: { type: String, default: null, }, // cell style option cellStyleOption: { type: Object, default: function () { return null; }, }, // cell span option cellSpanOption: { type: Object, default: function () { return null; }, }, // highlight row key highlightRowKey: { type: [String, Number], default: null, }, // event custom option eventCustomOption: { type: Object, default: function () { return null; }, }, // cell selection option cellSelectionOption: { type: Object, default: function () { return null; }, }, // cell selection data cellSelectionData: { type: Object, default: function () { return null; }, }, // cell selection range data cellSelectionRangeData: { type: Object, default: function () { return null; }, }, bodyIndicatorRowKeys: { type: Object, default: function () { return null; }, }, // edit option editOption: { type: Object, default: function () { return null; }, }, }, data() { return { // columns widths map colsWidths: new Map(), /* internal expand row keys 1、当没有设置 expandedRowKeys 时生效 */ internalExpandRowkeys: [], /* 1、存储当前多选功能的rowkey 信息 */ internalCheckboxSelectedRowKeys: [], /* 1、存储当前单选功能的rowkey 信息 */ internalRadioSelectedRowKey: null, // virtual scroll preview rendered rowKey virtualScrollPreviewRenderedRowKeys: [], // virtual scroll repeat rendered rowKey virtualScrollRepeatRenderedRowKeys: [], }; }, computed: { /* column collenction info 1、style of each column 2、class of each column */ columnCollection() { let columnCollection = []; const { colgroups } = this; colgroups.forEach((col) => { const colKey = col.key; let columnCollectionItem = { colKey: colKey, class: { [clsName("last-left-fixed-column")]: this.isLastLeftFixedColumn(col), [clsName("first-right-fixed-column")]: this.isfirstRightFixedColumn(col), }, style: {}, }; const { fixed, align } = col; columnCollectionItem.style["text-align"] = align || "center"; if (fixed) { let totalWidth = 0; // column index const columnIndex = colgroups.findIndex( (x) => x.key === colKey, ); if ( (fixed === "left" && columnIndex > 0) || (fixed === "right" && columnIndex < colgroups.length - 1) ) { totalWidth = getFixedTotalWidthByColumnKey({ colgroups, colKey, fixed, }); totalWidth = getValByUnit(totalWidth); } columnCollectionItem.style["left"] = fixed === "left" ? totalWidth : ""; columnCollectionItem.style["right"] = fixed === "right" ? totalWidth : ""; } columnCollection.push(columnCollectionItem); }); return columnCollection; }, // expand column expandColumn() { return this.colgroups.find((x) => x.type === COLUMN_TYPES.EXPAND); }, /* 是否是可控行展开 1、当设置了 expandedRowKeys 属性时则为可控行展开 */ isControlledExpand() { return ( this.expandOption && Array.isArray(this.expandOption.expandedRowKeys) ); }, // expanded row keys expandedRowkeys() { return this.isControlledExpand ? this.expandOption.expandedRowKeys : this.internalExpandRowkeys; }, // disable row selected row keys disableCheckboxSelectedRowKeys() { let result = []; const { checkboxOption, internalCheckboxSelectedRowKeys } = this; if (!checkboxOption) { return result; } const { disableSelectedRowKeys } = checkboxOption; if ( internalCheckboxSelectedRowKeys.length > 0 && Array.isArray(disableSelectedRowKeys) && disableSelectedRowKeys.length > 0 ) { disableSelectedRowKeys.forEach((rowkey) => { if (internalCheckboxSelectedRowKeys.includes(rowkey)) { result.push(rowkey); } }); } return result; }, // disable row unselected row keys disableCheckboxUnselectedRowKeys() { let result = []; const { checkboxOption, internalCheckboxSelectedRowKeys } = this; if (!checkboxOption) { return result; } const { disableSelectedRowKeys } = checkboxOption; if ( Array.isArray(disableSelectedRowKeys) && disableSelectedRowKeys.length > 0 ) { disableSelectedRowKeys.forEach((rowkey) => { if (!internalCheckboxSelectedRowKeys.includes(rowkey)) { result.push(rowkey); } }); } return result; }, /* is row keys selected all 为 true 的条件:选中数量 + 禁用选中数量 === 总量 */ isCheckboxSelectedAll() { if (this.allRowKeys.length > 0) { if ( this.internalCheckboxSelectedRowKeys.length + this.disableCheckboxUnselectedRowKeys.length === this.allRowKeys.length ) { return true; } } return false; }, // is checkbox indeterminate isCheckboxIndeterminate() { const { internalCheckboxSelectedRowKeys, allRowKeys } = this; return ( internalCheckboxSelectedRowKeys.length > 0 && internalCheckboxSelectedRowKeys.length < allRowKeys.length ); }, // 是否是受控属性(取决于selectedRowKey) isControlledRadio() { const { radioOption } = this; return ( radioOption && Object.keys(radioOption).includes("selectedRowKey") ); }, }, watch: { // watch expand Option expandOption: { handler: function () { this.initInternalExpandRowKeys(); }, immediate: true, }, // watch expandOption expandedRowKeys "expandOption.expandedRowKeys": { handler: function () { this.initInternalExpandRowKeys(); }, }, // watch checkbox option checkboxOption: { handler: function () { this.initInternalCheckboxSelectedRowKeys(); }, immediate: true, }, // watch selectedRowKeys "checkboxOption.selectedRowKeys": { handler: function () { this.resetInternalCheckboxSelectedRowKeys(); }, }, // watch internalCheckboxSelectedRowKeys internalCheckboxSelectedRowKeys: { handler: function () { // send to checkbox all(in header) this.sendToCheckboxAll(); }, }, // watch checkbox option radioOption: { handler: function () { this.initInternalRadioSelectedRowKey(); }, immediate: true, }, // watch selectedRowKeys "radioOption.selectedRowKey": { handler: function () { this.initInternalRadioSelectedRowKey(); }, }, }, methods: { // is last left fixed column isLastLeftFixedColumn(column) { let result = false; const { colgroups } = this; const { fixed } = column; if (fixed === "left") { const { field } = column; const leftFixedColumns = colgroups.filter( (x) => x.fixed === "left", ); const index = leftFixedColumns.findIndex( (x) => x.field === field, ); if (index === leftFixedColumns.length - 1) { result = true; } } return result; }, // is first right fixed column isfirstRightFixedColumn(column) { let result = false; const { colgroups } = this; const { fixed } = column; if (fixed === "right") { const { field } = column; const rightFixedColumns = colgroups.filter( (x) => x.fixed === "right", ); if (rightFixedColumns[0].field === field) { result = true; } } return result; }, /* * @expandRowChange * @desc row expand change * @param {object} rowData - row data * @param {number} rowIndex - row index */ expandRowChange(rowData, rowIndex) { const { expandOption, internalExpandRowkeys, expandedRowkeys, rowKeyFieldName, } = this; // deal before expand row method if (typeof expandOption.beforeExpandRowChange === "function") { const beforeExpandRowResult = expandOption.beforeExpandRowChange({ beforeExpandedRowKeys: expandedRowkeys, row: rowData, rowIndex, }); // interrupt execute if (beforeExpandRowResult === false) { return false; } } const rowKey = rowData[rowKeyFieldName]; const rowKeyIndex = internalExpandRowkeys.indexOf(rowKey); if (rowKeyIndex > -1) { internalExpandRowkeys.splice(rowKeyIndex, 1); } else { internalExpandRowkeys.push(rowKey); } // deal after expand row method if (typeof expandOption.afterExpandRowChange === "function") { expandOption.afterExpandRowChange({ afterExpandedRowKeys: internalExpandRowkeys, row: rowData, rowIndex, }); } }, /* * @rowClick * @desc row expand click event * @param {object} rowData - row data * @param {number} rowIndex - row index */ rowClick({ rowData, rowIndex }) { const { expandOption, isExpandRow, expandRowChange, rowKeyFieldName, } = this; // 行高亮功能 if (rowKeyFieldName) { const rowKey = rowData[rowKeyFieldName]; this.$emit(EMIT_EVENTS.HIGHLIGHT_ROW_CHANGE, { rowKey }); } // 行展开功能 if (!isExpandRow({ rowData, rowIndex })) { return false; } const trigger = expandOption.trigger; // expand row by click row if (trigger === EXPAND_TRIGGER_TYPES.ROW) { expandRowChange(rowData, rowIndex); } }, /* * @isExpandRow * @desc is expand row * @param {object} rowData - row data * @param {number} rowIndex - row index */ isExpandRow({ rowData, rowIndex }) { let result = false; const { expandColumn, expandOption } = this; if (expandColumn && expandOption) { // 是否允许展开 let expandable = true; if (typeof expandOption.expandable === "function") { expandable = expandOption.expandable({ row: rowData, column: expandColumn, rowIndex, }); } if (expandable !== false) { result = true; } } return result; }, /* * @tdSizeChange * @desc td size change * @param {any} key - column key * @param {number|string} width - column real width */ tdSizeChange({ key, width }) { const { colsWidths } = this; colsWidths.set(key, width); this.$emit(EMIT_EVENTS.BODY_CELL_WIDTH_CHANGE, colsWidths); }, // init internal expand row keys initInternalExpandRowKeys() { const { expandOption, isControlledExpand, allRowKeys } = this; if (!expandOption) { return false; } if (isControlledExpand) { this.internalExpandRowkeys = expandOption.expandedRowKeys.slice(0); } else if (expandOption.defaultExpandAllRows) { this.internalExpandRowkeys = allRowKeys; } else if (expandOption.defaultExpandedRowKeys) { this.internalExpandRowkeys = expandOption.defaultExpandedRowKeys.slice(0); } }, // get expand row getExpandRowComp({ rowData, rowIndex }) { if (this.isExpandRow({ rowData, rowIndex })) { const expandTrProps = { props: { tableViewportWidth: this.tableViewportWidth, colgroups: this.colgroups, expandOption: this.expandOption, expandedRowkeys: this.expandedRowkeys, expandColumn: this.expandColumn, rowKeyFieldName: this.rowKeyFieldName, rowData, rowIndex, }, }; return <ExpandTr {...expandTrProps} />; } return null; }, // send to checkbox all sendToCheckboxAll() { const { isCheckboxSelectedAll, isCheckboxIndeterminate } = this; this.dispatch( COMPS_NAME.VE_TABLE, EMIT_EVENTS.CHECKBOX_SELECTED_ALL_INFO, { isIndeterminate: isCheckboxIndeterminate, isSelected: isCheckboxSelectedAll, }, ); }, // init internal Radio SelectedRowKey initInternalRadioSelectedRowKey() { const { radioOption, isControlledRadio } = this; if (!radioOption) { return false; } const { selectedRowKey, defaultSelectedRowKey } = radioOption; this.internalRadioSelectedRowKey = isControlledRadio ? selectedRowKey : defaultSelectedRowKey; }, // init internal Checkbox SelectedRowKeys initInternalCheckboxSelectedRowKeys() { let result = []; const { checkboxOption, allRowKeys } = this; if (!checkboxOption) { return false; } const { selectedRowKeys, defaultSelectedAllRows, defaultSelectedRowKeys, } = checkboxOption; if (Array.isArray(selectedRowKeys)) { result = selectedRowKeys; } else if (defaultSelectedAllRows) { result = allRowKeys; } else if (Array.isArray(defaultSelectedRowKeys)) { result = defaultSelectedRowKeys; } this.internalCheckboxSelectedRowKeys = result; }, // reset internalCheckboxSelectedRowKeys by selectedRowKeys resetInternalCheckboxSelectedRowKeys() { this.internalCheckboxSelectedRowKeys = this.checkboxOption.selectedRowKeys.slice(0); }, /* * @checkboxSelectedRowChange * @desc selected row change * @param {number|string} rowKey - rowKey * @param {bool} isSelected */ checkboxSelectedRowChange({ rowKey, isSelected }) { const { checkboxOption, internalCheckboxSelectedRowKeys, rowKeyFieldName, } = this; const { selectedRowChange, selectedRowKeys } = checkboxOption; let internalCheckboxSelectedRowKeysTemp = internalCheckboxSelectedRowKeys.slice(0); // will selected const rowKeyIndex = internalCheckboxSelectedRowKeysTemp.indexOf(rowKey); if (isSelected) { // bug fixed:通过行点击触发,导致key重复的问题 if (rowKeyIndex === -1) { internalCheckboxSelectedRowKeysTemp.push(rowKey); } } else { if (rowKeyIndex > -1) { internalCheckboxSelectedRowKeysTemp.splice(rowKeyIndex, 1); } } // 非可控才改变 internalCheckboxSelectedRowKeys if (!Array.isArray(selectedRowKeys)) { this.internalCheckboxSelectedRowKeys = internalCheckboxSelectedRowKeysTemp; } selectedRowChange({ row: this.actualRenderTableData.find( (x) => x[rowKeyFieldName] === rowKey, ), isSelected, selectedRowKeys: internalCheckboxSelectedRowKeysTemp, }); }, /* * @checkboxSelectedAllChange * @desc selected all change * @param {bool} isSelected - is selected */ checkboxSelectedAllChange({ isSelected }) { const { checkboxOption, internalCheckboxSelectedRowKeys, allRowKeys, disableCheckboxSelectedRowKeys, disableCheckboxUnselectedRowKeys, } = this; const { selectedAllChange, selectedRowKeys } = checkboxOption; let internalCheckboxSelectedRowKeysTemp = internalCheckboxSelectedRowKeys.slice(0); // selected all if (isSelected) { // except disable Row Unselected keys let allSelectedKeys = allRowKeys.slice(0); if (disableCheckboxUnselectedRowKeys.length > 0) { disableCheckboxUnselectedRowKeys.forEach((rowkey) => { let index = allSelectedKeys.indexOf(rowkey); if (index > -1) { allSelectedKeys.splice(index, 1); } }); } internalCheckboxSelectedRowKeysTemp = allSelectedKeys; } else { // except disable Row Selected keys internalCheckboxSelectedRowKeysTemp = disableCheckboxSelectedRowKeys; } // 非可控才改变 internalCheckboxSelectedRowKeys if (!Array.isArray(selectedRowKeys)) { this.internalCheckboxSelectedRowKeys = internalCheckboxSelectedRowKeysTemp; } selectedAllChange && selectedAllChange({ isSelected, selectedRowKeys: internalCheckboxSelectedRowKeysTemp, //changeRowKeys: }); }, /* * @radioSelectedRowChange * @desc selected all change * @param {number|string} rowKey - rowKey */ radioSelectedRowChange({ rowKey }) { const { radioOption, rowKeyFieldName, isControlledRadio } = this; const { selectedRowChange } = radioOption; // 非受控 if (!isControlledRadio) { this.internalRadioSelectedRowKey = rowKey; } selectedRowChange({ row: this.actualRenderTableData.find( (x) => x[rowKeyFieldName] === rowKey, ), }); }, // get tr key getTrKey({ rowData, rowIndex }) { let result = rowIndex; const { rowKeyFieldName } = this; if (rowKeyFieldName) { result = rowData[rowKeyFieldName]; } return result; }, /* rendering row keys virtual scrolling will invoke */ renderingRowKeys(rowKeys) { const { virtualScrollPreviewRenderedRowKeys: previewRenderedRowKeys, } = this; this.virtualScrollRepeatRenderedRowKeys = rowKeys.filter( (rowKey) => { return previewRenderedRowKeys.indexOf(rowKey) != -1; }, ); this.virtualScrollPreviewRenderedRowKeys = rowKeys; }, }, mounted() { // receive checkbox row selected change from VE_TABLE_BODY_CHECKBOX_CONTENT this.$on(EMIT_EVENTS.CHECKBOX_SELECTED_ROW_CHANGE, (params) => { this.checkboxSelectedRowChange(params); }); // receive checkbox row selected change from VE_TABLE_BODY_CHECKBOX_CONTENT this.$on(EMIT_EVENTS.CHECKBOX_SELECTED_ALL_CHANGE, (params) => { this.checkboxSelectedAllChange(params); }); // receive radio row selected change from VE_TABLE_BODY_RADIO_CONTENT this.$on(EMIT_EVENTS.RADIO_SELECTED_ROW_CHANGE, (params) => { this.radioSelectedRowChange(params); }); // recieve tr click this.$on(EMIT_EVENTS.BODY_ROW_CLICK, (params) => { this.rowClick(params); }); if (this.checkboxOption) { // 这里 nextTick 解决由于子组件先初始化,导致父组件无法接收消息的问题 this.$nextTick(() => { this.sendToCheckboxAll(); }); } }, render() { const { colgroups, actualRenderTableData, expandOption, expandRowChange, isExpandRow, getExpandRowComp, expandedRowkeys, checkboxOption, radioOption, rowKeyFieldName, tdSizeChange, internalCheckboxSelectedRowKeys, internalRadioSelectedRowKey, isVirtualScroll, cellStyleOption, showVirtualScrollingPlaceholder, } = this; const { virtualScrollRepeatRenderedRowKeys } = this; return ( <tbody> {/* Measure each column width with additional hidden col */} <tr style="height:0;"> {colgroups.map((column) => { const measureTdProps = { key: getDomResizeObserverCompKey( column.key, this.columnsOptionResetTime, ), props: { tagName: "td", id: column.key, }, on: { "on-dom-resize-change": tdSizeChange, }, style: { padding: 0, border: 0, height: 0, }, }; return <VueDomResizeObserver {...measureTdProps} />; })} </tr> {actualRenderTableData.map((rowData, rowIndex) => { const trProps = { key: this.getTrKey({ rowData, rowIndex }), props: { rowIndex, rowData, colgroups, expandOption, expandedRowkeys, checkboxOption, radioOption, rowKeyFieldName, allRowKeys: this.allRowKeys, expandRowChange, internalCheckboxSelectedRowKeys, internalRadioSelectedRowKey, isVirtualScroll, isExpandRow: isExpandRow({ rowData, rowIndex, }), cellStyleOption, cellSpanOption: this.cellSpanOption, highlightRowKey: this.highlightRowKey, eventCustomOption: this.eventCustomOption, cellSelectionData: this.cellSelectionData, editOption: this.editOption, columnCollection: this.columnCollection, cellSelectionRangeData: this.cellSelectionRangeData, bodyIndicatorRowKeys: this.bodyIndicatorRowKeys, }, }; if (showVirtualScrollingPlaceholder) { const trPropsScrolling = { key: this.getTrKey({ rowData, rowIndex }), props: { colgroups, }, }; if ( virtualScrollRepeatRenderedRowKeys.indexOf( rowData[this.rowKeyFieldName], ) != -1 ) { return [ // body tr <BodyTr {...trProps} />, ]; } else { return <BodyTrScrolling {...trPropsScrolling} />; } } else { return [ // body tr <BodyTr {...trProps} />, // expand row getExpandRowComp({ rowData, rowIndex }), ]; } })} </tbody> ); }, };