UNPKG

vue-fantable

Version:
948 lines (858 loc) 26.3 kB
import { getDomResizeObserverCompKey, getFixedTotalWidthByColumnKey, clsName, } from '../util/index.js' import { getValByUnit } from '../../../src/utils/index.js' import { COMPS_NAME, COLUMN_TYPES, EXPAND_TRIGGER_TYPES, } from '../util/constant' // import eventCenter from '@P/events/event-center' import VueDomResizeObserver from '@P/src/components/resize-observer/index.js' import BodyTr from './body-tr.jsx' import BodyTrScrolling from './body-tr-scrolling.jsx' import ExpandTr from './expand-tr.jsx' import { GLOBAL_EVENT } from '@P/events/global-events.js' import { defineComponent } from 'vue' export default defineComponent({ name: COMPS_NAME.FAN_TABLE_BODY, components: { BodyTr }, inject: ['eventCenter'], 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 }, }, }, emits: ['highlightRowChange', 'bodyCellWidthChange'], 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() { const columnCollection = [] const { colgroups } = this colgroups.forEach((col) => { const colKey = col.key const columnCollectionItem = { 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() { const 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() { const 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() }, }, }, mounted() { // receive checkbox row selected change from FAN_TABLE_BODY_CHECKBOX_CONTENT this.eventCenter.on(GLOBAL_EVENT.CHECKBOX_SELECTED_ROW_CHANGE, (params) => { this.checkboxSelectedRowChange(params) }) // receive checkbox row selected change from FAN_TABLE_BODY_CHECKBOX_CONTENT this.eventCenter.on(GLOBAL_EVENT.CHECKBOX_SELECTED_ALL_CHANGE_BODY, (params) => { this.checkboxSelectedAllChange(params) }) // receive radio row selected change from FAN_TABLE_BODY_RADIO_CONTENT this.eventCenter.on(GLOBAL_EVENT.RADIO_SELECTED_ROW_CHANGE, (params) => { this.radioSelectedRowChange(params) }) // recieve tr click this.eventCenter.on(GLOBAL_EVENT.BODY_ROW_CLICK, (params) => { this.rowClick(params) }) if (this.checkboxOption) { // 这里 nextTick 解决由于子组件先初始化,导致父组件无法接收消息的问题 this.$nextTick(() => { this.sendToCheckboxAll() }) } }, 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 }) this.$emit('highlightRowChange', { 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) this.$emit('bodyCellWidthChange', 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 = { 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.eventCenter.emit(GLOBAL_EVENT.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 let selectedRowChange = null let selectedRowKeys = null if (!checkboxOption) return if (checkboxOption.selectedRowChange) { selectedRowChange = checkboxOption.selectedRowChange } if (checkboxOption.selectedRowKeys) { selectedRowKeys = checkboxOption.selectedRowKeys } const 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 if (!checkboxOption) return const { selectedAllChange, selectedRowKeys } = checkboxOption let internalCheckboxSelectedRowKeysTemp = internalCheckboxSelectedRowKeys.slice(0) // selected all if (isSelected) { // except disable Row Unselected keys const allSelectedKeys = allRowKeys.slice(0) if (disableCheckboxUnselectedRowKeys.length > 0) { disableCheckboxUnselectedRowKeys.forEach((rowkey) => { const 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 let selectedRowChange = null if (!radioOption) { return } if (radioOption.selectedRowChange) { selectedRowChange = radioOption.selectedRowChange } // 非受控 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 }, }, 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, ), tagName: 'td', id: column.key, onDomResizeChange: tdSizeChange, style: { padding: 0, border: 0, height: 0, }, } return <VueDomResizeObserver {...measureTdProps} /> })} </tr> {actualRenderTableData.map((rowData, rowIndex) => { const trProps = { key: this.getTrKey({ rowData, rowIndex }), 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 }), 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> ) }, } )