vxe-table
Version:
A PC-end table component based on Vxe UI, supporting copy-paste, data pivot table, and high-performance virtual list table solution.
15,056 lines • 599 kB
text/typescript
import { h, ComponentPublicInstance, reactive, ref, Ref, provide, inject, nextTick, Teleport, onActivated, onDeactivated, onBeforeUnmount, onUnmounted, watch, computed, onMounted } from 'vue'
import { defineVxeComponent } from '../../ui/src/comp'
import XEUtils from 'xe-utils'
import { initTpImg, getTpImg, isPx, isScale, hasClass, addClass, removeClass, wheelScrollTopTo, wheelScrollLeftTo, getEventTargetNode, getPaddingTopBottomSize, setScrollTop, setScrollLeft, toCssUnit, hasControlKey, checkTargetElement, hasEventInputTarget } from '../../ui/src/dom'
import { getLastZIndex, nextZIndex, hasChildrenList, getFuncText, isEnableConf, formatText, eqEmptyValue } from '../../ui/src/utils'
import { VxeUI } from '../../ui'
import { createReactData, createInternalData, getRowUniqueId, createRowId, clearTableAllStatus, getColumnList, toFilters, hasDeepKey, getRowkey, getRowid, rowToVisible, colToVisible, getCellValue, setCellValue, handleRowidOrRow, handleFieldOrColumn, toTreePathSeq, restoreScrollLocation, getRootColumn, getRefElem, getColReMinWidth, getColReMaxWidth, createHandleUpdateRowId, createHandleGetRowId, getCalcHeight, getCellRestHeight, getLastChildColumn, getRowMaxHeight, handleCustomStoreConfig, encodeRowid } from './util'
import { getSlotVNs } from '../../ui/src/vn'
import { moveRowAnimateToTb, clearRowAnimate, moveColAnimateToLr, clearColAnimate } from '../../ui/src/anime'
import { createComponentLog } from '../../ui/src/log'
import { getCrossTableDragRowInfo } from './store'
import Cell from './cell'
import TableBodyComponent from './body'
import TableHeaderComponent from './header'
import TableFooterComponent from './footer'
import { tableProps } from './props'
import { tableEmits } from './emits'
import TableCustomPanelComponent from '../module/custom/panel'
import TableFilterPanelComponent from '../module/filter/panel'
import TableImportPanelComponent from '../module/export/import-panel'
import TableExportPanelComponent from '../module/export/export-panel'
import TableMenuPanelComponent from '../module/menu/panel'
import '../module/filter/hook'
import '../module/menu/hook'
import '../module/edit/hook'
import '../module/export/hook'
import '../module/keyboard/hook'
import '../module/validator/hook'
import '../module/custom/hook'
import '../render'
import type { VxeTooltipInstance, VxeTabsConstructor, VxeTabsPrivateMethods, ValueOf, VxeComponentSlotType, VxeComponentStyleType, VxeModalConstructor, VxeModalMethods, VxeSplitterConstructor, VxeSplitterMethods } from 'vxe-pc-ui'
import type { VxeGridConstructor, VxeGridPrivateMethods, VxeTableConstructor, VxeTablePropTypes, VxeToolbarConstructor, TablePrivateMethods, VxeTablePrivateRef, VxeTablePrivateComputed, VxeTablePrivateMethods, TableMethods, VxeTableMethods, VxeTableDefines, VxeTableEmits, VxeTableProps, VxeColumnPropTypes, VxeTableCustomPanelConstructor } from '../../../types'
const { warnLog, errLog } = createComponentLog('table')
const { getConfig, getIcon, getI18n, renderer, formats, createEvent, globalResize, interceptor, hooks, globalEvents, GLOBAL_EVENT_KEYS, useFns, renderEmptyElement } = VxeUI
const supportMaxRow = 5e6
const customStorageKey = 'VXE_CUSTOM_STORE'
const maxYHeight = 5e6
const maxXWidth = 5e6
const sourceType = 'table'
let crossTableDragRowObj: {
$oldTable: VxeTableConstructor & VxeTablePrivateMethods
$newTable: (VxeTableConstructor & VxeTablePrivateMethods) | null
} | null = null
export default defineVxeComponent({
name: 'VxeTable',
props: tableProps,
emits: tableEmits,
setup (props, context) {
const { slots, emit } = context
const xID = XEUtils.uniqueId()
const browseObj = XEUtils.browse()
// 使用已安装的组件,如果未安装则不渲染
const VxeUILoadingComponent = VxeUI.getComponent('VxeLoading')
const VxeUITooltipComponent = VxeUI.getComponent('VxeTooltip')
const $xeModal = inject<(VxeModalConstructor & VxeModalMethods) | null>('$xeModal', null)
const $xeTabs = inject<(VxeTabsConstructor & VxeTabsPrivateMethods) | null>('$xeTabs', null)
const $xeParentTable = inject<(VxeTableConstructor & VxeTablePrivateMethods) | null>('$xeTable', null)
const $xeSplitter = inject<(VxeSplitterConstructor & VxeSplitterMethods) | null>('$xeSplitter', null)
const $xeGrid = inject<(VxeGridConstructor & VxeGridPrivateMethods) | null>('$xeGrid', null)
const $xeGantt = inject<VxeTableDefines.InjectGanttType | null>('$xeGantt', null)
const $xeGGWrapper = $xeGrid || $xeGantt
const { computeSize } = useFns.useSize(props)
const crossTableDragRowInfo = getCrossTableDragRowInfo()
const reactData = reactive(createReactData())
const internalData = createInternalData()
let tableMethods = {} as TableMethods
let tablePrivateMethods = {} as TablePrivateMethods
const refElem = ref() as Ref<HTMLDivElement>
const refVarElem = ref() as Ref<HTMLDivElement>
const refTooltip = ref() as Ref<VxeTooltipInstance>
const refCommTooltip = ref() as Ref<VxeTooltipInstance>
const refValidTooltip = ref() as Ref<VxeTooltipInstance>
const refTableMenu = ref() as Ref<any>
const refTableFilter = ref() as Ref<any>
const refTableCustom = ref() as Ref<VxeTableCustomPanelConstructor>
const refTableViewportElem = ref<HTMLDivElement>()
const refTableHeader = ref() as Ref<ComponentPublicInstance>
const refTableBody = ref() as Ref<ComponentPublicInstance>
const refTableFooter = ref() as Ref<ComponentPublicInstance>
const refTableLeftHeader = ref() as Ref<ComponentPublicInstance>
const refTableLeftBody = ref() as Ref<ComponentPublicInstance>
const refTableLeftFooter = ref() as Ref<ComponentPublicInstance>
const refTableRightHeader = ref() as Ref<ComponentPublicInstance>
const refTableRightBody = ref() as Ref<ComponentPublicInstance>
const refTableRightFooter = ref() as Ref<ComponentPublicInstance>
const refTeleportWrapper = ref<HTMLDivElement>()
const refPopupWrapperElem = ref<HTMLDivElement>()
const refCustomContainerElem = ref<HTMLDivElement>()
const refLeftContainer = ref() as Ref<HTMLDivElement>
const refRightContainer = ref() as Ref<HTMLDivElement>
const refColResizeBar = ref() as Ref<HTMLDivElement>
const refRowResizeBar = ref() as Ref<HTMLDivElement>
const refEmptyPlaceholder = ref() as Ref<HTMLDivElement>
const refDragTipElem = ref<HTMLDivElement>()
const refDragRowLineElem = ref<HTMLDivElement>()
const refDragColLineElem = ref<HTMLDivElement>()
const refRowExpandElem = ref<HTMLDivElement>()
const refRowExpandYSpaceElem = ref<HTMLDivElement>()
const refScrollXVirtualElem = ref<HTMLDivElement>()
const refScrollYVirtualElem = ref<HTMLDivElement>()
const refScrollXHandleElem = ref<HTMLDivElement>()
const refScrollXLeftCornerElem = ref<HTMLDivElement>()
const refScrollXRightCornerElem = ref<HTMLDivElement>()
const refScrollYHandleElem = ref<HTMLDivElement>()
const refScrollYTopCornerElem = ref<HTMLDivElement>()
const refScrollXWrapperElem = ref<HTMLDivElement>()
const refScrollYWrapperElem = ref<HTMLDivElement>()
const refScrollYBottomCornerElem = ref<HTMLDivElement>()
const refScrollXSpaceElem = ref<HTMLDivElement>()
const refScrollYSpaceElem = ref<HTMLDivElement>()
let $xeToolbar: VxeToolbarConstructor
const computeTableId = computed(() => {
const { id } = props
if (id) {
if (XEUtils.isFunction(id)) {
return `${id({ $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }) || ''}`
}
return `${id}`
}
return ''
})
const computeRowField = computed(() => {
const rowOpts = computeRowOpts.value
return `${props.rowId || rowOpts.keyField || '_X_ROW_KEY'}`
})
const computeValidOpts = computed(() => {
const opts = Object.assign({}, getConfig().table.validConfig, props.validConfig)
// 兼容老版本
if (XEUtils.isBoolean(opts.showMessage)) {
opts.showErrorMessage = opts.showMessage
}
return opts
})
/**
* @deprecated
*/
const computeSXOpts = computed(() => {
const virtualXOpts = computeVirtualXOpts.value
return virtualXOpts
})
const computeScrollXThreshold = computed(() => {
const virtualXOpts = computeVirtualXOpts.value
const { threshold } = virtualXOpts
if (threshold) {
return XEUtils.toNumber(threshold)
}
return 0
})
/**
* @deprecated
*/
const computeSYOpts = computed(() => {
const virtualYOpts = computeVirtualYOpts.value
return virtualYOpts
})
const computeVirtualXOpts = computed(() => {
const { virtualXConfig, scrollX } = props
const globalVirtualXConfig = getConfig().table.virtualXConfig
const globalScrollX = getConfig().table.scrollX
if (virtualXConfig) {
return Object.assign({}, globalVirtualXConfig, virtualXConfig) as VxeTablePropTypes.VirtualXConfig & { gt: number }
}
if (scrollX) {
// 已废弃,保留兼容
return Object.assign({}, globalScrollX, scrollX) as VxeTablePropTypes.VirtualXConfig & { gt: number }
}
if (globalVirtualXConfig) {
return Object.assign({}, globalVirtualXConfig, virtualXConfig) as VxeTablePropTypes.VirtualXConfig & { gt: number }
}
// 已废弃,保留兼容
return Object.assign({}, globalScrollX, scrollX) as VxeTablePropTypes.VirtualXConfig & { gt: number }
})
const computeVirtualYOpts = computed(() => {
const { virtualYConfig, scrollY } = props
const globalVirtualYConfig = getConfig().table.virtualYConfig
const globalScrollY = getConfig().table.scrollY
if (virtualYConfig) {
return Object.assign({}, globalVirtualYConfig, virtualYConfig) as VxeTablePropTypes.VirtualYConfig & { gt: number }
}
if (scrollY) {
// 已废弃,保留兼容
return Object.assign({}, globalScrollY, scrollY) as VxeTablePropTypes.VirtualYConfig & { gt: number }
}
if (globalVirtualYConfig) {
return Object.assign({}, globalVirtualYConfig, virtualYConfig) as VxeTablePropTypes.VirtualYConfig & { gt: number }
}
// 已废弃,保留兼容
return Object.assign({}, globalScrollY, scrollY) as VxeTablePropTypes.VirtualYConfig & { gt: number }
})
const computeScrollbarOpts = computed(() => {
return Object.assign({}, getConfig().table.scrollbarConfig, props.scrollbarConfig)
})
const computeScrollbarXOpts = computed(() => {
const scrollbarOpts = computeScrollbarOpts.value
return Object.assign({}, scrollbarOpts.x, props.scrollbarConfig?.x || {})
})
const computeScrollbarYOpts = computed(() => {
const scrollbarOpts = computeScrollbarOpts.value
return Object.assign({}, scrollbarOpts.y, props.scrollbarConfig?.y || {})
})
const computeScrollbarXToTop = computed(() => {
const scrollbarXOpts = computeScrollbarXOpts.value
return scrollbarXOpts.position === 'top'
})
const computeScrollbarYToLeft = computed(() => {
const scrollbarYOpts = computeScrollbarYOpts.value
return scrollbarYOpts.position === 'left'
})
const computeScrollYThreshold = computed(() => {
const virtualYOpts = computeVirtualYOpts.value
const { threshold } = virtualYOpts
if (threshold) {
return XEUtils.toNumber(threshold)
}
return 0
})
const computeRowHeightMaps = computed(() => {
return reactData.rowHeightStore
})
const computeDefaultRowHeight = computed(() => {
const vSize = computeSize.value
const rowHeightMaps = computeRowHeightMaps.value
return rowHeightMaps[vSize || 'default'] || 18
})
const computeColumnOpts = computed(() => {
return Object.assign({}, getConfig().table.columnConfig, props.columnConfig)
})
const computeCurrentColumnOpts = computed(() => {
return Object.assign({}, getConfig().table.currentColumnConfig, props.currentColumnConfig)
})
const computeCellOpts = computed(() => {
const cellOpts = Object.assign({}, getConfig().table.cellConfig, props.cellConfig)
if (cellOpts.height) {
cellOpts.height = XEUtils.toNumber(cellOpts.height)
}
return cellOpts
})
const computeHeaderCellOpts = computed(() => {
const headerCellOpts = Object.assign({}, getConfig().table.headerCellConfig, props.headerCellConfig)
const defaultRowHeight = computeDefaultRowHeight.value
const cellOpts = computeCellOpts.value
let headCellHeight = XEUtils.toNumber(getCalcHeight(headerCellOpts.height || cellOpts.height))
if ($xeGantt) {
const { computeTaskViewScales } = $xeGantt.getComputeMaps()
const taskViewScales = computeTaskViewScales.value
if (taskViewScales && taskViewScales.length > 2) {
const ganttMinHeadCellHeight = defaultRowHeight / 2 * taskViewScales.length
headCellHeight = Math.max(ganttMinHeadCellHeight, headCellHeight)
}
}
headerCellOpts.height = headCellHeight
return headerCellOpts
})
const computeFooterCellOpts = computed(() => {
const footerCellOpts = Object.assign({}, getConfig().table.footerCellConfig, props.footerCellConfig)
const cellOpts = computeCellOpts.value
footerCellOpts.height = XEUtils.toNumber(getCalcHeight(footerCellOpts.height || cellOpts.height))
return footerCellOpts
})
const computeRowOpts = computed(() => {
return Object.assign({}, getConfig().table.rowConfig, props.rowConfig)
})
const computeAggregateOpts = computed(() => {
return Object.assign({}, getConfig().table.aggregateConfig || getConfig().table.rowGroupConfig, props.aggregateConfig || props.rowGroupConfig)
})
const computeRowGroupOpts = computed(() => {
return computeAggregateOpts.value
})
const computeAggregateMode = computed(() => {
const aggregateOpts = computeAggregateOpts.value
return aggregateOpts.mode
})
const computeAggregateAccuracyOpts = computed(() => {
return Object.assign({}, getConfig().table.aggregateAccuracyConfig, props.aggregateAccuracyConfig)
})
const computeCurrentRowOpts = computed(() => {
return Object.assign({}, getConfig().table.currentRowConfig, props.currentRowConfig)
})
const computeRowDragOpts = computed(() => {
return Object.assign({}, getConfig().table.rowDragConfig, props.rowDragConfig)
})
const computeColumnDragOpts = computed(() => {
return Object.assign({}, getConfig().table.columnDragConfig, props.columnDragConfig)
})
const computeResizeOpts = computed(() => {
return Object.assign({}, getConfig().table.resizeConfig, props.resizeConfig) as VxeTablePropTypes.ResizeOpts
})
const computeResizableOpts = computed(() => {
return Object.assign({}, getConfig().table.resizableConfig, props.resizableConfig) as VxeTablePropTypes.ResizableOpts
})
const computeSeqOpts = computed(() => {
return Object.assign({ startIndex: 0 }, getConfig().table.seqConfig, props.seqConfig) as VxeTablePropTypes.SeqOpts
})
const computeRadioOpts = computed(() => {
return Object.assign({}, getConfig().table.radioConfig, props.radioConfig) as VxeTablePropTypes.RadioOpts
})
const computeCheckboxOpts = computed(() => {
return Object.assign({}, getConfig().table.checkboxConfig, props.checkboxConfig) as VxeTablePropTypes.CheckboxOpts
})
const computeTooltipOpts = computed(() => {
return Object.assign({}, getConfig().tooltip, getConfig().table.tooltipConfig, props.tooltipConfig)
})
const computeHeaderTooltipOpts = computed(() => {
return Object.assign({}, getConfig().tooltip, getConfig().table.headerTooltipConfig, props.headerTooltipConfig)
})
const computeFooterTooltipOpts = computed(() => {
return Object.assign({}, getConfig().tooltip, getConfig().table.footerTooltipConfig, props.footerTooltipConfig)
})
const computeTableTipConfig = computed(() => {
const { tooltipStore } = reactData
const tooltipOpts = computeTooltipOpts.value
return Object.assign({}, tooltipOpts, tooltipStore.currOpts)
})
const computeValidTipConfig = computed(() => {
const tooltipOpts = computeTooltipOpts.value
return Object.assign({}, tooltipOpts)
})
const computeEditOpts = computed(() => {
return Object.assign({}, getConfig().table.editConfig, props.editConfig) as VxeTablePropTypes.EditOpts
})
const computeEditDirtyOpts = computed(() => {
return Object.assign({}, getConfig().table.editDirtyConfig, props.editDirtyConfig)
})
const computeSortOpts = computed(() => {
return Object.assign({ orders: ['asc', 'desc', null] }, getConfig().table.sortConfig, props.sortConfig) as VxeTablePropTypes.SortOpts
})
const computeFilterOpts = computed(() => {
return Object.assign({}, getConfig().table.filterConfig, props.filterConfig) as VxeTablePropTypes.FilterOpts
})
const computeFloatingFilterOpts = computed(() => {
return Object.assign({}, getConfig().table.floatingFilterConfig, props.floatingFilterConfig)
})
const computeMouseOpts = computed(() => {
return Object.assign({}, getConfig().table.mouseConfig, props.mouseConfig) as VxeTablePropTypes.MouseOpts
})
const computeAreaOpts = computed(() => {
return Object.assign({}, getConfig().table.areaConfig, props.areaConfig) as VxeTablePropTypes.AreaOpts
})
const computeKeyboardOpts = computed(() => {
return Object.assign({}, getConfig().table.keyboardConfig, props.keyboardConfig) as VxeTablePropTypes.KeyboardOpts
})
const computeClipOpts = computed(() => {
return Object.assign({}, getConfig().table.clipConfig, props.clipConfig)
})
const computeUndoRedoHistoryOpts = computed(() => {
return Object.assign({}, getConfig().table.undoRedoHistoryConfig, props.undoRedoHistoryConfig)
})
const computeFNROpts = computed(() => {
const fnrOpts = computeFnrOpts.value
return fnrOpts
})
const computeFnrOpts = computed(() => {
return Object.assign({}, getConfig().table.fnrConfig, props.fnrConfig) as VxeTablePropTypes.FNROpts
})
const computeMenuOpts = computed(() => {
return Object.assign({}, getConfig().table.menuConfig, props.menuConfig) as VxeTablePropTypes.MenuOpts
})
const computeLeftFixedWidth = computed(() => {
const { columnStore } = reactData
const { leftList } = columnStore
let leftWidth = 0
for (let i = 0; i < leftList.length; i++) {
const column = leftList[i]
leftWidth += column.renderWidth
}
return leftWidth
})
const computeRightFixedWidth = computed(() => {
const { columnStore } = reactData
const { rightList } = columnStore
let leftWidth = 0
for (let i = 0; i < rightList.length; i++) {
const column = rightList[i]
leftWidth += column.renderWidth
}
return leftWidth
})
// 合并列是否超越冻结列
const computeBodyMergeCoverFixed = computed(() => {
const { columnStore, mergeBodyFlag } = reactData
const { mergeBodyList, visibleColumn } = internalData
const { leftList, rightList } = columnStore
const rscIndex = visibleColumn.length - rightList.length
if (mergeBodyFlag && (leftList.length || rightList.length)) {
const lecIndex = leftList.length
for (let i = 0; i < mergeBodyList.length; i++) {
const { col, colspan } = mergeBodyList[i]
if (col < lecIndex && col + colspan > lecIndex) {
return true
} else if (col < rscIndex && col + colspan > rscIndex) {
return true
}
}
}
return false
})
const computeIsHeaderRenderOptimize = computed(() => {
const { spanMethod, footerSpanMethod, showHeaderOverflow: allColumnHeaderOverflow } = props
const { isGroup, scrollXLoad } = reactData
let isOptimizeMode = false
if (isGroup) {
// 分组表头
} else {
// 如果是使用优化模式
if (scrollXLoad && allColumnHeaderOverflow) {
if (spanMethod || footerSpanMethod) {
// 如果不支持优化模式
} else {
isOptimizeMode = true
}
}
}
return isOptimizeMode
})
const computeIsBodyRenderOptimize = computed(() => {
const { spanMethod, footerSpanMethod } = props
const { scrollXLoad, scrollYLoad, isAllOverflow, expandColumn } = reactData
const bodyMergeCoverFixed = computeBodyMergeCoverFixed.value
const expandOpts = computeExpandOpts.value
let isOptimizeMode = false
// 如果是使用优化模式
if (scrollXLoad || scrollYLoad || isAllOverflow) {
// 如果是展开行,内联模式,不支持优化
// 如果是方法合并,不支持优化
// 如果固定列且配置式合并,不支持优化
if ((expandColumn && expandOpts.mode !== 'fixed') || bodyMergeCoverFixed || spanMethod || footerSpanMethod) {
// 如果不支持优化模式
} else {
isOptimizeMode = true
}
}
return isOptimizeMode
})
const computeIsFooterRenderOptimize = computed(() => {
const { spanMethod, footerSpanMethod, showFooterOverflow: allColumnFooterOverflow } = props
const { scrollXLoad } = reactData
let isOptimizeMode = false
// 如果是使用优化模式
if (scrollXLoad && allColumnFooterOverflow) {
if (spanMethod || footerSpanMethod) {
// 如果不支持优化模式
} else {
isOptimizeMode = true
}
}
return isOptimizeMode
})
const computeHeaderMenu = computed(() => {
const menuOpts = computeMenuOpts.value
const headerOpts = menuOpts.header
return headerOpts && headerOpts.options ? headerOpts.options : []
})
const computeBodyMenu = computed(() => {
const menuOpts = computeMenuOpts.value
const bodyOpts = menuOpts.body
return bodyOpts && bodyOpts.options ? bodyOpts.options : []
})
const computeFooterMenu = computed(() => {
const menuOpts = computeMenuOpts.value
const footerOpts = menuOpts.footer
return footerOpts && footerOpts.options ? footerOpts.options : []
})
const computeIsMenu = computed(() => {
const isContentMenu = computeIsContentMenu.value
return isContentMenu
})
const computeIsContentMenu = computed(() => {
const menuOpts = computeMenuOpts.value
const headerMenu = computeHeaderMenu.value
const bodyMenu = computeBodyMenu.value
const footerMenu = computeFooterMenu.value
return !!(props.menuConfig && isEnableConf(menuOpts) && (headerMenu.length || bodyMenu.length || footerMenu.length))
})
const computeMenuList = computed(() => {
const { ctxMenuStore } = reactData
const rest: any[] = []
ctxMenuStore.list.forEach((list) => {
list.forEach((item) => {
rest.push(item)
})
})
return rest
})
const computeExportOpts = computed(() => {
return Object.assign({}, getConfig().table.exportConfig, props.exportConfig) as VxeTablePropTypes.ExportOpts
})
const computeImportOpts = computed(() => {
return Object.assign({}, getConfig().table.importConfig, props.importConfig) as VxeTablePropTypes.ImportOpts
})
const computePrintOpts = computed(() => {
return Object.assign({}, getConfig().table.printConfig, props.printConfig) as VxeTablePropTypes.PrintOpts
})
const computeExpandOpts = computed(() => {
return Object.assign({}, getConfig().table.expandConfig, props.expandConfig) as VxeTablePropTypes.ExpandOpts
})
const computeTreeOpts = computed(() => {
return Object.assign({}, getConfig().table.treeConfig, props.treeConfig) as VxeTablePropTypes.TreeOpts
})
const computeEmptyOpts = computed(() => {
return Object.assign({}, getConfig().table.emptyRender, props.emptyRender) as VxeTablePropTypes.EmptyOpts
})
const computeLoadingOpts = computed(() => {
return Object.assign({}, getConfig().table.loadingConfig, props.loadingConfig) as VxeTablePropTypes.LoadingOpts
})
const computeCellOffsetWidth = computed(() => {
return props.border ? Math.max(2, Math.ceil(reactData.scrollbarWidth / reactData.tableColumn.length)) : 1
})
const computeCustomOpts = computed(() => {
return Object.assign({}, getConfig().table.customConfig, props.customConfig)
})
const computeCustomSimpleMode = computed(() => {
const { minHeight, height } = props
const customOpts = computeCustomOpts.value
const { mode, popupOptions, placement } = customOpts
if (!placement || placement === 'top-left' || placement === 'top-right') {
if (!(mode === 'modal' || mode === 'drawer')) {
const { mode } = popupOptions || {}
if (!mode || mode === 'auto') {
if (height || minHeight) {
return 'inside'
}
return 'outside'
}
if (mode) {
return mode
}
}
}
return ''
})
const computeTableStyle = computed(() => {
const scrollbarOpts = computeScrollbarOpts.value
const { width, height } = scrollbarOpts
const tStys: VxeComponentStyleType = {}
if (width) {
tStys['--vxe-ui-table-view-scrollbar-width'] = toCssUnit(width)
}
if (height) {
tStys['--vxe-ui-table-view-scrollbar-height'] = toCssUnit(height)
}
return tStys
})
const computeTableRowExpandedList = computed(() => {
const { tableData, rowExpandedFlag, expandColumn, rowGroupExpandedFlag, treeExpandedFlag } = reactData
const { visibleDataRowIdData, rowExpandedMaps } = internalData
const expandList: any[] = []
if (tableData.length && expandColumn && rowExpandedFlag && rowGroupExpandedFlag && treeExpandedFlag) {
XEUtils.each(rowExpandedMaps, (row, rowid) => {
if (visibleDataRowIdData[rowid]) {
expandList.push(row)
}
})
}
return expandList
})
const computeAutoWidthColumnList = computed(() => {
const { visibleColumn } = internalData
const { tableColumn } = reactData
return tableColumn.length || visibleColumn.length ? visibleColumn.filter(column => column.width === 'auto' || column.minWidth === 'auto') : []
})
const computeFixedColumnSize = computed(() => {
const { tableColumn } = reactData
const { collectColumn } = internalData
let fixedSize = 0
// 只判断第一层
if (tableColumn.length && collectColumn.length) {
collectColumn.forEach((column) => {
if (column.renderFixed) {
fixedSize++
}
})
}
return fixedSize
})
const computeIsMaxFixedColumn = computed(() => {
const fixedColumnSize = computeFixedColumnSize.value
const columnOpts = computeColumnOpts.value
const { maxFixedSize } = columnOpts
if (maxFixedSize) {
return fixedColumnSize >= maxFixedSize
}
return false
})
const computeKeepFields = computed(() => {
const { tableFullColumn } = internalData
const { updateColFlag } = reactData
const editDirtyOpts = computeEditDirtyOpts.value
const { extraFields, includeFields, excludeFields } = editDirtyOpts
const kpFields: string[] = []
if (updateColFlag) {
// 优先级 excludeFields > (includeFields || extraFields)
const excludeFdMaps: Record<string, number> = {}
if (excludeFields && excludeFields.length) {
excludeFields.forEach(field => {
excludeFdMaps[field] = 1
})
}
if (includeFields && includeFields.length) {
return excludeFields && excludeFields.length ? includeFields.filter(field => !excludeFdMaps[field]) : includeFields
}
const addFdMaps: Record<string, number> = {}
if (extraFields && extraFields.length) {
extraFields.forEach(field => {
if (!addFdMaps[field]) {
addFdMaps[field] = 1
kpFields.push(field)
}
})
}
for (let i = 0; i < tableFullColumn.length; i++) {
const column = tableFullColumn[i]
const { field, type, editRender, cellRender } = column
if (field && !type && (editRender || cellRender) && !addFdMaps[field] && !excludeFdMaps[field]) {
addFdMaps[field] = 1
kpFields.push(field)
}
}
}
return kpFields
})
const computeTableBorder = computed(() => {
const { border } = props
if (border === true) {
return 'full'
}
if (border) {
return border
}
return 'default'
})
const computeIsAllCheckboxDisabled = computed(() => {
const { treeConfig } = props
const { tableData } = reactData
const { tableFullData } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { strict, checkMethod } = checkboxOpts
if (strict) {
if (tableData.length || tableFullData.length) {
if (checkMethod) {
if (treeConfig) {
// 暂时不支持树形结构
}
// 如果所有行都被禁用
return tableFullData.every((row) => !checkMethod({ $table: $xeTable, row }))
}
return false
}
return true
}
return false
})
const computeVirtualScrollBars = computed(() => {
const { overflowX, scrollXLoad, overflowY, scrollYLoad } = reactData
return {
x: overflowX && scrollXLoad,
y: overflowY && scrollYLoad
}
})
const computeRowGroupFields = computed(() => {
const rowGroupOpts = computeRowGroupOpts.value
return rowGroupOpts.groupFields
})
const computeRowGroupColumns = computed(() => {
const { rowGroupList } = reactData
const { fullColumnFieldData } = internalData
const rgColumns: VxeTableDefines.ColumnInfo[] = []
rowGroupList.forEach(aggConf => {
const colRest = fullColumnFieldData[aggConf.field]
if (colRest) {
rgColumns.push(colRest.column)
}
})
return rgColumns
})
const computeAggFuncColumns = computed(() => {
const { rowGroupList, tableColumn } = reactData
if (rowGroupList.length) {
return tableColumn.filter(column => column.aggFunc)
}
return []
})
const combineTabsResizeFlag = computed(() => {
return $xeTabs ? $xeTabs.reactData.resizeFlag : null
})
const combineModalResizeFlag = computed(() => {
return $xeModal ? $xeModal.reactData.resizeFlag : null
})
const combineSplitterResizeFlag = computed(() => {
return $xeSplitter ? $xeSplitter.reactData.resizeFlag : null
})
const refMaps: VxeTablePrivateRef = {
refElem,
refTooltip,
refValidTooltip,
refTableFilter,
refTableCustom,
refTableMenu,
refTableHeader,
refTableBody,
refTableFooter,
refTableLeftHeader,
refTableLeftBody,
refTableLeftFooter,
refTableRightHeader,
refTableRightBody,
refTableRightFooter,
refLeftContainer,
refRightContainer,
refColResizeBar,
refRowResizeBar,
refCustomContainerElem,
refScrollXVirtualElem,
refScrollYVirtualElem,
refScrollXHandleElem,
refScrollYHandleElem,
refScrollXSpaceElem,
refScrollYSpaceElem
}
const computeMaps: VxeTablePrivateComputed = {
computeSize,
computeTableId,
computeValidOpts,
computeRowField,
computeVirtualXOpts,
computeVirtualYOpts,
computeScrollbarOpts,
computeScrollbarXOpts,
computeScrollbarYOpts,
computeScrollbarXToTop,
computeScrollbarYToLeft,
computeColumnOpts,
computeCurrentColumnOpts,
computeScrollXThreshold,
computeScrollYThreshold,
computeRowHeightMaps,
computeDefaultRowHeight,
computeCellOpts,
computeHeaderCellOpts,
computeFooterCellOpts,
computeRowOpts,
computeAggregateOpts,
computeAggregateAccuracyOpts,
computeRowGroupOpts,
computeCurrentRowOpts,
computeRowDragOpts,
computeColumnDragOpts,
computeResizeOpts,
computeResizableOpts,
computeSeqOpts,
computeRadioOpts,
computeCheckboxOpts,
computeTooltipOpts,
computeHeaderTooltipOpts,
computeFooterTooltipOpts,
computeEditOpts,
computeEditDirtyOpts,
computeSortOpts,
computeFilterOpts,
computeFloatingFilterOpts,
computeMouseOpts,
computeAreaOpts,
computeKeyboardOpts,
computeClipOpts,
computeFnrOpts,
computeHeaderMenu,
computeBodyMenu,
computeFooterMenu,
computeIsMenu,
computeIsContentMenu,
computeMenuList,
computeMenuOpts,
computeExportOpts,
computeImportOpts,
computePrintOpts,
computeExpandOpts,
computeTreeOpts,
computeEmptyOpts,
computeLoadingOpts,
computeCellOffsetWidth,
computeCustomOpts,
computeCustomSimpleMode,
computeLeftFixedWidth,
computeRightFixedWidth,
computeBodyMergeCoverFixed,
computeFixedColumnSize,
computeIsMaxFixedColumn,
computeKeepFields,
computeIsAllCheckboxDisabled,
computeIsHeaderRenderOptimize,
computeIsBodyRenderOptimize,
computeIsFooterRenderOptimize,
computeVirtualScrollBars,
computeRowGroupFields,
computeRowGroupColumns,
computeAggFuncColumns,
computeUndoRedoHistoryOpts,
computeFNROpts,
computeSXOpts,
computeSYOpts
}
const $xeTable = {
xID,
props: props as VxeTableProps,
context,
reactData,
internalData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps,
xeGrid: $xeGrid,
xeGantt: $xeGantt,
// 已废弃
xegrid: $xeGrid
} as unknown as VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods
const eqCellValue = (newRow: any, oldRow: any, field: string) => {
const val1 = XEUtils.get(newRow, field)
const val2 = XEUtils.get(oldRow, field)
let result = false
if (eqEmptyValue(val1) && eqEmptyValue(val2)) {
result = true
} else if (XEUtils.isString(val1) || XEUtils.isNumber(val1)) {
result = ('' + val1) === ('' + val2)
} else {
result = XEUtils.isEqual(val1, val2)
}
return { newValue: val1, oldValue: val2, result }
}
const handleKeyField = () => {
const keyField = computeRowField.value
internalData.currKeyField = keyField
internalData.isCurrDeepKey = hasDeepKey(keyField)
}
const getNextSortOrder = (column: VxeTableDefines.ColumnInfo) => {
const sortOpts = computeSortOpts.value
const { orders = [] } = sortOpts
const currOrder = column.order || null
const oIndex = orders.indexOf(currOrder) + 1
return orders[oIndex < orders.length ? oIndex : 0]
}
const getCustomStorageMap = (id?: string) => {
const version = getConfig().version
const rest = XEUtils.toStringJSON(localStorage.getItem(customStorageKey) || '')
const maps = rest && rest._v === version ? rest : { _v: version }
return (id ? maps[id] : maps) || {}
}
const setCustomStorageMap = (id: string, data: any) => {
const version = getConfig().version
const maps = getCustomStorageMap()
maps[id] = data || undefined
maps._v = version
localStorage.setItem(customStorageKey, XEUtils.toJSONString(maps))
}
const getRecoverRowMaps = (keyMaps: Record<string, any>) => {
const { fullAllDataRowIdData } = internalData
const restKeys: Record<string, any> = {}
XEUtils.each(keyMaps, (row, rowid) => {
if (fullAllDataRowIdData[rowid]) {
restKeys[rowid] = row
}
})
return restKeys
}
const handleReserveRow = (reserveRowMap: any) => {
const { fullDataRowIdData } = internalData
const reserveList: any[] = []
XEUtils.each(reserveRowMap, (item, rowid) => {
if (fullDataRowIdData[rowid] && $xeTable.findRowIndexOf(reserveList, fullDataRowIdData[rowid].row) === -1) {
reserveList.push(fullDataRowIdData[rowid].row)
}
})
return reserveList
}
const handleVirtualXVisible = () => {
const { isScrollXBig, scrollXWidth } = reactData
const { elemStore, visibleColumn, fullColumnIdData } = internalData
const leftFixedWidth = computeLeftFixedWidth.value
const rightFixedWidth = computeRightFixedWidth.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
if (bodyScrollElem) {
const clientWidth = bodyScrollElem.clientWidth
let scrollLeft = bodyScrollElem.scrollLeft
if (isScrollXBig) {
scrollLeft = Math.ceil((scrollXWidth - clientWidth) * Math.min(1, (scrollLeft / (maxXWidth - clientWidth))))
}
const startLeft = scrollLeft + leftFixedWidth
const endLeft = scrollLeft + clientWidth - rightFixedWidth
let leftIndex = 0
let rightIndex = visibleColumn.length
while (leftIndex < rightIndex) {
const cIndex = Math.floor((leftIndex + rightIndex) / 2)
const column = visibleColumn[cIndex]
const colid = column.id
const colRest = fullColumnIdData[colid] || {}
if (colRest.oLeft <= startLeft) {
leftIndex = cIndex + 1
} else {
rightIndex = cIndex
}
}
let visibleSize = 0
const toVisibleIndex = leftIndex === visibleColumn.length ? leftIndex : Math.max(0, leftIndex < visibleColumn.length ? leftIndex - 2 : 0)
for (let cIndex = toVisibleIndex, cLen = visibleColumn.length; cIndex < cLen; cIndex++) {
const column = visibleColumn[cIndex]
const colid = column.id
const colRest = fullColumnIdData[colid] || {}
visibleSize++
if (colRest.oLeft > endLeft || visibleSize >= 60) {
break
}
}
return { toVisibleIndex: Math.max(0, toVisibleIndex), visibleSize: Math.max(1, visibleSize) }
}
return { toVisibleIndex: 0, visibleSize: 6 }
}
const calcVarRowHeightConfig = (sizeKey: 'default' | 'medium' | 'small' | 'mini', sizeEl: Element) => {
const { rowHeightStore } = reactData
if (sizeEl && sizeEl.clientHeight) {
rowHeightStore[sizeKey] = sizeEl.clientHeight
}
}
const computeRowHeight = () => {
const { isAllOverflow } = reactData
const tableHeader = refTableHeader.value
const tableBody = refTableBody.value
const tableBodyElem = tableBody ? tableBody.$el as HTMLDivElement : null
const defaultRowHeight = computeDefaultRowHeight.value
let rowHeight = 0
if (isAllOverflow) {
if (tableBodyElem) {
const tableHeaderElem = tableHeader ? tableHeader.$el as HTMLDivElement : null
let firstTrElem
firstTrElem = tableBodyElem.querySelector('tr')
if (!firstTrElem && tableHeaderElem) {
firstTrElem = tableHeaderElem.querySelector('tr')
}
if (firstTrElem) {
rowHeight = firstTrElem.clientHeight
}
}
if (!rowHeight) {
rowHeight = defaultRowHeight
}
} else {
rowHeight = defaultRowHeight
}
// 最低支持 18px 行高
return Math.max(18, rowHeight)
}
const handleVirtualYVisible = () => {
const { isAllOverflow, expandColumn, isScrollYBig, scrollYHeight } = reactData
const { elemStore, isResizeCellHeight, afterFullData, fullAllDataRowIdData } = internalData
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
if (bodyScrollElem) {
const clientHeight = bodyScrollElem.clientHeight
let scrollTop = bodyScrollElem.scrollTop
if (isScrollYBig) {
scrollTop = Math.ceil((scrollYHeight - clientHeight) * Math.min(1, (scrollTop / (maxYHeight - clientHeight))))
}
const startTop = scrollTop
const endTop = scrollTop + clientHeight
let toVisibleIndex = -1
let visibleSize = 0
const isCustomCellHeight = isResizeCellHeight || cellOpts.height || rowOpts.height
if (!isCustomCellHeight && !expandColumn && isAllOverflow) {
toVisibleIndex = Math.floor(startTop / defaultRowHeight) - 1
visibleSize = Math.ceil(clientHeight / defaultRowHeight) + 1
} else {
const { handleGetRowId } = createHandleGetRowId($xeTable)
let leftIndex = 0
let rightIndex = afterFullData.length
while (leftIndex < rightIndex) {
const rIndex = Math.floor((leftIndex + rightIndex) / 2)
const row = afterFullData[rIndex]
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid] || {}
if (rowRest.oTop <= startTop) {
leftIndex = rIndex + 1
} else {
rightIndex = rIndex
}
}
toVisibleIndex = leftIndex === afterFullData.length ? leftIndex : Math.max(0, leftIndex < afterFullData.length ? leftIndex - 2 : 0)
for (let rIndex = toVisibleIndex, rLen = afterFullData.length; rIndex < rLen; rIndex++) {
const row = afterFullData[rIndex]
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid] || {}
visibleSize++
if (rowRest.oTop > endTop || visibleSize >= 100) {
break
}
}
}
return { toVisibleIndex: Math.max(0, toVisibleIndex), visibleSize: Math.max(6, visibleSize) }
}
return { toVisibleIndex: 0, visibleSize: 6 }
}
const calculateMergerOffsetIndex = (list: any[], mergeMaps: Record<string, VxeTableDefines.MergeCacheRow | VxeTableDefines.MergeCacheCol>, offsetItem: VxeTableDefines.MergeCacheRow | VxeTableDefines.MergeCacheCol, type: 'row' | 'col') => {
const mKey = `${offsetItem.startIndex}:${offsetItem.endIndex}`
const mObj = mergeMaps[mKey]
// 缓存
if (mObj) {
offsetItem.startIndex = mObj.startIndex
offsetItem.endIndex = mObj.endIndex
} else {
for (let mcIndex = 0, len = list.length; mcIndex < len; mcIndex++) {
const mergeItem = list[mcIndex]
const { startIndex, endIndex } = offsetItem
const mergeStartIndex = mergeItem[type]
const mergeSpanNumber = mergeItem[type + 'span']
const mergeEndIndex = mergeStartIndex + mergeSpanNumber
if (mergeStartIndex < startIndex && startIndex < mergeEndIndex) {
offsetItem.startIndex = mergeStartIndex
}
if (mergeStartIndex < endIndex && endIndex < mergeEndIndex) {
offsetItem.endIndex = mergeEndIndex
}
if (offsetItem.startIndex !== startIndex || offsetItem.endIndex !== endIndex) {
mcIndex = -1
}
}
mergeMaps[mKey] = offsetItem
}
}
function buildMergeData (mergeConfigs: VxeTableDefines.MergeItem[]) {
const mergeMaps: Record<string, VxeTableDefines.MergeCacheItem> = {}
const mergeRowMaps: Record<string, VxeTableDefines.MergeCacheRow> = {}
const mergeColMaps: Record<string, VxeTableDefines.MergeCacheCol> = {}
if (mergeConfigs && mergeConfigs.length) {
for (let mIndex = 0; mIndex < mergeConfigs.length; mIndex++) {
const { row: _rowIndex, col: _columnIndex, rowspan: mergeRowspan, colspan: mergeColspan } = mergeConfigs[mIndex]
for (let i = 0; i < mergeRowspan; i++) {
const currRIndex = _rowIndex + i
for (let j = 0; j < mergeColspan; j++) {
const currCIndex = _columnIndex + j
mergeMaps[`${currRIndex}:${currCIndex}`] = !i && !j
? {
rowspan: mergeRowspan,
colspan: mergeColspan
}
: {
rowspan: 0,
colspan: 0
}
}
}
}
}
return { mergeMaps, mergeRowMaps, mergeColMaps }
}
const handleUpdateMergeBodyCells = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
internalData.mergeBodyList = []
internalData.mergeBodyMaps = {}
internalData.mergeBodyCellMaps = {}
internalData.mergeBodyRowMaps = {}
internalData.mergeBodyColMaps = {}
$xeTable.setMergeCells(merges)
}
const handleBodyMerge = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { fullAllDataRowIdData, fullColumnIdData, visibleColumn, afterFullData, mergeBodyList, mergeBodyMaps } = internalData
if (merges) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
let { row: margeRow, col: margeCol, rowspan, colspan } = item
let mergeRowIndex = -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeRow)) {
mergeRowIndex = margeRow
} else {
const rowid = margeRow ? handleGetRowId(margeRow) : null
const rowRest = rowid ? fullAllDataRowIdData[rowid] : null
if (rowRest) {
mergeRowIndex = rowRest._index
}
}
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
if (mergeRowIndex > -1 && mergeColumnIndex > -1 && (rowspan || colspan)) {
rowspan = XEUtils.toNumber(rowspan) || 1
colspan = XEUtils.toNumber(colspan) || 1
if (rowspan > 1 || colspan > 1) {
const row = afterFullData[mergeRowIndex]
const column = visibleColumn[mergeColumnIndex]
let mergeItem = mergeBodyMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
if (mergeItem) {
mergeItem.rowspan = rowspan
mergeItem.colspan = colspan
mergeItem._rowspan = rowspan
mergeItem._colspan = colspan
} else {
mergeItem = {
row: mergeRowIndex,
col: mergeColumnIndex,
rowspan,
colspan,
_row: row,
_col: column,
_rowspan: rowspan,
_colspan: colspan
}
mergeBodyMaps[`${mergeRowIndex}:${mergeColumnIndex}`] = mergeItem
mergeBodyList.push(mergeItem)
}
}
}
})
}
}
const removeBodyMerges = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { mergeBodyList, fullColumnIdData, fullAllDataRowIdData, mergeBodyMaps } = internalData
const rest: VxeTableDefines.MergeItem[] = []
if (merges) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
const { row: margeRow, col: margeCol } = item
let mergeRowIndex = -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeRow)) {
mergeRowIndex = margeRow
} else {
const rowid = margeRow ? handleGetRowId(margeRow) : null
const rowRest = rowid ? fullAllDataRowIdData[rowid] : null
if (rowRest) {
mergeRowIndex = rowRest._index
}
}
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
const mcIndex = XEUtils.findIndexOf(mergeBodyList, item => item.row === mergeRowIndex && item.col === mergeColumnIndex)
if (mcIndex > -1) {
const rItems = mergeBodyList.splice(mcIndex, 1)
const item = rItems[0]
if (item) {
rest.push(rItems[0])
if (mergeBodyMaps[`${mergeRowIndex}:${mergeColumnIndex}`]) {
delete mergeBodyMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
}
}
}
})
}
return rest
}
const handleUpdateMergeHeaderCells = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
internalData.mergeHeaderList = []
internalData.mergeHeaderMaps = {}
internalData.mergeHeaderCellMaps = {}
internalData.mergeHeaderRowMaps = {}
internalData.mergeBodyColMaps = {}
$xeTable.setMergeHeaderCells(merges)
}
const handleHeaderMerge = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { showCustomHeader } = props
const { footerTableData } = reactData
const { mergeHeaderList, mergeHeaderMaps, fullColumnIdData } = internalData
if (merges) {
const { visibleColumn } = internalData
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
let { row: margeRow, col: margeCol, rowspan, colspan } = item
const mergeRowIndex = XEUtils.isNumber(margeRow) ? margeRow : -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
if (mergeRowIndex > -1 && mergeColumnIndex > -1 && (rowspan || colspan)) {
rowspan = XEUtils.toNumber(rowspan) || 1
colspan = XEUtils.toNumber(colspan) || 1
if (!showCustomHeader && rowspan > 1) {
errLog('vxe.error.notSupportProp', ['[table] show-custom-header=false', `rowspan=${rowspan}`, 'rowspan=1'])
return
}
if (rowspan > 1 || colspan > 1) {
const row = footerTableData[mergeRowIndex]
const column = visibleColumn[mergeColumnIndex]
let mergeItem = mergeHeaderMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
if (mergeItem) {
mergeItem.rowspan = rowspan
mergeItem.colspan = colspan
mergeItem._rowspan = rowspan
mergeItem._colspan = colspan
} else {
mergeItem = {
row: mergeRowIndex,
col: mergeColumnIndex,
rowspan,
colspan,
_row: row,
_col: column,
_rowspan: rowspan,
_colspan: colspan
}
mergeHeaderMaps[`${mergeRowIndex}:${mergeColumnIndex}`] = mergeItem
mergeHeaderList.push(mergeItem)
}
}
}
})
}
}
const removeHeaderMerges = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { mergeHeaderList, fullColumnIdData, mergeHeaderMaps } = internalData
const rest: VxeTableDefines.MergeItem[] = []
if (merges) {
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
const { row: margeRow, col: margeCol } = item
const mergeRowIndex = XEUtils.isNumber(margeRow) ? margeRow : -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
const mcIndex = XEUtils.findIndexOf(mergeHeaderList, item => item.row === mergeRowIndex && item.col === mergeColumnIndex)
if (mcIndex > -1) {
const rItems = mergeHeaderList.splice(mcIndex, 1)
const item = rItems[0]
if (item) {
rest.push(item)
if (mergeHeaderMaps[`${mergeRowIndex}:${mergeColumnIndex}`]) {
delete mergeHeaderMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
}
}
}
})
}
return rest
}
const handleUpdateMergeFooterCells = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
internalData.mergeFooterList = []
internalData.mergeFooterMaps = {}
internalData.mergeFooterCellMaps = {}
internalData.mergeFooterRowMaps = {}
internalData.mergeFooterColMaps = {}
$xeTable.setMergeFooterCells(merges)
}
const handleFooterMerge = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { footerTableData } = reactData
const { mergeFooterList, mergeFooterMaps, fullColumnIdData } = internalData
if (merges) {
const { visibleColumn } = internalData
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
let { row: margeRow, col: margeCol, rowspan, colspan } = item
const mergeRowIndex = XEUtils.isNumber(margeRow) ? margeRow : -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
if (mergeRowIndex > -1 && mergeColumnIndex > -1 && (rowspan || colspan)) {
rowspan = XEUtils.toNumber(rowspan) || 1
colspan = XEUtils.toNumber(colspan) || 1
if (rowspan > 1 || colspan > 1) {
const row = footerTableData[mergeRowIndex]
const column = visibleColumn[mergeColumnIndex]
let mergeItem = mergeFooterMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
if (mergeItem) {
mergeItem.rowspan = rowspan
mergeItem.colspan = colspan
mergeItem._rowspan = rowspan
mergeItem._colspan = colspan
} else {
mergeItem = {
row: mergeRowIndex,
col: mergeColumnIndex,
rowspan,
colspan,
_row: row,
_col: column,
_rowspan: rowspan,
_colspan: colspan
}
mergeFooterMaps[`${mergeRowIndex}:${mergeColumnIndex}`] = mergeItem
mergeFooterList.push(mergeItem)
}
}
}
})
}
}
const removeFooterMerges = (merges: VxeTableDefines.MergeOptions | VxeTableDefines.MergeOptions[]) => {
const { mergeFooterList, fullColumnIdData, mergeFooterMaps } = internalData
const rest: VxeTableDefines.MergeItem[] = []
if (merges) {
if (!XEUtils.isArray(merges)) {
merges = [merges]
}
merges.forEach((item) => {
const { row: margeRow, col: margeCol } = item
const mergeRowIndex = XEUtils.isNumber(margeRow) ? margeRow : -1
let mergeColumnIndex = -1
if (XEUtils.isNumber(margeCol)) {
mergeColumnIndex = margeCol
} else {
const colid = margeCol ? margeCol.id : null
const colRest = colid ? fullColumnIdData[colid] : null
if (colRest) {
mergeColumnIndex = colRest._index
}
}
const mcIndex = XEUtils.findIndexOf(mergeFooterList, item => item.row === mergeRowIndex && item.col === mergeColumnIndex)
if (mcIndex > -1) {
const rItems = mergeFooterList.splice(mcIndex, 1)
const item = rItems[0]
if (item) {
rest.push(item)
if (mergeFooterMaps[`${mergeRowIndex}:${mergeColumnIndex}`]) {
delete mergeFooterMaps[`${mergeRowIndex}:${mergeColumnIndex}`]
}
}
}
})
}
return rest
}
const handleSortEvent = (evnt: Event | null, sortConfs: VxeTableDefines.SortConfs | VxeTableDefines.SortConfs[], isUpdate?: boolean) => {
const { tableFullColumn } = internalData
const sortOpts = computeSortOpts.value
const { multiple, remote, orders } = sortOpts
if (!XEUtils.isArray(sortConfs)) {
sortConfs = [sortConfs]
}
if (sortConfs && sortConfs.length) {
const orderActiveMaps: Record<string, VxeTableDefines.ColumnInfo> = {}
if (!multiple) {
sortConfs = [sortConfs[0]]
tableFullColumn.forEach((column) => {
if (column.order) {
orderActiveMaps[column.id] = column
}
})
}
const sortColMpps: Record<string, VxeTableDefines.ColumnInfo> = {}
let firstColumn: any = null
sortConfs.forEach((confs: any, index: number) => {
let { field, order } = confs
let column = field
if (XEUtils.isString(field)) {
column = $xeTable.getColumnByField(field)
}
if (!firstColumn) {
firstColumn = column
}
if (column && column.sortable) {
if (orders && orders.indexOf(order) === -1) {
order = getNextSortOrder(column)
}
if (column.order !== order) {
column.order = order
}
column.sortTime = Date.now() + index
sortColMpps[column.id] = column
}
})
if (!multiple) {
XEUtils.each(orderActiveMaps, (oaCol: VxeTableDefines.ColumnInfo, oaId) => {
if (!sortColMpps[oaId]) {
oaCol.order = null
}
})
}
if (isUpdate) {
if (!remote) {
$xeTable.handleTableData(true)
}
}
if (evnt) {
$xeTable.handleColumnSortEvent(evnt, firstColumn)
}
return nextTick().then(() => {
updateRowOffsetTop()
$xeTable.updateCellAreas()
return updateStyle()
})
}
return nextTick()
}
const clearAllSort = () => {
const { tableFullColumn } = internalData
tableFullColumn.forEach((column) => {
column.order = null
})
}
const calcTableHeight = (key: 'height' | 'minHeight' | 'maxHeight') => {
const { editConfig, editRules } = props
const { parentHeight, tableColumn } = reactData
let val = props[key]
if (key === 'minHeight') {
const defMinHeight = getConfig().table.minHeight
if (XEUtils.eqNull(val)) {
if (eqEmptyValue(defMinHeight)) {
if (!tableColumn.length) {
// 如果全部列被隐藏
val = 40
} else if (editRules && isEnableConf(editConfig)) {
// 编辑模式默认最小高度
val = 144
}
} else {
val = defMinHeight
}
}
}
let num = 0
if (val) {
if (val === '100%' || val === 'auto') {
num = parentHeight
} else {
const excludeHeight = $xeTable.getExcludeHeight()
if (isScale(val)) {
num = Math.floor((XEUtils.toInteger(val) || 1) / 100 * parentHeight)
} else {
num = XEUtils.toNumber(val)
}
num = Math.max(40, num - excludeHeight)
}
}
return num
}
const handleCustomRestore = (storeData: VxeTableDefines.CustomStoreData) => {
const { aggregateConfig, rowGroupConfig } = props
const { collectColumn } = internalData
const { isCustomAlign, isCustomHeaderAlign, isCustomFooterAlign, isCustomResizable, isCustomVisible, isCustomFixed, isCustomSort, isCustomAggGroup, isCustomAggFunc, isCustomPager } = handleCustomStoreConfig($xeTable)
let { alignData, headerAlignData, footerAlignData, resizableData, sortData, visibleData, fixedData, aggGroupData, aggFuncData, pagerData } = storeData
// 处理还原
if ((isCustomAlign && alignData) ||
(isCustomHeaderAlign && headerAlignData) ||
(isCustomFooterAlign && footerAlignData) ||
(isCustomResizable && resizableData) ||
(isCustomSort && sortData) ||
(isCustomVisible && visibleData) ||
(isCustomFixed && fixedData) ||
(isCustomAggGroup && aggGroupData) ||
(isCustomAggFunc && aggFuncData) ||
(isCustomPager && pagerData && $xeGGWrapper)) {
const sortColMaps: Record<string, {
key: string
sNum: number
pKey: string | null
}> = {}
if (isCustomSort && sortData) {
// 转换兼容老版本数据,即将废弃兼容
if (!XEUtils.isArray(sortData)) {
const sortRests: {key: string, index: number}[] = []
XEUtils.each(sortData, (index: number, colKey: string) => {
sortRests.push({ key: colKey, index })
})
sortData = XEUtils.orderBy(sortRests, { field: 'index', order: 'asc' }).map(item => ({ k: item.key }))
}
let colNum = 1
XEUtils.eachTree(sortData, (sObj, index, sOjs, path, pSObj) => {
sortColMaps[sObj.k] = {
key: sObj.k,
sNum: colNum++,
pKey: pSObj ? pSObj.k : null
}
}, { children: 'c' })
}
const colKeyMaps: Record<string, VxeTableDefines.ColumnInfo> = {}
const allCols: VxeTableDefines.ColumnInfo[] = []
const aggGroupConfs: VxeTableDefines.RowGroupItem[] = []
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn) => {
const colKey = column.getKey()
// 支持一级
if (!parentColumn) {
if (isCustomFixed && fixedData && fixedData[colKey] !== undefined) {
column.fixed = fixedData[colKey]
}
}
if (isCustomResizable && resizableData && XEUtils.isNumber(resizableData[colKey])) {
column.resizeWidth = resizableData[colKey]
}
if (isCustomVisible && visibleData && XEUtils.isBoolean(visibleData[colKey])) {
column.visible = visibleData[colKey]
}
if (isCustomAggFunc && aggFuncData && (aggregateConfig || rowGroupConfig) && aggFuncData[colKey]) {
column.aggFunc = aggFuncData[colKey]
}
if (isCustomAggGroup && aggGroupData && aggGroupData[colKey]) {
aggGroupConfs.push({ field: column.field })
}
colKeyMaps[colKey] = column
allCols.push(column)
})
if ((aggregateConfig || rowGroupConfig) && aggGroupConfs.length) {
const groupRest = handleGroupData(internalData.tableFullData, aggGroupConfs)
internalData.tableFullTreeData = []
internalData.tableFullGroupData = groupRest.treeData
reactData.isRowGroupStatus = true
reactData.rowGroupList = aggGroupConfs
$xeTable.cacheRowMap(false)
}
// 如果自定义了顺序
if (isCustomSort && sortData) {
allCols.forEach(column => {
const colKey = column.getKey()
const scItem = sortColMaps[colKey]
if (scItem) {
const parentColumn = scItem.pKey ? colKeyMaps[scItem.pKey] : null
column.parentId = parentColumn ? parentColumn.id : null
column.renderSortNumber = scItem.sNum
}
})
const newCollectCols = XEUtils.toArrayTree(XEUtils.orderBy(allCols, 'renderSortNumber'), { key: 'id', parentKey: 'parentId', children: 'children' })
internalData.collectColumn = newCollectCols
internalData.tableFullColumn = getColumnList(newCollectCols)
reactData.updateColFlag++
}
reactData.isCustomStatus = true
} else {
reactData.isCustomStatus = false
}
}
/**
* 还原自定义列操作状态
*/
const restoreCustomStorage = () => {
const { customConfig } = props
const tableId = computeTableId.value
const customOpts = computeCustomOpts.value
const { storage, restoreStore } = customOpts
const { isCustomAlign, isCustomHeaderAlign, isCustomFooterAlign, isCustomResizable, isCustomVisible, isCustomFixed, isCustomSort, isCustomAggGroup, isCustomAggFunc } = handleCustomStoreConfig($xeTable)
if (storage &&
(customConfig ? isEnableConf(customOpts) : customOpts.enabled) &&
(
isCustomAlign ||
isCustomHeaderAlign ||
isCustomFooterAlign ||
isCustomResizable ||
isCustomVisible ||
isCustomFixed ||
isCustomSort ||
isCustomAggGroup ||
isCustomAggFunc
)
) {
if (!tableId) {
errLog('vxe.error.reqProp', ['id'])
return
}
const storeData: VxeTableDefines.CustomStoreData = getCustomStorageMap(tableId)
if (restoreStore) {
return Promise.resolve(
restoreStore({ $table: $xeTable, id: tableId, type: 'restore', storeData })
).then(storeData => {
if (!storeData) {
return
}
return handleCustomRestore(storeData)
}).catch(e => e)
} else {
return handleCustomRestore(storeData)
}
}
}
const updateColumnAllOverflow = () => {
const { showOverflow } = props
const { isGroup } = reactData
const { fullAllDataRowIdData, tableFullColumn, collectColumn } = internalData
let isAllOverflow = !!showOverflow
const handleFunc = (column: VxeTableDefines.ColumnInfo) => {
if (isAllOverflow && column.showOverflow === false) {
isAllOverflow = false
}
}
if (isGroup) {
XEUtils.eachTree(collectColumn, handleFunc)
} else {
tableFullColumn.forEach(handleFunc)
}
XEUtils.each(fullAllDataRowIdData, (rowRest: VxeTableDefines.RowCacheItem) => {
rowRest.height = 0
})
reactData.isAllOverflow = isAllOverflow
}
/**
* 更新数据列的 Map
* 牺牲数据组装的耗时,用来换取使用过程中的流畅
*/
const cacheColumnMap = () => {
const { treeConfig, showOverflow } = props
const { tableFullColumn, collectColumn } = internalData
const fullColIdData: Record<string, VxeTableDefines.ColumnCacheItem> = internalData.fullColumnIdData = {}
const fullColFieldData: Record<string, VxeTableDefines.ColumnCacheItem> = internalData.fullColumnFieldData = {}
const mouseOpts = computeMouseOpts.value
const expandOpts = computeExpandOpts.value
const columnOpts = computeColumnOpts.value
const columnDragOpts = computeColumnDragOpts.value
const virtualYOpts = computeVirtualYOpts.value
const { isCrossDrag, isSelfToChildDrag } = columnDragOpts
const customOpts = computeCustomOpts.value
const treeOpts = computeTreeOpts.value
const { storage } = customOpts
const rowOpts = computeRowOpts.value
const isGroup = collectColumn.some(hasChildrenList)
let isAllOverflow = !!showOverflow
let rowGroupColumn: VxeTableDefines.ColumnInfo | undefined
let expandColumn: VxeTableDefines.ColumnInfo | undefined
let treeNodeColumn: VxeTableDefines.ColumnInfo | undefined
let checkboxColumn: VxeTableDefines.ColumnInfo | undefined
let radioColumn: VxeTableDefines.ColumnInfo | undefined
let htmlColumn: VxeTableDefines.ColumnInfo | undefined
let hasFixed: VxeColumnPropTypes.Fixed | undefined
const handleFunc = (column: VxeTableDefines.ColumnInfo, index: number, items: VxeTableDefines.ColumnInfo[], path?: string[], parentColumn?: VxeTableDefines.ColumnInfo) => {
const { id: colid, field, fixed, type, treeNode, rowGroupNode } = column
const rest = { $index: -1, _index: -1, column, colid, index, items, parent: parentColumn || null, width: 0, oLeft: 0 }
if (field) {
if (fullColFieldData[field]) {
errLog('vxe.error.colRepet', ['field', field])
}
fullColFieldData[field] = rest
} else {
if (storage && !type) {
errLog('vxe.error.reqSupportProp', ['storage', `[${type ? `type=${type}` : `title=${column.getTitle()}`}]field=?`])
}
if (columnOpts.drag && (isCrossDrag || isSelfToChildDrag)) {
errLog('vxe.error.reqSupportProp', ['column-drag-config.isCrossDrag | column-drag-config.isSelfToChildDrag', `${column.getTitle() || type || ''} -> field=?`])
}
}
if (!hasFixed && fixed) {
hasFixed = fixed
}
if (!htmlColumn && type === 'html') {
htmlColumn = column
}
if (treeNode) {
if (treeNodeColumn) {
warnLog('vxe.error.colRepet', ['tree-node', treeNode])
}
if (!treeNodeColumn) {
treeNodeColumn = column
}
}
if (rowGroupNode) {
if (treeNodeColumn) {
warnLog('vxe.error.colRepet', ['row-group-node', rowGroupNode])
}
if (!rowGroupColumn) {
rowGroupColumn = column
}
}
if (type === 'expand') {
if (expandColumn) {
warnLog('vxe.error.colRepet', ['type', type])
}
if (!expandColumn) {
expandColumn = column
}
}
if (type === 'checkbox') {
if (checkboxColumn) {
warnLog('vxe.error.colRepet', ['type', type])
}
if (!checkboxColumn) {
checkboxColumn = column
}
} else if (type === 'radio') {
if (radioColumn) {
warnLog('vxe.error.colRepet', ['type', type])
}
if (!radioColumn) {
radioColumn = column
}
}
if (isAllOverflow && column.showOverflow === false) {
isAllOverflow = false
}
if (fullColIdData[colid]) {
errLog('vxe.error.colRepet', ['colId', colid])
}
fullColIdData[colid] = rest
}
if (isGroup) {
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn, nodes) => {
column.level = nodes.length
handleFunc(column, index, items, path, parentColumn)
})
} else {
tableFullColumn.forEach(handleFunc)
}
if (expandColumn) {
if (expandOpts.mode !== 'fixed' && virtualYOpts.enabled) {
warnLog('vxe.error.notConflictProp', ['column.type="expand', 'virtual-y-config.enabled=false'])
}
if ((expandOpts.mode !== 'fixed') && mouseOpts.area) {
errLog('vxe.error.errConflicts', ['mouse-config.area', 'column.type=expand'])
}
if (expandOpts.mode !== 'inside' && (treeConfig && !treeOpts.transform)) {
errLog('vxe.error.notConflictProp', ['tree-config.transform=false', 'expand-config.mode=inside'])
}
// if (props.spanMethod) {
// warnLog('vxe.error.notSupportProp', ['column.type=expand', 'span-method', 'span-method=null'])
// }
}
if (htmlColumn) {
if (!columnOpts.useKey) {
errLog('vxe.error.notSupportProp', ['column.type=html', 'column-config.useKey=false', 'column-config.useKey=true'])
}
if (!rowOpts.useKey) {
errLog('vxe.error.notSupportProp', ['column.type=html', 'row-config.useKey=false', 'row-config.useKey=true'])
}
}
reactData.isGroup = isGroup
reactData.rowGroupColumn = rowGroupColumn
reactData.treeNodeColumn = treeNodeColumn
reactData.expandColumn = expandColumn
reactData.checkboxColumn = checkboxColumn
reactData.radioColumn = radioColumn
reactData.isAllOverflow = isAllOverflow
}
const updateHeight = () => {
internalData.customHeight = calcTableHeight('height')
internalData.customMinHeight = calcTableHeight('minHeight')
internalData.customMaxHeight = calcTableHeight('maxHeight')
// 如果启用虚拟滚动,纠正高度
if (reactData.scrollYLoad && !(internalData.customHeight || internalData.customMinHeight || internalData.customMaxHeight)) {
internalData.customHeight = 300
}
}
/**
* 计算自适应列宽
*/
const calcColumnAutoWidth = (column: VxeTableDefines.ColumnInfo, wrapperEl: HTMLDivElement) => {
const { scrollYLoad } = reactData
const columnOpts = computeColumnOpts.value
const { autoOptions } = columnOpts
const { isCalcHeader, isCalcBody, isCalcFooter } = autoOptions || {}
const querySelections: string[] = []
if (isCalcHeader) {
querySelections.push(`.vxe-header-cell--wrapper[colid="${column.id}"]`)
}
if (isCalcBody) {
querySelections.push(`.vxe-body-cell--wrapper[colid="${column.id}"]`)
}
if (isCalcFooter) {
querySelections.push(`.vxe-footer-cell--wrapper[colid="${column.id}"]`)
}
const cellElemList = querySelections.length ? wrapperEl.querySelectorAll(querySelections.join(',')) : []
let leftRightPadding = 0
const firstCellEl = cellElemList[0]
if (firstCellEl && firstCellEl.parentElement) {
const cellStyle = getComputedStyle(firstCellEl.parentElement)
leftRightPadding = Math.ceil(XEUtils.toNumber(cellStyle.paddingLeft) + XEUtils.toNumber(cellStyle.paddingRight))
}
let colWidth = (scrollYLoad ? column.renderAutoWidth : 0) - leftRightPadding
for (let i = 0; i < cellElemList.length; i++) {
const celEl = cellElemList[i] as HTMLDivElement
colWidth = Math.max(colWidth, celEl ? Math.ceil(celEl.scrollWidth) + 4 : 0)
}
return colWidth + leftRightPadding
}
/**
* 自适应列宽
*/
const calcCellWidth = () => {
const autoWidthColumnList = computeAutoWidthColumnList.value
const { fullColumnIdData } = internalData
const el = refElem.value
if (el && autoWidthColumnList.length) {
el.setAttribute('data-calc-col', 'Y')
autoWidthColumnList.forEach(column => {
const colid = column.id
const colRest = fullColumnIdData[colid]
const colWidth = calcColumnAutoWidth(column, el)
if (colRest) {
colRest.width = Math.max(colWidth, colRest.width)
}
column.renderAutoWidth = colWidth
})
$xeTable.analyColumnWidth()
el.removeAttribute('data-calc-col')
}
}
/**
* 列宽算法,计算单元格列宽,动态分配可用剩余空间
* 支持 px、%、固定 混合分配
* 支持动态列表调整分配
* 支持自动分配偏移量
* 支持 width=60 width=60px width=10% min-width=60 min-width=60px min-width=10%
*/
const autoCellWidth = () => {
const { elemStore } = internalData
const bodyWrapperElem = getRefElem(elemStore['main-body-wrapper'])
if (!bodyWrapperElem) {
return
}
const yHandleEl = refScrollYHandleElem.value
if (!yHandleEl) {
return
}
const xHandleEl = refScrollXHandleElem.value
if (!xHandleEl) {
return
}
const columnOpts = computeColumnOpts.value
let tWidth = 0
const minCellWidth = 40 // 列宽最少限制 40px
const bodyWidth = bodyWrapperElem.clientWidth
let remainWidth = bodyWidth
let meanWidth = remainWidth / 100
const { fit } = props
const { columnStore } = reactData
const { resizeList, pxMinList, autoMinList, pxList, scaleList, scaleMinList, autoList, remainList } = columnStore
const parseColumnMaxWidth = (column: VxeTableDefines.ColumnInfo) => {
const maxWidth = column.maxWidth || columnOpts.maxWidth
if (maxWidth && maxWidth !== 'auto') {
if (isScale(maxWidth)) {
return Math.floor(XEUtils.toInteger(maxWidth) * meanWidth)
}
return XEUtils.toInteger(maxWidth)
}
return 0
}
// 最小宽
pxMinList.forEach((column) => {
let miWidth = XEUtils.toInteger(column.minWidth)
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
miWidth = Math.min(miWidth, mxWidth)
}
tWidth += miWidth
column.renderWidth = miWidth
})
// 最小自适应
autoMinList.forEach((column) => {
let caWidth = Math.max(60, XEUtils.toInteger(column.renderAutoWidth))
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
caWidth = Math.min(caWidth, mxWidth)
}
tWidth += caWidth
column.renderWidth = caWidth
})
// 最小百分比
scaleMinList.forEach((column) => {
let smWidth = Math.floor(XEUtils.toInteger(column.minWidth) * meanWidth)
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
smWidth = Math.min(smWidth, mxWidth)
}
tWidth += smWidth
column.renderWidth = smWidth
})
// 固定百分比
scaleList.forEach((column) => {
let sfWidth = Math.floor(XEUtils.toInteger(column.width) * meanWidth)
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
sfWidth = Math.min(sfWidth, mxWidth)
}
tWidth += sfWidth
column.renderWidth = sfWidth
})
// 固定宽
pxList.forEach((column) => {
let pWidth = XEUtils.toInteger(column.width)
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
pWidth = Math.min(pWidth, mxWidth)
}
tWidth += pWidth
column.renderWidth = pWidth
})
// 自适应宽
autoList.forEach((column) => {
let aWidth = Math.max(60, XEUtils.toInteger(column.renderAutoWidth))
const mxWidth = parseColumnMaxWidth(column)
if (mxWidth) {
aWidth = Math.min(aWidth, mxWidth)
}
tWidth += aWidth
column.renderWidth = aWidth
})
// 调整了列宽
resizeList.forEach((column) => {
const reWidth = XEUtils.toInteger(column.resizeWidth)
tWidth += reWidth
column.renderWidth = reWidth
})
const zoomColumnList = scaleMinList.concat(pxMinList).concat(autoMinList).filter(column => !parseColumnMaxWidth(column))
remainWidth -= tWidth
meanWidth = remainWidth > 0 ? Math.floor(remainWidth / (zoomColumnList.length + remainList.length)) : 0
if (fit) {
if (remainWidth > 0) {
zoomColumnList.forEach((column) => {
tWidth += meanWidth
column.renderWidth += meanWidth
})
}
} else {
meanWidth = minCellWidth
}
// 剩余均分
remainList.forEach((column) => {
const width = Math.max(meanWidth, minCellWidth)
column.renderWidth = width
tWidth += width
})
if (fit) {
/**
* 偏移量算法
* 如果所有列足够放的情况下,从最后动态列开始分配
* 排除已设置 max-width
*/
const dynamicList = scaleList.concat(scaleMinList).concat(pxMinList).concat(autoMinList).concat(remainList).filter(column => !parseColumnMaxWidth(column))
let dynamicSize = dynamicList.length - 1
if (dynamicSize > 0) {
let i = bodyWidth - tWidth
if (i > 0) {
while (i > 0 && dynamicSize >= 0) {
i--
dynamicList[dynamicSize--].renderWidth++
}
tWidth = bodyWidth
}
}
}
reactData.scrollXWidth = tWidth
reactData.resizeWidthFlag++
updateColumnOffsetLeft()
updateHeight()
}
/**
* 计算自适应行高
*/
const calcCellAutoHeight = (rowid: string | number, rowRest: VxeTableDefines.RowCacheItem, wrapperEl: HTMLDivElement) => {
const { fullCellHeightMaps } = internalData
let chRest = fullCellHeightMaps[rowid]
if (!chRest) {
chRest = {}
fullCellHeightMaps[rowid] = chRest
}
const wrapperElemList = wrapperEl.querySelectorAll(`.vxe-cell--wrapper[rowid="${rowRest.rowid}"]`)
let firstCellStyle: CSSStyleDeclaration | null = null
let topBottomPadding = 0
let changeCH = false
for (let i = 0; i < wrapperElemList.length; i++) {
const wrapperElem = wrapperElemList[i] as HTMLElement
const cellElem = wrapperElem.parentElement as HTMLTableCellElement
const cellStyle = cellElem.style
const orHeight = cellStyle.height
const colid = wrapperElem.getAttribute('colid') || ''
cellStyle.height = ''
if (!firstCellStyle) {
firstCellStyle = getComputedStyle(cellElem)
topBottomPadding = firstCellStyle ? Math.ceil(XEUtils.toNumber(firstCellStyle.paddingTop) + XEUtils.toNumber(firstCellStyle.paddingBottom)) : 0
}
const cellHeight = wrapperElem ? wrapperElem.clientHeight : 0
const colHeight = Math.ceil(cellHeight + topBottomPadding)
if (chRest[colid] !== colHeight) {
changeCH = true
chRest[colid] = colHeight
}
cellStyle.height = orHeight
}
const cellMaxHeight = getRowMaxHeight(chRest, changeCH)
return cellMaxHeight
}
/**
* 自适应行高
*/
const calcCellHeight = () => {
const { treeConfig } = props
const { tableData, isAllOverflow, scrollYLoad, scrollXLoad } = reactData
const { fullAllDataRowIdData } = internalData
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
const treeOpts = computeTreeOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
if (!isAllOverflow && (scrollYLoad || scrollXLoad || (treeConfig && treeOpts.showLine))) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
el.setAttribute('data-calc-row', 'Y')
tableData.forEach(row => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const reHeight = calcCellAutoHeight(rowid, rowRest, el)
rowRest.height = Math.max(defaultRowHeight, reHeight)
}
el.removeAttribute('data-calc-row')
})
reactData.calcCellHeightFlag++
}
}
const getOrderField = (column: VxeTableDefines.ColumnInfo) => {
const { isRowGroupStatus } = reactData
const { sortBy, sortType, aggFunc } = column
return isRowGroupStatus && aggFunc
? (row: any) => {
if (row.isAggregate) {
const aggData = row.aggData
const currAggData = aggData ? aggData[column.field] : null
return currAggData ? currAggData.value : null
}
let cellValue
if (sortBy) {
cellValue = XEUtils.isFunction(sortBy) ? sortBy({ row, column }) : XEUtils.get(row, sortBy)
} else {
cellValue = $xeTable.getCellLabel(row, column)
}
if (!sortType || sortType === 'auto') {
return eqEmptyValue(cellValue) || isNaN(cellValue) ? cellValue : XEUtils.toNumber(cellValue)
} else if (sortType === 'number') {
return XEUtils.toNumber(cellValue)
} else if (sortType === 'string') {
return XEUtils.toValueString(cellValue)
}
return cellValue
}
: (row: any) => {
let cellValue
if (sortBy) {
cellValue = XEUtils.isFunction(sortBy) ? sortBy({ row, column }) : XEUtils.get(row, sortBy)
} else {
cellValue = $xeTable.getCellLabel(row, column)
}
if (!sortType || sortType === 'auto') {
return eqEmptyValue(cellValue) || isNaN(cellValue) ? cellValue : XEUtils.toNumber(cellValue)
} else if (sortType === 'number') {
return XEUtils.toNumber(cellValue)
} else if (sortType === 'string') {
return XEUtils.toValueString(cellValue)
}
return cellValue
}
}
const updateAfterTreeIndex = () => {
const { fullDataRowIdData, fullAllDataRowIdData, afterFullData, afterTreeFullData } = internalData
const treeOpts = computeTreeOpts.value
const { transform, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const fullMaps: Record<string, any> = {}
let rowNum = 0
const { handleGetRowId } = createHandleGetRowId($xeTable)
XEUtils.eachTree(afterTreeFullData, (row, index, items, path, parentRow) => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
const seq = path.map((num, i) => i % 2 === 0 ? (Number(num) + 1) : '.').join('')
if (rowRest) {
rowRest.seq = seq
rowRest._seq = rowNum
rowRest.treeIndex = index
rowRest._index = rowNum
rowRest._tIndex = rowNum
} else {
const rest = { row, rowid, _seq: rowNum, seq, index, $index: -1, _index: rowNum, treeIndex: index, _tIndex: rowNum, items, parent: parentRow, level: 0, height: 0, resizeHeight: 0, oTop: 0, expandHeight: 0 }
fullAllDataRowIdData[rowid] = rest
fullDataRowIdData[rowid] = rest
}
rowNum++
fullMaps[rowid] = row
}, { children: transform ? mapChildrenField : childrenField })
if (transform) {
afterFullData.forEach((row, index) => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest._index = index
}
})
}
internalData.afterFullRowMaps = fullMaps
}
const updateAfterListIndex = () => {
const { afterFullData, fullDataRowIdData, fullAllDataRowIdData } = internalData
const { handleGetRowId } = createHandleGetRowId($xeTable)
const fullMaps: Record<string, any> = {}
let rowNum = 0
afterFullData.forEach((row, index) => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
const seq = index + 1
if (rowRest) {
rowRest.seq = seq
rowRest._seq = rowNum
rowRest._index = index
} else {
const rest = { row, rowid, _seq: rowNum, seq, index: -1, $index: -1, _index: index, treeIndex: -1, _tIndex: -1, items: [], parent: null, level: 0, height: 0, resizeHeight: 0, oTop: 0, expandHeight: 0 }
fullAllDataRowIdData[rowid] = rest
fullDataRowIdData[rowid] = rest
}
rowNum++
fullMaps[rowid] = row
})
internalData.afterFullRowMaps = fullMaps
}
/**
* 预编译
* 对渲染中的数据提前解析序号及索引。牺牲提前编译耗时换取渲染中额外损耗,使运行时更加流畅
*/
const updateAfterDataIndex = () => {
const { treeConfig } = props
if (treeConfig) {
updateAfterTreeIndex()
} else {
updateAfterListIndex()
}
}
/**
* 如果为虚拟树、行分组、则将树结构拍平
* @returns
*/
const handleVirtualTreeToList = () => {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { fullAllDataRowIdData, treeExpandedMaps, rowGroupExpandedMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const { handleGetRowId } = createHandleGetRowId($xeTable)
const fullData: any[] = []
const expandMaps: {
[key: string]: number
} = {}
if (treeConfig && treeOpts.transform) {
const childrenField = treeOpts.children || treeOpts.childrenField
XEUtils.eachTree(internalData.afterTreeFullData, (row, index, items, path, parentRow) => {
const rowid = handleGetRowId(row)
const parentRowid = handleGetRowId(parentRow)
if (!parentRow || (expandMaps[parentRowid] && treeExpandedMaps[parentRowid])) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest._index = fullData.length
}
expandMaps[rowid] = 1
fullData.push(row)
}
}, { children: childrenField })
internalData.afterFullData = fullData
updateScrollYStatus(fullData)
return fullData
} else if (isRowGroupStatus) {
const { childrenField } = aggregateOpts
XEUtils.eachTree(internalData.afterGroupFullData, (row, index, items, path, parentRow) => {
const rowid = handleGetRowId(row)
const parentRowid = handleGetRowId(parentRow)
if (!parentRow || (expandMaps[parentRowid] && rowGroupExpandedMaps[parentRowid])) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest._index = fullData.length
}
expandMaps[rowid] = 1
fullData.push(row)
}
}, { children: childrenField })
internalData.afterFullData = fullData
updateScrollYStatus(fullData)
return fullData
}
return internalData.afterFullData
}
/**
* 编译处理后全量的表格数据
* 如果存在筛选条件,继续处理
*/
const updateAfterFullData = () => {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { tableFullColumn, tableFullData, tableFullTreeData, tableFullGroupData } = internalData
const filterOpts = computeFilterOpts.value
const sortOpts = computeSortOpts.value
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const { transform, rowField, parentField, mapChildrenField } = treeOpts
const { isEvery, remote: allRemoteFilter, filterMethod: allFilterMethod, isDeep: isFilterDeep } = filterOpts
const { remote: allRemoteSort, sortMethod: allSortMethod, multiple: sortMultiple, chronological, isDeep: isSortDeep } = sortOpts
let tableData: any[] = []
let tableTree: any[] = []
// 处理数据
if (!allRemoteFilter || !allRemoteSort) {
const filterColumns: {
column: VxeTableDefines.ColumnInfo
valueList: any[]
itemList: VxeTableDefines.FilterOption[]
}[] = []
let orderColumns: VxeTableDefines.SortCheckedParams[] = []
tableFullColumn.forEach((column) => {
const { field, sortable, order, filters } = column
if (!allRemoteFilter && filters && filters.length) {
const valueList: any[] = []
const itemList: VxeTableDefines.FilterOption[] = []
filters.forEach((item) => {
if (item.checked) {
itemList.push(item as VxeTableDefines.FilterOption)
valueList.push(item.value)
}
})
if (itemList.length) {
filterColumns.push({ column, valueList, itemList })
}
}
if (!allRemoteSort && sortable && order) {
orderColumns.push({ column, field, property: field, order: order, sortTime: column.sortTime })
}
})
if (sortMultiple && chronological && orderColumns.length > 1) {
orderColumns = XEUtils.orderBy(orderColumns, 'sortTime')
}
// 处理筛选
// 支持单列、多列、组合筛选
if (!allRemoteFilter && filterColumns.length) {
const handleFilter = (row: any) => {
return filterColumns.every(({ column, valueList, itemList }) => {
const { filterMethod, filterRender } = column
const compConf = isEnableConf(filterRender) ? renderer.get(filterRender.name) : null
const compFilterMethod = compConf ? (compConf.tableFilterMethod || compConf.filterMethod) : null
const tdFilterMethod = compConf ? (compConf.tableFilterDefaultMethod || compConf.defaultTableFilterMethod || compConf.defaultFilterMethod) : null
const cellValue = getCellValue(row, column)
if (filterMethod) {
return itemList.some((item) => filterMethod({ value: item.value, option: item, cellValue, row, column, $table: $xeTable }))
} else if (compFilterMethod) {
return itemList.some((item) => compFilterMethod({ value: item.value, option: item, cellValue, row, column, $table: $xeTable }))
} else if (allFilterMethod) {
return allFilterMethod({ $table: $xeTable, options: itemList, values: valueList, cellValue, row, column })
} else if (tdFilterMethod) {
return itemList.some((item) => tdFilterMethod({ value: item.value, option: item, cellValue, row, column, $table: $xeTable }))
}
return valueList.indexOf(XEUtils.get(row, column.field)) > -1
})
}
if (isRowGroupStatus) {
// 行分组
tableTree = XEUtils.searchTree(tableFullGroupData, handleFilter, {
original: true,
isEvery: true,
children: aggregateOpts.mapChildrenField,
mapChildren: aggregateOpts.childrenField
})
tableData = tableTree
} else if (treeConfig && transform && isFilterDeep !== false) {
// 筛选虚拟树
tableTree = XEUtils.searchTree(tableFullTreeData, handleFilter, {
original: true,
isEvery,
children: mapChildrenField,
mapChildren: childrenField
})
tableData = tableTree
} else {
tableData = treeConfig ? tableFullTreeData.filter(handleFilter) : tableFullData.filter(handleFilter)
tableTree = tableData
}
} else {
if (isRowGroupStatus) {
// 还原行分组
tableTree = XEUtils.searchTree(tableFullGroupData, () => true, {
original: true,
isEvery: true,
children: aggregateOpts.mapChildrenField,
mapChildren: aggregateOpts.childrenField
})
tableData = tableTree
} else if (treeConfig && transform && isFilterDeep !== false) {
// 还原虚拟树
tableTree = XEUtils.searchTree(tableFullTreeData, () => true, {
original: true,
isEvery,
children: mapChildrenField,
mapChildren: childrenField
})
tableData = tableTree
} else {
tableData = treeConfig ? tableFullTreeData.slice(0) : tableFullData.slice(0)
tableTree = tableData
}
}
// 处理排序
// 支持单列、多列、组合排序
if (!allRemoteSort && orderColumns.length) {
if (isRowGroupStatus) {
// 行分组的排序
if (allSortMethod) {
const sortRests = allSortMethod({ data: tableTree, sortList: orderColumns, $table: $xeTable })
tableTree = XEUtils.isArray(sortRests) ? sortRests : tableTree
} else {
const treeList = XEUtils.toTreeArray(tableTree, {
key: aggregateOpts.rowField,
parentKey: aggregateOpts.parentField,
children: aggregateOpts.mapChildrenField
})
tableTree = XEUtils.toArrayTree(
XEUtils.orderBy(treeList, orderColumns.map(({ column, order }) => [getOrderField(column), order])),
{
key: aggregateOpts.rowField,
parentKey: aggregateOpts.parentField,
children: aggregateOpts.childrenField,
mapChildren: aggregateOpts.mapChildrenField
}
)
}
tableData = tableTree
} else if (treeConfig && transform && isSortDeep !== false) {
// 虚拟树的排序
if (allSortMethod) {
const sortRests = allSortMethod({ data: tableTree, sortList: orderColumns, $table: $xeTable })
tableTree = XEUtils.isArray(sortRests) ? sortRests : tableTree
} else {
const treeList = XEUtils.toTreeArray(tableTree, {
children: mapChildrenField
})
tableTree = XEUtils.toArrayTree(
XEUtils.orderBy(treeList, orderColumns.map(({ column, order }) => [getOrderField(column), order])),
{
key: rowField,
parentKey: parentField,
children: childrenField,
mapChildren: mapChildrenField
}
)
}
tableData = tableTree
} else {
if (allSortMethod) {
const sortRests = allSortMethod({ data: tableData, sortList: orderColumns, $table: $xeTable })
tableData = XEUtils.isArray(sortRests) ? sortRests : tableData
} else {
tableData = XEUtils.orderBy(tableData, orderColumns.map(({ column, order }) => [getOrderField(column), order]))
}
tableTree = tableData
}
}
} else {
if (isRowGroupStatus) {
// 还原行分组
// 还原虚拟树
tableTree = XEUtils.searchTree(tableFullGroupData, () => true, {
original: true,
isEvery: true,
children: aggregateOpts.mapChildrenField,
mapChildren: aggregateOpts.childrenField
})
tableData = tableTree
} else if (treeConfig && transform && isSortDeep !== false) {
// 还原虚拟树
tableTree = XEUtils.searchTree(tableFullTreeData, () => true, {
original: true,
isEvery,
children: mapChildrenField,
mapChildren: childrenField
})
tableData = tableTree
} else {
tableData = treeConfig ? tableFullTreeData.slice(0) : tableFullData.slice(0)
tableTree = tableData
}
}
internalData.afterFullData = tableData
internalData.afterTreeFullData = tableTree
internalData.afterGroupFullData = tableTree
updateAfterDataIndex()
}
const updateStyle = () => {
const { mouseConfig } = props
const { isGroup, tableColumn, overflowX, scrollbarWidth, overflowY, scrollbarHeight, scrollXWidth, columnStore, editStore, isColLoading } = reactData
const { visibleColumn, tableHeight, elemStore, currentRow, customHeight, customMinHeight, customMaxHeight, tHeaderHeight, tFooterHeight } = internalData
const $xeGanttView = internalData.xeGanttView
const el = refElem.value
if (!el || (internalData.tBodyHeight && !el.clientHeight)) {
return
}
const containerList = ['main', 'left', 'right']
const { leftList, rightList } = columnStore
let osbWidth = overflowY ? scrollbarWidth : 0
let osbHeight = overflowX ? scrollbarHeight : 0
const emptyPlaceholderElem = refEmptyPlaceholder.value
const isHeaderRenderOptimize = computeIsHeaderRenderOptimize.value
const isBodyRenderOptimize = computeIsBodyRenderOptimize.value
const isFooterRenderOptimize = computeIsFooterRenderOptimize.value
const scrollbarOpts = computeScrollbarOpts.value
const mouseOpts = computeMouseOpts.value
const bodyWrapperElem = getRefElem(elemStore['main-body-wrapper'])
const bodyTableElem = getRefElem(elemStore['main-body-table'])
if (emptyPlaceholderElem) {
emptyPlaceholderElem.style.top = `${tHeaderHeight}px`
emptyPlaceholderElem.style.height = bodyWrapperElem ? `${bodyWrapperElem.offsetHeight - osbHeight}px` : ''
}
const scrollbarXConf = scrollbarOpts.x || {}
const scrollbarYConf = scrollbarOpts.y || {}
const scrollbarYToLeft = computeScrollbarYToLeft.value
let xScrollbarVisible = overflowX ? 'visible' : 'hidden'
if (scrollbarXConf.visible === 'visible' || $xeGanttView) {
osbHeight = scrollbarHeight
xScrollbarVisible = 'visible'
} else if (scrollbarXConf.visible === 'hidden' || scrollbarXConf.visible === false) {
osbHeight = 0
xScrollbarVisible = 'hidden'
}
let yScrollbarVisible = overflowY ? 'visible' : 'hidden'
if ((scrollbarYConf.visible === 'hidden' || scrollbarYConf.visible === false) || (($xeGantt && $xeGanttView && $xeGantt.reactData.showRightView) && !scrollbarYToLeft)) {
osbWidth = 0
yScrollbarVisible = 'hidden'
} else if (scrollbarYConf.visible === 'visible') {
osbWidth = scrollbarWidth
yScrollbarVisible = 'visible'
}
let tbHeight = 0
let bodyMaxHeight = 0
const bodyMinHeight = customMinHeight - tHeaderHeight - tFooterHeight - osbHeight
if (customMaxHeight) {
bodyMaxHeight = Math.max(bodyMinHeight, customMaxHeight - tHeaderHeight - tFooterHeight - osbHeight)
}
if (customHeight) {
tbHeight = customHeight - tHeaderHeight - tFooterHeight - osbHeight
}
if (!tbHeight) {
if (bodyTableElem) {
tbHeight = bodyTableElem.clientHeight
}
}
if (tbHeight) {
if (bodyMaxHeight) {
tbHeight = Math.min(bodyMaxHeight, tbHeight)
}
tbHeight = Math.max(bodyMinHeight, tbHeight)
}
const xLeftCornerEl = refScrollXLeftCornerElem.value
const xRightCornerEl = refScrollXRightCornerElem.value
const scrollXVirtualEl = refScrollXVirtualElem.value
if (scrollXVirtualEl) {
scrollXVirtualEl.style.height = `${osbHeight}px`
scrollXVirtualEl.style.visibility = xScrollbarVisible
}
const xWrapperEl = refScrollXWrapperElem.value
if (xWrapperEl) {
xWrapperEl.style.width = `${el.clientWidth - osbWidth}px`
if (scrollbarYToLeft) {
xWrapperEl.style.left = `${osbWidth}px`
} else {
xWrapperEl.style.left = ''
}
}
if (xLeftCornerEl) {
if (scrollbarYToLeft) {
xLeftCornerEl.style.width = `${osbWidth}px`
xLeftCornerEl.style.display = overflowY && osbWidth ? 'block' : ''
} else {
xLeftCornerEl.style.display = ''
}
}
if (xRightCornerEl) {
if (scrollbarYToLeft) {
xRightCornerEl.style.display = ''
} else {
xRightCornerEl.style.width = `${osbWidth}px`
xRightCornerEl.style.display = xScrollbarVisible === 'visible' ? 'block' : ''
}
}
const scrollYVirtualEl = refScrollYVirtualElem.value
if (scrollYVirtualEl) {
scrollYVirtualEl.style.width = `${osbWidth}px`
scrollYVirtualEl.style.height = `${tbHeight + tHeaderHeight + tFooterHeight}px`
scrollYVirtualEl.style.visibility = yScrollbarVisible
}
const yTopCornerEl = refScrollYTopCornerElem.value
if (yTopCornerEl) {
yTopCornerEl.style.height = `${tHeaderHeight}px`
yTopCornerEl.style.display = tHeaderHeight && yScrollbarVisible === 'visible' ? 'block' : ''
}
const yWrapperEl = refScrollYWrapperElem.value
if (yWrapperEl) {
yWrapperEl.style.height = `${tbHeight}px`
yWrapperEl.style.top = `${tHeaderHeight}px`
}
const yBottomCornerEl = refScrollYBottomCornerElem.value
if (yBottomCornerEl) {
yBottomCornerEl.style.height = `${tFooterHeight}px`
yBottomCornerEl.style.top = `${tHeaderHeight + tbHeight}px`
yBottomCornerEl.style.display = tFooterHeight && yScrollbarVisible === 'visible' ? 'block' : ''
}
const rowExpandEl = refRowExpandElem.value
if (rowExpandEl) {
rowExpandEl.style.height = `${tbHeight}px`
rowExpandEl.style.top = `${tHeaderHeight}px`
}
internalData.tBodyHeight = tbHeight
containerList.forEach((name, index) => {
const fixedType = index > 0 ? name : ''
const layoutList = ['header', 'body', 'footer']
const isFixedLeft = fixedType === 'left'
let fixedColumn: VxeTableDefines.ColumnInfo[] = []
let fixedWrapperElem: HTMLDivElement
if (fixedType) {
fixedColumn = isFixedLeft ? leftList : rightList
fixedWrapperElem = isFixedLeft ? refLeftContainer.value : refRightContainer.value
}
layoutList.forEach(layout => {
const wrapperElem = getRefElem(elemStore[`${name}-${layout}-wrapper`])
const currScrollElem = getRefElem(elemStore[`${name}-${layout}-scroll`])
const tableElem = getRefElem(elemStore[`${name}-${layout}-table`])
if (layout === 'header') {
// 表头体样式处理
// 横向滚动渲染
let renderColumnList = tableColumn
const isOptimizeMode = isHeaderRenderOptimize
if (isGroup) {
renderColumnList = visibleColumn
} else {
if (!isOptimizeMode || (!isColLoading && (fixedType || !overflowX))) {
renderColumnList = visibleColumn
}
if (fixedType) {
// 如果是使用优化模式
if (isOptimizeMode) {
renderColumnList = fixedColumn || []
}
}
}
const tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (fixedType) {
if (isGroup) {
if (wrapperElem) {
wrapperElem.style.width = scrollXWidth ? `${scrollXWidth}px` : ''
}
} else {
if (isOptimizeMode) {
if (wrapperElem) {
wrapperElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else {
if (wrapperElem) {
wrapperElem.style.width = scrollXWidth ? `${scrollXWidth}px` : ''
}
}
}
}
if (currScrollElem) {
currScrollElem.style.height = `${tHeaderHeight}px`
}
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else if (layout === 'body') {
if (currScrollElem) {
currScrollElem.style.maxHeight = customMaxHeight ? `${bodyMaxHeight}px` : ''
currScrollElem.style.height = customHeight ? `${tbHeight}px` : ''
currScrollElem.style.minHeight = `${bodyMinHeight}px`
}
// 如果是固定列
if (fixedWrapperElem) {
if (wrapperElem) {
wrapperElem.style.top = `${tHeaderHeight}px`
}
fixedWrapperElem.style.height = `${customHeight > 0 ? customHeight : (tableHeight + tHeaderHeight + tFooterHeight + osbHeight)}px`
fixedWrapperElem.style.width = `${fixedColumn.reduce((previous, column) => previous + column.renderWidth, 0)}px`
}
let renderColumnList = tableColumn
const isOptimizeMode = isBodyRenderOptimize
if (fixedType) {
renderColumnList = visibleColumn
if (isOptimizeMode) {
renderColumnList = fixedColumn || []
}
}
const tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (fixedType) {
if (isOptimizeMode) {
if (wrapperElem) {
wrapperElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else {
if (wrapperElem) {
wrapperElem.style.width = scrollXWidth ? `${scrollXWidth}px` : ''
}
}
}
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth}px` : ''
// 兼容性处理
tableElem.style.paddingRight = osbWidth && fixedType && (browseObj.firefox || browseObj.safari) ? `${osbWidth}px` : ''
}
const emptyBlockElem = getRefElem(elemStore[`${name}-${layout}-emptyBlock`])
if (emptyBlockElem) {
emptyBlockElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else if (layout === 'footer') {
let renderColumnList = tableColumn
const isOptimizeMode = isFooterRenderOptimize
if (!isOptimizeMode || (!isColLoading && (fixedType || !overflowX))) {
renderColumnList = visibleColumn
}
if (fixedType) {
if (isOptimizeMode) {
renderColumnList = fixedColumn || []
}
}
const tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (fixedType) {
if (isOptimizeMode) {
if (wrapperElem) {
wrapperElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else {
if (wrapperElem) {
wrapperElem.style.width = scrollXWidth ? `${scrollXWidth}px` : ''
}
}
}
if (currScrollElem) {
currScrollElem.style.height = `${tFooterHeight}px`
// 如果是固定列
if (fixedWrapperElem) {
if (wrapperElem) {
wrapperElem.style.top = `${customHeight > 0 ? customHeight - tFooterHeight - osbHeight : tableHeight + tHeaderHeight}px`
}
}
}
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth}px` : ''
}
}
})
})
if (currentRow) {
$xeTable.setCurrentRow(currentRow)
}
if (mouseConfig && mouseOpts.selected && editStore.selected.row && editStore.selected.column) {
$xeTable.addCellSelectedClass()
}
if ($xeGanttView && $xeGanttView.handleUpdateStyle) {
$xeGanttView.handleUpdateStyle()
}
return nextTick()
}
const checkValidate = (type: any) => {
if ($xeTable.triggerValidate) {
return $xeTable.triggerValidate(type)
}
return nextTick()
}
/**
* 当单元格发生改变时
* 如果存在规则,则校验
*/
const handleChangeCell = (evnt: Event, params: any) => {
checkValidate('blur')
.catch((e: any) => e)
.then(() => {
$xeTable.handleEdit(params, evnt)
.then(() => checkValidate('change'))
.catch((e: any) => e)
})
}
const handleDefaultSort = () => {
const { sortConfig } = props
if (sortConfig) {
const sortOpts = computeSortOpts.value
let { defaultSort } = sortOpts
if (defaultSort) {
if (!XEUtils.isArray(defaultSort)) {
defaultSort = [defaultSort]
}
if (defaultSort.length) {
(sortConfig.multiple ? defaultSort : defaultSort.slice(0, 1)).forEach((item: any, index: number) => {
const { field, order } = item
if (field && order) {
const column = $xeTable.getColumnByField(field)
if (column && column.sortable) {
column.order = order
column.sortTime = Date.now() + index
}
}
})
if (!sortOpts.remote) {
$xeTable.handleTableData(true).then(updateStyle)
}
}
}
}
}
/**
* 处理默认勾选
*/
const handleDefaultSelectionChecked = () => {
const { checkboxConfig } = props
if (checkboxConfig) {
const { fullDataRowIdData } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { checkAll, checkRowKeys } = checkboxOpts
if (checkAll) {
handleCheckedAllCheckboxRow(true, true)
} else if (checkRowKeys) {
const defSelection: any[] = []
checkRowKeys.forEach((rowKey: any) => {
const rowid = encodeRowid(rowKey)
if (fullDataRowIdData[rowid]) {
defSelection.push(fullDataRowIdData[rowid].row)
}
})
handleCheckedCheckboxRow(defSelection, true, true)
}
}
}
/**
* 处理单选框默认勾选
*/
const handleDefaultRadioChecked = () => {
const { radioConfig } = props
if (radioConfig) {
const { fullDataRowIdData } = internalData
const radioOpts = computeRadioOpts.value
const { checkRowKey: rowKey, reserve } = radioOpts
const rowid = rowKey ? encodeRowid(rowKey) : null
if (rowid) {
if (fullDataRowIdData[rowid]) {
handleCheckedRadioRow(fullDataRowIdData[rowid].row, true)
}
if (reserve) {
const rowkey = getRowkey($xeTable)
internalData.radioReserveRow = { [rowkey]: rowid }
}
}
}
}
/**
* 处理默认展开行
*/
const handleDefaultRowExpand = () => {
const { expandConfig } = props
if (expandConfig) {
const { fullDataRowIdData } = internalData
const expandOpts = computeExpandOpts.value
const { expandAll, expandRowKeys } = expandOpts
if (expandAll) {
$xeTable.setAllRowExpand(true)
} else if (expandRowKeys) {
const defExpandeds: any[] = []
expandRowKeys.forEach((rowKey: any) => {
const rowid = encodeRowid(rowKey)
if (fullDataRowIdData[rowid]) {
defExpandeds.push(fullDataRowIdData[rowid].row)
}
})
$xeTable.setRowExpand(defExpandeds, true)
}
}
}
const handleRadioReserveRow = (row: any) => {
const radioOpts = computeRadioOpts.value
if (radioOpts.reserve) {
internalData.radioReserveRow = row
}
}
const handleCheckboxReserveRow = (row: any, checked: boolean) => {
const { checkboxReserveRowMap } = internalData
const checkboxOpts = computeCheckboxOpts.value
if (checkboxOpts.reserve) {
const rowid = getRowid($xeTable, row)
if (checked) {
checkboxReserveRowMap[rowid] = row
} else if (checkboxReserveRowMap[rowid]) {
delete checkboxReserveRowMap[rowid]
}
}
}
const handleCheckedRadioRow = (row: any, isForce: boolean) => {
const radioOpts = computeRadioOpts.value
const { checkMethod } = radioOpts
if (row && (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row })))) {
reactData.selectRadioRow = row
handleRadioReserveRow(row)
}
return nextTick()
}
const handleCheckedCheckboxRow = (rows: any[], value: boolean, isForce?: boolean) => {
if (rows && !XEUtils.isArray(rows)) {
rows = [rows]
}
$xeTable.handleBatchSelectRows(rows, !!value, isForce)
$xeTable.checkSelectionStatus()
return nextTick()
}
const handleCheckedAllCheckboxRow = (checked: boolean, isForce?: boolean) => {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { afterFullData, afterTreeFullData, afterGroupFullData, checkboxReserveRowMap, selectCheckboxMaps } = internalData
const treeOpts = computeTreeOpts.value
const aggregateOpts = computeAggregateOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const checkboxOpts = computeCheckboxOpts.value
const { checkField, reserve, checkMethod } = checkboxOpts
const { handleGetRowId } = createHandleGetRowId($xeTable)
// indeterminateField 仅支持读取
const indeterminateField = checkboxOpts.indeterminateField || checkboxOpts.halfField
const selectRowMaps: Record<string, any> = {}
/**
* 绑定属性方式(有污染)
* 必须在行数据存在对应的属性,否则将不响应
*/
if (checkField) {
const checkValFn = (row: any) => {
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
if (checked) {
selectRowMaps[handleGetRowId(row)] = row
}
XEUtils.set(row, checkField, checked)
}
if ((treeConfig || isRowGroupStatus) && indeterminateField) {
XEUtils.set(row, indeterminateField, false)
}
}
// 如果存在选中方法
// 如果方法成立,则更新值,否则忽略该数据
if ((treeConfig || isRowGroupStatus)) {
XEUtils.eachTree(afterFullData, checkValFn, { children: childrenField })
} else {
afterFullData.forEach(checkValFn)
}
} else {
/**
* 默认方式(无污染)
* 无需任何属性,直接绑定
*/
if (isRowGroupStatus) {
if (checked) {
/**
* 如果是行分组勾选
* 如果方法成立,则添加到临时集合中
*/
XEUtils.eachTree(afterGroupFullData, (row) => {
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
const rowid = handleGetRowId(row)
selectRowMaps[rowid] = row
}
}, { children: aggregateOpts.mapChildrenField })
} else {
/**
* 如果是树取消
* 如果方法成立,则不添加到临时集合中
*/
if (!isForce && checkMethod) {
XEUtils.eachTree(afterGroupFullData, (row) => {
const rowid = handleGetRowId(row)
if (checkMethod({ $table: $xeTable, row }) ? false : selectCheckboxMaps[rowid]) {
selectRowMaps[rowid] = row
}
}, { children: aggregateOpts.mapChildrenField })
}
}
} else if (treeConfig) {
if (checked) {
/**
* 如果是树勾选
* 如果方法成立,则添加到临时集合中
*/
XEUtils.eachTree(afterTreeFullData, (row) => {
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
const rowid = handleGetRowId(row)
selectRowMaps[rowid] = row
}
}, { children: childrenField })
} else {
/**
* 如果是树取消
* 如果方法成立,则不添加到临时集合中
*/
if (!isForce && checkMethod) {
XEUtils.eachTree(afterTreeFullData, (row) => {
const rowid = handleGetRowId(row)
if (checkMethod({ $table: $xeTable, row }) ? false : selectCheckboxMaps[rowid]) {
selectRowMaps[rowid] = row
}
}, { children: childrenField })
}
}
} else {
if (checked) {
/**
* 如果是行勾选
* 如果存在选中方法且成立或者本身已勾选,则添加到临时集合中
* 如果不存在选中方法,则添加所有数据到临时集合中
*/
if (!isForce && checkMethod) {
afterFullData.forEach((row) => {
const rowid = handleGetRowId(row)
if (selectCheckboxMaps[rowid] || checkMethod({ $table: $xeTable, row })) {
selectRowMaps[rowid] = row
}
})
} else {
afterFullData.forEach(row => {
const rowid = handleGetRowId(row)
selectRowMaps[rowid] = row
})
}
} else {
/**
* 如果是行取消
* 如果方法成立,则不添加到临时集合中;如果方法不成立则判断当前是否已勾选,如果已被勾选则添加到新集合中
* 如果不存在选中方法,无需处理,临时集合默认为空
*/
if (!isForce && checkMethod) {
afterFullData.forEach((row) => {
const rowid = handleGetRowId(row)
if (checkMethod({ $table: $xeTable, row }) ? false : selectCheckboxMaps[rowid]) {
selectRowMaps[rowid] = row
}
})
}
}
}
}
if (reserve) {
if (checked) {
XEUtils.each(selectRowMaps, (row, rowid) => {
checkboxReserveRowMap[rowid] = row
})
} else {
afterFullData.forEach((row) => handleCheckboxReserveRow(row, false))
}
}
reactData.updateCheckboxFlag++
internalData.selectCheckboxMaps = checkField ? {} : selectRowMaps
reactData.isAllSelected = checked
reactData.isIndeterminate = false
internalData.treeIndeterminateRowMaps = {}
$xeTable.checkSelectionStatus()
return nextTick()
}
// 还原展开、选中等相关状态
const handleReserveStatus = () => {
const { treeConfig } = props
const { expandColumn, selectRadioRow } = reactData
const { fullDataRowIdData, fullAllDataRowIdData, radioReserveRow, selectCheckboxMaps, treeExpandedMaps, rowExpandedMaps, currentRow } = internalData
const expandOpts = computeExpandOpts.value
const treeOpts = computeTreeOpts.value
const radioOpts = computeRadioOpts.value
const checkboxOpts = computeCheckboxOpts.value
// 单选框
if (selectRadioRow && !fullAllDataRowIdData[getRowid($xeTable, selectRadioRow)]) {
reactData.selectRadioRow = null // 刷新单选行状态
}
// 还原保留选中状态
if (radioOpts.reserve && radioReserveRow) {
const rowid = getRowid($xeTable, radioReserveRow)
if (fullDataRowIdData[rowid]) {
handleCheckedRadioRow(fullDataRowIdData[rowid].row, true)
}
}
// 复选框
internalData.selectCheckboxMaps = getRecoverRowMaps(selectCheckboxMaps) // 刷新多选行状态
reactData.updateCheckboxFlag++
// 还原保留选中状态
if (checkboxOpts.reserve) {
handleCheckedCheckboxRow(handleReserveRow(internalData.checkboxReserveRowMap), true, true)
}
if (currentRow && !fullAllDataRowIdData[getRowid($xeTable, currentRow)]) {
internalData.currentRow = null // 刷新当前行状态
}
// 行展开
internalData.rowExpandedMaps = expandColumn ? getRecoverRowMaps(rowExpandedMaps) : {} // 刷新行展开状态
reactData.rowExpandedFlag++
// 还原保留状态
if (expandColumn && expandOpts.reserve) {
$xeTable.setRowExpand(handleReserveRow(internalData.rowExpandedReserveRowMap), true)
}
// 树展开
internalData.treeExpandedMaps = treeConfig ? getRecoverRowMaps(treeExpandedMaps) : {} // 刷新树展开状态
reactData.treeExpandedFlag++
if (treeConfig && treeOpts.reserve) {
$xeTable.setTreeExpand(handleReserveRow(internalData.treeExpandedReserveRowMap), true)
}
}
/**
* 处理默认展开树节点
*/
const handleDefaultTreeExpand = () => {
const { treeConfig } = props
if (treeConfig) {
const { fullAllDataRowIdData } = internalData
const treeOpts = computeTreeOpts.value
const { expandAll, expandRowKeys } = treeOpts
if (expandAll) {
$xeTable.setAllTreeExpand(true)
} else if (expandRowKeys) {
const defExpandeds: any[] = []
expandRowKeys.forEach((rowKey) => {
const rowid = encodeRowid(rowKey)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
defExpandeds.push(rowRest.row)
}
})
$xeTable.setTreeExpand(defExpandeds, true)
}
}
}
const handleAsyncTreeExpandChilds = (row: any): Promise<void> => {
const treeOpts = computeTreeOpts.value
const checkboxOpts = computeCheckboxOpts.value
const { transform, loadMethod } = treeOpts
const { checkStrictly } = checkboxOpts
return new Promise<void>(resolve => {
if (loadMethod) {
const { fullAllDataRowIdData, treeExpandLazyLoadedMaps } = internalData
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
treeExpandLazyLoadedMaps[rowid] = row
Promise.resolve(
loadMethod({ $table: $xeTable, row })
).then((childRecords: any) => {
if (rowRest) {
rowRest.treeLoaded = true
}
if (treeExpandLazyLoadedMaps[rowid]) {
delete treeExpandLazyLoadedMaps[rowid]
}
if (!XEUtils.isArray(childRecords)) {
childRecords = []
}
if (childRecords) {
return $xeTable.loadTreeChildren(row, childRecords).then(childRows => {
const { treeExpandedMaps } = internalData
if (childRows.length && !treeExpandedMaps[rowid]) {
treeExpandedMaps[rowid] = row
}
reactData.treeExpandedFlag++
// 如果当前节点已选中,则展开后子节点也被选中
if (!checkStrictly && $xeTable.isCheckedByCheckboxRow(row)) {
handleCheckedCheckboxRow(childRows, true)
}
return nextTick().then(() => {
if (transform) {
$xeTable.handleTableData()
updateAfterDataIndex()
return nextTick()
}
})
})
}
}).catch(() => {
const { treeExpandLazyLoadedMaps } = internalData
if (rowRest) {
rowRest.treeLoaded = false
}
if (treeExpandLazyLoadedMaps[rowid]) {
delete treeExpandLazyLoadedMaps[rowid]
}
}).finally(() => {
reactData.treeExpandedFlag++
nextTick().then(() => $xeTable.recalculate()).then(() => resolve())
})
} else {
resolve()
}
})
}
const handleTreeExpandReserve = (row: any, expanded: boolean) => {
const { treeExpandedReserveRowMap } = internalData
const treeOpts = computeTreeOpts.value
if (treeOpts.reserve) {
const rowid = getRowid($xeTable, row)
if (expanded) {
treeExpandedReserveRowMap[rowid] = row
} else if (treeExpandedReserveRowMap[rowid]) {
delete treeExpandedReserveRowMap[rowid]
}
}
}
const handleAsyncRowExpand = (row: any): Promise<void> => {
return new Promise<void>(resolve => {
const expandOpts = computeExpandOpts.value
const { loadMethod } = expandOpts
if (loadMethod) {
const { fullAllDataRowIdData, rowExpandLazyLoadedMaps } = internalData
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
rowExpandLazyLoadedMaps[rowid] = row
loadMethod({ $table: $xeTable, row, rowIndex: $xeTable.getRowIndex(row), $rowIndex: $xeTable.getVMRowIndex(row) }).then(() => {
const { rowExpandedMaps } = internalData
if (rowRest) {
rowRest.expandLoaded = true
}
rowExpandedMaps[rowid] = row
reactData.rowExpandedFlag++
}).catch(() => {
if (rowRest) {
rowRest.expandLoaded = false
}
}).finally(() => {
const { rowExpandLazyLoadedMaps } = internalData
if (rowExpandLazyLoadedMaps[rowid]) {
delete rowExpandLazyLoadedMaps[rowid]
}
reactData.rowExpandedFlag++
nextTick()
.then(() => $xeTable.recalculate())
.then(() => $xeTable.updateCellAreas())
.then(() => resolve())
})
} else {
resolve()
}
})
}
const handleRowExpandReserve = (row: any, expanded: boolean) => {
const { rowExpandedReserveRowMap } = internalData
const expandOpts = computeExpandOpts.value
if (expandOpts.reserve) {
const rowid = getRowid($xeTable, row)
if (expanded) {
rowExpandedReserveRowMap[rowid] = row
} else if (rowExpandedReserveRowMap[rowid]) {
delete rowExpandedReserveRowMap[rowid]
}
}
}
const handleDefaultMergeCells = () => {
const { mergeCells } = props
if (mergeCells) {
$xeTable.setMergeCells(mergeCells)
}
}
const handleDefaultMergeHeaderItems = () => {
const { mergeHeaderCells } = props
if (mergeHeaderCells) {
$xeTable.setMergeHeaderCells(mergeHeaderCells)
}
}
const handleDefaultMergeFooterItems = () => {
const { mergeFooterCells, mergeFooterItems } = props
const mFooterCells = mergeFooterCells || mergeFooterItems
if (mFooterCells) {
$xeTable.setMergeFooterCells(mFooterCells)
}
}
// 计算可视渲染相关数据
const computeScrollLoad = () => {
return nextTick().then(() => {
const { scrollXLoad, scrollYLoad } = reactData
const { scrollXStore, scrollYStore } = internalData
const virtualYOpts = computeVirtualYOpts.value
const virtualXOpts = computeVirtualXOpts.value
// 计算 X 逻辑
if (scrollXLoad) {
const { toVisibleIndex: toXVisibleIndex, visibleSize: visibleXSize } = handleVirtualXVisible()
const offsetXSize = Math.max(0, virtualXOpts.oSize ? XEUtils.toNumber(virtualXOpts.oSize) : 0)
scrollXStore.preloadSize = XEUtils.toNumber(virtualXOpts.preSize)
scrollXStore.offsetSize = offsetXSize
scrollXStore.visibleSize = visibleXSize
scrollXStore.endIndex = Math.max(scrollXStore.startIndex + scrollXStore.visibleSize + offsetXSize, scrollXStore.endIndex)
scrollXStore.visibleStartIndex = Math.max(scrollXStore.startIndex, toXVisibleIndex)
scrollXStore.visibleEndIndex = Math.min(scrollXStore.endIndex, toXVisibleIndex + visibleXSize)
$xeTable.updateScrollXData().then(() => {
loadScrollXData()
})
} else {
$xeTable.updateScrollXSpace()
}
// 计算 Y 逻辑
const rowHeight = computeRowHeight()
;(scrollYStore as any).rowHeight = rowHeight // 已废弃
reactData.rowHeight = rowHeight
const { toVisibleIndex: toYVisibleIndex, visibleSize: visibleYSize } = handleVirtualYVisible()
if (scrollYLoad) {
const offsetYSize = Math.max(0, virtualYOpts.oSize ? XEUtils.toNumber(virtualYOpts.oSize) : 0)
scrollYStore.preloadSize = XEUtils.toNumber(virtualYOpts.preSize)
scrollYStore.offsetSize = offsetYSize
scrollYStore.visibleSize = visibleYSize
scrollYStore.endIndex = Math.max(scrollYStore.startIndex + visibleYSize + offsetYSize, scrollYStore.endIndex)
scrollYStore.visibleStartIndex = Math.max(scrollYStore.startIndex, toYVisibleIndex)
scrollYStore.visibleEndIndex = Math.min(scrollYStore.endIndex, toYVisibleIndex + visibleYSize)
$xeTable.updateScrollYData().then(() => {
loadScrollYData()
})
} else {
$xeTable.updateScrollYSpace()
}
})
}
const calcScrollbar = () => {
const { scrollXWidth, scrollYHeight } = reactData
const { elemStore } = internalData
const scrollbarOpts = computeScrollbarOpts.value
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
const bodyWrapperElem = getRefElem(elemStore['main-body-wrapper'])
const headerTableElem = getRefElem(elemStore['main-header-table'])
const footerTableElem = getRefElem(elemStore['main-footer-table'])
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
let overflowY = false
let overflowX = false
if (bodyWrapperElem) {
overflowY = scrollYHeight > bodyWrapperElem.clientHeight
if (yHandleEl) {
reactData.scrollbarWidth = scrollbarOpts.width || (yHandleEl.offsetWidth - yHandleEl.clientWidth) || 14
}
reactData.overflowY = overflowY
overflowX = scrollXWidth > bodyWrapperElem.clientWidth
if (xHandleEl) {
reactData.scrollbarHeight = scrollbarOpts.height || (xHandleEl.offsetHeight - xHandleEl.clientHeight) || 14
}
const hHeight = headerTableElem ? headerTableElem.clientHeight : 0
const fHeight = footerTableElem ? footerTableElem.clientHeight : 0
internalData.tableHeight = bodyWrapperElem.offsetHeight
internalData.tHeaderHeight = hHeight
internalData.tFooterHeight = fHeight
reactData.overflowX = overflowX
reactData.parentHeight = Math.max(hHeight + fHeight + 20, $xeTable.getParentHeight())
}
if (overflowX) {
$xeTable.checkScrolling()
}
}
const handleRecalculateStyle = (reFull: boolean, reWidth: boolean, reHeight: boolean) => {
const el = refElem.value
if (!el || !el.clientWidth) {
return nextTick()
}
const varEl = refVarElem.value
if (varEl) {
const [defEl, mediumEl, smallEl, miniEl] = varEl.children
calcVarRowHeightConfig('default', defEl)
calcVarRowHeightConfig('medium', mediumEl)
calcVarRowHeightConfig('small', smallEl)
calcVarRowHeightConfig('mini', miniEl)
}
if (reWidth) {
calcCellWidth()
}
if (reWidth || reFull) {
autoCellWidth()
}
calcScrollbar()
updateStyle()
updateRowExpandStyle()
if (reFull) {
updateTreeLineStyle()
}
internalData.rceRunTime = Date.now()
return computeScrollLoad().then(() => {
// 初始化时需要在列计算之后再执行优化运算,达到最优显示效果
if (reWidth) {
calcCellWidth()
}
if (reWidth || reFull) {
autoCellWidth()
}
if (reHeight) {
calcCellHeight()
}
updateStyle()
calcScrollbar()
if (reFull) {
updateRowOffsetTop()
}
updateRowExpandStyle()
if (reFull) {
updateTreeLineStyle()
}
internalData.rceRunTime = Date.now()
if (reFull) {
return computeScrollLoad()
}
})
}
const defaultMinRunDelay = 50
const handleLazyRecalculate = (reFull: boolean, reWidth: boolean, reHeight: boolean, options?: {
minRunDelay?: number // 多少毫秒内至少执行一次
}) => {
const minRunDelay = (options ? options.minRunDelay : defaultMinRunDelay) || defaultMinRunDelay
return new Promise<void>(resolve => {
const $xeGanttView = internalData.xeGanttView
const { customStore, tableData, tableColumn } = reactData
const { rceTimeout, rceRunTime } = internalData
const resizeOpts = computeResizeOpts.value
let rceDelay = internalData.rceDelay
// 如果在500毫秒内频繁执行,则执行次数减缓
if (rceRunTime && rceRunTime > Date.now() - 500) {
rceDelay = Math.min(Math.max(60, Math.min(800, tableData.length / 8)), rceDelay + 50)
} else {
rceDelay = 0
}
internalData.rceDelay = rceDelay
const refreshDelay = resizeOpts.refreshDelay || 30
const reDelay = rceDelay + refreshDelay
const el = refElem.value
if (el && el.clientWidth) {
autoCellWidth()
updateRowExpandStyle()
}
if (customStore.visible && $xeTable.handleCustomStyle) {
$xeTable.handleCustomStyle()
}
const renderCellSize = tableData.length * tableColumn.length
// 渲染量低的场景
if (renderCellSize < 400) {
rceDelay = 20
} else if (renderCellSize < 600) {
rceDelay = 40
} else if (renderCellSize < 800) {
rceDelay = 80
}
if (rceTimeout) {
clearTimeout(rceTimeout)
// 多少毫秒内至少执行一次
if (rceRunTime && rceRunTime + minRunDelay < Date.now()) {
resolve(
handleRecalculateStyle(reFull, reWidth, reHeight)
)
} else {
nextTick(() => {
resolve()
})
}
} else {
resolve(
handleRecalculateStyle(reFull, reWidth, reHeight)
)
}
if ($xeGanttView && $xeGanttView.handleLazyRecalculate) {
$xeGanttView.handleLazyRecalculate()
}
internalData.rceTimeout = setTimeout(() => {
internalData.rceDelay = 0
internalData.rceTimeout = undefined
handleRecalculateStyle(reFull, reWidth, reHeight)
if ($xeGanttView && $xeGanttView.handleLazyRecalculate) {
$xeGanttView.handleLazyRecalculate()
}
}, reDelay)
})
}
const handleResizeEvent = () => {
if (internalData.rsePending) {
return
}
internalData.rsePending = true
handleLazyRecalculate(true, true, true, {
minRunDelay: 200
}).then(() => {
internalData.rsePending = false
}).catch(() => {
internalData.rsePending = false
})
}
const handleUpdateAggValues = () => {
const { visibleColumn } = internalData
const aggCols: VxeTableDefines.ColumnInfo[] = []
visibleColumn.forEach(column => {
if (column.aggFunc) {
aggCols.push(column)
}
})
reactData.aggHandleAggColumns = aggCols
}
const handleUpdateRowGroup = (groupFields?: string[]) => {
const aggGroupFields: string[] = []
const aggGroupConfs: { field: string }[] = []
if (groupFields) {
(XEUtils.isArray(groupFields) ? groupFields : [groupFields]).forEach(field => {
aggGroupFields.push(field)
aggGroupConfs.push({
field
})
})
}
reactData.rowGroupList = aggGroupConfs
reactData.aggHandleFields = aggGroupFields
handleUpdateAggValues()
}
const handleeGroupSummary = (aggList: VxeTableDefines.AggregateRowInfo[]) => {
const { fullColumnFieldData } = internalData
const aggregateOpts = computeAggregateOpts.value
const aggFuncColumns = computeAggFuncColumns.value
const { mapChildrenField } = aggregateOpts
const aggCalcMethod = aggregateOpts.calcValuesMethod || aggregateOpts.countMethod || aggregateOpts.aggregateMethod
if (mapChildrenField) {
XEUtils.lastEach(aggList, aggRow => {
let count = 0
XEUtils.each(aggRow[mapChildrenField], (row: VxeTableDefines.AggregateRowInfo) => {
if (row.isAggregate) {
count += row.childCount || 0
} else {
count++
}
})
aggRow.childCount = count
})
if ($xeTable.handlePivotTableAggData) {
$xeTable.handlePivotTableAggData(aggList)
} else {
if (aggFuncColumns.length) {
XEUtils.lastEach(aggList, aggRow => {
const aggDtObj: Record<string, {
type: string
value: any
label: any
}> = {}
const aggData = aggRow.aggData
const groupField = aggRow.groupField
const groupContent = aggRow.groupContent
const childList = mapChildrenField ? (aggRow[mapChildrenField] || []) : []
const childCount = aggRow.childCount
const colRest = fullColumnFieldData[groupField] || {}
aggFuncColumns.forEach(column => {
const { field } = column
const currAggData = aggData ? aggData[field] : null
const ctParams = {
$table: $xeTable,
groupField,
groupColumn: (colRest ? colRest.column : null) as VxeTableDefines.ColumnInfo,
column,
groupValue: groupContent,
childList,
childCount,
aggValue: currAggData ? currAggData.value : 0,
/**
* 已废弃
* @deprecated
*/
children: childList,
/**
* 已废弃
* @deprecated
*/
totalValue: childCount
}
let aggVal = 0
// 如果下层同时也是分组
if (childList.length && childList[0].isAggregate) {
XEUtils.each(childList, (row: VxeTableDefines.AggregateRowInfo) => {
if (row.isAggregate) {
const currAggData = row.aggData[field]
if (currAggData) {
aggVal += currAggData.value
}
}
})
} else {
aggVal = aggCalcMethod ? aggCalcMethod(ctParams) : aggRow.childCount
}
aggDtObj[field] = {
type: 'count',
value: aggVal,
label: aggVal
}
})
aggRow.aggData = aggDtObj
})
}
}
}
}
const updateGroupData = () => {
const { aggregateConfig, rowGroupConfig } = props
const { isRowGroupStatus } = reactData
const { tableFullGroupData } = internalData
const aggregateOpts = computeAggregateOpts.value
const { mapChildrenField } = aggregateOpts
if ((aggregateConfig || rowGroupConfig) && isRowGroupStatus) {
const aggList: VxeTableDefines.AggregateRowInfo[] = []
XEUtils.eachTree(tableFullGroupData, row => {
if (row.isAggregate) {
aggList.push(row)
}
}, { children: mapChildrenField })
handleeGroupSummary(aggList)
}
}
const handleGroupData = (list: any[], rowGroups: VxeTableDefines.RowGroupItem[], isCombinedField?: boolean) => {
let fullData = list
let treeData = list
if (rowGroups) {
const aggregateOpts = computeAggregateOpts.value
const { rowField, parentField, separator, childrenField, mapChildrenField } = aggregateOpts
const rowOpts = computeRowOpts.value
const checkboxOpts = computeCheckboxOpts.value
const { checkField } = checkboxOpts
const indeterminateField = checkboxOpts.indeterminateField || checkboxOpts.halfField
const rgItem = rowGroups[0]
if (rgItem && rowField && parentField && childrenField && mapChildrenField) {
fullData = []
treeData = []
const groupField = rgItem.field
const groupColumn = $xeTable.getColumnByField(groupField)
const groupMaps: Record<string, any[]> = {}
const groupCombinedMaps: Record<string, any[]> = {}
const aggList: VxeTableDefines.AggregateRowInfo[] = []
const rowkey = getRowkey($xeTable)
list.forEach((row) => {
const cellValue = groupColumn ? $xeTable.getCellLabel(row, groupColumn) : XEUtils.get(row, groupField)
let groupValue = XEUtils.eqNull(cellValue) ? '' : cellValue
if (isCombinedField) {
const groupVals = [groupValue]
rowGroups.slice(1).forEach(ogItem => {
const ogField = ogItem.field
const ogColumn = $xeTable.getColumnByField(ogField)
const ocValue = ogColumn ? $xeTable.getCellLabel(row, ogColumn) : XEUtils.get(row, ogField)
const ogValue = XEUtils.eqNull(ocValue) ? '' : ocValue
groupValue += (separator || ' / ') + ogValue
groupVals.push(ogValue)
})
if (!groupCombinedMaps[groupValue]) {
groupCombinedMaps[groupValue] = groupVals
}
}
let childList = groupMaps[groupValue]
if (!childList) {
childList = []
groupMaps[groupValue] = childList
}
if (row.isAggregate) {
row.isAggregate = undefined
}
childList.push(row)
})
if (isCombinedField) {
XEUtils.objectEach(groupMaps, (childList, groupValue) => {
const aggRow: VxeTableDefines.AggregateRowInfo = {
isAggregate: true,
aggData: {},
groupContent: groupValue,
groupCombineds: groupCombinedMaps[groupValue],
groupField,
childCount: 0,
[rowField]: getRowUniqueId(),
[parentField]: null,
[childrenField]: childList,
[mapChildrenField]: childList
}
aggRow[rowkey] = createRowId(rowOpts, aggRow, rowkey)
if (checkField) {
aggRow[checkField] = false
}
if (indeterminateField) {
aggRow[indeterminateField] = false
}
aggList.push(aggRow)
treeData.push(aggRow)
fullData.push(aggRow)
})
} else {
XEUtils.objectEach(groupMaps, (childList, groupValue) => {
const { fullData: childFullData, treeData: childTreeData } = handleGroupData(childList, rowGroups.slice(1))
const aggRow: VxeTableDefines.AggregateRowInfo = {
isAggregate: true,
aggData: {},
groupContent: groupValue,
groupCombineds: [],
groupField,
childCount: 0,
[rowField]: getRowUniqueId(),
[parentField]: null,
[childrenField]: childTreeData,
[mapChildrenField]: childTreeData
}
aggRow[rowkey] = createRowId(rowOpts, aggRow, rowkey)
if (checkField) {
aggRow[checkField] = false
}
if (indeterminateField) {
aggRow[indeterminateField] = false
}
aggList.push(aggRow)
treeData.push(aggRow)
fullData.push(aggRow)
if (childFullData.length) {
fullData.push(...childFullData)
}
})
}
handleeGroupSummary(aggList)
}
}
return {
treeData,
fullData
}
}
const initGroupData = (list: any[], rowGroups: VxeTableDefines.RowGroupItem[]) => {
const aggregateOpts = computeAggregateOpts.value
const { mode } = aggregateOpts
const isCombinedField = mode === 'combined'
const groupRest = handleGroupData(list, rowGroups, isCombinedField)
return groupRest
}
const initData = () => {
const { data } = props
loadTableData(data || [], true, true).then(() => {
if (data && data.length) {
internalData.inited = true
internalData.initStatus = true
handleLoadDefaults()
}
handleInitDefaults()
updateStyle()
if (!reactData.isAllOverflow) {
calcCellHeight()
updateRowOffsetTop()
}
})
nextTick(() => {
dispatchEvent('ready', {}, null)
})
}
/**
* 加载表格数据
* @param {Array} datas 数据
*/
const loadTableData = (datas: any[], isReload: boolean, isReset: boolean) => {
const { keepSource, treeConfig, rowGroupConfig, aggregateConfig } = props
const { rowGroupList, scrollYLoad: oldScrollYLoad } = reactData
const { initStatus, scrollYStore, scrollXStore, lastScrollLeft, lastScrollTop } = internalData
const rowOpts = computeRowOpts.value
const treeOpts = computeTreeOpts.value
const expandOpts = computeExpandOpts.value
const { transform } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
let treeData = []
let fullData = reactive(datas ? datas.slice(0) : []) // 转为响应式数据
if (fullData.length > supportMaxRow) {
errLog('vxe.error.errMaxRow', [supportMaxRow])
}
if (treeConfig && rowGroupList.length) {
errLog('vxe.error.noTree', ['aggregate-config'])
return nextTick()
}
if (rowOpts.drag && rowGroupList.length) {
errLog('vxe.error.errConflicts', ['row-config.drag', 'aggregate-config'])
return nextTick()
}
let isRGroup = false
if (treeConfig) {
if (transform) {
// 树结构自动转换
if (!treeOpts.rowField) {
errLog('vxe.error.reqProp', ['tree-config.rowField'])
}
if (!treeOpts.parentField) {
errLog('vxe.error.reqProp', ['tree-config.parentField'])
}
if (!childrenField) {
errLog('vxe.error.reqProp', ['tree-config.childrenField'])
}
if (!treeOpts.mapChildrenField) {
errLog('vxe.error.reqProp', ['tree-config.mapChildrenField'])
}
if (childrenField === treeOpts.mapChildrenField) {
errLog('vxe.error.errConflicts', ['tree-config.childrenField', 'tree-config.mapChildrenField'])
}
// fullData.forEach(row => {
// if (row[treeOpts.children] && row[treeOpts.children].length) {
// warnLog('vxe.error.errConflicts', ['tree-config.transform', `row.${treeOpts.children}`])
// }
// })
treeData = XEUtils.toArrayTree(fullData, {
key: treeOpts.rowField,
parentKey: treeOpts.parentField,
children: childrenField,
mapChildren: treeOpts.mapChildrenField
})
fullData = treeData.slice(0)
} else {
treeData = fullData.slice(0)
}
} else if ((aggregateConfig || rowGroupConfig) && rowGroupList.length) {
const groupRest = initGroupData(fullData, rowGroupList)
treeData = groupRest.treeData
fullData = groupRest.fullData
isRGroup = true
}
reactData.isRowGroupStatus = isRGroup
scrollYStore.startIndex = 0
scrollYStore.endIndex = 1
scrollXStore.startIndex = 0
scrollXStore.endIndex = 1
internalData.cvCacheMaps = {}
reactData.isRowLoading = true
reactData.scrollVMLoading = false
reactData.treeExpandedFlag++
reactData.rowExpandedFlag++
internalData.insertRowMaps = {}
reactData.insertRowFlag++
internalData.removeRowMaps = {}
reactData.removeRowFlag++
internalData.fullCellHeightMaps = {}
internalData.fullCellStoreMaps = {}
const sYLoad = updateScrollYStatus(fullData)
// 全量数据
internalData.tableFullData = fullData
internalData.tableFullTreeData = isRGroup ? [] : treeData
internalData.tableFullGroupData = isRGroup ? treeData : []
// 缓存数据
$xeTable.cacheRowMap(isReset)
// 原始数据
internalData.tableSynchData = datas
if (isReset) {
internalData.isResizeCellHeight = false
}
// 克隆原数据,用于显示编辑状态,与编辑值做对比
if (keepSource) {
$xeTable.cacheSourceMap(fullData)
}
if ($xeTable.clearCellAreas && props.mouseConfig) {
$xeTable.clearCellAreas()
$xeTable.clearCopyCellArea()
}
$xeTable.clearMergeCells()
$xeTable.clearMergeFooterItems()
$xeTable.clearHistory()
$xeTable.handleTableData(true)
$xeTable.updateFooter()
$xeTable.handleUpdateBodyMerge()
return nextTick().then(() => {
updateHeight()
updateStyle()
}).then(() => {
computeScrollLoad()
syncGanttScrollYStatus()
}).then(() => {
const virtualYOpts = computeVirtualYOpts.value
// 是否启用了虚拟滚动
if (sYLoad) {
scrollYStore.endIndex = scrollYStore.visibleSize
}
if (sYLoad) {
if (reactData.expandColumn && expandOpts.mode !== 'fixed') {
errLog('vxe.error.notConflictProp', ['column.type="expand', 'expand-config.mode="fixed"'])
}
if (virtualYOpts.mode === 'scroll' && reactData.expandColumn && expandOpts.mode === 'fixed') {
warnLog('vxe.error.notConflictProp', ['virtual-y-config.mode=scroll', 'expand-config.mode=inside'])
}
// if (showOverflow) {
// if (!rowOpts.height) {
// const errColumn = internalData.tableFullColumn.find(column => column.showOverflow === false)
// if (errColumn) {
// errLog('vxe.error.errProp', [`column[field="${errColumn.field}"].show-overflow=false`, 'show-overflow=true'])
// }
// }
// }
if (!(props.height || props.maxHeight)) {
errLog('vxe.error.reqSupportProp', ['virtual-y-config.enabled = true', 'height | max-height'])
}
// if (!props.showOverflow) {
// warnLog('vxe.error.reqProp', ['table.show-overflow'])
// }
// if (props.spanMethod) {
// warnLog('vxe.error.scrollErrProp', ['span-method'])
// }
}
handleReserveStatus()
$xeTable.checkSelectionStatus()
$xeTable.dispatchEvent('data-change', {
visibleColumn: internalData.visibleColumn,
visibleData: internalData.afterFullData
}, null)
return new Promise<void>(resolve => {
nextTick()
.then(() => handleRecalculateStyle(false, false, false))
.then(() => {
handleRecalculateStyle(false, true, true)
updateRowOffsetTop()
})
.then(() => {
let targetScrollLeft = lastScrollLeft
let targetScrollTop = lastScrollTop
const virtualXOpts = computeVirtualXOpts.value
const virtualYOpts = computeVirtualYOpts.value
// 是否在更新数据之后自动滚动重置滚动条
if (virtualXOpts.scrollToLeftOnChange) {
targetScrollLeft = 0
}
if (virtualYOpts.scrollToTopOnChange) {
targetScrollTop = 0
}
reactData.isRowLoading = false
handleRecalculateStyle(false, false, false)
updateTreeLineStyle()
syncGanttScrollYStatus()
// 如果是自动行高,特殊情况需调用 recalculate 手动刷新
if (!props.showOverflow) {
setTimeout(() => {
handleLazyRecalculate(false, true, true)
setTimeout(() => handleLazyRecalculate(false, true, true), 3000)
}, 2000)
}
// 是否变更虚拟滚动
if (oldScrollYLoad === sYLoad) {
restoreScrollLocation($xeTable, targetScrollLeft, targetScrollTop)
.then(() => {
handleRecalculateStyle(false, true, true)
updateRowOffsetTop()
updateTreeLineStyle()
nextTick(() => resolve())
})
} else {
setTimeout(() => {
restoreScrollLocation($xeTable, targetScrollLeft, targetScrollTop)
.then(() => {
handleRecalculateStyle(false, true, true)
updateRowOffsetTop()
updateTreeLineStyle()
nextTick(() => resolve())
})
})
}
})
}).then(() => {
if (initStatus) {
dispatchEvent('data-rendered', {
isReload,
visibleColumn: internalData.visibleColumn,
visibleData: internalData.afterFullData
}, null)
} else {
dispatchEvent('init-rendered', {
visibleColumn: internalData.visibleColumn,
visibleData: internalData.afterFullData
}, null)
}
})
})
}
/**
* 处理数据加载默认行为
* 默认执行一次,除非被重置
*/
const handleLoadDefaults = () => {
handleDefaultSelectionChecked()
handleDefaultRadioChecked()
handleDefaultRowExpand()
handleDefaultTreeExpand()
handleDefaultRowGroupExpand()
handleDefaultMergeCells()
handleDefaultMergeHeaderItems()
handleDefaultMergeFooterItems()
nextTick(() => {
setTimeout(() => $xeTable.recalculate())
})
}
/**
* 处理初始化的默认行为
* 只会执行一次
*/
const handleInitDefaults = () => {
handleDefaultSort()
}
const handleTableColumn = () => {
const { scrollXLoad } = reactData
const { visibleColumn, scrollXStore, fullColumnIdData } = internalData
const tableColumn = scrollXLoad ? visibleColumn.slice(scrollXStore.startIndex, scrollXStore.endIndex) : visibleColumn.slice(0)
tableColumn.forEach((column, $index) => {
const colid = column.id
const colRest = fullColumnIdData[colid]
if (colRest) {
colRest.$index = $index
}
})
reactData.tableColumn = tableColumn
}
const handleUpdateColumn = () => {
const columnList = XEUtils.orderBy(internalData.collectColumn, 'renderSortNumber')
internalData.collectColumn = columnList
const tFullColumn = getColumnList(columnList)
internalData.tableFullColumn = tFullColumn
reactData.updateColFlag++
cacheColumnMap()
}
const loadScrollXData = () => {
const { isScrollXBig } = reactData
const { mergeBodyList, mergeFooterList, mergeBodyRowMaps, scrollXStore } = internalData
const { preloadSize, startIndex, endIndex, offsetSize } = scrollXStore
const { toVisibleIndex, visibleSize } = handleVirtualXVisible()
const offsetItem: VxeTableDefines.MergeCacheCol = {
startIndex: Math.max(0, isScrollXBig ? toVisibleIndex - 1 : toVisibleIndex - 1 - offsetSize - preloadSize),
endIndex: isScrollXBig ? toVisibleIndex + visibleSize : toVisibleIndex + visibleSize + offsetSize + preloadSize
}
scrollXStore.visibleStartIndex = toVisibleIndex - 1
scrollXStore.visibleEndIndex = toVisibleIndex + visibleSize + 1
calculateMergerOffsetIndex(mergeBodyList.concat(mergeFooterList), mergeBodyRowMaps, offsetItem, 'col')
const { startIndex: offsetStartIndex, endIndex: offsetEndIndex } = offsetItem
if (toVisibleIndex <= startIndex || toVisibleIndex >= endIndex - visibleSize - 1) {
if (startIndex !== offsetStartIndex || endIndex !== offsetEndIndex) {
scrollXStore.startIndex = offsetStartIndex
scrollXStore.endIndex = offsetEndIndex
$xeTable.updateScrollXData()
}
}
$xeTable.closeTooltip()
}
const parseColumns = (isReset: boolean) => {
// const { showOverflow } = props
// const rowOpts = computeRowOpts.value
const leftList: VxeTableDefines.ColumnInfo[] = []
const centerList: VxeTableDefines.ColumnInfo[] = []
const rightList: VxeTableDefines.ColumnInfo[] = []
const { isGroup, columnStore } = reactData
const { collectColumn, tableFullColumn, scrollXStore, fullColumnIdData } = internalData
// 如果是分组表头,如果子列全部被隐藏,则根列也隐藏
if (isGroup) {
const leftGroupList: VxeTableDefines.ColumnInfo[] = []
const centerGroupList: VxeTableDefines.ColumnInfo[] = []
const rightGroupList: VxeTableDefines.ColumnInfo[] = []
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn) => {
const isColGroup = hasChildrenList(column)
// 如果是分组,必须按组设置固定列,不允许给子列设置固定
if (parentColumn && parentColumn.fixed) {
column.fixed = parentColumn.fixed
}
if (parentColumn && (column.fixed || '') !== (parentColumn.fixed || '')) {
errLog('vxe.error.groupFixed')
}
if (isColGroup) {
column.visible = !!XEUtils.findTree(column.children, (subColumn) => hasChildrenList(subColumn) ? false : subColumn.visible)
} else if (column.visible) {
if (column.fixed === 'left') {
leftList.push(column)
} else if (column.fixed === 'right') {
rightList.push(column)
} else {
centerList.push(column)
}
}
})
collectColumn.forEach((column) => {
if (column.visible) {
if (column.fixed === 'left') {
leftGroupList.push(column)
} else if (column.fixed === 'right') {
rightGroupList.push(column)
} else {
centerGroupList.push(column)
}
}
})
reactData.tableGroupColumn = leftGroupList.concat(centerGroupList).concat(rightGroupList)
} else {
// 重新分配列
tableFullColumn.forEach((column) => {
if (column.visible) {
if (column.fixed === 'left') {
leftList.push(column)
} else if (column.fixed === 'right') {
rightList.push(column)
} else {
centerList.push(column)
}
}
})
}
const visibleColumn = leftList.concat(centerList).concat(rightList)
internalData.visibleColumn = visibleColumn
updateColumnOffsetLeft()
const sXLoad = updateScrollXStatus()
reactData.hasFixedColumn = leftList.length > 0 || rightList.length > 0
Object.assign(columnStore, { leftList, centerList, rightList })
if (sXLoad) {
// if (showOverflow) {
// if (!rowOpts.height) {
// const errColumn = internalData.tableFullColumn.find(column => column.showOverflow === false)
// if (errColumn) {
// errLog('vxe.error.errProp', [`column[field="${errColumn.field}"].show-overflow=false`, 'show-overflow=true'])
// }
// }
// }
// if (props.showHeader && !props.showHeaderOverflow) {
// warnLog('vxe.error.reqProp', ['show-header-overflow'])
// }
// if (props.showFooter && !props.showFooterOverflow) {
// warnLog('vxe.error.reqProp', ['show-footer-overflow'])
// }
if (props.spanMethod) {
warnLog('vxe.error.scrollErrProp', ['span-method'])
}
if (props.footerSpanMethod) {
warnLog('vxe.error.scrollErrProp', ['footer-span-method'])
}
if (isReset) {
const { visibleSize } = handleVirtualXVisible()
scrollXStore.startIndex = 0
scrollXStore.endIndex = visibleSize
scrollXStore.visibleSize = visibleSize
scrollXStore.visibleStartIndex = 0
scrollXStore.visibleEndIndex = visibleSize
}
}
// 如果列被显示/隐藏,则清除合并状态
// 如果列被设置为固定,则清除合并状态
if (visibleColumn.length !== internalData.visibleColumn.length || !internalData.visibleColumn.every((column, index) => column === visibleColumn[index])) {
$xeTable.clearMergeCells()
$xeTable.clearMergeFooterItems()
}
visibleColumn.forEach((column, index) => {
const colid = column.id
const colRest = fullColumnIdData[colid]
if (colRest) {
colRest._index = index
}
})
handleTableColumn()
handleUpdateAggValues()
if (isReset) {
updateColumnOffsetLeft()
return $xeTable.updateFooter().then(() => {
return $xeTable.recalculate()
}).then(() => {
$xeTable.updateCellAreas()
return $xeTable.recalculate()
})
}
return $xeTable.updateFooter()
}
const initColumnHierarchy = () => {
const { collectColumn } = internalData
const fullColIdData: Record<string, VxeTableDefines.ColumnCacheItem> = {}
const fullColFieldData: Record<string, VxeTableDefines.ColumnCacheItem> = {}
let sortIndex = 1
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn) => {
const { id: colid, field } = column
const parentId = parentColumn ? parentColumn.id : null
const rest = { $index: -1, _index: -1, column, colid, index, items, parent: parentColumn || null, width: 0, oLeft: 0 }
column.parentId = parentId
column.defaultParentId = parentId
column.sortNumber = sortIndex
column.renderSortNumber = sortIndex
sortIndex++
if (field) {
if (fullColFieldData[field]) {
errLog('vxe.error.colRepet', ['field', field])
}
fullColFieldData[field] = rest
}
fullColIdData[colid] = rest
})
internalData.fullColumnIdData = fullColIdData
internalData.fullColumnFieldData = fullColFieldData
}
const buildColumnInfo = () => {
const { scrollXLoad, scrollYLoad, expandColumn } = reactData
const expandOpts = computeExpandOpts.value
cacheColumnMap()
parseColumns(true).then(() => {
if (reactData.scrollXLoad) {
loadScrollXData()
}
})
$xeTable.clearHeaderFormatterCache()
$xeTable.clearMergeCells()
$xeTable.clearMergeFooterItems()
$xeTable.handleTableData(true)
$xeTable.handleAggregateSummaryData()
if ((scrollXLoad || scrollYLoad) && (expandColumn && expandOpts.mode !== 'fixed')) {
warnLog('vxe.error.scrollErrProp', ['column.type=expand'])
}
return nextTick().then(() => {
if ($xeToolbar) {
$xeToolbar.syncUpdate({
collectColumn: internalData.collectColumn,
$table: $xeTable
})
}
if ($xeTable.handleUpdateCustomColumn) {
$xeTable.handleUpdateCustomColumn()
}
const columnOpts = computeColumnOpts.value
if (props.showCustomHeader && reactData.isGroup && (columnOpts.resizable || props.resizable)) {
warnLog('vxe.error.notConflictProp', ['show-custom-header & colgroup', 'column-config.resizable=false'])
}
reactData.isColLoading = false
return handleLazyRecalculate(false, true, true)
})
}
const handleInitColumn = (collectColumn: VxeTableDefines.ColumnInfo[]) => {
internalData.collectColumn = collectColumn
const tFullColumn = getColumnList(collectColumn)
internalData.tableFullColumn = tFullColumn
reactData.updateColFlag++
reactData.isColLoading = true
initColumnHierarchy()
return Promise.resolve(
restoreCustomStorage()
).then(() => {
return buildColumnInfo()
})
}
const updateScrollXStatus = (fullColumn?: any[]) => {
const virtualXOpts = computeVirtualXOpts.value
const allCols = fullColumn || internalData.tableFullColumn
// 如果gt为0,则总是启用
const scrollXLoad = !!virtualXOpts.enabled && virtualXOpts.gt > -1 && (virtualXOpts.gt === 0 || virtualXOpts.gt < allCols.length)
reactData.scrollXLoad = scrollXLoad
return scrollXLoad
}
const syncGanttScrollYStatus = () => {
const $xeGanttView = internalData.xeGanttView
if ($xeGanttView && $xeGanttView.handleUpdateSYStatus) {
$xeGanttView.handleUpdateSYStatus(reactData.scrollYLoad)
}
}
const updateScrollYStatus = (fullData?: any[]) => {
const { treeConfig } = props
const virtualYOpts = computeVirtualYOpts.value
const treeOpts = computeTreeOpts.value
const { transform } = treeOpts
const allList = fullData || internalData.tableFullData
// 如果gt为0,则总是启用
let scrollYLoad = !!virtualYOpts.enabled && virtualYOpts.gt > -1 && (virtualYOpts.gt === 0 || virtualYOpts.gt < allList.length)
if (scrollYLoad && (treeConfig && !transform)) {
errLog('vxe.error.notSupportProp', ['[table] virtual-y-config.enabled=true', 'tree-config.transform=false', 'tree-config.transform=true'])
scrollYLoad = false
}
reactData.scrollYLoad = scrollYLoad
syncGanttScrollYStatus()
return scrollYLoad
}
/**
* 展开与收起树节点
* @param rows
* @param expanded
* @returns
*/
const handleBaseTreeExpand = (rows: any[], expanded: boolean) => {
const { treeNodeColumn } = reactData
const { fullAllDataRowIdData, tableFullTreeData, treeExpandedMaps, treeExpandLazyLoadedMaps } = internalData
const treeOpts = computeTreeOpts.value
const { reserve, lazy, accordion, toggleMethod } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const hasChildField = treeOpts.hasChild || treeOpts.hasChildField
const result: any[] = []
const columnIndex = $xeTable.getColumnIndex(treeNodeColumn)
const $columnIndex = $xeTable.getVMColumnIndex(treeNodeColumn)
const _columnIndex = $xeTable.getVTColumnIndex(treeNodeColumn)
const { handleGetRowId } = createHandleGetRowId($xeTable)
let validRows = toggleMethod ? rows.filter((row: any) => toggleMethod({ $table: $xeTable, expanded, column: treeNodeColumn, columnIndex, $columnIndex, _columnIndex, row })) : rows
if (accordion) {
validRows = validRows.length ? [validRows[validRows.length - 1]] : []
// 同一级只能展开一个
const matchObj = XEUtils.findTree(tableFullTreeData, item => item === validRows[0], { children: childrenField })
if (matchObj) {
matchObj.items.forEach(item => {
const rowid = handleGetRowId(item)
if (treeExpandedMaps[rowid]) {
delete treeExpandedMaps[rowid]
}
})
}
}
if (expanded) {
validRows.forEach((row: any) => {
const rowid = handleGetRowId(row)
if (!treeExpandedMaps[rowid]) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const isLoad = lazy && row[hasChildField] && !rowRest.treeLoaded && !treeExpandLazyLoadedMaps[rowid]
// 是否使用懒加载
if (isLoad) {
result.push(handleAsyncTreeExpandChilds(row))
} else {
if (row[childrenField] && row[childrenField].length) {
treeExpandedMaps[rowid] = row
}
}
}
}
})
} else {
validRows.forEach(item => {
const rowid = handleGetRowId(item)
if (treeExpandedMaps[rowid]) {
delete treeExpandedMaps[rowid]
}
})
}
if (reserve) {
validRows.forEach((row: any) => handleTreeExpandReserve(row, expanded))
}
reactData.treeExpandedFlag++
return Promise.all(result).then(() => {
updateTreeLineStyle()
return $xeTable.recalculate()
})
}
/**
* 虚拟树的展开与收起
* @param rows
* @param expanded
* @returns
*/
const handleVirtualTreeExpand = (rows: any[], expanded: boolean) => {
const { lastScrollLeft, lastScrollTop } = internalData
return handleBaseTreeExpand(rows, expanded).then(() => {
handleVirtualTreeToList()
$xeTable.handleTableData()
reactData.treeExpandedFlag++
updateAfterDataIndex()
return nextTick()
}).then(() => {
return handleRecalculateStyle(true, true, true)
}).then(() => {
restoreScrollLocation($xeTable, lastScrollLeft, lastScrollTop)
updateTreeLineStyle()
setTimeout(() => {
$xeTable.updateCellAreas()
}, 30)
})
}
/**
* 展开与收起行分组节点
* @param rows
* @param expanded
* @returns
*/
const handleRowGroupBaseExpand = (rows: any[], expanded: boolean) => {
const { fullAllDataRowIdData, tableFullGroupData, rowGroupExpandedMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const { mapChildrenField, accordion } = aggregateOpts
const { handleGetRowId } = createHandleGetRowId($xeTable)
let validRows = rows
if (mapChildrenField) {
if (accordion) {
validRows = validRows.length ? [validRows[validRows.length - 1]] : []
// 同一级只能展开一个
const matchObj = XEUtils.findTree(tableFullGroupData, item => getRowid($xeTable, item) === getRowid($xeTable, validRows[0]), { children: mapChildrenField })
if (matchObj) {
matchObj.items.forEach(item => {
const rowid = handleGetRowId(item)
if (rowGroupExpandedMaps[rowid]) {
delete rowGroupExpandedMaps[rowid]
}
})
}
}
if (expanded) {
validRows.forEach((row) => {
const rowid = handleGetRowId(row)
if (!rowGroupExpandedMaps[rowid]) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
if (row[mapChildrenField] && row[mapChildrenField].length) {
rowGroupExpandedMaps[rowid] = row
}
}
}
})
} else {
validRows.forEach(item => {
const rowid = handleGetRowId(item)
if (rowGroupExpandedMaps[rowid]) {
delete rowGroupExpandedMaps[rowid]
}
})
}
}
reactData.rowGroupExpandedFlag++
return $xeTable.recalculate()
}
/**
* 行分组的展开与收起
* @param rows
* @param expanded
* @returns
*/
const handleRowGroupVirtualExpand = (rows: any[], expanded: boolean) => {
return handleRowGroupBaseExpand(rows, expanded).then(() => {
handleVirtualTreeToList()
$xeTable.handleTableData()
reactData.rowGroupExpandedFlag++
updateAfterDataIndex()
return nextTick()
}).then(() => {
return handleLazyRecalculate(true, true, true)
}).then(() => {
setTimeout(() => {
$xeTable.updateCellAreas()
}, 30)
})
}
/**
* 处理默认展开分组行
*/
const handleDefaultRowGroupExpand = () => {
const { isRowGroupStatus } = reactData
if (isRowGroupStatus) {
const aggregateOpts = computeAggregateOpts.value
const { expandAll, expandGroupFields } = aggregateOpts
if (expandAll) {
$xeTable.setAllRowGroupExpand(true)
} else if (expandGroupFields && expandGroupFields.length) {
$xeTable.setRowGroupExpandByField(expandGroupFields, true)
}
}
}
const handleCheckAllEvent = (evnt: Event | null, value: any) => {
handleCheckedAllCheckboxRow(value)
if (evnt) {
dispatchEvent('checkbox-all', {
records: () => $xeTable.getCheckboxRecords(),
reserves: () => $xeTable.getCheckboxReserveRecords(),
indeterminates: () => $xeTable.getCheckboxIndeterminateRecords(),
checked: value
}, evnt)
}
}
/**
* 纵向 Y 可视渲染处理
*/
const loadScrollYData = () => {
const { isAllOverflow, isScrollYBig } = reactData
const { mergeBodyList, mergeBodyColMaps, scrollYStore } = internalData
const { preloadSize, startIndex, endIndex, offsetSize } = scrollYStore
const autoOffsetYSize = isAllOverflow ? offsetSize : offsetSize + 1
const { toVisibleIndex, visibleSize } = handleVirtualYVisible()
const offsetItem: VxeTableDefines.MergeCacheRow = {
startIndex: Math.max(0, isScrollYBig ? toVisibleIndex - 1 : toVisibleIndex - 1 - offsetSize - preloadSize),
endIndex: isScrollYBig ? (toVisibleIndex + visibleSize) : (toVisibleIndex + visibleSize + autoOffsetYSize + preloadSize)
}
scrollYStore.visibleStartIndex = toVisibleIndex - 1
scrollYStore.visibleEndIndex = toVisibleIndex + visibleSize + 1
calculateMergerOffsetIndex(mergeBodyList, mergeBodyColMaps, offsetItem, 'row')
const { startIndex: offsetStartIndex, endIndex: offsetEndIndex } = offsetItem
if (toVisibleIndex <= startIndex || toVisibleIndex >= endIndex - visibleSize - 1) {
if (startIndex !== offsetStartIndex || endIndex !== offsetEndIndex) {
scrollYStore.startIndex = offsetStartIndex
scrollYStore.endIndex = offsetEndIndex
$xeTable.updateScrollYData()
}
}
}
const createGetRowCacheProp = (prop: 'seq' | 'index' | '_index' | '$index') => {
return function (row: any) {
const { fullAllDataRowIdData } = internalData
if (row) {
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
return rowRest[prop]
}
}
return -1
}
}
const createGetColumnCacheProp = (prop: 'index' | '_index' | '$index') => {
return function (column: VxeTableDefines.ColumnInfo) {
const { fullColumnIdData } = internalData
if (column) {
const colRest = fullColumnIdData[column.id]
if (colRest) {
return colRest[prop]
}
}
return -1
}
}
const lazyScrollXData = () => {
const { lxTimeout, lxRunTime, scrollXStore } = internalData
const { visibleSize } = scrollXStore
const fpsTime = visibleSize > 26 ? 26 : (visibleSize > 16 ? 14 : 6)
if (lxTimeout) {
clearTimeout(lxTimeout)
}
if (!lxRunTime || lxRunTime + fpsTime < Date.now()) {
internalData.lxRunTime = Date.now()
loadScrollXData()
}
internalData.lxTimeout = setTimeout(() => {
internalData.lxTimeout = undefined
internalData.lxRunTime = undefined
loadScrollXData()
}, fpsTime)
}
const lazyScrollYData = () => {
const { lyTimeout, lyRunTime, scrollYStore } = internalData
const { visibleSize } = scrollYStore
const fpsTime = visibleSize > 30 ? 32 : (visibleSize > 20 ? 18 : 8)
if (lyTimeout) {
clearTimeout(lyTimeout)
}
if (!lyRunTime || lyRunTime + fpsTime < Date.now()) {
internalData.lyRunTime = Date.now()
loadScrollYData()
}
internalData.lyTimeout = setTimeout(() => {
internalData.lyTimeout = undefined
internalData.lyRunTime = undefined
loadScrollYData()
}, fpsTime)
}
const handleSyncScroll = (isRollX: boolean, isRollY: boolean) => {
const { scrollXLoad, scrollYLoad, isAllOverflow } = reactData
const autoWidthColumnList = computeAutoWidthColumnList.value
internalData.lcsRunTime = Date.now()
internalData.lcsTimeout = undefined
internalData.intoRunScroll = false
internalData.inVirtualScroll = false
internalData.inWheelScroll = false
internalData.inHeaderScroll = false
internalData.inBodyScroll = false
internalData.inFooterScroll = false
reactData.lazScrollLoading = false
internalData.scrollRenderType = ''
let xRest = null
let yRest = null
if (!isAllOverflow) {
calcCellHeight()
updateRowOffsetTop()
}
if (isRollX && scrollXLoad) {
xRest = $xeTable.updateScrollXData()
}
if (isRollY && scrollYLoad) {
if (autoWidthColumnList.length) {
calcCellWidth()
}
yRest = $xeTable.updateScrollYData().then(() => {
if (autoWidthColumnList.length) {
calcCellWidth()
autoCellWidth()
}
if (!isAllOverflow) {
calcCellHeight()
updateRowOffsetTop()
}
return $xeTable.updateScrollYSpace()
})
}
updateRowExpandStyle()
return Promise.all([
xRest,
yRest,
scrollXLoad || scrollYLoad ? $xeTable.updateCellAreas() : null
])
}
const checkLastSyncScroll = (isRollX: boolean, isRollY: boolean) => {
const { lcsTimeout } = internalData
reactData.lazScrollLoading = true
if (lcsTimeout) {
clearTimeout(lcsTimeout)
}
internalData.lcsTimeout = setTimeout(() => {
handleSyncScroll(isRollX, isRollY).then(() => {
if (reactData.scrollXLoad || reactData.scrollYLoad) {
nextTick(() => {
updateRowExpandStyle()
if (!internalData.lcsTimeout) {
handleSyncScroll(isRollX, isRollY)
}
})
}
})
}, 240)
}
const getWheelSpeed = (lastScrollTime: number) => {
let multiple = 1
const currTime = Date.now()
if (lastScrollTime + 25 > currTime) {
multiple = 1.18
} else if (lastScrollTime + 30 > currTime) {
multiple = 1.15
} else if (lastScrollTime + 40 > currTime) {
multiple = 1.12
} else if (lastScrollTime + 55 > currTime) {
multiple = 1.09
} else if (lastScrollTime + 75 > currTime) {
multiple = 1.06
} else if (lastScrollTime + 100 > currTime) {
multiple = 1.03
}
return multiple
}
const syncGanttScrollTop = (scrollTop: number) => {
const $xeGanttView = internalData.xeGanttView
if ($xeGanttView) {
const ganttInternalData = $xeGanttView.internalData
const { elemStore: ganttElemStore } = ganttInternalData
const ganttBodyScrollElem = getRefElem(ganttElemStore['main-body-scroll'])
if (ganttBodyScrollElem) {
ganttBodyScrollElem.scrollTop = scrollTop
}
}
}
const dispatchEvent = (type: ValueOf<VxeTableEmits>, params: Record<string, any>, evnt: Event | null) => {
emit(type, createEvent(evnt, { $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }, params))
}
const handleScrollToRowColumn = (fieldOrColumn: string | VxeTableDefines.ColumnInfo | null, row: any, options?: VxeTableDefines.ScrollToRowConfig) => {
const { fullColumnIdData } = internalData
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column && fullColumnIdData[column.id]) {
return colToVisible($xeTable, options ? options.colAlign !== false : true, column, row)
}
return nextTick()
}
const handleUpdateResize = () => {
const el = refElem.value
if (el && el.clientWidth && el.clientHeight) {
$xeTable.recalculate()
}
}
const handleUpdateColResize = (evnt: MouseEvent, params: any) => {
$xeTable.analyColumnWidth()
$xeTable.recalculate().then(() => {
$xeTable.saveCustomStore('update:width')
$xeTable.refreshScroll().then(() => {
$xeTable.updateCellAreas()
})
$xeTable.dispatchEvent('column-resizable-change', params, evnt)
// 已废弃 resizable-change
$xeTable.dispatchEvent('resizable-change', params, evnt)
setTimeout(() => {
$xeTable.recalculate(true)
}, 300)
})
}
const handleUpdateRowResize = (evnt: MouseEvent, params: any) => {
reactData.resizeHeightFlag++
$xeTable.recalculate().then(() => {
$xeTable.refreshScroll().then(() => {
$xeTable.updateCellAreas()
})
$xeTable.dispatchEvent('row-resizable-change', params, evnt)
setTimeout(() => {
$xeTable.recalculate(true)
}, 300)
})
}
const updateColumnOffsetLeft = () => {
const { visibleColumn, fullColumnIdData } = internalData
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
let offsetLeft = 0
for (let cIndex = 0, rLen = visibleColumn.length; cIndex < rLen; cIndex++) {
const column = visibleColumn[cIndex]
const colid = column.id
const colRest = fullColumnIdData[colid]
if (colRest) {
colRest.oLeft = offsetLeft
}
offsetLeft += column.renderWidth
}
}
const updateRowOffsetTop = () => {
const { expandColumn } = reactData
const { afterFullData, fullAllDataRowIdData, rowExpandedMaps } = internalData
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
const expandOpts = computeExpandOpts.value
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const { handleGetRowId } = createHandleGetRowId($xeTable)
let offsetTop = 0
for (let rIndex = 0, rLen = afterFullData.length; rIndex < rLen; rIndex++) {
const row = afterFullData[rIndex]
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid] || {}
rowRest.oTop = offsetTop
offsetTop += getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
// 是否展开行
if (expandColumn && rowExpandedMaps[rowid]) {
offsetTop += rowRest.expandHeight || expandOpts.height || 0
}
}
}
/**
* 更新展开行样式
*/
const updateRowExpandStyle = () => {
const { expandColumn, scrollYLoad, scrollYTop, isScrollYBig } = reactData
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
const expandOpts = computeExpandOpts.value
const { mode } = expandOpts
if (expandColumn && mode === 'fixed') {
const { elemStore, fullAllDataRowIdData } = internalData
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const rowExpandEl = refRowExpandElem.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
if (rowExpandEl && bodyScrollElem) {
let isUpdateHeight = false
XEUtils.arrayEach(rowExpandEl.children, reEl => {
const expandEl = reEl as HTMLDivElement
const rowid = expandEl.getAttribute('rowid') || ''
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const expandHeight = expandEl.offsetHeight
const trEl = bodyScrollElem.querySelector(`.vxe-body--row[rowid="${rowid}"]`) as HTMLTableCellElement
let offsetTop = 0
if (scrollYLoad) {
if (isScrollYBig && trEl) {
offsetTop = trEl.offsetTop + trEl.offsetHeight
} else {
offsetTop = rowRest.oTop + getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
}
} else {
if (trEl) {
offsetTop = trEl.offsetTop + trEl.offsetHeight
}
}
if (isScrollYBig) {
offsetTop += scrollYTop
}
expandEl.style.top = toCssUnit(offsetTop)
if (!isUpdateHeight) {
if (rowRest.expandHeight !== expandHeight) {
isUpdateHeight = true
}
}
rowRest.expandHeight = expandHeight
}
})
if (isUpdateHeight) {
reactData.rowExpandHeightFlag++
nextTick(() => {
updateRowOffsetTop()
})
}
}
}
}
/**
* 更新树连接线样式
*/
const updateTreeLineStyle = () => {
const { treeConfig } = props
if (!treeConfig) {
return
}
const { tableData } = reactData
const { fullAllDataRowIdData, treeExpandedMaps } = internalData
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
const cellOpts = computeCellOpts.value
const rowOpts = computeRowOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const treeOpts = computeTreeOpts.value
const { transform, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const { handleGetRowId } = createHandleGetRowId($xeTable)
const expParentList: {
row: any
prevRow: any
nextRow: any
}[] = []
const handleNodeRow = (row: any, rIndex: number, rows: any[]) => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid] || {}
const childList: any[] = row[transform ? mapChildrenField : childrenField]
const prevRow = rows[rIndex - 1] || null
const nextRow = rows[rIndex + 1] || null
if (childList && childList.length && treeExpandedMaps[rowid]) {
expParentList.push({ row, prevRow, nextRow })
childList.forEach((childRow, crIndex) => {
const childRowid = handleGetRowId(childRow)
if (treeExpandedMaps[childRowid]) {
handleNodeRow(childRow, crIndex, childList)
}
})
} else {
if (nextRow) {
const nextRowid = handleGetRowId(nextRow)
const nextRowRest = fullAllDataRowIdData[nextRowid] || {}
const currCellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
const nextCellHeight = getCellRestHeight(nextRowRest, cellOpts, rowOpts, defaultRowHeight)
rowRest.oHeight = currCellHeight
rowRest.lineHeight = Math.floor(currCellHeight / 2 + nextCellHeight / 2)
} else {
rowRest.oHeight = 0
rowRest.lineHeight = 0
}
}
}
tableData.forEach((row, rIndex) => {
handleNodeRow(row, rIndex, tableData)
})
XEUtils.lastArrayEach(expParentList, ({ row, nextRow }) => {
const rowid = handleGetRowId(row)
const childList: any[] = row[transform ? mapChildrenField : childrenField]
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const currCellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
let countOffsetHeight = currCellHeight
let countLineHeight = 0
childList.forEach((childRow) => {
const childRowid = handleGetRowId(childRow)
const childRowRest = fullAllDataRowIdData[childRowid] || {}
const childList: any[] = childRow[transform ? mapChildrenField : childrenField]
if (treeExpandedMaps[childRowid] && childList && childList.length) {
countOffsetHeight += (childRowRest.oHeight || 0)
countLineHeight += (childRowRest.oHeight || 0)
} else {
const cellHeight = getCellRestHeight(childRowRest, cellOpts, rowOpts, defaultRowHeight)
childRowRest.oHeight = cellHeight
childRowRest.lineHeight = cellHeight
countOffsetHeight += cellHeight
countLineHeight += cellHeight
}
})
if (nextRow) {
const nextRowid = handleGetRowId(nextRow)
const nextRowRest = fullAllDataRowIdData[nextRowid] || {}
const currCellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
const nextCellHeight = getCellRestHeight(nextRowRest, cellOpts, rowOpts, defaultRowHeight)
countOffsetHeight += currCellHeight
countLineHeight += Math.floor(currCellHeight / 2 + nextCellHeight / 2)
}
rowRest.lineHeight = countLineHeight
rowRest.oHeight = countOffsetHeight
}
})
}
const handleRowExpandScroll = () => {
const { elemStore } = internalData
const rowExpandEl = refRowExpandElem.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
if (rowExpandEl && bodyScrollElem) {
rowExpandEl.scrollTop = bodyScrollElem.scrollTop
}
}
const handleColumnVisible = (visible: boolean) => {
return function (fieldOrColumn: string | string[] | VxeTableDefines.ColumnInfo | VxeTableDefines.ColumnInfo[]) {
let status = false
const cols = XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : [fieldOrColumn]
cols.forEach(item => {
const column = handleFieldOrColumn($xeTable, item)
if (column) {
if (column.children && column.children.length) {
XEUtils.eachTree([column], (item) => {
item.visible = visible
item.renderVisible = visible
})
} else {
column.visible = visible
column.renderVisible = visible
}
if (!status) {
status = true
}
}
})
if (status) {
return $xeTable.handleCustom()
}
return nextTick()
}
}
tableMethods = {
dispatchEvent,
getEl () {
return refElem.value
},
/**
* 重置表格的一切数据状态
*/
clearAll () {
return clearTableAllStatus($xeTable)
},
/**
* 同步 data 数据(即将废弃)
* 如果用了该方法,那么组件将不再记录增删改的状态,只能自行实现对应逻辑
* 对于某些特殊的场景,比如深层树节点元素发生变动时可能会用到
*/
syncData () {
errLog('vxe.error.delFunc', ['syncData', 'getData'])
return nextTick().then(() => {
reactData.tableData = []
emit('update:data', internalData.tableFullData)
return nextTick()
})
},
/**
* 手动处理数据,用于手动排序与筛选
* 对于手动更改了排序、筛选...等条件后需要重新处理数据时可能会用到
*/
updateData () {
const { scrollXLoad, scrollYLoad } = reactData
return $xeTable.handleTableData(true).then(() => {
$xeTable.updateFooter()
if (scrollXLoad || scrollYLoad) {
if (scrollXLoad) {
$xeTable.updateScrollXSpace()
}
if (scrollYLoad) {
$xeTable.updateScrollYSpace()
}
return $xeTable.refreshScroll()
}
}).then(() => {
$xeTable.updateCellAreas()
return handleLazyRecalculate(true, true, true)
}).then(() => {
// 存在滚动行为未结束情况
setTimeout(() => handleLazyRecalculate(false, true, true), 50)
})
},
/**
* 重新加载数据,不会清空表格状态
* @param {Array} datas 数据
*/
loadData (datas) {
const { initStatus } = internalData
return loadTableData(datas, false, false).then(() => {
internalData.inited = true
internalData.initStatus = true
if (!initStatus) {
handleLoadDefaults()
}
return handleLazyRecalculate(false, true, true)
})
},
/**
* 重新加载数据,会清空表格状态
* @param {Array} datas 数据
*/
reloadData (datas) {
return $xeTable.clearAll()
.then(() => {
internalData.inited = true
internalData.initStatus = true
return loadTableData(datas, true, true)
}).then(() => {
handleLoadDefaults()
return handleLazyRecalculate(false, true, true)
})
},
/**
* 修改行数据
*/
setRow (rows, record) {
if (rows && record) {
let rest: any[] = rows
if (!XEUtils.isArray(rows)) {
rest = [rows]
}
const rowkey = getRowkey($xeTable)
rest.forEach(row => {
const rowid = getRowid($xeTable, row)
const newRecord = XEUtils.clone(Object.assign({}, record), true)
XEUtils.set(newRecord, rowkey, rowid)
Object.assign(row, newRecord)
})
}
return nextTick()
},
/**
* 局部加载行数据并恢复到初始状态
* 对于行数据需要局部更改的场景中可能会用到
* @param {Row} row 行对象
* @param {Object} record 新数据
* @param {String} field 字段名
*/
reloadRow (row, record, field?: string) {
const { keepSource } = props
const { tableData } = reactData
const { sourceDataRowIdData } = internalData
if (keepSource) {
if ($xeTable.isAggregateRecord(row)) {
return nextTick()
}
const rowkey = getRowkey($xeTable)
const rowid = XEUtils.get(row, rowkey)
const oRow = sourceDataRowIdData[rowid]
if (oRow && row) {
if (field) {
const newValue = XEUtils.clone(XEUtils.get(record || row, field), true)
XEUtils.set(row, field, newValue)
XEUtils.set(oRow, field, newValue)
} else {
const newRecord = XEUtils.clone(Object.assign({}, record), true)
XEUtils.set(newRecord, rowkey, rowid)
XEUtils.destructuring(oRow, Object.assign(row, newRecord))
}
}
reactData.tableData = tableData.slice(0)
} else {
errLog('vxe.error.reqProp', ['keep-source'])
}
return nextTick()
},
getParams () {
return props.params
},
/**
* 用于树结构,给行数据加载子节点
*/
loadTreeChildren (row, childRecords) {
const { keepSource } = props
const { tableSourceData, fullDataRowIdData, fullAllDataRowIdData, sourceDataRowIdData } = internalData
const treeOpts = computeTreeOpts.value
const { transform, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const parentRest = fullAllDataRowIdData[getRowid($xeTable, row)]
const parentLevel = parentRest ? parentRest.level : 0
return $xeTable.createData(childRecords).then((rows) => {
if (keepSource) {
const rowid = getRowid($xeTable, row)
const matchObj = XEUtils.findTree(tableSourceData, (item) => rowid === getRowid($xeTable, item), { children: childrenField })
if (matchObj) {
matchObj.item[childrenField] = XEUtils.clone(rows, true)
}
rows.forEach(childRow => {
const rowid = getRowid($xeTable, childRow)
sourceDataRowIdData[rowid] = XEUtils.clone(childRow, true)
})
}
XEUtils.eachTree(rows, (childRow, index, items, path, parentItem, nodes) => {
const rowid = getRowid($xeTable, childRow)
const parentRow = parentItem || parentRest.row
const rest = { row: childRow, rowid, _seq: -1, seq: -1, index, _index: -1, $index: -1, treeIndex: -1, _tIndex: -1, items, parent: parentRow, level: parentLevel + nodes.length, height: 0, resizeHeight: 0, oTop: 0, expandHeight: 0 }
fullDataRowIdData[rowid] = rest
fullAllDataRowIdData[rowid] = rest
}, { children: childrenField })
row[childrenField] = rows
if (transform) {
row[mapChildrenField] = XEUtils.clone(rows, false)
}
updateAfterDataIndex()
return rows
})
},
/**
* 加载列配置
* 对于表格列需要重载、局部递增场景下可能会用到
* @param {ColumnInfo} columns 列配置
*/
loadColumn (columns) {
const { lastScrollLeft, lastScrollTop } = internalData
const collectColumn = XEUtils.mapTree(columns, column => reactive(Cell.createColumn($xeTable, column)))
return handleInitColumn(collectColumn).then(() => {
let targetScrollLeft = lastScrollLeft
let targetScrollTop = lastScrollTop
const virtualXOpts = computeVirtualXOpts.value
const virtualYOpts = computeVirtualYOpts.value
// 是否在更新数据之后自动滚动重置滚动条
if (virtualXOpts.scrollToLeftOnChange) {
targetScrollLeft = 0
}
if (virtualYOpts.scrollToTopOnChange) {
targetScrollTop = 0
}
restoreScrollLocation($xeTable, targetScrollLeft, targetScrollTop)
})
},
/**
* 加载列配置并恢复到初始状态
* 对于表格列需要重载、局部递增场景下可能会用到
* @param {ColumnInfo} columns 列配置
*/
reloadColumn (columns) {
return $xeTable.clearAll().then(() => {
return $xeTable.loadColumn(columns)
})
},
/**
* 根据 tr 元素获取对应的 row 信息
* @param {Element} tr 元素
*/
getRowNode (tr) {
if (tr) {
const { fullAllDataRowIdData } = internalData
const rowid = tr.getAttribute('rowid')
if (rowid) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
return {
rowid: rowRest.rowid,
item: rowRest.row,
index: rowRest.index,
items: rowRest.items,
parent: rowRest.parent
}
}
}
}
return null
},
/**
* 根据 th/td 元素获取对应的 column 信息
* @param {Element} cell 元素
*/
getColumnNode (cell) {
if (cell) {
const { fullColumnIdData } = internalData
const colid = cell.getAttribute('colid')
if (colid) {
const colRest = fullColumnIdData[colid]
if (colRest) {
return {
colid: colRest.colid,
item: colRest.column,
index: colRest.index,
items: colRest.items,
parent: colRest.parent
}
}
}
}
return null
},
/**
* 根据 row 获取序号
* @param {Row} row 行对象
*/
getRowSeq: createGetRowCacheProp('seq'),
/**
* 根据 row 获取相对于 data 中的索引
* @param {Row} row 行对象
*/
getRowIndex: createGetRowCacheProp('index') as ((row: any) => number),
/**
* 根据 row 获取相对于当前数据中的索引
* @param {Row} row 行对象
*/
getVTRowIndex: createGetRowCacheProp('_index') as ((row: any) => number),
/**
* 根据 row 获取渲染中的虚拟索引
* @param {Row} row 行对象
*/
getVMRowIndex: createGetRowCacheProp('$index') as ((row: any) => number),
/**
* 根据 column 获取相对于 columns 中的索引
* @param {ColumnInfo} column 列配置
*/
getColumnIndex: createGetColumnCacheProp('index'),
/**
* 根据 column 获取相对于当前表格列中的索引
* @param {ColumnInfo} column 列配置
*/
getVTColumnIndex: createGetColumnCacheProp('_index'),
/**
* 根据 column 获取渲染中的虚拟索引
* @param {ColumnInfo} column 列配置
*/
getVMColumnIndex: createGetColumnCacheProp('$index'),
/**
* 创建 data 对象
* 对于某些特殊场景可能会用到,会自动对数据的字段名进行检测,如果不存在就自动定义
* @param {Array} records 新数据
*/
createData (records) {
return nextTick().then(() => {
return reactive($xeTable.defineField(records))
})
},
/**
* 创建 Row|Rows 对象
* 对于某些特殊场景需要对数据进行手动插入时可能会用到
* @param {Array/Object} records 新数据
*/
createRow (records) {
const isArr = XEUtils.isArray(records)
if (!isArr) {
records = [records || {}]
}
return $xeTable.createData(records).then((rows) => isArr ? rows : rows[0])
},
/**
* 还原数据
* 如果不传任何参数,则还原整个表格
* 如果传 row 则还原一行
* 如果传 rows 则还原多行
* 如果还额外传了 field 则还原指定的单元格数据
*/
revertData (rows: any, field) {
const { keepSource, treeConfig } = props
const { fullAllDataRowIdData, fullDataRowIdData, tableSourceData, sourceDataRowIdData, tableFullData, afterFullData, removeRowMaps } = internalData
const treeOpts = computeTreeOpts.value
const { transform } = treeOpts
const { handleGetRowId } = createHandleGetRowId($xeTable)
if (!keepSource) {
errLog('vxe.error.reqProp', ['keep-source'])
return nextTick()
}
let targetRows = rows
if (rows) {
if (!XEUtils.isArray(rows)) {
targetRows = [rows]
}
} else {
targetRows = XEUtils.toArray($xeTable.getUpdateRecords())
}
let reDelFlag = false
if (targetRows.length) {
targetRows.forEach((item: any) => {
const rowid = handleGetRowId(item)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const row = rowRest.row
if (!$xeTable.isInsertByRow(row)) {
const oRow = sourceDataRowIdData[rowid]
if (oRow && row) {
if (field) {
XEUtils.set(row, field, XEUtils.clone(XEUtils.get(oRow, field), true))
} else {
XEUtils.destructuring(row, XEUtils.clone(oRow, true))
}
if (!fullDataRowIdData[rowid] && $xeTable.isRemoveByRow(row)) {
if (removeRowMaps[rowid]) {
delete removeRowMaps[rowid]
}
tableFullData.unshift(row)
afterFullData.unshift(row)
reDelFlag = true
}
}
}
}
})
}
if (rows) {
if (reDelFlag) {
reactData.removeRowFlag++
$xeTable.updateFooter()
$xeTable.cacheRowMap(false)
$xeTable.handleTableData(treeConfig && transform)
if (!(treeConfig && transform)) {
$xeTable.updateAfterDataIndex()
}
$xeTable.checkSelectionStatus()
if (reactData.scrollYLoad) {
$xeTable.updateScrollYSpace()
}
}
return nextTick().then(() => {
$xeTable.updateCellAreas()
return handleLazyRecalculate(false, true, true)
})
}
return $xeTable.reloadData(tableSourceData)
},
/**
* 清空单元格内容
* 如果不创参数,则清空整个表格内容
* 如果传 row 则清空一行内容
* 如果传 rows 则清空多行内容
* 如果还额外传了 field 则清空指定单元格内容
* @param {Array/Row} rows 行数据
* @param {String} field 字段名
*/
clearData (rows: any, field: string) {
const { tableFullData, visibleColumn } = internalData
if (!arguments.length) {
rows = tableFullData
} else if (rows && !XEUtils.isArray(rows)) {
rows = [rows]
}
if (field) {
rows.forEach((row: any) => XEUtils.set(row, field, null))
} else {
rows.forEach((row: any) => {
visibleColumn.forEach((column) => {
if (column.field) {
setCellValue(row, column, null)
}
})
})
}
return nextTick()
},
effectCellData (row, column, options) {
const { fullCellStoreMaps } = internalData
const editOpts = computeEditOpts.value
const { key, isChanged, setValue, getResult } = options
const rowid = getRowid($xeTable, row)
const colid = column.id
let rowStore = fullCellStoreMaps[rowid]
const cellValue = XEUtils.get(row, column.field)
const restParams: VxeTableDefines.CellCacheHandleParams = {
$table: $xeTable,
row,
rowid,
column,
colid,
cellValue,
oldValue: null
}
if (editOpts.cache === false && key === 'render_table_cell') {
const rest = restParams as VxeTableDefines.CellCacheResultObj
rest.cellResult = getResult ? getResult(restParams) : cellValue
return rest
}
if (!rowStore) {
rowStore = {}
fullCellStoreMaps[rowid] = rowStore
}
let cellStore = rowStore[colid]
if (!cellStore) {
cellStore = {
value: undefined,
result: {}
}
rowStore[colid] = cellStore
}
const restData = rowStore[colid]
const oldValue = restData.value
restParams.oldValue = oldValue
if (isChanged ? !isChanged(restParams) : oldValue !== cellValue) {
restData.value = setValue ? setValue(restParams) : cellValue
restData.result = {}
}
const storeData = restData.result
if (XEUtils.isUndefined(storeData[key])) {
storeData[key] = getResult ? getResult(restParams) : cellValue
}
const cellResult = storeData[key]
const rest = restParams as VxeTableDefines.CellCacheResultObj
rest.cellResult = cellResult
return rest
},
getCellElement (row, fieldOrColumn) {
const { elemStore } = internalData
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const rowid = getRowid($xeTable, row)
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
let bodyElem
if (column) {
if (column.fixed) {
if (column.fixed === 'left') {
if (leftScrollElem) {
bodyElem = leftScrollElem
}
} else {
if (rightScrollElem) {
bodyElem = rightScrollElem
}
}
}
if (!bodyElem) {
bodyElem = bodyScrollElem
}
if (bodyElem) {
return bodyElem.querySelector(`.vxe-body--row[rowid="${rowid}"] .${column.id}`)
}
}
return null
},
getCellLabel (row, fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const { editConfig } = props
const { formatter, editRender, cellRender } = column
let etOpSize = null
let etOgSize = null
let clOpSize = null
let clOgSize = null
if (editRender) {
etOpSize = editRender.options ? editRender.options.length : 0
etOgSize = editRender.optionGroups ? editRender.optionGroups.length : 0
}
if (cellRender) {
clOpSize = cellRender.options ? cellRender.options.length : 0
clOgSize = cellRender.optionGroups ? cellRender.optionGroups.length : 0
}
// formatter > tableCellFormatter
const renderOpts = formatter ? null : (editConfig && isEnableConf(editRender) ? editRender : (isEnableConf(cellRender) ? cellRender : null))
const compConf = renderOpts ? renderer.get(renderOpts.name) : null
const tcFormatter = compConf ? compConf.tableCellFormatter : null
const cellValue = getCellValue(row, column)
let cellLabel = cellValue
if (formatter || tcFormatter) {
let formatData: Record<string, VxeTableDefines.RowCacheFormatObj> | undefined
const { fullAllDataRowIdData } = internalData
const rowid = getRowid($xeTable, row)
const colid = column.id
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
formatData = rowRest.formatData
if (!formatData) {
formatData = fullAllDataRowIdData[rowid].formatData = {}
}
if (rowRest && formatData[colid]) {
const ftValue = formatData[colid].value
if (ftValue && ftValue[0] === cellValue && ftValue[1] === etOpSize && ftValue[2] === etOgSize && ftValue[3] === clOpSize && ftValue[4] === clOgSize) {
return formatData[colid].label
}
}
}
const formatParams = {
$table: $xeTable,
cellValue,
row,
rowIndex: $xeTable.getRowIndex(row),
column,
columnIndex: $xeTable.getColumnIndex(column)
}
if (formatter) {
if (XEUtils.isString(formatter)) {
const gFormatOpts = formats.get(formatter)
const tcFormatMethod = gFormatOpts ? (gFormatOpts.tableCellFormatMethod || gFormatOpts.cellFormatMethod) : null
cellLabel = tcFormatMethod ? tcFormatMethod(formatParams) : ''
} else if (XEUtils.isArray(formatter)) {
const gFormatOpts = formats.get(formatter[0])
const tcFormatMethod = gFormatOpts ? (gFormatOpts.tableCellFormatMethod || gFormatOpts.cellFormatMethod) : null
cellLabel = tcFormatMethod ? tcFormatMethod(formatParams, ...formatter.slice(1)) : ''
} else {
cellLabel = formatter(formatParams)
}
} else if (renderOpts && tcFormatter) {
cellLabel = tcFormatter(renderOpts, formatParams)
}
if (formatData) {
const ftValue = [cellValue, etOpSize, etOgSize, clOpSize, clOgSize]
formatData[colid] = { value: ftValue, label: cellLabel }
}
}
return cellLabel
},
updateCellLabel (row, fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const { fullAllDataRowIdData } = internalData
const rowid = getRowid($xeTable, row)
if (rowid) {
const colid = column.id
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const formatData = rowRest.formatData
if (formatData) {
delete formatData[colid]
}
}
}
return $xeTable.getFooterCellLabel(row, column)
},
clearFormatterCache (isUpdate) {
const { tableData, tableColumn } = reactData
const { fullAllDataRowIdData } = internalData
XEUtils.each(fullAllDataRowIdData, (rowRest: VxeTableDefines.FooterRowCacheItem) => {
if (rowRest.formatData) {
rowRest.formatData = undefined
}
})
if (isUpdate) {
tableData.forEach(row => {
tableColumn.forEach(column => {
$xeTable.getCellLabel(row, column)
})
})
}
return nextTick()
},
getHeaderCellLabel (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const { headerFormatter } = column
const _columnIndex = $xeTable.getVTColumnIndex(column)
let cellLabel = column.getTitle()
if (headerFormatter) {
let formatData: Record<string, VxeTableDefines.RowCacheFormatObj> | undefined
const { headerFullDataColData } = internalData
const colid = column.id
let colRest = headerFullDataColData[colid]
if (!colRest) {
colRest = headerFullDataColData[colid] = {}
}
const formatObj = colRest.formatObj
if (formatObj && formatObj.value === cellLabel) {
return formatObj.label
}
const headFormatParams = {
$table: $xeTable,
cellTitle: cellLabel,
cellValue: cellLabel,
column,
_columnIndex,
columnIndex: $xeTable.getColumnIndex(column)
}
if (XEUtils.isString(headerFormatter)) {
const gFormatOpts = formats.get(headerFormatter)
const fcFormatMethod = gFormatOpts ? gFormatOpts.tableHeaderCellFormatMethod : null
cellLabel = XEUtils.toValueString(fcFormatMethod ? fcFormatMethod(headFormatParams) : '')
} else if (XEUtils.isArray(headerFormatter)) {
const gFormatOpts = formats.get(headerFormatter[0])
const fcFormatMethod = gFormatOpts ? gFormatOpts.tableHeaderCellFormatMethod : null
cellLabel = XEUtils.toValueString(fcFormatMethod ? fcFormatMethod(headFormatParams, ...headerFormatter.slice(1)) : '')
} else {
cellLabel = XEUtils.toValueString(headerFormatter(headFormatParams))
}
if (formatData) {
colRest.formatObj = { value: cellLabel, label: cellLabel }
}
}
return cellLabel
},
updateHeaderCellLabel (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return ''
}
const { headerFullDataColData } = internalData
const colid = column.id
const colRest = headerFullDataColData[colid]
if (colRest) {
colRest.formatObj = undefined
}
return $xeTable.getHeaderCellLabel(column)
},
clearHeaderFormatterCache (isUpdate) {
const { tableColumn } = reactData
const { headerFullDataColData } = internalData
XEUtils.each(headerFullDataColData, (colRest: VxeTableDefines.HeaderColCacheItem) => {
if (colRest.formatObj) {
colRest.formatObj = undefined
}
})
if (isUpdate) {
tableColumn.forEach(column => {
$xeTable.getHeaderCellLabel(column)
})
}
return nextTick()
},
getFooterCellLabel (row, fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const { footerFormatter } = column
const _columnIndex = $xeTable.getVTColumnIndex(column)
let itemValue = ''
// 兼容老模式
if (XEUtils.isArray(row)) {
itemValue = row[_columnIndex]
} else {
itemValue = XEUtils.get(row, column.field)
}
let cellLabel: any = itemValue
if (footerFormatter) {
let formatData: Record<string, VxeTableDefines.RowCacheFormatObj> | undefined
const { footerTableData } = reactData
const { footerFullDataRowData } = internalData
const colid = column.id
const $rowIndex = footerTableData.indexOf(row)
let rowRest: any = null
if ($rowIndex > -1) {
rowRest = footerFullDataRowData[$rowIndex]
if (!rowRest) {
rowRest = footerFullDataRowData[$rowIndex] = {}
}
formatData = rowRest.formatData
if (!formatData) {
formatData = footerFullDataRowData[$rowIndex].formatData = {}
}
if (rowRest && formatData[colid]) {
if (formatData[colid].value === itemValue) {
return formatData[colid].label
}
}
}
const footerFormatParams = {
$table: $xeTable,
cellValue: itemValue,
itemValue,
row,
items: row,
$rowIndex,
column,
_columnIndex,
columnIndex: $xeTable.getColumnIndex(column)
}
if (XEUtils.isString(footerFormatter)) {
const gFormatOpts = formats.get(footerFormatter)
const fcFormatMethod = gFormatOpts ? gFormatOpts.tableFooterCellFormatMethod : null
cellLabel = fcFormatMethod ? fcFormatMethod(footerFormatParams) : ''
} else if (XEUtils.isArray(footerFormatter)) {
const gFormatOpts = formats.get(footerFormatter[0])
const fcFormatMethod = gFormatOpts ? gFormatOpts.tableFooterCellFormatMethod : null
cellLabel = fcFormatMethod ? fcFormatMethod(footerFormatParams, ...footerFormatter.slice(1)) : ''
} else {
cellLabel = footerFormatter(footerFormatParams)
}
if (formatData) {
formatData[colid] = { value: itemValue, label: cellLabel }
}
}
return cellLabel
},
updateFooterCellLabel (row, fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (!column) {
return null
}
const { footerTableData } = reactData
const { footerFullDataRowData } = internalData
const colid = column.id
const $rowIndex = footerTableData.indexOf(row)
const rowRest = footerFullDataRowData[$rowIndex]
if (rowRest) {
const formatData = rowRest.formatData
if (formatData) {
delete formatData[colid]
}
}
return $xeTable.getFooterCellLabel(row, column)
},
clearFooterFormatterCache (isUpdate) {
const { tableData, tableColumn } = reactData
const { footerFullDataRowData } = internalData
XEUtils.each(footerFullDataRowData, (rowRest: VxeTableDefines.FooterRowCacheItem) => {
if (rowRest.formatData) {
rowRest.formatData = undefined
}
})
if (isUpdate) {
tableData.forEach(row => {
tableColumn.forEach(column => {
$xeTable.getFooterCellLabel(row, column)
})
})
}
return nextTick()
},
/**
* 检查是否为临时行数据
*/
isInsertByRow (row) {
const rowid = getRowid($xeTable, row)
return !!reactData.insertRowFlag && !!internalData.insertRowMaps[rowid]
},
isRemoveByRow (row) {
const rowid = getRowid($xeTable, row)
return !!reactData.removeRowFlag && !!internalData.removeRowMaps[rowid]
},
/**
* 删除所有新增的临时数据
*/
removeInsertRow () {
const { insertRowMaps } = internalData
return $xeTable.remove(XEUtils.values(insertRowMaps))
},
/**
* 检查行或列数据是否发生改变
*/
isUpdateByRow (rowidOrRow, field) {
const { keepSource } = props
const { fullDataRowIdData, sourceDataRowIdData } = internalData
const keepFields = computeKeepFields.value
if (keepSource) {
const rowid = XEUtils.isString(rowidOrRow) || XEUtils.isNumber(rowidOrRow) ? rowidOrRow : getRowid($xeTable, rowidOrRow)
const rowRest = fullDataRowIdData[rowid]
// 新增的数据不需要检测
if (!rowRest) {
return false
}
const row = rowRest.row
const oRow = sourceDataRowIdData[rowid]
if (!oRow) {
return false
}
if (arguments.length > 1) {
return !eqCellValue(row, oRow, field as string).result
}
for (let i = 0; i < keepFields.length; i++) {
if (!eqCellValue(row, oRow, keepFields[i]).result) {
return true
}
}
}
return false
},
getUpdateCells () {
const { keepSource, treeConfig } = props
const { tableFullData, tableFullTreeData, fullDataRowIdData, sourceDataRowIdData, fullColumnFieldData } = internalData
const treeOpts = computeTreeOpts.value
const keepFields = computeKeepFields.value
const cellList: VxeTableDefines.UpdateCellObj[] = []
if (keepSource) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const handleCell = (item: any) => {
const rowid = handleGetRowId(item)
const rowRest = fullDataRowIdData[rowid]
// 新增的数据不需要检测
if (!rowRest) {
return
}
const row = rowRest.row
const oRow = sourceDataRowIdData[rowid]
if (!oRow) {
return
}
for (let i = 0; i < keepFields.length; i++) {
const field = keepFields[i]
const { result, newValue, oldValue } = eqCellValue(row, oRow, field)
if (!result) {
const column = fullColumnFieldData[field] ? fullColumnFieldData[field].column : null
cellList.push({ row, rowid, column, field, newValue, oldValue })
}
}
}
if (treeConfig) {
XEUtils.eachTree(tableFullTreeData, handleCell, treeOpts)
} else {
tableFullData.forEach(handleCell)
}
}
return cellList
},
/**
* 获取表格的可视列,也可以指定索引获取列
* @param {Number} columnIndex 索引
*/
getColumns (columnIndex?: number): any {
const { visibleColumn } = internalData
return XEUtils.isUndefined(columnIndex) ? visibleColumn.slice(0) : visibleColumn[columnIndex]
},
/**
* 根据列获取列的唯一主键
*/
getColid (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
return column ? column.id : null
},
/**
* 根据列的唯一主键获取列
* @param {String} colid 列主键
*/
getColumnById (colid) {
const { fullColumnIdData } = internalData
return colid && fullColumnIdData[colid] ? fullColumnIdData[colid].column : null
},
/**
* 根据列的字段名获取列
* @param {String} field 字段名
*/
getColumnByField (field) {
const fullColumnFieldData = internalData.fullColumnFieldData
return field && fullColumnFieldData[field] ? fullColumnFieldData[field].column : null
},
getParentColumn (fieldOrColumn) {
const { fullColumnIdData } = internalData
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
return column && column.parentId && fullColumnIdData[column.parentId] ? fullColumnIdData[column.parentId].column : null
},
/**
* 获取当前表格的列
* 收集到的全量列、全量表头列、处理条件之后的全量表头列、当前渲染中的表头列
*/
getTableColumn () {
return {
collectColumn: internalData.collectColumn.slice(0),
fullColumn: internalData.tableFullColumn.slice(0),
visibleColumn: internalData.visibleColumn.slice(0),
tableColumn: reactData.tableColumn.slice(0)
}
},
/**
* 移动列到指定列的位置
* @param fieldOrColumn
* @param targetFieldOrColumn
* @param options
*/
moveColumnTo (fieldOrColumn, targetFieldOrColumn, options) {
const { fullColumnIdData, visibleColumn } = internalData
const { dragToChild, dragPos, isCrossDrag } = Object.assign({}, options)
const dragCol = handleFieldOrColumn($xeTable, fieldOrColumn)
let prevDragCol: VxeTableDefines.ColumnInfo | null = null
const colRest = dragCol ? fullColumnIdData[dragCol.id] : null
let defPos: 'left' | 'right' = 'left'
if (XEUtils.isNumber(targetFieldOrColumn)) {
if (colRest && targetFieldOrColumn) {
let currList = colRest.items
let offsetIndex = colRest._index + targetFieldOrColumn
if (isCrossDrag) {
currList = visibleColumn
offsetIndex = colRest._index + targetFieldOrColumn
}
if (offsetIndex > 0 && offsetIndex < currList.length - 1) {
prevDragCol = currList[offsetIndex]
}
if (targetFieldOrColumn > 0) {
defPos = 'right'
}
}
} else {
prevDragCol = handleFieldOrColumn($xeTable, targetFieldOrColumn)
const targetColRest = prevDragCol ? fullColumnIdData[prevDragCol.id] : null
if (colRest && targetColRest) {
if (targetColRest._index > colRest._index) {
defPos = 'right'
}
}
}
return $xeTable.handleColDragSwapEvent(null, true, dragCol, prevDragCol, dragPos || defPos, dragToChild === true)
},
/**
* 移动行到指定行的位置
* @param rowidOrRow
* @param targetRowidOrRow
* @param options
*/
moveRowTo (rowidOrRow, targetRowidOrRow, options) {
const { treeConfig } = props
const { fullAllDataRowIdData, afterFullData } = internalData
const { dragToChild, dragPos, isCrossDrag } = Object.assign({}, options)
const treeOpts = computeTreeOpts.value
const dragRow = handleRowidOrRow($xeTable, rowidOrRow)
let prevDragRow: any = null
let defPos: 'top' | 'bottom' = 'top'
const rowRest = dragRow ? fullAllDataRowIdData[getRowid($xeTable, dragRow)] : null
if (XEUtils.isNumber(targetRowidOrRow)) {
if (rowRest && targetRowidOrRow) {
let currList = afterFullData
let offsetIndex = rowRest._index + targetRowidOrRow
if (treeConfig) {
currList = rowRest.items
if (treeOpts.transform) {
offsetIndex = rowRest.treeIndex + targetRowidOrRow
if (isCrossDrag) {
currList = afterFullData
offsetIndex = rowRest._index + targetRowidOrRow
}
}
}
if (offsetIndex >= 0 && offsetIndex <= currList.length - 1) {
prevDragRow = currList[offsetIndex]
}
if (targetRowidOrRow > 0) {
defPos = 'bottom'
}
}
} else {
prevDragRow = handleRowidOrRow($xeTable, targetRowidOrRow)
const targetRowRest = prevDragRow ? fullAllDataRowIdData[getRowid($xeTable, prevDragRow)] : null
if (rowRest && targetRowRest) {
if (targetRowRest._index > rowRest._index) {
defPos = 'bottom'
}
}
}
const rest = $xeTable.handleRowDragSwapEvent(null, true, dragRow, prevDragRow, dragPos || defPos, dragToChild === true)
return rest
},
/**
* 获取表格的全量列
*/
getFullColumns () {
const { collectColumn } = internalData
return collectColumn.slice(0)
},
/**
* 获取数据,和 data 的行为一致,也可以指定索引获取数据
*/
getData (rowIndex?: number) {
const tableSynchData = props.data || internalData.tableSynchData
return XEUtils.isUndefined(rowIndex) ? tableSynchData.slice(0) : tableSynchData[rowIndex]
},
/**
* 用于多选行,获取已选中的数据
*/
getCheckboxRecords (isFull) {
const { treeConfig } = props
const { updateCheckboxFlag } = reactData
const { tableFullData, afterFullData, tableFullTreeData, fullDataRowIdData, afterFullRowMaps, selectCheckboxMaps } = internalData
const treeOpts = computeTreeOpts.value
const checkboxOpts = computeCheckboxOpts.value
const { transform, mapChildrenField } = treeOpts
const { checkField } = checkboxOpts
const childrenField = treeOpts.children || treeOpts.childrenField
let rowList: any[] = []
if (updateCheckboxFlag) {
if (checkField) {
if (treeConfig) {
// 如果开启 transform 默认就是完整数据
const currTableData = isFull ? (transform ? tableFullTreeData : tableFullData) : (transform ? tableFullTreeData : afterFullData)
rowList = XEUtils.filterTree(currTableData, row => XEUtils.get(row, checkField), { children: transform ? mapChildrenField : childrenField })
} else {
const currTableData = isFull ? tableFullData : afterFullData
rowList = currTableData.filter((row) => XEUtils.get(row, checkField))
}
} else {
const currMaps = isFull || (treeConfig && !transform) ? fullDataRowIdData : afterFullRowMaps
XEUtils.each(selectCheckboxMaps, (row, rowid) => {
if (currMaps[rowid]) {
rowList.push(fullDataRowIdData[rowid].row)
}
})
}
}
return rowList
},
/**
* 只对 tree-config 有效,获取行的子级
*/
getTreeRowChildren (rowOrRowid) {
const { treeConfig } = props
const { fullAllDataRowIdData } = internalData
const treeOpts = computeTreeOpts.value
const { transform, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
if (rowOrRowid && treeConfig) {
let rowid
if (XEUtils.isString(rowOrRowid)) {
rowid = rowOrRowid
} else {
rowid = getRowid($xeTable, rowOrRowid)
}
if (rowid) {
const rest = fullAllDataRowIdData[rowid]
const row = rest ? rest.row : null
if (row) {
return row[transform ? mapChildrenField : childrenField] || []
}
}
}
return []
},
/**
* 只对 tree-config 有效,用于树形结构,获取指定行的层级
*/
getTreeRowLevel (rowOrRowid) {
const { treeConfig } = props
const { fullAllDataRowIdData } = internalData
if (rowOrRowid && treeConfig) {
let rowid
if (XEUtils.isString(rowOrRowid)) {
rowid = rowOrRowid
} else {
rowid = getRowid($xeTable, rowOrRowid)
}
if (rowid) {
const rest = fullAllDataRowIdData[rowid]
if (rest) {
return rest.level
}
}
}
return -1
},
/**
* 只对 tree-config 有效,获取行的父级
*/
getTreeParentRow (rowOrRowid) {
const { treeConfig } = props
const { fullAllDataRowIdData } = internalData
if (rowOrRowid && treeConfig) {
let rowid
if (XEUtils.isString(rowOrRowid)) {
rowid = rowOrRowid
} else {
rowid = getRowid($xeTable, rowOrRowid)
}
if (rowid) {
const rest = fullAllDataRowIdData[rowid]
if (rest) {
return rest.parent
}
}
}
return null
},
getParentRow (rowOrRowid) {
warnLog('vxe.error.delFunc', ['getParentRow', 'getTreeParentRow'])
return $xeTable.getTreeParentRow(rowOrRowid)
},
/**
* 根据行的唯一主键获取行
* @param {String/Number} rowid 行主键
*/
getRowById (cellValue) {
const { fullAllDataRowIdData } = internalData
const rowid = XEUtils.eqNull(cellValue) ? '' : encodeURIComponent(cellValue || '')
return fullAllDataRowIdData[rowid] ? fullAllDataRowIdData[rowid].row : null
},
/**
* 根据行获取行的唯一主键
* @param {Row} row 行对象
*/
getRowid (row) {
return getRowid($xeTable, row)
},
/**
* 获取处理后的表格数据
* 如果存在筛选条件,继续处理
* 如果存在排序,继续处理
*/
getTableData () {
const { tableData, footerTableData } = reactData
const { tableFullData, afterFullData, tableFullTreeData } = internalData
return {
fullData: props.treeConfig ? tableFullTreeData.slice(0) : tableFullData.slice(0),
visibleData: afterFullData.slice(0),
tableData: tableData.slice(0),
footerData: footerTableData.slice(0)
}
},
/**
* 获取表格的全量数据,如果是 tree-config 则返回带层级的树结构
*/
getFullData () {
const { treeConfig } = props
const { tableFullData, tableFullTreeData } = internalData
if (treeConfig) {
return tableFullTreeData.slice(0)
}
return tableFullData.slice(0)
},
/**
* 设置为固定列
*/
setColumnFixed (fieldOrColumn, fixed) {
let status = false
const cols = XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : [fieldOrColumn]
const columnOpts = computeColumnOpts.value
const isMaxFixedColumn = computeIsMaxFixedColumn.value
for (let i = 0; i < cols.length; i++) {
const item = cols[i]
const column = handleFieldOrColumn($xeTable, item)
const targetColumn = getRootColumn($xeTable, column as any)
if (targetColumn && targetColumn.fixed !== fixed) {
// 是否超过最大固定列数量
if (!targetColumn.fixed && isMaxFixedColumn) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.table.maxFixedCol', [columnOpts.maxFixedSize])
})
}
return nextTick()
}
XEUtils.eachTree([targetColumn], (column) => {
column.fixed = fixed
column.renderFixed = fixed
})
$xeTable.saveCustomStore('update:fixed')
if (!status) {
status = true
}
}
}
if (status) {
return $xeTable.refreshColumn()
}
return nextTick()
},
/**
* 取消指定固定列
*/
clearColumnFixed (fieldOrColumn) {
let status = false
const cols = XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : [fieldOrColumn]
cols.forEach(item => {
const column = handleFieldOrColumn($xeTable, item)
const targetColumn = getRootColumn($xeTable, column as any)
if (targetColumn && targetColumn.fixed) {
XEUtils.eachTree([targetColumn], (column) => {
column.fixed = null
column.renderFixed = null
})
$xeTable.saveCustomStore('update:fixed')
if (!status) {
status = true
}
}
})
if (status) {
return $xeTable.refreshColumn()
}
return nextTick()
},
/**
* 隐藏指定列
*/
hideColumn: handleColumnVisible(false),
/**
* 显示指定列
*/
showColumn: handleColumnVisible(true),
setColumnWidth (fieldOrColumn, width) {
const { elemStore } = internalData
let status = false
const cols = XEUtils.isArray(fieldOrColumn) ? fieldOrColumn : [fieldOrColumn]
let cWidth = XEUtils.toInteger(width)
if (isScale(width)) {
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const bodyWidth = bodyScrollElem ? bodyScrollElem.clientWidth - 1 : 0
cWidth = Math.floor(cWidth * bodyWidth)
}
if (cWidth) {
cols.forEach(item => {
const column = handleFieldOrColumn($xeTable, item)
if (column) {
column.resizeWidth = cWidth
if (!status) {
status = true
}
}
})
if (status) {
return $xeTable.refreshColumn().then(() => {
return { status }
})
}
}
return nextTick().then(() => {
return { status }
})
},
getColumnWidth (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
return column.renderWidth
}
return 0
},
/**
* 手动重置列的显示隐藏、列宽拖动的状态、固定列、排序列;
* 如果为 true 则重置所有状态
* 如果已关联工具栏,则会同步更新
*/
resetColumn (options) {
warnLog('vxe.error.delFunc', ['resetColumn', 'resetCustom'])
return $xeTable.resetCustom(options)
},
/**
* 刷新列信息
* 将固定的列左边、右边分别靠边
* 如果传 true 则会检查列顺序并排序
*/
refreshColumn (initSort) {
if (initSort) {
handleUpdateColumn()
}
return parseColumns(true).then(() => {
return $xeTable.refreshScroll()
}).then(() => {
return handleLazyRecalculate(false, true, true)
})
},
setRowHeightConf (heightConf) {
const { fullAllDataRowIdData } = internalData
let status = false
if (heightConf) {
XEUtils.each(heightConf, (height, rowid) => {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const rHeight = XEUtils.toInteger(height)
if (rHeight) {
rowRest.resizeHeight = rHeight
if (!status) {
status = true
}
}
}
})
if (status) {
internalData.isResizeCellHeight = true
reactData.resizeHeightFlag++
}
}
return nextTick().then(() => {
updateRowOffsetTop()
return { status }
})
},
getRowHeightConf (isFull) {
const { fullAllDataRowIdData, afterFullData } = internalData
const { handleGetRowId } = createHandleGetRowId($xeTable)
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const rest: Record<string, number> = {}
afterFullData.forEach(row => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const resizeHeight = rowRest.resizeHeight
if (resizeHeight || isFull) {
const currCellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
rest[rowid] = currCellHeight
}
}
})
return rest
},
recalcRowHeight (rowOrId) {
const { fullAllDataRowIdData } = internalData
const rows = XEUtils.isArray(rowOrId) ? rowOrId : [rowOrId]
const el = refElem.value
if (el) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
el.setAttribute('data-calc-row', 'Y')
rows.forEach(row => {
const rowid = XEUtils.isString(row) || XEUtils.isNumber(row) ? row : handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest.resizeHeight = calcCellAutoHeight(rowid, rowRest, el)
}
el.removeAttribute('data-calc-row')
})
reactData.calcCellHeightFlag++
}
return nextTick()
},
setRowHeight (rowOrId, height) {
const { fullAllDataRowIdData } = internalData
let status = false
const rows = XEUtils.isArray(rowOrId) ? rowOrId : [rowOrId]
let rHeight = XEUtils.toInteger(height)
if (isScale(height)) {
const tableBody = refTableBody.value
const bodyElem = tableBody ? tableBody.$el as HTMLDivElement : null
const bodyHeight = bodyElem ? bodyElem.clientHeight - 1 : 0
rHeight = Math.floor(rHeight * bodyHeight)
}
if (rHeight) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
rows.forEach(row => {
const rowid = XEUtils.isString(row) || XEUtils.isNumber(row) ? row : handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest.resizeHeight = rHeight
if (!status) {
status = true
}
}
})
if (status) {
internalData.isResizeCellHeight = true
reactData.resizeHeightFlag++
}
}
return nextTick().then(() => {
return { status }
})
},
getRowHeight (rowOrId) {
const { fullAllDataRowIdData } = internalData
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const rowid = XEUtils.isString(rowOrId) || XEUtils.isNumber(rowOrId) ? rowOrId : getRowid($xeTable, rowOrId)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
return getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
}
return 0
},
/**
* 刷新滚动操作,手动同步滚动相关位置(对于某些特殊的操作,比如滚动条错位、固定列不同步)
*/
refreshScroll () {
const { elemStore, lastScrollLeft, lastScrollTop } = internalData
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
return new Promise<void>(resolve => {
// 还原滚动条位置
if (lastScrollLeft || lastScrollTop) {
return restoreScrollLocation($xeTable, lastScrollLeft, lastScrollTop).then(() => {
// 存在滚动行为未结束情况
setTimeout(resolve, 10)
})
}
internalData.intoRunScroll = true
// 重置
setScrollTop(yHandleEl, lastScrollTop)
setScrollTop(bodyScrollElem, lastScrollTop)
setScrollTop(leftScrollElem, lastScrollTop)
setScrollTop(rightScrollElem, lastScrollTop)
setScrollLeft(xHandleEl, lastScrollLeft)
setScrollLeft(bodyScrollElem, lastScrollLeft)
setScrollLeft(headerScrollElem, lastScrollLeft)
setScrollLeft(footerScrollElem, lastScrollLeft)
// 存在滚动行为未结束情况
setTimeout(() => {
internalData.intoRunScroll = false
resolve()
}, 10)
})
},
/**
* 重新渲染布局
* 刷新布局
*/
recalculate (reFull?: boolean) {
const isForce = !!reFull
return handleLazyRecalculate(isForce, isForce, isForce)
},
openTooltip (target, content) {
const $commTip = refCommTooltip.value
if ($commTip && $commTip.open) {
return $commTip.open(target, content)
}
return nextTick()
},
/**
* 关闭 tooltip
*/
closeTooltip () {
const { tooltipStore } = reactData
const $tooltip = refTooltip.value
const $commTip = refCommTooltip.value
if (tooltipStore.visible) {
Object.assign(tooltipStore, {
row: null,
column: null,
content: null,
visible: false,
type: null,
currOpts: {}
})
if ($tooltip && $tooltip.close) {
$tooltip.close()
}
}
if ($commTip && $commTip.close) {
$commTip.close()
}
return nextTick()
},
/**
* 判断列头复选框是否被选中
*/
isAllCheckboxChecked () {
return reactData.isAllSelected
},
/**
* 判断列头复选框是否被半选
*/
isAllCheckboxIndeterminate () {
return !reactData.isAllSelected && reactData.isIndeterminate
},
/**
* 获取复选框半选状态的行数据
*/
getCheckboxIndeterminateRecords (isFull) {
const { treeConfig } = props
const { fullDataRowIdData, treeIndeterminateRowMaps } = internalData
if (treeConfig) {
const fullRest: any[] = []
const defRest: any[] = []
XEUtils.each(treeIndeterminateRowMaps, (item, rowid) => {
if (item) {
fullRest.push(item)
if (fullDataRowIdData[rowid]) {
defRest.push(item)
}
}
})
if (isFull) {
return fullRest
}
return defRest
}
return []
},
/**
* 用于多选行,设置行为选中状态,第二个参数为选中与否
* @param {Array/Row} rows 行数据
* @param {Boolean} value 是否选中
*/
setCheckboxRow (rows, checked) {
if (rows && !XEUtils.isArray(rows)) {
rows = [rows]
}
return handleCheckedCheckboxRow(rows, checked, true)
},
setCheckboxRowByKey (keys: any, checked) {
const { fullAllDataRowIdData } = internalData
if (!XEUtils.isArray(keys)) {
keys = [keys]
}
const rows: any = []
keys.forEach((rowKey: string) => {
const rowid = encodeRowid(rowKey)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rows.push(rowRest.row)
}
})
return handleCheckedCheckboxRow(rows, checked, true)
},
setCheckboxRowKey (keys, checked) {
return $xeTable.setCheckboxRowByKey(keys, checked)
},
isCheckedByCheckboxRow (row) {
const { updateCheckboxFlag } = reactData
const { selectCheckboxMaps } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { checkField } = checkboxOpts
if (checkField) {
return XEUtils.get(row, checkField)
}
return !!updateCheckboxFlag && !!selectCheckboxMaps[getRowid($xeTable, row)]
},
isCheckedByCheckboxRowKey (rowKey: any) {
const { updateCheckboxFlag } = reactData
const { fullAllDataRowIdData, selectCheckboxMaps } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { checkField } = checkboxOpts
const rowid = encodeRowid(rowKey)
if (checkField) {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
return XEUtils.get(rowRest.row, checkField)
}
return false
}
return !!updateCheckboxFlag && !!selectCheckboxMaps[rowid]
},
isIndeterminateByCheckboxRow (row) {
const { treeIndeterminateRowMaps } = internalData
return !!treeIndeterminateRowMaps[getRowid($xeTable, row)] && !$xeTable.isCheckedByCheckboxRow(row)
},
isIndeterminateByCheckboxRowKey (rowKey: any) {
const { treeIndeterminateRowMaps } = internalData
const rowid = encodeRowid(rowKey)
return !!treeIndeterminateRowMaps[rowid] && !$xeTable.isCheckedByCheckboxRowKey(rowid)
},
/**
* 多选,切换某一行的选中状态
*/
toggleCheckboxRow (row) {
const { selectCheckboxMaps } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { checkField } = checkboxOpts
const checked = checkField ? !XEUtils.get(row, checkField) : !selectCheckboxMaps[getRowid($xeTable, row)]
$xeTable.handleBatchSelectRows([row], checked, true)
$xeTable.checkSelectionStatus()
return nextTick()
},
/**
* 用于多选行,设置所有行的选中状态
* @param {Boolean} value 是否选中
*/
setAllCheckboxRow (value) {
return handleCheckedAllCheckboxRow(value, true)
},
/**
* 获取单选框保留选中的行
*/
getRadioReserveRecord (isFull) {
const { treeConfig } = props
const { fullDataRowIdData, radioReserveRow, afterFullData } = internalData
const radioOpts = computeRadioOpts.value
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
if (radioOpts.reserve && radioReserveRow) {
const rowid = getRowid($xeTable, radioReserveRow)
if (isFull) {
if (!fullDataRowIdData[rowid]) {
return radioReserveRow
}
} else {
const rowkey = getRowkey($xeTable)
if (treeConfig) {
const matchObj = XEUtils.findTree(afterFullData, row => rowid === XEUtils.get(row, rowkey), { children: childrenField })
if (matchObj) {
return radioReserveRow
}
} else {
if (!afterFullData.some(row => rowid === XEUtils.get(row, rowkey))) {
return radioReserveRow
}
}
}
}
return null
},
clearRadioReserve () {
internalData.radioReserveRow = null
return nextTick()
},
/**
* 获取复选框保留选中的行
*/
getCheckboxReserveRecords (isFull) {
const { treeConfig } = props
const { afterFullData, fullDataRowIdData, checkboxReserveRowMap } = internalData
const checkboxOpts = computeCheckboxOpts.value
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const reserveSelection: any[] = []
if (checkboxOpts.reserve) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const afterFullIdMaps: { [key: string]: number } = {}
if (treeConfig) {
XEUtils.eachTree(afterFullData, row => {
afterFullIdMaps[handleGetRowId(row)] = 1
}, { children: childrenField })
} else {
afterFullData.forEach(row => {
afterFullIdMaps[handleGetRowId(row)] = 1
})
}
XEUtils.each(checkboxReserveRowMap, (oldRow, oldRowid) => {
if (oldRow) {
if (isFull) {
if (!fullDataRowIdData[oldRowid]) {
reserveSelection.push(oldRow)
}
} else {
if (!afterFullIdMaps[oldRowid]) {
reserveSelection.push(oldRow)
}
}
}
})
}
return reserveSelection
},
clearCheckboxReserve () {
internalData.checkboxReserveRowMap = {}
return nextTick()
},
/**
* 多选,切换所有行的选中状态
*/
toggleAllCheckboxRow () {
handleCheckAllEvent(null, !reactData.isAllSelected)
return nextTick()
},
/**
* 用于多选行,手动清空用户的选择
* 清空行为不管是否被禁用还是保留记录,都将彻底清空选中状态
*/
clearCheckboxRow () {
const { treeConfig } = props
const { tableFullData } = internalData
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const checkboxOpts = computeCheckboxOpts.value
const { checkField, reserve } = checkboxOpts
// indeterminateField 仅支持读取
const indeterminateField = checkboxOpts.indeterminateField || checkboxOpts.halfField
if (checkField) {
const handleClearChecked = (item: any) => {
if (treeConfig && indeterminateField) {
XEUtils.set(item, indeterminateField, false)
}
XEUtils.set(item, checkField, false)
}
if (treeConfig) {
XEUtils.eachTree(tableFullData, handleClearChecked, { children: childrenField })
} else {
tableFullData.forEach(handleClearChecked)
}
}
if (reserve) {
tableFullData.forEach((row) => handleCheckboxReserveRow(row, false))
}
reactData.isAllSelected = false
reactData.isIndeterminate = false
internalData.selectCheckboxMaps = {}
internalData.treeIndeterminateRowMaps = {}
reactData.updateCheckboxFlag++
return nextTick()
},
/**
* 用于当前行,设置某一行为高亮状态
* @param {Row} row 行对象
*/
setCurrentRow (row) {
const $xeGanttView = internalData.xeGanttView
const rowOpts = computeRowOpts.value
const el = refElem.value
$xeTable.clearCurrentRow()
// $xeTable.clearCurrentColumn()
internalData.currentRow = row
if (rowOpts.isCurrent || props.highlightCurrentRow) {
if (el) {
XEUtils.arrayEach(el.querySelectorAll(`[rowid="${getRowid($xeTable, row)}"]`), elem => addClass(elem, 'row--current'))
}
}
if ($xeGanttView && $xeGanttView.handleUpdateCurrentRow) {
$xeGanttView.handleUpdateCurrentRow(row)
}
return nextTick()
},
isCheckedByRadioRow (row) {
const { selectRadioRow } = reactData
if (row && selectRadioRow) {
return $xeTable.eqRow(selectRadioRow, row)
}
return false
},
isCheckedByRadioRowKey (key) {
const { selectRadioRow } = reactData
if (selectRadioRow) {
return key === getRowid($xeTable, selectRadioRow)
}
return false
},
/**
* 用于单选行,设置某一行为选中状态
* @param {Row} row 行对象
*/
setRadioRow (row) {
return handleCheckedRadioRow(row, true)
},
/**
* 用于单选行,设置某一行为选中状态
* @param key 行主键
*/
setRadioByRowKey (rowKey: any) {
const { fullAllDataRowIdData } = internalData
const rowid = encodeRowid(rowKey)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
return handleCheckedRadioRow(rowRest.row, true)
}
return nextTick()
},
setRadioRowKey (rowKey) {
return $xeTable.setRadioByRowKey(rowKey)
},
/**
* 用于当前行,手动清空当前高亮的状态
*/
clearCurrentRow () {
const $xeGanttView = internalData.xeGanttView
const el = refElem.value
internalData.currentRow = null
internalData.hoverRow = null
if (el) {
XEUtils.arrayEach(el.querySelectorAll('.row--current'), elem => removeClass(elem, 'row--current'))
}
if ($xeGanttView && $xeGanttView.handleUpdateCurrentRow) {
$xeGanttView.handleUpdateCurrentRow()
}
return nextTick()
},
/**
* 用于单选行,手动清空用户的选择
*/
clearRadioRow () {
reactData.selectRadioRow = null
return nextTick()
},
/**
* 用于当前行,获取当前行的数据
*/
getCurrentRecord (isFull) {
const { fullDataRowIdData, afterFullRowMaps, currentRow } = internalData
const rowOpts = computeRowOpts.value
if (rowOpts.isCurrent || props.highlightCurrentRow) {
const rowid = getRowid($xeTable, currentRow)
if (isFull) {
if (fullDataRowIdData[rowid]) {
return currentRow
}
} else {
if (afterFullRowMaps[rowid]) {
return currentRow
}
}
}
return null
},
/**
* 用于单选行,获取当已选中的数据
*/
getRadioRecord (isFull) {
const { fullDataRowIdData, afterFullRowMaps } = internalData
const { selectRadioRow } = reactData
if (selectRadioRow) {
const rowid = getRowid($xeTable, selectRadioRow)
if (isFull) {
if (fullDataRowIdData[rowid]) {
return selectRadioRow
}
} else {
if (afterFullRowMaps[rowid]) {
return selectRadioRow
}
}
}
return null
},
getCurrentColumn () {
const columnOpts = computeColumnOpts.value
return columnOpts.isCurrent || props.highlightCurrentColumn ? reactData.currentColumn : null
},
/**
* 用于当前列,设置某列行为高亮状态
*/
setCurrentColumn (fieldOrColumn) {
const { mouseConfig } = props
const mouseOpts = computeMouseOpts.value
const isMouseSelected = mouseConfig && mouseOpts.selected
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
$xeTable.clearCurrentColumn()
reactData.currentColumn = column
}
return nextTick().then(() => {
// 更新状选中态
if (isMouseSelected) {
$xeTable.addCellSelectedClass()
}
})
},
/**
* 用于当前列,手动清空当前高亮的状态
*/
clearCurrentColumn () {
reactData.currentColumn = null
return nextTick()
},
setPendingRow (rows: any | any[], status: boolean) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const { pendingRowMaps } = internalData
if (rows && !XEUtils.isArray(rows)) {
rows = [rows]
}
if (status) {
rows.forEach((row: any) => {
const rowid = handleGetRowId(row)
if (rowid && !pendingRowMaps[rowid]) {
pendingRowMaps[rowid] = row
}
})
} else {
rows.forEach((row: any) => {
const rowid = handleGetRowId(row)
if (rowid && pendingRowMaps[rowid]) {
delete pendingRowMaps[rowid]
}
})
}
reactData.pendingRowFlag++
return nextTick()
},
togglePendingRow (rows: any | any[]) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const { pendingRowMaps } = internalData
if (rows && !XEUtils.isArray(rows)) {
rows = [rows]
}
rows.forEach((row: any) => {
const rowid = handleGetRowId(row)
if (rowid) {
if (pendingRowMaps[rowid]) {
delete pendingRowMaps[rowid]
} else {
pendingRowMaps[rowid] = row
}
}
})
reactData.pendingRowFlag++
return nextTick()
},
hasPendingByRow (row) {
return $xeTable.isPendingByRow(row)
},
isPendingByRow (row) {
const { pendingRowMaps } = internalData
const rowid = getRowid($xeTable, row)
return !!pendingRowMaps[rowid]
},
getPendingRecords () {
const { fullAllDataRowIdData, pendingRowMaps } = internalData
const insertRecords: any[] = []
XEUtils.each(pendingRowMaps, (row, rowid) => {
if (fullAllDataRowIdData[rowid]) {
insertRecords.push(row)
}
})
return insertRecords
},
clearPendingRow () {
internalData.pendingRowMaps = {}
reactData.pendingRowFlag++
return nextTick()
},
sort (sortConfs: any, sortOrder?: VxeTablePropTypes.SortOrder) {
const sortOpts = computeSortOpts.value
const { multiple, remote, orders } = sortOpts
if (sortConfs) {
if (XEUtils.isString(sortConfs)) {
sortConfs = [
{ field: sortConfs, order: sortOrder }
]
}
}
if (!XEUtils.isArray(sortConfs)) {
sortConfs = [sortConfs]
}
if (sortConfs.length) {
if (!multiple) {
clearAllSort()
}
(multiple ? sortConfs : [sortConfs[0]]).forEach((confs: any, index: number) => {
let { field, order } = confs
let column = field
if (XEUtils.isString(field)) {
column = $xeTable.getColumnByField(field)
}
if (column && column.sortable) {
if (orders && orders.indexOf(order) === -1) {
order = getNextSortOrder(column)
}
if (column.order !== order) {
column.order = order
}
column.sortTime = Date.now() + index
}
})
// 如果是服务端排序,则跳过本地排序处理
if (!remote) {
$xeTable.handleTableData(true)
}
return nextTick().then(() => {
updateRowOffsetTop()
$xeTable.updateCellAreas()
return updateStyle()
})
}
return nextTick()
},
setSort (sortConfs, isUpdate) {
return handleSortEvent(null, sortConfs, isUpdate)
},
setSortByEvent (evnt, sortConfs) {
return handleSortEvent(evnt, sortConfs, true)
},
/**
* 清空指定列的排序条件
* 如果为空则清空所有列的排序条件
* @param {String} fieldOrColumn 列或字段名
*/
clearSort (fieldOrColumn) {
const sortOpts = computeSortOpts.value
if (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
column.order = null
}
} else {
clearAllSort()
}
if (!sortOpts.remote) {
$xeTable.handleTableData(true)
}
return nextTick().then(() => {
updateRowOffsetTop()
return updateStyle()
})
},
clearSortByEvent (evnt, fieldOrColumn) {
const { tableFullColumn } = internalData
const sortOpts = computeSortOpts.value
const { multiple } = sortOpts
const sortCols: VxeTableDefines.ColumnInfo[] = []
let column: VxeTableDefines.ColumnInfo<any> | null = null
if (evnt) {
if (fieldOrColumn) {
column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
column.order = null
}
} else {
tableFullColumn.forEach((column) => {
if (column.order) {
column.order = null
sortCols.push(column)
}
})
}
if (!sortOpts.remote) {
$xeTable.handleTableData(true)
}
if (!multiple) {
column = sortCols[0]
}
if (column) {
$xeTable.handleColumnSortEvent(evnt, column)
}
if (multiple && sortCols.length) {
const params = { $table: $xeTable, $event: evnt, cols: sortCols, sortList: [] }
dispatchEvent('clear-all-sort', params, evnt)
}
}
return nextTick().then(() => {
updateRowOffsetTop()
return updateStyle()
})
},
isSort (fieldOrColumn) {
if (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
return column ? column.sortable && !!column.order : false
}
return $xeTable.getSortColumns().length > 0
},
getSortColumns () {
const sortOpts = computeSortOpts.value
const { multiple, chronological } = sortOpts
const sortList: VxeTableDefines.SortCheckedParams[] = []
const { tableFullColumn } = internalData
tableFullColumn.forEach((column) => {
const { field, order } = column
if (column.sortable && order) {
sortList.push({ column, field, property: field, order: order, sortTime: column.sortTime })
}
})
if (multiple && chronological && sortList.length > 1) {
return XEUtils.orderBy(sortList, 'sortTime')
}
return sortList
},
setFilterByEvent (evnt, fieldOrColumn, options) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column && column.filters) {
column.filters = toFilters(options || [], column.id)
return $xeTable.handleColumnConfirmFilter(column, evnt)
}
return nextTick()
},
/**
* 关闭筛选
* @param {Event} evnt 事件
*/
closeFilter () {
const { filterStore } = reactData
const { column, visible } = filterStore
filterStore.isAllSelected = false
filterStore.isIndeterminate = false
filterStore.visible = false
if (visible) {
const field = column ? column.field : null
dispatchEvent('filter-visible', {
column,
property: field,
field,
filterList: () => $xeTable.getCheckedFilters(),
visible: false
}, null)
}
return nextTick()
},
/**
* 判断指定列是否为筛选状态,如果为空则判断所有列
* @param {String} fieldOrColumn 字段名
*/
isActiveFilterByColumn (fieldOrColumn) {
const column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
return column.filters && column.filters.some((option) => option.checked)
}
return $xeTable.getCheckedFilters().length > 0
},
isFilter (fieldOrColumn) {
return $xeTable.isActiveFilterByColumn(fieldOrColumn)
},
clearFilterByEvent (evnt, fieldOrColumn) {
const { filterStore } = reactData
const { tableFullColumn } = internalData
const filterOpts = computeFilterOpts.value
const { multiple } = filterOpts
const filterCols: VxeTableDefines.ColumnInfo[] = []
let column: VxeTableDefines.ColumnInfo<any> | null = null
if (fieldOrColumn) {
column = handleFieldOrColumn($xeTable, fieldOrColumn)
if (column) {
$xeTable.handleClearFilter(column)
}
} else {
tableFullColumn.forEach(column => {
if (column.filters) {
filterCols.push(column)
$xeTable.handleClearFilter(column)
}
})
}
if (!fieldOrColumn || column !== filterStore.column) {
Object.assign(filterStore, {
isAllSelected: false,
isIndeterminate: false,
style: null,
options: [],
column: null,
multiple: false, // 选项是覅多选
visible: false
})
}
if (!filterOpts.remote) {
$xeTable.updateData()
}
if (!multiple) {
column = filterCols[0]
}
if (column) {
const filterList = () => $xeTable.getCheckedFilters()
const values: any[] = []
const datas: any[] = []
column.filters.forEach((item: any) => {
if (item.checked) {
values.push(item.value)
datas.push(item.data)
}
})
const params = {
$table: $xeTable,
$event: evnt as Event,
column,
field: column.field,
property: column.field,
values,
datas,
filters: filterList,
filterList
}
$xeTable.dispatchEvent('filter-change', params, evnt)
$xeTable.dispatchEvent('clear-filter', params, evnt)
}
if (multiple && filterCols.length) {
const params = { $table: $xeTable, $event: evnt, cols: filterCols, filterList: [] }
dispatchEvent('clear-all-filter', params, evnt)
}
return nextTick()
},
/**
* 判断展开行是否懒加载完成
* @param {Row} row 行对象
*/
isRowExpandLoaded (row) {
const { fullAllDataRowIdData } = internalData
const rowRest = fullAllDataRowIdData[getRowid($xeTable, row)]
return rowRest && !!rowRest.expandLoaded
},
clearRowExpandLoaded (row) {
const { fullAllDataRowIdData, rowExpandLazyLoadedMaps } = internalData
const expandOpts = computeExpandOpts.value
const { lazy } = expandOpts
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (lazy && rowRest) {
rowRest.expandLoaded = false
delete rowExpandLazyLoadedMaps[rowid]
}
reactData.rowExpandedFlag++
return nextTick()
},
/**
* 重新懒加载展开行,并展开内容
* @param {Row} row 行对象
*/
reloadRowExpand (row) {
const { rowExpandLazyLoadedMaps } = internalData
const expandOpts = computeExpandOpts.value
const { lazy } = expandOpts
const rowid = getRowid($xeTable, row)
if (lazy && !rowExpandLazyLoadedMaps[rowid]) {
$xeTable.clearRowExpandLoaded(row)
.then(() => handleAsyncRowExpand(row))
}
return nextTick()
},
reloadExpandContent (row) {
warnLog('vxe.error.delFunc', ['reloadExpandContent', 'reloadRowExpand'])
// 即将废弃
return $xeTable.reloadRowExpand(row)
},
/**
* 切换展开行
*/
toggleRowExpand (row) {
return $xeTable.setRowExpand(row, !$xeTable.isRowExpandByRow(row))
},
/**
* 设置所有行的展开与否
* @param {Boolean} expanded 是否展开
*/
setAllRowExpand (expanded) {
const treeOpts = computeTreeOpts.value
const { tableFullData, tableFullTreeData } = internalData
const childrenField = treeOpts.children || treeOpts.childrenField
let expandedRows: any[] = []
if (props.treeConfig) {
XEUtils.eachTree(tableFullTreeData, (row) => {
expandedRows.push(row)
}, { children: childrenField })
} else {
expandedRows = tableFullData
}
return $xeTable.setRowExpand(expandedRows, expanded)
},
/**
* 设置展开行,二个参数设置这一行展开与否
* 支持单行
* 支持多行
* @param {Array/Row} rows 行数据
* @param {Boolean} expanded 是否展开
*/
setRowExpand (rows, expanded) {
const { expandColumn } = reactData
let { fullAllDataRowIdData, rowExpandedMaps, rowExpandLazyLoadedMaps } = internalData
const { handleGetRowId } = createHandleGetRowId($xeTable)
const expandOpts = computeExpandOpts.value
const { reserve, lazy, accordion, toggleMethod } = expandOpts
const lazyRests: any[] = []
const columnIndex = expandColumn ? $xeTable.getColumnIndex(expandColumn) : -1
const $columnIndex = expandColumn ? $xeTable.getVMColumnIndex(expandColumn) : -1
if (rows) {
if (!XEUtils.isArray(rows)) {
rows = [rows]
}
if (accordion) {
// 只能同时展开一个
rowExpandedMaps = {}
internalData.rowExpandedMaps = rowExpandedMaps
rows = rows.slice(rows.length - 1, rows.length)
}
const validRows: any[] = toggleMethod ? rows.filter((row: any) => toggleMethod({ $table: $xeTable, expanded, column: expandColumn as VxeTableDefines.ColumnInfo, columnIndex, $columnIndex, row, rowIndex: $xeTable.getRowIndex(row), $rowIndex: $xeTable.getVMRowIndex(row) })) : rows
if (expanded) {
validRows.forEach((row) => {
const rowid = handleGetRowId(row)
if (!rowExpandedMaps[rowid]) {
const rowRest = fullAllDataRowIdData[rowid]
const isLoad = lazy && !rowRest.expandLoaded && !rowExpandLazyLoadedMaps[rowid]
if (isLoad) {
lazyRests.push(handleAsyncRowExpand(row))
} else {
rowExpandedMaps[rowid] = row
}
}
})
} else {
validRows.forEach(item => {
const rowid = handleGetRowId(item)
if (rowExpandedMaps[rowid]) {
delete rowExpandedMaps[rowid]
}
})
}
if (reserve) {
validRows.forEach((row) => handleRowExpandReserve(row, expanded))
}
}
reactData.rowExpandedFlag++
return Promise.all(lazyRests)
.then(() => nextTick())
.then(() => handleLazyRecalculate(true, true, true))
.then(() => {
updateRowOffsetTop()
updateRowExpandStyle()
handleRowExpandScroll()
return $xeTable.updateCellAreas()
})
},
/**
* 判断行是否为展开状态
* @param {Row} row 行对象
*/
isRowExpandByRow (row) {
const { rowExpandedFlag } = reactData
const { rowExpandedMaps } = internalData
const rowid = getRowid($xeTable, row)
return !!rowExpandedFlag && !!rowExpandedMaps[rowid]
},
isExpandByRow (row) {
// 已废弃
warnLog('vxe.error.delFunc', ['isExpandByRow', 'isRowExpandByRow'])
return $xeTable.isRowExpandByRow(row)
},
/**
* 手动清空展开行状态,数据会恢复成未展开的状态
*/
clearRowExpand () {
const { tableFullData, scrollYStore } = internalData
const expandOpts = computeExpandOpts.value
const { reserve } = expandOpts
const expList = $xeTable.getRowExpandRecords()
internalData.rowExpandedMaps = {}
if (reserve) {
tableFullData.forEach((row) => handleRowExpandReserve(row, false))
}
reactData.rowExpandedFlag++
scrollYStore.startIndex = 0
scrollYStore.endIndex = 1
return nextTick().then(() => {
if (expList.length) {
return handleLazyRecalculate(true, true, true)
}
}).then(() => {
updateRowOffsetTop()
updateRowExpandStyle()
handleRowExpandScroll()
return $xeTable.updateCellAreas()
})
},
clearRowExpandReserve () {
internalData.rowExpandedReserveRowMap = {}
return nextTick()
},
getRowExpandRecords () {
const rest: any[] = []
XEUtils.each(internalData.rowExpandedMaps, item => {
if (item) {
rest.push(item)
}
})
return rest
},
setRowGroups (fieldOrColumns) {
const { aggregateConfig, rowGroupConfig } = props
const aggregateOpts = computeAggregateOpts.value
const { maxGroupSize } = aggregateOpts
if (!(aggregateConfig || rowGroupConfig)) {
errLog('vxe.error.reqProp', ['aggregate-config'])
return nextTick()
}
const confList = fieldOrColumns ? (XEUtils.isArray(fieldOrColumns) ? fieldOrColumns : [fieldOrColumns]) : []
if (maxGroupSize && confList.length > maxGroupSize) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.table.maxGroupCol', [maxGroupSize])
})
}
return nextTick()
}
if (confList.length) {
handleUpdateRowGroup(confList.map(fieldOrColumn => {
return XEUtils.isString(fieldOrColumn) ? fieldOrColumn : fieldOrColumn.field
}))
return loadTableData(internalData.tableSynchData, false, true)
}
return nextTick()
},
getRowGroups () {
const { aggregateConfig, rowGroupConfig } = props
const { fullColumnFieldData } = internalData
if (aggregateConfig || rowGroupConfig) {
const { rowGroupList } = reactData
return rowGroupList.map(({ field }) => {
const colRet = fullColumnFieldData[field]
if (colRet) {
return colRet.column
}
return { field }
})
}
return []
},
getRowGroupFields () {
return $xeTable.getRowGroups().map(item => item.field)
},
clearRowGroups () {
const { aggregateConfig, rowGroupConfig } = props
if (!(aggregateConfig || rowGroupConfig)) {
errLog('vxe.error.reqProp', ['aggregate-config'])
return nextTick()
}
handleUpdateRowGroup([])
return loadTableData(internalData.tableSynchData, false, true)
},
isRowGroupRecord (row) {
warnLog('vxe.error.delFunc', ['isRowGroupRecord', 'isAggregateRecord'])
return $xeTable.isAggregateRecord(row)
},
isRowGroupExpandByRow (row) {
warnLog('vxe.error.delFunc', ['isRowGroupExpandByRow', 'isAggregateExpandByRow'])
return $xeTable.isAggregateExpandByRow(row)
},
isAggregateRecord (row) {
const { isRowGroupStatus } = reactData
return isRowGroupStatus && row.isAggregate
},
getAggregateContentByRow (row) {
const { isRowGroupStatus } = reactData
return isRowGroupStatus && row && row.isAggregate ? row.groupContent : ''
},
getAggregateRowChildren (row) {
const aggregateOpts = computeAggregateOpts.value
const { childrenField, mapChildrenField } = aggregateOpts
const { isRowGroupStatus } = reactData
return isRowGroupStatus && row && row.isAggregate && childrenField && mapChildrenField ? (row[mapChildrenField] || []) : []
},
refreshAggregateCalcValues () {
updateGroupData()
return nextTick()
},
isAggregateExpandByRow (row) {
const { rowGroupExpandedFlag } = reactData
const { rowGroupExpandedMaps } = internalData
return !!rowGroupExpandedFlag && !!rowGroupExpandedMaps[getRowid($xeTable, row)]
},
setRowGroupExpand (rows, expanded) {
if (rows) {
if (!XEUtils.isArray(rows)) {
rows = [rows]
}
return handleRowGroupVirtualExpand(rows, expanded)
}
return nextTick()
},
setRowGroupExpandByField (groupFields, expanded) {
const { isRowGroupStatus } = reactData
const aggregateOpts = computeAggregateOpts.value
const { childrenField } = aggregateOpts
if (groupFields) {
if (!XEUtils.isArray(groupFields)) {
groupFields = [groupFields]
}
if (isRowGroupStatus) {
const rows: any[] = []
const gfKeys: Record<string, boolean> = {}
groupFields.forEach(groupField => {
gfKeys[groupField] = true
})
XEUtils.eachTree(internalData.afterGroupFullData, (row) => {
if (row.isAggregate && gfKeys[row.groupField]) {
rows.push(row)
}
}, { children: childrenField })
if (rows.length) {
return handleRowGroupVirtualExpand(rows, expanded)
}
}
}
return nextTick()
},
setAllRowGroupExpand (expanded) {
const { tableFullGroupData } = internalData
const aggregateOpts = computeAggregateOpts.value
const { mapChildrenField } = aggregateOpts
const rgExpandedMaps: Record<string, any> = {}
if (expanded && mapChildrenField) {
XEUtils.eachTree(tableFullGroupData, (row) => {
if (row[mapChildrenField] && row[mapChildrenField].length) {
rgExpandedMaps[getRowid($xeTable, row)] = row
}
}, { children: mapChildrenField })
}
internalData.rowGroupExpandedMaps = rgExpandedMaps
handleVirtualTreeToList()
$xeTable.handleTableData()
updateAfterDataIndex()
reactData.rowGroupExpandedFlag++
return handleLazyRecalculate(true, true, true)
},
clearRowGroupExpand () {
internalData.rowGroupExpandedMaps = {}
handleVirtualTreeToList()
$xeTable.handleTableData()
updateAfterDataIndex()
reactData.rowGroupExpandedFlag++
return handleLazyRecalculate(true, true, true)
},
getTreeExpandRecords () {
const rest: any[] = []
XEUtils.each(internalData.treeExpandedMaps, item => {
if (item) {
rest.push(item)
}
})
return rest
},
/**
* 判断树节点是否懒加载完成
* @param {Row} row 行对象
*/
isTreeExpandLoaded (row) {
const { fullAllDataRowIdData } = internalData
const rowRest = fullAllDataRowIdData[getRowid($xeTable, row)]
return rowRest && !!rowRest.treeLoaded
},
clearTreeExpandLoaded (rows: any) {
const { fullAllDataRowIdData, treeExpandedMaps } = internalData
const treeOpts = computeTreeOpts.value
const { transform } = treeOpts
if (rows) {
if (!XEUtils.isArray(rows)) {
rows = [rows]
}
rows.forEach((row: any) => {
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
rowRest.treeLoaded = false
if (treeExpandedMaps[rowid]) {
delete treeExpandedMaps[rowid]
}
}
})
} else {
XEUtils.each(fullAllDataRowIdData, (rowRest) => {
rowRest.treeLoaded = false
})
internalData.treeExpandedMaps = {}
}
if (transform) {
handleVirtualTreeToList()
$xeTable.handleTableData()
}
reactData.treeExpandedFlag++
return nextTick()
},
/**
* 重新懒加载树节点,并展开该节点
* @param {Row} row 行对象
*/
reloadTreeExpand (row) {
const { treeExpandLazyLoadedMaps } = internalData
const treeOpts = computeTreeOpts.value
const hasChildField = treeOpts.hasChild || treeOpts.hasChildField
const { transform, lazy } = treeOpts
const rowid = getRowid($xeTable, row)
if (lazy && row[hasChildField] && !treeExpandLazyLoadedMaps[rowid]) {
return $xeTable.clearTreeExpandLoaded(row).then(() => {
return handleAsyncTreeExpandChilds(row)
}).then(() => {
if (transform) {
handleVirtualTreeToList()
$xeTable.handleTableData()
}
reactData.treeExpandedFlag++
}).then(() => {
return $xeTable.recalculate()
})
}
return nextTick()
},
reloadTreeChilds (row) {
warnLog('vxe.error.delFunc', ['reloadTreeChilds', 'reloadTreeExpand'])
// 即将废弃
return $xeTable.reloadTreeExpand(row)
},
/**
* 切换/展开树节点
*/
toggleTreeExpand (row) {
return $xeTable.setTreeExpand(row, !$xeTable.isTreeExpandByRow(row))
},
/**
* 设置所有树节点的展开与否
* @param {Boolean} expanded 是否展开
*/
setAllTreeExpand (expanded: boolean) {
const { tableFullTreeData } = internalData
const treeOpts = computeTreeOpts.value
const { transform, lazy } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const expandeds: any[] = []
XEUtils.eachTree(tableFullTreeData, (row) => {
const rowChildren = row[childrenField]
if (lazy || (rowChildren && rowChildren.length)) {
expandeds.push(row)
}
}, { children: childrenField })
return $xeTable.setTreeExpand(expandeds, expanded).then(() => {
if (transform) {
handleVirtualTreeToList()
reactData.treeExpandedFlag++
return $xeTable.recalculate()
}
})
},
/**
* 设置展开树形节点,二个参数设置这一行展开与否
* 支持单行
* 支持多行
* @param {Array/Row} rows 行数据
* @param {Boolean} expanded 是否展开
*/
setTreeExpand (rows, expanded) {
const treeOpts = computeTreeOpts.value
const { transform } = treeOpts
if (rows) {
if (!XEUtils.isArray(rows)) {
rows = [rows]
}
if (rows.length) {
// 如果为虚拟树
if (transform) {
return handleVirtualTreeExpand(rows, expanded)
} else {
return handleBaseTreeExpand(rows, expanded)
}
}
}
return nextTick()
},
/**
* 判断行是否为树形节点展开状态
* @param {Row} row 行对象
*/
isTreeExpandByRow (row) {
const { treeExpandedFlag } = reactData
const { treeExpandedMaps } = internalData
return !!treeExpandedFlag && !!treeExpandedMaps[getRowid($xeTable, row)]
},
/**
* 手动清空树形节点的展开状态,数据会恢复成未展开的状态
*/
clearTreeExpand () {
const { tableFullTreeData } = internalData
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const { transform, reserve } = treeOpts
const expList = $xeTable.getTreeExpandRecords()
internalData.treeExpandedMaps = {}
if (reserve) {
XEUtils.eachTree(tableFullTreeData, row => handleTreeExpandReserve(row, false), { children: childrenField })
}
return $xeTable.handleTableData().then(() => {
if (transform) {
handleVirtualTreeToList()
$xeTable.handleTableData()
}
reactData.treeExpandedFlag++
}).then(() => {
updateTreeLineStyle()
if (expList.length) {
return $xeTable.recalculate()
}
})
},
clearTreeExpandReserve () {
internalData.treeExpandedReserveRowMap = {}
return nextTick()
},
/**
* 获取表格的滚动状态
*/
getScroll () {
return $xeTable.getScrollData()
},
/**
* 获取表格的滚动数据
*/
getScrollData () {
const { scrollXLoad, scrollYLoad, scrollbarHeight, scrollbarWidth } = reactData
const { elemStore } = internalData
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const scrollTop = bodyScrollElem ? bodyScrollElem.scrollTop : 0
const scrollLeft = bodyScrollElem ? bodyScrollElem.scrollLeft : 0
const clientHeight = bodyScrollElem ? bodyScrollElem.clientHeight : 0
const clientWidth = bodyScrollElem ? bodyScrollElem.clientWidth : 0
const scrollHeight = bodyScrollElem ? bodyScrollElem.scrollHeight : 0
const scrollWidth = bodyScrollElem ? bodyScrollElem.scrollWidth : 0
const isTop = scrollTop <= 0
const isBottom = scrollTop + clientHeight >= scrollHeight
const isLeft = scrollLeft <= 0
const isRight = scrollLeft + clientWidth >= scrollWidth
return {
virtualX: scrollXLoad,
virtualY: scrollYLoad,
isTop,
isBottom,
isLeft,
isRight,
scrollbarHeight,
scrollbarWidth,
scrollTop,
scrollLeft,
scrollHeight,
scrollWidth,
clientHeight,
clientWidth
}
},
/**
* 如果有滚动条,则滚动到对应的位置
*/
scrollTo (scrollLeft, scrollTop) {
const { elemStore } = internalData
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
internalData.intoRunScroll = true
if (scrollLeft) {
if (!XEUtils.isNumber(scrollLeft)) {
scrollTop = scrollLeft.top
scrollLeft = scrollLeft.left
}
}
if (XEUtils.isNumber(scrollLeft)) {
setScrollLeft(xHandleEl, scrollLeft)
setScrollLeft(bodyScrollElem, scrollLeft)
setScrollLeft(headerScrollElem, scrollLeft)
setScrollLeft(footerScrollElem, scrollLeft)
if (reactData.scrollXLoad) {
loadScrollXData()
}
}
if (XEUtils.isNumber(scrollTop)) {
setScrollTop(yHandleEl, scrollTop)
setScrollTop(bodyScrollElem, scrollTop)
setScrollTop(leftScrollElem, scrollTop)
setScrollTop(rightScrollElem, scrollTop)
if (reactData.scrollYLoad) {
loadScrollYData()
}
}
return new Promise<void>(resolve => {
setTimeout(() => {
nextTick(() => {
internalData.intoRunScroll = false
resolve()
})
}, (reactData.scrollXLoad || reactData.scrollYLoad) ? 30 : 0)
})
},
/**
* 如果有滚动条,则滚动到对应的行
*/
scrollToRow (rowOrRowid, fieldOrColumn, options) {
const { isAllOverflow, scrollYLoad, scrollXLoad } = reactData
const { fullAllDataRowIdData, _sToTime } = internalData
const rest = []
let row: any
if (XEUtils.isString(rowOrRowid) || XEUtils.isNumber(rowOrRowid)) {
const rowid = rowOrRowid
const rowRest = rowid ? fullAllDataRowIdData[rowid] : null
if (rowRest) {
row = rowRest.row
}
} else {
row = rowOrRowid
}
if (row) {
if (props.treeConfig) {
rest.push($xeTable.scrollToTreeRow(row))
} else {
rest.push(rowToVisible($xeTable, row))
}
}
if (fieldOrColumn) {
rest.push(handleScrollToRowColumn(fieldOrColumn, row, options))
}
if (_sToTime) {
clearTimeout(_sToTime)
}
return Promise.all(rest).then(() => {
if (row) {
if (!isAllOverflow && (scrollYLoad || scrollXLoad)) {
calcCellHeight()
calcCellWidth()
// 可视区被渲染后位置被偏移
internalData._sToTime = setTimeout(() => {
internalData._sToTime = undefined
if (scrollYLoad) {
if (props.treeConfig) {
$xeTable.scrollToTreeRow(row)
} else {
rowToVisible($xeTable, row)
}
}
if (scrollXLoad && fieldOrColumn) {
handleScrollToRowColumn(fieldOrColumn, row, options)
}
}, 350)
}
return nextTick()
}
})
},
/**
* 如果有滚动条,则滚动到第一行
*/
scrollToStartRow (fieldOrColumn, options) {
const { afterFullData } = internalData
return $xeTable.scrollToRow(afterFullData[0], fieldOrColumn, options)
},
/**
* 如果有滚动条,则滚动到最后一行
*/
scrollToEndRow (fieldOrColumn, options) {
const { afterFullData } = internalData
return $xeTable.scrollToRow(afterFullData[afterFullData.length - 1], fieldOrColumn, options)
},
/**
* 如果有滚动条,则滚动到对应的列
*/
scrollToColumn (fieldOrColumn, options) {
return handleScrollToRowColumn(fieldOrColumn, null, options)
},
/**
* 如果有滚动条,则滚动到第一列
*/
scrollToStartColumn (options) {
const { visibleColumn } = internalData
return $xeTable.scrollToColumn(visibleColumn[0], options)
},
/**
* 如果有滚动条,则滚动到最后一列
*/
scrollToEndColumn (options) {
const { visibleColumn } = internalData
return $xeTable.scrollToColumn(visibleColumn[visibleColumn.length - 1], options)
},
/**
* 手动清除滚动相关信息,还原到初始状态
*/
clearScroll () {
const { elemStore, scrollXStore, scrollYStore } = internalData
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
internalData.intoRunScroll = true
setScrollLeft(xHandleEl, 0)
setScrollLeft(bodyScrollElem, 0)
setScrollLeft(headerScrollElem, 0)
setScrollLeft(footerScrollElem, 0)
setScrollTop(yHandleEl, 0)
setScrollTop(bodyScrollElem, 0)
setScrollTop(leftScrollElem, 0)
setScrollTop(rightScrollElem, 0)
scrollXStore.startIndex = 0
scrollXStore.visibleStartIndex = 0
scrollXStore.endIndex = scrollXStore.visibleSize
scrollXStore.visibleEndIndex = scrollXStore.visibleSize
scrollYStore.startIndex = 0
scrollYStore.visibleStartIndex = 0
scrollYStore.endIndex = scrollYStore.visibleSize
scrollYStore.visibleEndIndex = scrollYStore.visibleSize
return nextTick().then(() => {
internalData.lastScrollLeft = 0
internalData.lastScrollTop = 0
internalData.intoRunScroll = false
})
},
/**
* 更新表尾合计
*/
updateFooter () {
const { showFooter, footerData, footerMethod } = props
const { visibleColumn, afterFullData } = internalData
let footData: any[] = []
if (showFooter && footerData && footerData.length) {
footData = footerData.slice(0)
} else if (showFooter && footerMethod) {
footData = visibleColumn.length ? footerMethod({ columns: visibleColumn, data: afterFullData, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }) : []
}
reactData.footerTableData = footData
$xeTable.clearFooterFormatterCache()
$xeTable.handleUpdateFooterMerge()
$xeTable.dispatchEvent('footer-data-change', {
visibleColumn: internalData.visibleColumn,
footData
}, null)
return nextTick()
},
/**
* 更新列状态 updateStatus({ row, column }, cellValue)
* 如果组件值 v-model 发生 change 时,调用改函数用于更新某一列编辑状态
* 如果单元格配置了校验规则,则会进行校验
*/
updateStatus (slotParams, cellValue) {
return nextTick().then(() => {
const { editRules } = props
const { isActivated } = internalData
if (isActivated && slotParams && editRules) {
return $xeTable.handleCellRuleUpdateStatus('change', slotParams, cellValue)
}
})
},
/**
* 设置合并单元格 [{ row: Row|number, column: ColumnInfo|number, rowspan: number, colspan: number }]
*/
setMergeCells (merges) {
if (props.spanMethod) {
errLog('vxe.error.errConflicts', ['merge-cells', 'span-method'])
}
handleBodyMerge(merges)
$xeTable.handleUpdateBodyMerge()
return nextTick().then(() => {
const { expandColumn } = reactData
const { mergeBodyList } = internalData
if (expandColumn && mergeBodyList.length) {
warnLog('vxe.error.errConflicts', ['type=expand', 'merge-cells | span-method'])
}
$xeTable.updateCellAreas()
return updateStyle()
})
},
/**
* 移除单元格合并 [{row:Row|number, col:ColumnInfo|number}]
*/
removeMergeCells (merges) {
if (props.spanMethod) {
errLog('vxe.error.errConflicts', ['merge-cells', 'span-method'])
}
const rest = removeBodyMerges(merges)
$xeTable.handleUpdateBodyMerge()
return nextTick().then(() => {
$xeTable.updateCellAreas()
updateStyle()
return rest
})
},
/**
* 获取所有被合并的单元格
*/
getMergeCells () {
return internalData.mergeBodyList.slice(0)
},
/**
* 清除所有单元格合并
*/
clearMergeCells () {
internalData.mergeBodyList = []
internalData.mergeBodyMaps = {}
internalData.mergeBodyCellMaps = {}
internalData.mergeBodyRowMaps = {}
internalData.mergeBodyColMaps = {}
reactData.mergeBodyFlag++
return nextTick().then(() => {
return updateStyle()
})
},
setMergeHeaderCells (merges) {
handleHeaderMerge(merges)
$xeTable.handleUpdateHeaderMerge()
return nextTick().then(() => {
return updateStyle()
})
},
/**
* 移除表头单元格合并 [{row:Row|number, col:ColumnInfo|number}]
*/
removeMergeHeaderCells (merges) {
const rest = removeHeaderMerges(merges)
$xeTable.handleUpdateHeaderMerge()
return nextTick().then(() => {
updateStyle()
return rest
})
},
/**
* 获取所有被合并的表头单元格
*/
getMergeHeaderCells () {
return internalData.mergeHeaderList.slice(0)
},
/**
* 清除所有表头单元格合并
*/
clearMergeHeaderCells () {
internalData.mergeHeaderList = []
internalData.mergeHeaderMaps = {}
internalData.mergeHeaderCellMaps = {}
internalData.mergeHeaderRowMaps = {}
internalData.mergeBodyColMaps = {}
reactData.mergeHeadFlag++
return nextTick().then(() => {
return updateStyle()
})
},
setMergeFooterCells (merges) {
if (props.footerSpanMethod) {
errLog('vxe.error.errConflicts', ['merge-footer-cells | merge-footer-items', 'footer-span-method'])
}
handleFooterMerge(merges)
$xeTable.handleUpdateFooterMerge()
return nextTick().then(() => {
return updateStyle()
})
},
setMergeFooterItems (merges) {
// errLog('vxe.error.delFunc', ['setMergeFooterItems', 'setMergeFooterCells'])
return $xeTable.setMergeFooterCells(merges)
},
removeMergeFooterCells (merges) {
if (props.footerSpanMethod) {
errLog('vxe.error.errConflicts', ['merge-footer-cells | merge-footer-items', 'footer-span-method'])
}
const rest = removeFooterMerges(merges)
$xeTable.handleUpdateFooterMerge()
return nextTick().then(() => {
updateStyle()
return rest
})
},
removeMergeFooterItems (merges) {
// errLog('vxe.error.delFunc', ['removeMergeFooterItems', 'removeMergeFooterCells'])
return $xeTable.removeMergeFooterCells(merges)
},
/**
* 获取所有被合并的表尾
*/
getMergeFooterCells () {
return internalData.mergeFooterList.slice(0)
},
getMergeFooterItems () {
// errLog('vxe.error.delFunc', ['getMergeFooterItems', 'getMergeFooterCells'])
return $xeTable.getMergeFooterCells()
},
/**
* 清除所有表尾合并
*/
clearMergeFooterCells () {
internalData.mergeFooterList = []
internalData.mergeFooterMaps = {}
internalData.mergeFooterCellMaps = {}
internalData.mergeFooterRowMaps = {}
internalData.mergeFooterColMaps = {}
reactData.mergeFootFlag++
return nextTick().then(() => {
return updateStyle()
})
},
clearMergeFooterItems () {
// errLog('vxe.error.delFunc', ['clearMergeFooterItems', 'clearMergeFooterCells'])
return $xeTable.clearMergeFooterCells()
},
updateCellAreas () {
const { mouseConfig } = props
const mouseOpts = computeMouseOpts.value
if (mouseConfig && mouseOpts.area && $xeTable.handleRecalculateCellAreaEvent) {
return $xeTable.handleRecalculateCellAreaEvent()
}
return nextTick()
},
undo () {
return $xeTable.handleUndoStackEvent(null)
},
redo () {
return $xeTable.handleRedoStackEvent(null)
},
clearHistory () {
return $xeTable.handleClearStack()
},
/**
* 用于 custom-config,用于手动恢复自定义列设置信息,恢复表格重置为初始状态
* @param storeData
* @returns
*/
setCustomStoreData (storeData) {
if (!storeData) {
return nextTick()
}
const customOpts = computeCustomOpts.value
const { checkMethod } = customOpts
// 重置状态
clearTableAllStatus($xeTable)
// 恢复列
const allCols: VxeTableDefines.ColumnInfo[] = []
XEUtils.eachTree(internalData.collectColumn, (column) => {
column.resizeWidth = 0
column.fixed = column.defaultFixed
column.renderSortNumber = column.sortNumber
column.parentId = column.defaultParentId
if (!checkMethod || checkMethod({ $table: $xeTable, column })) {
column.visible = column.defaultVisible
}
column.align = column.defaultAlign
column.headerAlign = column.defaultHeaderAlign
column.footerAlign = column.defaultFooterAlign
column.aggFunc = column.defaultAggFunc
column.renderAggFn = column.defaultAggFunc
column.renderResizeWidth = column.renderWidth
allCols.push(column)
})
const newCollectCols = XEUtils.toArrayTree(XEUtils.orderBy(allCols, 'renderSortNumber'), { key: 'id', parentKey: 'parentId', children: 'children' })
internalData.collectColumn = newCollectCols
internalData.tableFullColumn = getColumnList(newCollectCols)
reactData.updateColFlag++
reactData.isColLoading = true
initColumnHierarchy()
return Promise.resolve(
handleCustomRestore(storeData)
).then(() => {
return buildColumnInfo()
}).then(() => {
// 恢复数据聚合分组
const { isRowGroupStatus, rowGroupList } = reactData
if (isRowGroupStatus && !!$xeTable.handlePivotTableAggData) {
const rowGroupFields = computeRowGroupFields.value
if (rowGroupFields ? rowGroupFields.length : rowGroupList.length) {
if (rowGroupFields && rowGroupFields.length) {
$xeTable.setRowGroups(rowGroupFields)
} else {
$xeTable.clearRowGroups()
}
} else {
$xeTable.handleUpdateAggData()
}
}
})
},
/**
* 用于 custom-config,用于获取自定义列设置信息,用于自定义保持
* @returns
*/
getCustomStoreData () {
const { id } = props
const customOpts = computeCustomOpts.value
const { isRowGroupStatus, rowGroupList } = reactData
const { fullColumnFieldData, collectColumn } = internalData
const { storage, checkMethod } = customOpts
const { isCustomAlign, isCustomHeaderAlign, isCustomFooterAlign, isCustomResizable, isCustomVisible, isCustomFixed, isCustomSort, isCustomAggGroup, isCustomAggFunc, isCustomPager } = handleCustomStoreConfig($xeTable)
const alignData: Record<string, VxeColumnPropTypes.Align> = {}
const headerAlignData: Record<string, VxeColumnPropTypes.HeaderAlign> = {}
const footerAlignData: Record<string, VxeColumnPropTypes.FooterAlign> = {}
const resizableData: Record<string, number> = {}
const sortData: VxeTableDefines.CustomSortStoreObj[] = []
const visibleData: Record<string, boolean> = {}
const fixedData: Record<string, VxeColumnPropTypes.Fixed> = {}
const aggGroupData: Record<string, boolean> = {}
const aggFuncData: Record<string, VxeColumnPropTypes.AggFunc> = {}
const storeData: VxeTableDefines.CustomStoreData = {
pagerData: isCustomPager && $xeGGWrapper && $xeGGWrapper.getCustomPagerData ? $xeGGWrapper.getCustomPagerData() : undefined,
alignData: undefined,
headerAlignData: undefined,
footerAlignData: undefined,
resizableData: undefined,
sortData: undefined,
visibleData: undefined,
fixedData: undefined,
aggGroupData: undefined,
aggFuncData: undefined
}
if (!id) {
if (storage) {
errLog('vxe.error.reqProp', ['id'])
}
return storeData
}
let hasAlign = 0
let hasHeaderAlign = 0
let hasFooterAlign = 0
let hasResizable = 0
let hasSort = 0
let hasFixed = 0
let hasVisible = 0
let hasAggFunc = 0
const sortMaps: Record<string, VxeTableDefines.CustomSortStoreObj> = {}
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn) => {
const colKey = column.getKey()
if (!colKey) {
errLog('vxe.error.reqProp', [`${column.getTitle() || column.type || ''} -> column.field=?`])
return
}
if (parentColumn) {
if (isCustomSort) {
const pColKey = parentColumn.getKey()
const psObj = sortMaps[pColKey]
hasSort = 1
if (psObj) {
const sObj = { k: colKey }
sortMaps[colKey] = sObj
if (!psObj.c) {
psObj.c = []
}
psObj.c.push(sObj)
}
}
} else {
if (isCustomSort) {
hasSort = 1
const sObj = { k: colKey }
sortMaps[colKey] = sObj
sortData.push(sObj)
}
// 只支持一级
if (isCustomFixed && (column.fixed || '') !== (column.defaultFixed || '')) {
hasFixed = 1
fixedData[colKey] = column.fixed
}
}
if (isCustomAlign && column.align) {
hasAlign = 1
alignData[colKey] = column.align
}
if (isCustomHeaderAlign && column.headerAlign) {
hasHeaderAlign = 1
headerAlignData[colKey] = column.headerAlign
}
if (isCustomFooterAlign && column.footerAlign) {
hasFooterAlign = 1
footerAlignData[colKey] = column.footerAlign
}
if (isCustomResizable && column.resizeWidth) {
hasResizable = 1
resizableData[colKey] = column.renderWidth
}
if (isCustomVisible && (!checkMethod || checkMethod({ $table: $xeTable, column }))) {
if (!column.visible && column.defaultVisible) {
hasVisible = 1
visibleData[colKey] = false
} else if (column.visible && !column.defaultVisible) {
hasVisible = 1
visibleData[colKey] = true
}
}
if (isCustomAggFunc && (column.aggFunc || '') !== (column.defaultAggFunc || '')) {
hasAggFunc = 1
aggFuncData[colKey] = column.aggFunc
}
})
if (hasAlign) {
storeData.alignData = alignData
}
if (hasHeaderAlign) {
storeData.headerAlignData = headerAlignData
}
if (hasFooterAlign) {
storeData.footerAlignData = footerAlignData
}
if (hasResizable) {
storeData.resizableData = resizableData
}
if (hasSort) {
storeData.sortData = sortData
}
if (hasFixed) {
storeData.fixedData = fixedData
}
if (hasVisible) {
storeData.visibleData = visibleData
}
if (isCustomAggGroup && isRowGroupStatus) {
rowGroupList.forEach(aggConf => {
const colRest = fullColumnFieldData[aggConf.field]
if (colRest) {
aggGroupData[colRest.column.getKey()] = true
}
})
storeData.aggGroupData = aggGroupData
}
if (hasAggFunc) {
storeData.aggFuncData = aggFuncData
}
return storeData
},
focus () {
internalData.isActivated = true
return nextTick()
},
blur () {
internalData.isActivated = false
return nextTick()
},
/**
* 已废弃,被 connectToolbar 替换
* @deprecated
*/
connect ($toolbar) {
return $xeTable.connectToolbar($toolbar)
},
/**
* 连接工具栏
* @param $toolbar
*/
connectToolbar ($toolbar) {
if ($toolbar) {
$xeToolbar = $toolbar
$xeToolbar.syncUpdate({ collectColumn: internalData.collectColumn, $table: $xeTable })
} else {
errLog('vxe.error.barUnableLink')
}
return nextTick()
}
}
/**
* 全局按下事件处理
*/
const handleGlobalMousedownEvent = (evnt: MouseEvent) => {
const { editStore, ctxMenuStore, customStore } = reactData
const { mouseConfig, editRules } = props
const el = refElem.value
const editOpts = computeEditOpts.value
const validOpts = computeValidOpts.value
const areaOpts = computeAreaOpts.value
const { actived } = editStore
const $validTooltip = refValidTooltip.value
const tableFilter = refTableFilter.value
const tableCustom = refTableCustom.value
const tableMenu = refTableMenu.value
// 筛选
if (tableFilter) {
if (getEventTargetNode(evnt, el, 'vxe-cell--filter').flag) {
// 如果点击了筛选按钮
} else if (getEventTargetNode(evnt, tableFilter.getRefMaps().refElem.value).flag) {
// 如果点击筛选容器
} else {
if (!getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag) {
$xeTable.preventEvent(evnt, 'event.clearFilter', internalData._currFilterParams, $xeTable.closeFilter)
}
}
}
// 自定义列
if (tableCustom) {
if (customStore.btnEl === evnt.target || getEventTargetNode(evnt, document.body, 'vxe-toolbar-custom-target').flag) {
// 如果点击了自定义列按钮
} else if (getEventTargetNode(evnt, tableCustom.getRefMaps().refElem.value).flag) {
// 如果点击自定义列容器
} else {
if (!getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag) {
if (customStore.visible && $xeTable.closeCustom) {
$xeTable.preventEvent(evnt, 'event.clearCustom', {}, () => {
$xeTable.closeCustom()
$xeTable.dispatchEvent('custom', { type: 'close' }, evnt)
})
}
}
}
}
// 如果已激活了编辑状态
if (actived.row) {
if (!(editOpts.autoClear === false)) {
// 如果是激活状态,点击了单元格之外
const cell = actived.args.cell
if ((!cell || !getEventTargetNode(evnt, cell).flag)) {
if ($validTooltip && getEventTargetNode(evnt, $validTooltip.$el as HTMLDivElement).flag) {
// 如果是激活状态,且点击了校验提示框
} else if (!internalData._lastCallTime || internalData._lastCallTime + 50 < Date.now()) {
// 如果是激活状态,点击了单元格之外
if (!getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag) {
// 如果手动调用了激活单元格,避免触发源被移除后导致重复关闭
$xeTable.preventEvent(evnt, 'event.clearEdit', actived.args, () => {
let isClear
if (editOpts.mode === 'row') {
const rowTargetNode = getEventTargetNode(evnt, el, 'vxe-body--row')
const rowNodeRest = rowTargetNode.flag ? $xeTable.getRowNode(rowTargetNode.targetElem) : null
// row 方式,如果点击了不同行
isClear = rowNodeRest ? !$xeTable.eqRow(rowNodeRest.item, actived.args.row) : false
} else {
// cell 方式,如果是非编辑列
isClear = !getEventTargetNode(evnt, el, 'col--edit').flag
}
// 如果点击表头行,则清除激活状态
if (!isClear) {
isClear = getEventTargetNode(evnt, el, 'vxe-header--row').flag
}
// 如果点击表尾行,则清除激活状态
if (!isClear) {
isClear = getEventTargetNode(evnt, el, 'vxe-footer--row').flag
}
// 如果固定了高度且点击了行之外的空白处,则清除激活状态
if (!isClear && props.height && !reactData.overflowY) {
const bodyWrapperElem = evnt.target as HTMLDivElement
if (hasClass(bodyWrapperElem, 'vxe-table--body-wrapper')) {
isClear = evnt.offsetY < bodyWrapperElem.clientHeight
}
}
if (
isClear ||
// 如果点击了当前表格之外
!getEventTargetNode(evnt, el).flag
) {
setTimeout(() => {
$xeTable.handleClearEdit(evnt).then(() => {
// 如果存在校验,点击了表格之外则清除
if (!internalData.isActivated && editRules && validOpts.autoClear) {
reactData.validErrorMaps = {}
}
})
})
}
})
}
}
}
}
} else if (mouseConfig) {
if (!getEventTargetNode(evnt, el).flag && !($xeGGWrapper && getEventTargetNode(evnt, $xeGGWrapper.getRefMaps().refElem.value).flag) && !(tableMenu && getEventTargetNode(evnt, tableMenu.getRefMaps().refElem.value).flag) && !($xeToolbar && getEventTargetNode(evnt, $xeToolbar.getRefMaps().refElem.value).flag)) {
if ($xeTable.clearSelected) {
$xeTable.clearSelected()
}
if (areaOpts.autoClear) {
if ($xeTable.getCellAreas) {
const cellAreas = $xeTable.getCellAreas()
if (cellAreas && cellAreas.length && !getEventTargetNode(evnt, document.body, 'vxe-table--ignore-areas-clear').flag) {
$xeTable.preventEvent(evnt, 'event.clearAreas', {}, () => {
$xeTable.clearCellAreas()
$xeTable.clearCopyCellArea()
dispatchEvent('clear-cell-area-selection', { cellAreas }, evnt)
})
}
}
}
}
}
// 如果配置了快捷菜单且,点击了其他地方则关闭
if ($xeTable.closeMenu) {
if (ctxMenuStore.visible && tableMenu && !getEventTargetNode(evnt, tableMenu.getRefMaps().refElem.value).flag) {
$xeTable.closeMenu()
}
}
if (!(actived.row && getEventTargetNode(evnt, document.body, 'vxe-table--ignore-clear').flag)) {
const isActivated = getEventTargetNode(evnt, $xeGGWrapper ? $xeGGWrapper.getRefMaps().refElem.value : el).flag
// 如果存在校验,点击了表格之外则清除
if (!isActivated && editRules && validOpts.autoClear) {
reactData.validErrorMaps = {}
}
// 最后激活的表格
internalData.isActivated = isActivated
}
}
/**
* 窗口失焦事件处理
*/
const handleGlobalBlurEvent = () => {
$xeTable.closeFilter()
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
}
const reUpdateCustomStyleEvent = (evnt: MouseEvent) => {
const { customStore } = reactData
const customOpts = computeCustomOpts.value
const { popupOptions } = customOpts
const { transfer } = popupOptions || {}
if (transfer && customStore.visible) {
const tableCustom = refTableCustom.value
if (tableCustom && $xeTable.handleCustomStyle && !checkTargetElement(evnt.target, [tableCustom.getRefMaps().refElem.value as HTMLDivElement], evnt.currentTarget)) {
$xeTable.handleCustomStyle().then(() => {
$xeTable.handleCustomStyle()
})
}
}
}
const handleGlobalScrollEvent = (evnt: MouseEvent) => {
reUpdateCustomStyleEvent(evnt)
}
/**
* 全局滚动事件
*/
const handleGlobalMousewheelEvent = () => {
$xeTable.closeTooltip()
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
}
/**
* 表格键盘事件
*/
const keydownEvent = (evnt: KeyboardEvent) => {
const { mouseConfig, keyboardConfig } = props
const { filterStore, ctxMenuStore, editStore } = reactData
const mouseOpts = computeMouseOpts.value
const keyboardOpts = computeKeyboardOpts.value
const { actived } = editStore
const isEsc = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ESCAPE)
if (isEsc) {
$xeTable.preventEvent(evnt, 'event.keydown', null, () => {
dispatchEvent('keydown-start', {}, evnt)
if (keyboardConfig && mouseConfig && mouseOpts.area && $xeTable.handleKdClAreaEvent) {
$xeTable.handleKdClAreaEvent(evnt)
} else if (actived.row || filterStore.visible || ctxMenuStore.visible) {
evnt.stopPropagation()
// 如果按下了 Esc 键,关闭快捷菜单、筛选
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
$xeTable.closeFilter()
if (keyboardConfig && keyboardOpts.isEsc) {
// 如果是激活编辑状态,则取消编辑
if (actived.row) {
const params = actived.args
$xeTable.handleClearEdit(evnt)
// 如果配置了选中功能,则为选中状态
if (mouseOpts.selected) {
nextTick(() => $xeTable.handleSelected(params, evnt))
}
}
}
}
dispatchEvent('keydown', {}, evnt)
dispatchEvent('keydown-end', {}, evnt)
})
}
}
const contextMenuEvent = (evnt: MouseEvent) => {
dispatchEvent('context-menu', {}, evnt)
}
/**
* 全局键盘事件
*/
const handleGlobalKeydownEvent = (evnt: KeyboardEvent) => {
// 该行为只对当前激活的表格有效
if (internalData.isActivated) {
$xeTable.preventEvent(evnt, 'event.keydown', null, () => {
const { mouseConfig, keyboardConfig, treeConfig, editConfig, highlightCurrentRow, highlightCurrentColumn } = props
const { ctxMenuStore, editStore } = reactData
const { afterFullData, visibleColumn, currentRow } = internalData
const isContentMenu = computeIsContentMenu.value
const bodyMenu = computeBodyMenu.value
const keyboardOpts = computeKeyboardOpts.value
const mouseOpts = computeMouseOpts.value
const editOpts = computeEditOpts.value
const treeOpts = computeTreeOpts.value
const menuList = computeMenuList.value
const rowOpts = computeRowOpts.value
const columnOpts = computeColumnOpts.value
const { isLastEnterAppendRow, beforeEnterMethod, enterMethod, isLastTabAppendRow, beforeTabMethod, tabMethod } = keyboardOpts
const { selected, actived } = editStore
const childrenField = treeOpts.children || treeOpts.childrenField
const keyCode = evnt.keyCode
const isProcess = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.PROCESS)
const isEsc = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ESCAPE)
const hasBackspaceKey = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.BACKSPACE)
const isTab = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.TAB)
const isEnter = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ENTER)
const isSpacebar = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.SPACEBAR)
const isLeftArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_LEFT)
const isUpArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_UP)
const isRightArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_RIGHT)
const isDwArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_DOWN)
const hasDeleteKey = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.DELETE)
const isY = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.Y)
const isZ = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.Z)
const isF2 = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.F2)
const isContextMenu = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.CONTEXT_MENU)
const isControlKey = hasControlKey(evnt)
const hasShiftKey = evnt.shiftKey
const isAltKey = evnt.altKey
const operArrow = isLeftArrow || isUpArrow || isRightArrow || isDwArrow
const operCtxMenu = isContentMenu && ctxMenuStore.visible && (isEnter || isSpacebar || operArrow)
const isEditStatus = isEnableConf(editConfig) && actived.column && actived.row
const beforeEditMethod = editOpts.beforeEditMethod || editOpts.activeMethod
const selectRow = selected.row
const selectColumn = selected.column
if (operCtxMenu) {
// 如果配置了右键菜单; 支持方向键操作、回车
evnt.preventDefault()
if (ctxMenuStore.showChild && hasChildrenList(ctxMenuStore.selected)) {
$xeTable.moveCtxMenu(evnt, ctxMenuStore, 'selectChild', isLeftArrow, false, ctxMenuStore.selected.children)
} else {
$xeTable.moveCtxMenu(evnt, ctxMenuStore, 'selected', isRightArrow, true, menuList)
}
} else if (keyboardConfig && mouseConfig && mouseOpts.area && $xeTable.handleKdClAreaEvent) {
$xeTable.handleKdClAreaEvent(evnt)
} else if (isEsc) {
// 如果按下了 Esc 键,关闭快捷菜单、筛选
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
$xeTable.closeFilter()
if (keyboardConfig && keyboardOpts.isEsc) {
// 如果是激活编辑状态,则取消编辑
if (actived.row) {
const params = actived.args
$xeTable.handleClearEdit(evnt)
// 如果配置了选中功能,则为选中状态
if (mouseOpts.selected) {
nextTick(() => $xeTable.handleSelected(params, evnt))
}
}
}
} else if (isSpacebar && keyboardConfig && keyboardOpts.isChecked && selectRow && selectColumn && (selectColumn.type === 'checkbox' || selectColumn.type === 'radio')) {
// 空格键支持选中复选框
evnt.preventDefault()
if (selectColumn.type === 'checkbox') {
$xeTable.handleToggleCheckRowEvent(evnt, selected.args)
} else {
$xeTable.triggerRadioRowEvent(evnt, selected.args)
}
} else if (isF2 && isEnableConf(editConfig)) {
if (!isEditStatus) {
// 如果按下了 F2 键
if (selectRow && selectColumn) {
evnt.preventDefault()
$xeTable.handleEdit(selected.args, evnt)
}
}
} else if (isContextMenu) {
// 如果按下上下文键
internalData._keyCtx = selectRow && selectColumn && bodyMenu.length
clearTimeout(internalData.keyCtxTimeout)
internalData.keyCtxTimeout = setTimeout(() => {
internalData._keyCtx = false
}, 1000)
} else if (isEnter && !isAltKey && keyboardConfig && keyboardOpts.isEnter && (selectRow || actived.row || (treeConfig && (rowOpts.isCurrent || highlightCurrentRow) && currentRow))) {
// 退出选中
if (isControlKey) {
// 如果是激活编辑状态,则取消编辑
if (actived.row) {
const params = actived.args
$xeTable.handleClearEdit(evnt)
// 如果配置了选中功能,则为选中状态
if (mouseOpts.selected) {
nextTick(() => {
$xeTable.handleSelected(params, evnt)
})
}
}
} else {
// 如果是激活状态,退则出到上一行/下一行
if (selectRow || actived.row) {
const activeRow = selectRow || actived.row
const activeColumn = selectColumn || actived.column
if (activeColumn) {
const activeParams = selectRow ? selected.args : actived.args
if (hasShiftKey) {
if (keyboardOpts.enterToTab) {
$xeTable.moveTabSelected(activeParams, hasShiftKey, evnt)
} else {
$xeTable.moveEnterSelected(activeParams, isLeftArrow, true, isRightArrow, false, evnt)
}
} else {
const _rowIndex = $xeTable.getVTRowIndex(activeRow)
const _columnIndex = $xeTable.getVTColumnIndex(activeColumn)
if (keyboardOpts.enterToTab) {
const ttrParams = {
row: activeRow,
rowIndex: $xeTable.getRowIndex(activeRow),
$rowIndex: $xeTable.getVMRowIndex(activeRow),
_rowIndex,
column: activeColumn,
columnIndex: $xeTable.getColumnIndex(activeColumn),
$columnIndex: $xeTable.getVMColumnIndex(activeColumn),
_columnIndex,
$table: $xeTable
}
if (!beforeTabMethod || beforeTabMethod(ttrParams) !== false) {
evnt.preventDefault()
// 最后一行按下Tab键,自动追加一行
if (isLastTabAppendRow) {
const newColumn = visibleColumn[0]
if (_rowIndex >= afterFullData.length - 1 && _columnIndex >= visibleColumn.length - 1) {
if (actived.row) {
$xeTable.handleClearEdit(evnt)
}
$xeTable.insertAt({}, -1).then(({ row: newRow }) => {
$xeTable.scrollToRow(newRow, newColumn)
$xeTable.handleSelected({ ...activeParams, row: newRow, column: newColumn }, evnt)
})
$xeTable.dispatchEvent('tab-append-row', ttrParams, evnt)
return
}
}
if (tabMethod) {
tabMethod(ttrParams)
} else {
$xeTable.moveTabSelected(activeParams, hasShiftKey, evnt)
}
}
} else {
const etrParams = {
row: activeRow,
rowIndex: $xeTable.getRowIndex(activeRow),
$rowIndex: $xeTable.getVMRowIndex(activeRow),
_rowIndex,
column: activeColumn,
columnIndex: $xeTable.getColumnIndex(activeColumn),
$columnIndex: $xeTable.getVMColumnIndex(activeColumn),
_columnIndex,
$table: $xeTable
}
if (!beforeEnterMethod || beforeEnterMethod(etrParams) !== false) {
evnt.preventDefault()
// 最后一行按下回车键,自动追加一行
if (isLastEnterAppendRow) {
if (_rowIndex >= afterFullData.length - 1) {
$xeTable.insertAt({}, -1).then(({ row: newRow }) => {
$xeTable.scrollToRow(newRow, activeColumn)
$xeTable.handleSelected({ ...activeParams, row: newRow }, evnt)
})
$xeTable.dispatchEvent('enter-append-row', etrParams, evnt)
return
}
}
if (enterMethod) {
enterMethod(etrParams)
} else {
if (actived.row) {
$xeTable.handleClearEdit(evnt)
}
$xeTable.moveEnterSelected(activeParams, isLeftArrow, false, isRightArrow, true, evnt)
}
}
}
}
}
} else if (treeConfig && (rowOpts.isCurrent || highlightCurrentRow) && currentRow) {
// 如果是树形表格当前行回车移动到子节点
const childrens = currentRow[childrenField]
if (childrens && childrens.length) {
evnt.preventDefault()
const targetRow = childrens[0]
const params = {
$table: $xeTable,
row: targetRow,
rowIndex: $xeTable.getRowIndex(targetRow),
$rowIndex: $xeTable.getVMRowIndex(targetRow)
}
$xeTable.setTreeExpand(currentRow, true)
.then(() => $xeTable.scrollToRow(targetRow))
.then(() => $xeTable.triggerCurrentRowEvent(evnt, params))
}
}
}
} else if (operArrow && keyboardConfig && keyboardOpts.isArrow) {
if (!isEditStatus) {
// 如果按下了方向键
if (mouseOpts.selected && selectRow && selectColumn) {
$xeTable.moveArrowSelected(selected.args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow, evnt)
} else {
// 当前行按键上下移动
if ((isUpArrow || isDwArrow) && (rowOpts.isCurrent || highlightCurrentRow)) {
$xeTable.moveCurrentRow(isUpArrow, isDwArrow, evnt)
}
// 当前行按键左右移动
if ((isLeftArrow || isRightArrow) && (columnOpts.isCurrent || highlightCurrentColumn)) {
$xeTable.moveCurrentColumn(isLeftArrow, isRightArrow, evnt)
}
}
}
} else if (isTab && keyboardConfig && keyboardOpts.isTab) {
// 如果按下了 Tab 键切换
if (selectRow || actived.row) {
const activeRow = selectRow || actived.row
const activeColumn = selectColumn || actived.column
if (activeColumn) {
const activeParams = selectRow ? selected.args : actived.args
const _rowIndex = $xeTable.getVTRowIndex(activeRow)
const _columnIndex = $xeTable.getVTColumnIndex(activeColumn)
const ttrParams = {
row: activeRow,
rowIndex: $xeTable.getRowIndex(activeRow),
$rowIndex: $xeTable.getVMRowIndex(activeRow),
_rowIndex,
column: activeColumn as VxeTableDefines.ColumnInfo,
columnIndex: $xeTable.getColumnIndex(activeColumn),
$columnIndex: $xeTable.getVMColumnIndex(activeColumn),
_columnIndex,
$table: $xeTable
}
if (!beforeTabMethod || beforeTabMethod(ttrParams) !== false) {
evnt.preventDefault()
// 最后一行按下Tab键,自动追加一行
if (isLastTabAppendRow) {
const newColumn = visibleColumn[0]
if (_rowIndex >= afterFullData.length - 1 && _columnIndex >= visibleColumn.length - 1) {
if (actived.row) {
$xeTable.handleClearEdit(evnt)
}
$xeTable.insertAt({}, -1).then(({ row: newRow }) => {
$xeTable.scrollToRow(newRow, newColumn)
$xeTable.handleSelected({ ...activeParams, row: newRow, column: newColumn }, evnt)
})
$xeTable.dispatchEvent('tab-append-row', ttrParams, evnt)
return
}
}
if (tabMethod) {
tabMethod(ttrParams)
} else {
if (actived.row) {
$xeTable.handleClearEdit(evnt)
}
$xeTable.moveTabSelected(activeParams, hasShiftKey, evnt)
}
}
}
}
} else if (keyboardConfig && keyboardOpts.isDel && hasDeleteKey && isEnableConf(editConfig) && (selectRow || selectColumn)) {
// 如果是删除键
if (!isEditStatus) {
const { delMethod } = keyboardOpts
const params = {
row: selectRow,
rowIndex: $xeTable.getRowIndex(selectRow),
column: selectColumn as VxeTableDefines.ColumnInfo,
columnIndex: $xeTable.getColumnIndex(selectColumn),
$table: $xeTable,
$grid: $xeGrid,
$gantt: $xeGantt
}
// 是否被禁用
if (!beforeEditMethod || beforeEditMethod(params)) {
if (delMethod) {
delMethod(params)
} else {
const selectCellValue = getCellValue(selectRow, selectColumn)
if (selectCellValue !== null) {
$xeTable.handlePushStack()
setCellValue(selectRow, selectColumn, null)
}
}
// 如果按下 del 键,更新表尾数据
$xeTable.updateFooter()
dispatchEvent('cell-delete-value', params, evnt)
}
}
} else if (hasBackspaceKey && keyboardConfig && keyboardOpts.isBack && isEnableConf(editConfig) && (selectRow || selectColumn)) {
if (!isEditStatus) {
const { backMethod } = keyboardOpts
// 如果是删除键
if (keyboardOpts.isDel && isEnableConf(editConfig) && (selectRow || selectColumn)) {
const params = {
row: selectRow,
rowIndex: $xeTable.getRowIndex(selectRow),
$rowIndex: $xeTable.getVMRowIndex(selectRow),
_rowIndex: $xeTable.getVTRowIndex(selectRow),
column: selectColumn as VxeTableDefines.ColumnInfo,
columnIndex: $xeTable.getColumnIndex(selectColumn),
$columnIndex: $xeTable.getVMColumnIndex(selectColumn),
_columnIndex: $xeTable.getVTColumnIndex(selectColumn),
$table: $xeTable,
$grid: $xeGrid,
$gantt: $xeGantt
}
// 是否被禁用
if (!beforeEditMethod || beforeEditMethod(params)) {
if (backMethod) {
backMethod(params)
} else {
$xeTable.handleEdit(params, evnt, {
isClear: true
})
}
dispatchEvent('cell-backspace-value', params, evnt)
}
}
}
} else if (hasBackspaceKey && keyboardConfig && treeConfig && keyboardOpts.isBack && (rowOpts.isCurrent || highlightCurrentRow) && currentRow) {
// 如果树形表格回退键关闭当前行返回父节点
const { parent: parentRow } = XEUtils.findTree(internalData.afterTreeFullData, item => item === currentRow, { children: childrenField })
if (parentRow) {
evnt.preventDefault()
const params = {
row: parentRow,
rowIndex: $xeTable.getRowIndex(parentRow),
$rowIndex: $xeTable.getVMRowIndex(parentRow),
$table: $xeTable,
$grid: $xeGrid,
$gantt: $xeGantt
}
$xeTable.setTreeExpand(parentRow, false)
.then(() => $xeTable.scrollToRow(parentRow))
.then(() => $xeTable.triggerCurrentRowEvent(evnt, params))
}
} else if (keyboardConfig && keyboardOpts.isUndoRedo && isControlKey && (isZ || isY) && !hasEventInputTarget(evnt.target)) {
if (evnt.target) {
if (isY || (hasShiftKey && isZ)) {
// 恢复被撤销的操作:Ctrl + Y 或 Ctrl + Shift + Z
evnt.preventDefault()
$xeTable.handleRedoStackEvent(evnt)
} else if (isZ) {
// 撤销上一步操作:Ctrl + Z
evnt.preventDefault()
$xeTable.handleUndoStackEvent(evnt)
}
}
} else if (keyboardConfig && isEnableConf(editConfig) && keyboardOpts.isEdit && !isControlKey && (isSpacebar || isProcess || evnt.key === 'Process' || (keyCode >= 48 && keyCode <= 57) || (keyCode >= 65 && keyCode <= 90) || (keyCode >= 96 && keyCode <= 111) || (keyCode >= 186 && keyCode <= 192) || (keyCode >= 219 && keyCode <= 222))) {
const { editMode, editMethod } = keyboardOpts
// 启用编辑后,空格键功能将失效
// if (isSpacebar) {
// evnt.preventDefault()
// }
// 如果是按下非功能键之外允许直接编辑
if (selectColumn && selectRow && isEnableConf(selectColumn.editRender)) {
const beforeEditMethod = editOpts.beforeEditMethod || editOpts.activeMethod
const params = {
row: selectRow,
rowIndex: $xeTable.getRowIndex(selectRow),
$rowIndex: $xeTable.getVMRowIndex(selectRow),
_rowIndex: $xeTable.getVTRowIndex(selectRow),
column: selectColumn,
columnIndex: $xeTable.getColumnIndex(selectColumn),
$columnIndex: $xeTable.getVMColumnIndex(selectColumn),
_columnIndex: $xeTable.getVTColumnIndex(selectColumn),
$table: $xeTable,
$grid: $xeGrid,
$gantt: $xeGantt
}
if (!beforeEditMethod || beforeEditMethod(params)) {
if (editMethod) {
editMethod(params)
} else {
let isClearValue = false
// 追加方式与覆盖式
if (editMode !== 'insert') {
isClearValue = true
}
$xeTable.handleEdit(params, evnt, {
isClear: isClearValue
})
}
}
}
}
dispatchEvent('keydown', {}, evnt)
})
}
}
const handleGlobalPasteEvent = (evnt: ClipboardEvent) => {
const { keyboardConfig, mouseConfig } = props
const { editStore, filterStore } = reactData
const { isActivated } = internalData
const mouseOpts = computeMouseOpts.value
const keyboardOpts = computeKeyboardOpts.value
const { actived } = editStore
if (isActivated && !filterStore.visible) {
if (!(actived.row || actived.column)) {
if (keyboardConfig && keyboardOpts.isClip && mouseConfig && mouseOpts.area && $xeTable.handlePeClAreaEvent) {
$xeTable.handlePeClAreaEvent(evnt)
}
}
dispatchEvent('paste', {}, evnt)
}
}
const handleGlobalCopyEvent = (evnt: ClipboardEvent) => {
const { keyboardConfig, mouseConfig } = props
const { editStore, filterStore } = reactData
const { isActivated } = internalData
const mouseOpts = computeMouseOpts.value
const keyboardOpts = computeKeyboardOpts.value
const { actived } = editStore
if (isActivated && !filterStore.visible) {
if (!(actived.row || actived.column)) {
if (keyboardConfig && keyboardOpts.isClip && mouseConfig && mouseOpts.area && $xeTable.handleCyClAreaEvent) {
$xeTable.handleCyClAreaEvent(evnt)
}
}
dispatchEvent('copy', {}, evnt)
}
}
const handleGlobalCutEvent = (evnt: ClipboardEvent) => {
const { keyboardConfig, mouseConfig } = props
const { editStore, filterStore } = reactData
const { isActivated } = internalData
const mouseOpts = computeMouseOpts.value
const keyboardOpts = computeKeyboardOpts.value
const { actived } = editStore
if (isActivated && !filterStore.visible) {
if (!(actived.row || actived.column)) {
if (keyboardConfig && keyboardOpts.isClip && mouseConfig && mouseOpts.area && $xeTable.handleCutCellAreaEvent) {
$xeTable.handleCutCellAreaEvent(evnt)
}
}
dispatchEvent('cut', {}, evnt)
}
}
const handleGlobalResizeEvent = () => {
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
const el = refElem.value
if (!el || !el.clientWidth) {
return
}
if (internalData.rsePending) {
return
}
handleResizeEvent()
}
const handleTargetEnterEvent = (isClear: boolean) => {
const $tooltip = refTooltip.value
clearTimeout(internalData.tooltipTimeout)
if (isClear) {
$xeTable.closeTooltip()
} else {
if ($tooltip && $tooltip.setActived) {
$tooltip.setActived(true)
}
}
}
const clearCrossTableDragStatus = () => {
crossTableDragRowObj = null
crossTableDragRowInfo.row = null
}
const clearDragStatus = () => {
const { dragRow, dragCol } = reactData
if (dragRow || dragCol) {
clearColDropOrigin()
clearRowDropOrigin()
hideDropTip()
clearCrossTableDragStatus()
reactData.dragRow = null
reactData.dragCol = null
}
}
const handleRowDragEndClearStatus = () => {
clearRowDragData()
clearCrossTableDragStatus()
handleRecalculateStyle(false, true, true)
}
const handleColDragEndClearStatus = () => {
clearColDragData()
clearCrossTableDragStatus()
handleRecalculateStyle(true, true, true)
}
const clearRowDropOrigin = () => {
const el = refElem.value
if (el) {
const clss = 'row--drag-origin'
XEUtils.arrayEach(el.querySelectorAll(`.${clss}`), (elem) => {
(elem as HTMLTableCellElement).draggable = false
removeClass(elem, clss)
})
}
}
const updateRowDropOrigin = (row: any) => {
const el = refElem.value
if (el) {
const clss = 'row--drag-origin'
const rowid = getRowid($xeTable, row)
XEUtils.arrayEach(el.querySelectorAll(`[rowid="${rowid}"]`), (elem) => {
addClass(elem, clss)
})
}
}
const updateRowDropTipContent = (tdEl: HTMLElement) => {
const { dragConfig } = props
const { dragRow } = reactData
const rowDragOpts = computeRowDragOpts.value
const { tooltipMethod } = rowDragOpts
const rTooltipMethod = tooltipMethod || (dragConfig ? dragConfig.rowTooltipMethod : null)
let tipContent = ''
if (rTooltipMethod) {
const rtParams = {
$table: $xeTable,
row: dragRow
}
tipContent = `${rTooltipMethod(rtParams) || ''}`
} else {
tipContent = getI18n('vxe.table.dragTip', [tdEl.textContent || ''])
}
reactData.dragTipText = tipContent
}
const updateColDropOrigin = (column: VxeTableDefines.ColumnInfo) => {
const el = refElem.value
if (el) {
const colQuerys: string[] = []
XEUtils.eachTree([column], item => {
colQuerys.push(`[colid="${item.id}"]`)
})
const clss = 'col--drag-origin'
XEUtils.arrayEach(el.querySelectorAll(colQuerys.join(',')), (elem) => {
addClass(elem, clss)
})
}
}
const clearColDropOrigin = () => {
const el = refElem.value
if (el) {
const clss = 'col--drag-origin'
XEUtils.arrayEach(el.querySelectorAll(`.${clss}`), (elem) => {
(elem as HTMLTableCellElement).draggable = false
removeClass(elem, clss)
})
}
}
const updateColDropTipContent = (tdEl: HTMLElement) => {
const { dragCol } = reactData
const columnDragOpts = computeColumnDragOpts.value
const { tooltipMethod } = columnDragOpts
let tipContent = ''
if (tooltipMethod) {
const dtParams = {
$table: $xeTable,
column: dragCol as VxeTableDefines.ColumnInfo
}
tipContent = `${tooltipMethod(dtParams) || ''}`
} else {
tipContent = getI18n('vxe.table.dragTip', [tdEl.textContent || ''])
}
reactData.dragTipText = tipContent
}
const showDropTip = (evnt: DragEvent | MouseEvent, trEl: HTMLElement | null, thEl: HTMLElement | null, showLine: boolean, dragPos: string) => {
let wrapperEl = refElem.value
if ($xeGantt && trEl) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
wrapperEl = ganttContainerElem
}
}
if (!wrapperEl) {
return
}
const { overflowX, scrollbarWidth, overflowY, scrollbarHeight } = reactData
const { prevDragToChild } = internalData
const wrapperRect = wrapperEl.getBoundingClientRect()
const osbWidth = overflowY ? scrollbarWidth : 0
const osbHeight = overflowX ? scrollbarHeight : 0
const tableWrapperWidth = wrapperEl.clientWidth
const tableWrapperHeight = wrapperEl.clientHeight
if (trEl) {
const rdLineEl = refDragRowLineElem.value
if (rdLineEl) {
if (showLine) {
const scrollbarYToLeft = computeScrollbarYToLeft.value
const trRect = trEl.getBoundingClientRect()
let trHeight = trEl.clientHeight
const offsetTop = Math.max(1, trRect.y - wrapperRect.y)
if (offsetTop + trHeight > tableWrapperHeight - osbHeight) {
trHeight = tableWrapperHeight - offsetTop - osbHeight
}
rdLineEl.style.display = 'block'
rdLineEl.style.left = `${scrollbarYToLeft ? osbWidth : 0}px`
rdLineEl.style.top = `${offsetTop}px`
rdLineEl.style.height = `${trHeight}px`
rdLineEl.style.width = `${tableWrapperWidth - osbWidth}px`
rdLineEl.setAttribute('drag-pos', dragPos)
rdLineEl.setAttribute('drag-to-child', prevDragToChild ? 'y' : 'n')
} else {
rdLineEl.style.display = ''
}
}
} else if (thEl) {
const cdLineEl = refDragColLineElem.value
if (cdLineEl) {
if (showLine) {
const scrollbarXToTop = computeScrollbarXToTop.value
const leftContainerElem = refLeftContainer.value
const leftContainerWidth = leftContainerElem ? leftContainerElem.clientWidth : 0
const rightContainerElem = refRightContainer.value
const rightContainerWidth = rightContainerElem ? rightContainerElem.clientWidth : 0
const thRect = thEl.getBoundingClientRect()
let thWidth = thEl.clientWidth
const offsetTop = Math.max(0, thRect.y - wrapperRect.y)
const startX = leftContainerWidth
let offsetLeft = thRect.x - wrapperRect.x
if (offsetLeft < startX) {
thWidth -= startX - offsetLeft
offsetLeft = startX
}
const endX = tableWrapperWidth - rightContainerWidth - (rightContainerWidth ? 0 : osbWidth)
if (offsetLeft + thWidth > endX) {
thWidth = endX - offsetLeft
}
cdLineEl.style.display = 'block'
cdLineEl.style.top = `${offsetTop}px`
cdLineEl.style.left = `${offsetLeft}px`
cdLineEl.style.width = `${thWidth}px`
if (prevDragToChild) {
cdLineEl.style.height = `${thRect.height}px`
} else {
cdLineEl.style.height = `${tableWrapperHeight - offsetTop - (scrollbarXToTop ? 0 : osbHeight)}px`
}
cdLineEl.setAttribute('drag-pos', dragPos)
cdLineEl.setAttribute('drag-to-child', prevDragToChild ? 'y' : 'n')
} else {
cdLineEl.style.display = ''
}
}
}
const rdTipEl = refDragTipElem.value
if (rdTipEl) {
rdTipEl.style.display = 'block'
rdTipEl.style.top = `${Math.min(wrapperEl.clientHeight - wrapperEl.scrollTop - rdTipEl.clientHeight, evnt.clientY - wrapperRect.y)}px`
rdTipEl.style.left = `${Math.min(wrapperEl.clientWidth - wrapperEl.scrollLeft - rdTipEl.clientWidth - 16, evnt.clientX - wrapperRect.x)}px`
rdTipEl.setAttribute('drag-status', showLine ? (prevDragToChild ? 'sub' : 'normal') : 'disabled')
}
}
const hideDropTip = () => {
const rdTipEl = refDragTipElem.value
const rdLineEl = refDragRowLineElem.value
const cdLineEl = refDragColLineElem.value
if (rdTipEl) {
rdTipEl.style.display = ''
}
if (rdLineEl) {
rdLineEl.style.display = ''
}
if (cdLineEl) {
cdLineEl.style.display = ''
}
}
const clearRowDragData = () => {
let wrapperEl = refElem.value
const dtClss = ['.vxe-body--row']
if ($xeGantt) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
wrapperEl = ganttContainerElem
}
dtClss.push('.vxe-gantt-view--body-row', '.vxe-gantt-view--chart-row')
}
hideDropTip()
clearRowDropOrigin()
clearRowAnimate(wrapperEl, dtClss)
internalData.prevDragToChild = false
reactData.dragRow = null
reactData.dragCol = null
}
const clearColDragData = () => {
const el = refElem.value
hideDropTip()
clearColDropOrigin()
clearColAnimate(el, ['.vxe-table--column'])
internalData.prevDragToChild = false
reactData.dragRow = null
reactData.dragCol = null
}
/**
* 处理显示 tooltip
* @param {Event} evnt 事件
* @param {Row} row 行对象
*/
const handleTooltip = (evnt: MouseEvent, tipOpts: VxeTablePropTypes.TooltipConfig | VxeTablePropTypes.HeaderTooltipConfig | VxeTablePropTypes.FooterTooltipConfig, type: 'header' | 'body' | 'footer', tdEl: HTMLTableCellElement, overflowElem: HTMLElement | null, tipElem: HTMLElement | null, params: any) => {
const tipOverEl = overflowElem || tdEl
if (!tipOverEl) {
return nextTick()
}
params.cell = tdEl
const { tooltipStore } = reactData
const { column, row } = params
const { showAll, contentMethod } = tipOpts
const cellText = XEUtils.toString(column.type === 'html' ? tipOverEl.innerText : tipOverEl.textContent).trim()
const isOver = tipOverEl.scrollWidth > tipOverEl.clientWidth
let isShow = false
let customContent: any = null
let content = ''
if (contentMethod) {
customContent = contentMethod(params)
}
// 是否全部展示
if (showAll) {
// 如果为 null 默认显示
if (XEUtils.eqNull(customContent)) {
isShow = true
content = cellText
} else if (customContent !== '' && customContent !== false) {
// 如果为 '' | false 则不显示
isShow = true
content = '' + customContent
}
} else {
// 如果为 null 使用默认逻辑
if (XEUtils.eqNull(customContent)) {
isShow = isOver
content = cellText
} else if (customContent !== '' && customContent !== false) {
// 如果为 '' | false 则不显示
isShow = true
content = customContent
} else {
content = cellText
}
}
if (isShow && content) {
const tipContent = formatText(content)
Object.assign(tooltipStore, {
row,
column,
visible: true,
content: tipContent,
type,
currOpts: tipOpts
})
nextTick(() => {
const $tooltip = refTooltip.value
if ($tooltip && $tooltip.open) {
$tooltip.open(isOver ? tipOverEl : tipElem, tipContent)
}
})
}
return nextTick()
}
const truncateStackHistory = () => {
const { stackHistoryStore } = internalData
const { undoStacks } = stackHistoryStore
const undoRedoHistoryOpts = computeUndoRedoHistoryOpts.value
const { stackSize } = undoRedoHistoryOpts
if (undoStacks.length > XEUtils.toNumber(stackSize)) {
undoStacks.shift()
}
}
/**
* 生成快照数据
*/
const getSnapshotStackData = () => {
const { editStore } = reactData
const { afterFullData, elemStore, insertRowMaps, removeRowMaps } = internalData
const { selected, actived } = editStore
const { row: selectRow, column: selectColumn } = selected
const { row: editRow, column: editColumn } = actived
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const stackObj: VxeTableDefines.HistoryStackObj = {
selectActiveInfo: selectRow && selectColumn
? {
rowid: getRowid($xeTable, selectRow),
colid: selectColumn.id
}
: undefined,
editActiveInfo: editRow && editColumn
? {
rowid: getRowid($xeTable, editRow),
colid: editColumn.id
}
: undefined,
scrollInfo: {
top: bodyScrollElem ? bodyScrollElem.scrollTop : 0,
left: bodyScrollElem ? bodyScrollElem.scrollLeft : 0
},
insertData: XEUtils.clone(XEUtils.values(insertRowMaps), true),
removeData: XEUtils.clone(XEUtils.values(removeRowMaps), true),
visibleData: XEUtils.clone(afterFullData, true),
visibleTreeData: [],
visibleColumn: []
}
return $xeTable.getCellAreaPushStackObj ? $xeTable.getCellAreaPushStackObj(stackObj) : stackObj
}
/**
* 刷新栈视图
*/
const handleUpdateSnapshotStackData = (stackObj: VxeTableDefines.HistoryStackObj | null | undefined) => {
const { treeConfig } = props
const { editStore } = reactData
const { fullAllDataRowIdData, fullColumnIdData } = internalData
if (!stackObj) {
return
}
const treeOpts = computeTreeOpts.value
const { transform } = treeOpts
const { visibleData, editActiveInfo, selectActiveInfo, scrollInfo, insertData, removeData } = stackObj
const iRowMaps: Record<string, any> = {}
insertData.forEach(item => {
const rowid = getRowid($xeTable, item)
const rest = fullAllDataRowIdData[rowid]
let row = item
if (rest) {
row = rest.row
Object.assign(row, item)
}
iRowMaps[rowid] = row
})
const rRowMaps: Record<string, any> = {}
removeData.forEach(item => {
const rowid = getRowid($xeTable, item)
const rest = fullAllDataRowIdData[rowid]
let row = item
if (rest) {
row = rest.row
Object.assign(row, item)
}
rRowMaps[rowid] = row
})
const afterFullList = visibleData.map(item => {
const rowid = getRowid($xeTable, item)
const rest = fullAllDataRowIdData[rowid]
let row = item
if (rest) {
row = rest.row
Object.assign(row, item)
}
return row
})
internalData.insertRowMaps = iRowMaps
internalData.removeRowMaps = rRowMaps
internalData.afterFullData = afterFullList
$xeTable.cacheRowMap(false)
$xeTable.handleTableData(treeConfig && transform)
$xeTable.updateFooter()
$xeTable.handleUpdateBodyMerge()
if (!(treeConfig && transform)) {
$xeTable.updateAfterDataIndex()
}
$xeTable.checkSelectionStatus()
if (reactData.scrollYLoad) {
$xeTable.updateScrollYSpace()
}
if ($xeTable.handleCellAreaSnapshotStackData) {
$xeTable.handleCellAreaSnapshotStackData(stackObj)
} else {
const { selected } = editStore
const mouseOpts = computeMouseOpts.value
let selectRowid = ''
let selectColid = ''
if (editActiveInfo) {
selectRowid = editActiveInfo.rowid
selectColid = editActiveInfo.colid
} else if (selectActiveInfo) {
selectRowid = selectActiveInfo.rowid
selectColid = selectActiveInfo.colid
}
if (selectRowid && selectColid && mouseOpts.selected) {
const editRow = fullAllDataRowIdData[selectRowid] ? fullAllDataRowIdData[selectRowid].row : null
const editColumn = fullColumnIdData[selectColid] ? fullColumnIdData[selectColid].column : null
if (editRow && editColumn) {
const params = {
row: editRow,
column: editColumn
}
selected.row = editRow
selected.column = editColumn
selected.args = params
nextTick(() => {
$xeTable.addCellSelectedClass()
$xeTable.scrollTo(scrollInfo)
})
}
}
}
}
const callSlot = <T>(slotFunc: ((params: T) => VxeComponentSlotType | VxeComponentSlotType[]) | string | null, params: T): VxeComponentSlotType[] => {
if (slotFunc) {
if ($xeGGWrapper) {
return $xeGGWrapper.callSlot(slotFunc, params)
}
// if (XEUtils.isString(slotFunc)) {
// slotFunc = slots[slotFunc] || null
// }
if (XEUtils.isFunction(slotFunc)) {
return getSlotVNs(slotFunc(params))
}
}
return []
}
/**
* 内部方法
*/
tablePrivateMethods = {
getSetupOptions () {
return getConfig()
},
updateAfterDataIndex,
callSlot,
/**
* 获取父容器元素
*/
getParentElem () {
const el = refElem.value
if ($xeGGWrapper) {
const gridEl = $xeGGWrapper.getRefMaps().refElem.value
return gridEl ? gridEl.parentNode as HTMLElement : null
}
return el ? el.parentNode as HTMLElement : null
},
/**
* 获取父容器的高度
*/
getParentHeight () {
const { height } = props
const el = refElem.value
if (el) {
const parentElem = el.parentNode as HTMLElement
let parentPaddingSize = 0
let parentWrapperHeight = 0
if (parentElem) {
if ($xeGantt && hasClass(parentElem, 'vxe-gantt--table-wrapper')) {
parentWrapperHeight = $xeGantt.getParentHeight()
} else if ($xeGrid && hasClass(parentElem, 'vxe-grid--table-wrapper')) {
parentWrapperHeight = $xeGrid.getParentHeight()
} else {
parentWrapperHeight = parentElem.clientHeight
parentPaddingSize = height === '100%' || height === 'auto' ? getPaddingTopBottomSize(parentElem) : 0
}
}
return Math.floor(parentWrapperHeight - parentPaddingSize)
}
return 0
},
/**
* 获取需要排除的高度
* 但渲染表格高度时,需要排除工具栏或分页等相关组件的高度
* 如果存在表尾合计滚动条,则需要排除滚动条高度
*/
getExcludeHeight () {
return $xeGGWrapper ? $xeGGWrapper.getExcludeHeight() : 0
},
/**
* 定义行数据中的列属性,如果不存在则定义
* @param {Row} records 行数据
*/
defineField (records) {
const { treeConfig } = props
const expandOpts = computeExpandOpts.value
const treeOpts = computeTreeOpts.value
const radioOpts = computeRadioOpts.value
const rowOpts = computeRowOpts.value
const checkboxOpts = computeCheckboxOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const rowkey = getRowkey($xeTable)
if (!XEUtils.isArray(records)) {
records = [records]
}
return records.map(record => {
internalData.tableFullColumn.forEach(column => {
const { field, editRender } = column
if (field && !XEUtils.has(record, field) && !record[field]) {
let cellValue = null
if (editRender) {
const { defaultValue } = editRender
if (XEUtils.isFunction(defaultValue)) {
cellValue = defaultValue({ column })
} else if (!XEUtils.isUndefined(defaultValue)) {
cellValue = defaultValue
}
}
XEUtils.set(record, field, cellValue)
}
})
const otherFields: (string | undefined)[] = [radioOpts.labelField, checkboxOpts.checkField, checkboxOpts.labelField, expandOpts.labelField]
otherFields.forEach((key) => {
if (key && eqEmptyValue(XEUtils.get(record, key))) {
XEUtils.set(record, key, null)
}
})
if (treeConfig && treeOpts.lazy && XEUtils.isUndefined(record[childrenField])) {
record[childrenField] = null
}
// 必须有行数据的唯一主键,可以自行设置;也可以默认生成一个随机数
if (eqEmptyValue(XEUtils.get(record, rowkey))) {
XEUtils.set(record, rowkey, createRowId(rowOpts, record, rowkey))
}
return record
})
},
handleTableData (force?: boolean) {
const { scrollYLoad } = reactData
const { scrollYStore, fullDataRowIdData } = internalData
const $xeGanttView = internalData.xeGanttView
let fullList: any[] = internalData.afterFullData
// 是否进行数据处理
if (force) {
// 更新数据,处理筛选和排序
updateAfterFullData()
// 如果为虚拟树,将树结构拍平
fullList = handleVirtualTreeToList()
// 更新数据后清除合并缓存,涉及分组、筛选、排序等
internalData.mergeHeaderRowMaps = {}
internalData.mergeHeaderColMaps = {}
internalData.mergeBodyRowMaps = {}
internalData.mergeBodyColMaps = {}
internalData.mergeFooterRowMaps = {}
internalData.mergeFooterColMaps = {}
}
const tableData = scrollYLoad ? fullList.slice(scrollYStore.startIndex, scrollYStore.endIndex) : fullList.slice(0)
const visibleDataRowIdMaps: Record<string, any> = {}
tableData.forEach((row, $index) => {
const rowid = getRowid($xeTable, row)
const rest = fullDataRowIdData[rowid]
if (rest) {
rest.$index = $index
}
visibleDataRowIdMaps[rowid] = row
})
reactData.tableData = tableData
internalData.visibleDataRowIdData = visibleDataRowIdMaps
if ($xeGanttView && $xeGanttView.updateViewData) {
$xeGanttView.updateViewData(force)
}
return nextTick()
},
/**
* 更新数据行的 Map
*/
cacheRowMap (isReset) {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { currKeyField, fullAllDataRowIdData, tableFullData, tableFullTreeData, tableFullGroupData, treeExpandedMaps, rowExpandedMaps, selectCheckboxMaps } = internalData
const fullAllDataRowIdMaps: Record<string, VxeTableDefines.RowCacheItem> = isReset ? {} : { ...fullAllDataRowIdData } // 存在已删除数据
const fullDataRowIdMaps: Record<string, VxeTableDefines.RowCacheItem> = {}
const idMaps: Record<string, boolean> = {}
const { handleUpdateRowId } = createHandleUpdateRowId($xeTable)
let rowNum = 0
const handleRowCache = (row: any, index: number, items: any, currIndex: number, parentRow: any, rowid: string, level: number, seq: string | number) => {
let rowRest = fullAllDataRowIdMaps[rowid]
if (idMaps[rowid]) {
errLog('vxe.error.repeatKey', [currKeyField, rowid])
}
if (!rowRest) {
rowRest = { row, rowid, _seq: -1, seq, index: -1, _index: -1, $index: -1, treeIndex: index, _tIndex: -1, items, parent: parentRow, level, height: 0, resizeHeight: 0, oTop: 0, expandHeight: 0 }
fullDataRowIdMaps[rowid] = rowRest
fullAllDataRowIdMaps[rowid] = rowRest
}
rowRest.treeLoaded = false
rowRest.expandLoaded = false
rowRest._seq = rowNum++
rowRest.row = row
rowRest.items = items
rowRest.parent = parentRow
rowRest.level = level
rowRest.index = currIndex
rowRest.treeIndex = index
// 更新缓存
if (selectCheckboxMaps[rowid]) {
selectCheckboxMaps[rowid] = row
}
if (rowExpandedMaps[rowid]) {
rowExpandedMaps[rowid] = row
}
rowNum++
idMaps[rowid] = true
fullDataRowIdMaps[rowid] = rowRest
fullAllDataRowIdMaps[rowid] = rowRest
}
if (treeConfig) {
const treeOpts = computeTreeOpts.value
const { lazy } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const hasChildField = treeOpts.hasChild || treeOpts.hasChildField
XEUtils.eachTree(tableFullTreeData, (row, index, items, path, parentRow, nodes) => {
const rowid = handleUpdateRowId(row)
if (lazy) {
if (row[hasChildField] && row[childrenField] === undefined) {
row[childrenField] = null
}
if (treeExpandedMaps[rowid]) {
if (!row[childrenField] || !row[childrenField].length) {
delete treeExpandedMaps[rowid]
}
}
}
handleRowCache(row, index, items, parentRow ? -1 : index, parentRow, rowid, nodes.length - 1, toTreePathSeq(path))
}, { children: childrenField })
} else if (isRowGroupStatus) {
const aggregateOpts = computeAggregateOpts.value
const { mapChildrenField } = aggregateOpts
XEUtils.eachTree(tableFullGroupData, (row, index, items, path, parentRow, nodes) => {
const rowid = handleUpdateRowId(row)
handleRowCache(row, index, items, parentRow ? -1 : index, parentRow, rowid, nodes.length - 1, toTreePathSeq(path))
}, { children: mapChildrenField })
} else {
tableFullData.forEach((row, index, items) => {
handleRowCache(row, index, items, index, null, handleUpdateRowId(row), 0, index + 1)
})
}
internalData.fullDataRowIdData = fullDataRowIdMaps
internalData.fullAllDataRowIdData = fullAllDataRowIdMaps
reactData.treeExpandedFlag++
},
cacheSourceMap (fullData) {
const { treeConfig } = props
const treeOpts = computeTreeOpts.value
const sourceData = XEUtils.clone(fullData, true)
const { handleUpdateRowId } = createHandleUpdateRowId($xeTable)
const sourceRowIdData: Record<string, any> = {}
const handleSourceRow = (row: any) => {
const rowid = handleUpdateRowId(row)
sourceRowIdData[rowid] = row
}
// 源数据缓存
if (treeConfig) {
const childrenField = treeOpts.children || treeOpts.childrenField
XEUtils.eachTree(sourceData, handleSourceRow, { children: treeOpts.transform ? treeOpts.mapChildrenField : childrenField })
} else {
sourceData.forEach(handleSourceRow)
}
internalData.sourceDataRowIdData = sourceRowIdData
internalData.tableSourceData = sourceData
},
/**
* 指定列宽的列进行拆分
*/
analyColumnWidth () {
const { tableFullColumn } = internalData
const columnOpts = computeColumnOpts.value
const { width: defaultWidth, minWidth: defaultMinWidth } = columnOpts
const resizeList: VxeTableDefines.ColumnInfo[] = []
const pxList: VxeTableDefines.ColumnInfo[] = []
const pxMinList: VxeTableDefines.ColumnInfo[] = []
const autoMinList: VxeTableDefines.ColumnInfo[] = []
const scaleList: VxeTableDefines.ColumnInfo[] = []
const scaleMinList: VxeTableDefines.ColumnInfo[] = []
const autoList: VxeTableDefines.ColumnInfo[] = []
const remainList: VxeTableDefines.ColumnInfo[] = []
tableFullColumn.forEach((column) => {
if (defaultWidth && !column.width) {
column.width = defaultWidth
}
if (defaultMinWidth && !column.minWidth) {
column.minWidth = defaultMinWidth
}
if (column.visible) {
if (column.resizeWidth) {
resizeList.push(column)
} else if (column.width === 'auto') {
autoList.push(column)
} else if (isPx(column.width)) {
pxList.push(column)
} else if (isScale(column.width)) {
scaleList.push(column)
} else if (isPx(column.minWidth)) {
pxMinList.push(column)
} else if (column.minWidth === 'auto') {
autoMinList.push(column)
} else if (isScale(column.minWidth)) {
scaleMinList.push(column)
} else {
remainList.push(column)
}
}
})
Object.assign(reactData.columnStore, { resizeList, pxList, pxMinList, autoMinList, scaleList, scaleMinList, autoList, remainList })
},
handleColResizeMousedownEvent (evnt, fixedType, params) {
const isLeftBtn = evnt.button === 0
if (!isLeftBtn) {
return
}
evnt.stopPropagation()
evnt.preventDefault()
const { column } = params
const { columnStore, overflowX, scrollbarHeight } = reactData
const { visibleColumn } = internalData
const { leftList, rightList } = columnStore
const resizableOpts = computeResizableOpts.value
const osbHeight = overflowX ? scrollbarHeight : 0
const tableEl = refElem.value
const leftContainerElem = refLeftContainer.value
const rightContainerElem = refRightContainer.value
const resizeBarElem = refColResizeBar.value
if (!resizeBarElem) {
return
}
const isLeftFixed = fixedType === 'left'
const isRightFixed = fixedType === 'right'
const resizeTipElem = resizeBarElem.firstElementChild as HTMLDivElement
const scrollbarXToTop = computeScrollbarXToTop.value
const { clientX: dragClientX } = evnt
const dragBtnElem = evnt.target as HTMLDivElement
let cell = dragBtnElem.parentElement as HTMLTableCellElement | null
let resizeColumn = column
const isDragGroupCol = column.children && column.children.length
if (isDragGroupCol) {
resizeColumn = getLastChildColumn(column)
if (isDragGroupCol) {
const trEl = cell ? cell.parentElement as HTMLTableRowElement : null
const theadEl = trEl ? trEl.parentElement as HTMLTableElement : null
cell = theadEl ? theadEl.querySelector<HTMLTableCellElement>(`.vxe-header--column[colid="${resizeColumn.id}"]`) : null
}
}
if (!cell) {
return
}
const cellParams = XEUtils.assign(params, { cell, $table: $xeTable })
let dragLeft = 0
const tableRect = tableEl.getBoundingClientRect()
const rightContainerRect = rightContainerElem ? rightContainerElem.getBoundingClientRect() : null
const cellRect = cell.getBoundingClientRect()
const dragBtnRect = dragBtnElem.getBoundingClientRect()
const dragBtnWidth = dragBtnElem.clientWidth
const dragBtnOffsetWidth = XEUtils.floor(dragBtnWidth / 2)
const dragPosLeft = dragBtnRect.x - tableRect.x + dragBtnOffsetWidth
const minInterval = getColReMinWidth(cellParams) - dragBtnOffsetWidth // 列之间的最小间距
const maxInterval = getColReMaxWidth(cellParams) // 列之间的最大间距
const dragMinLeft = isRightFixed ? 0 : (cellRect.x - tableRect.x + dragBtnWidth + minInterval)
const dragMaxLeft = cellRect.x - tableRect.x + cell.clientWidth - minInterval
let fixedLeftRemainWidth = 0
let fixedRightRemainWidth = 0
if (isLeftFixed || isRightFixed) {
let isMach = false
const fixedColumn = isLeftFixed ? leftList : rightList
for (let i = 0; i < fixedColumn.length; i++) {
const item = fixedColumn[i]
if (isMach) {
fixedLeftRemainWidth += item.renderWidth
} else {
isMach = item.id === resizeColumn.id
if (!isMach) {
fixedRightRemainWidth += item.renderWidth
}
}
}
}
// 处理拖动事件
const updateEvent = (evnt: MouseEvent) => {
evnt.stopPropagation()
evnt.preventDefault()
const tableHeight = tableEl.clientHeight
const offsetX = evnt.clientX - dragClientX
let left = dragPosLeft + offsetX
if (isLeftFixed) {
if (rightContainerRect) {
left = Math.min(left, rightContainerRect.x - tableRect.x - fixedLeftRemainWidth - minInterval)
}
} else if (isRightFixed) {
if (leftContainerElem) {
left = Math.max(left, leftContainerElem.clientWidth + fixedRightRemainWidth + minInterval)
}
left = Math.min(left, dragMaxLeft)
}
dragLeft = Math.max(left, dragMinLeft)
// 最大宽
if (maxInterval > 1) {
dragLeft = Math.min(dragLeft, maxInterval + dragMinLeft - minInterval)
}
const resizeBarLeft = Math.max(1, dragLeft)
resizeBarElem.style.left = `${resizeBarLeft}px`
resizeBarElem.style.top = `${scrollbarXToTop ? osbHeight : 0}px`
resizeBarElem.style.height = `${scrollbarXToTop ? tableHeight - osbHeight : tableHeight}px`
if (resizableOpts.showDragTip && resizeTipElem) {
resizeTipElem.textContent = getI18n('vxe.table.resizeColTip', [Math.floor(resizeColumn.renderWidth + (isRightFixed ? dragPosLeft - dragLeft : dragLeft - dragPosLeft))])
const tableWrapperWidth = tableEl.clientWidth
const resizeBarWidth = resizeBarElem.clientWidth
const resizeTipWidth = resizeTipElem.clientWidth
const resizeTipHeight = resizeTipElem.clientHeight
let resizeTipLeft = -resizeTipWidth
if (resizeBarLeft < resizeTipWidth + resizeBarWidth) {
resizeTipLeft = 0
} else if (resizeBarLeft > tableWrapperWidth) {
resizeTipLeft += tableWrapperWidth - resizeBarLeft
}
resizeTipElem.style.left = `${resizeTipLeft}px`
resizeTipElem.style.top = `${Math.min(tableHeight - resizeTipHeight, Math.max(0, evnt.clientY - tableRect.y - resizeTipHeight / 2))}px`
}
reactData.isDragResize = true
}
reactData.isDragResize = true
addClass(tableEl, 'col-drag--resize')
resizeBarElem.style.display = 'block'
document.onmousemove = updateEvent
document.onmouseup = function (evnt) {
document.onmousemove = null
document.onmouseup = null
resizeBarElem.style.display = 'none'
internalData._lastResizeTime = Date.now()
setTimeout(() => {
reactData.isDragResize = false
}, 50)
const resizeWidth = resizeColumn.renderWidth + (isRightFixed ? dragPosLeft - dragLeft : dragLeft - dragPosLeft)
const resizeParams = { ...params, resizeWidth, resizeColumn }
if (resizableOpts.dragMode === 'fixed') {
visibleColumn.forEach(item => {
if (item.id !== resizeColumn.id) {
if (!item.resizeWidth) {
item.resizeWidth = item.renderWidth
}
}
})
}
if ($xeTable.handleColResizeCellAreaEvent) {
$xeTable.handleColResizeCellAreaEvent(evnt, resizeParams)
} else {
resizeColumn.resizeWidth = resizeWidth
handleUpdateColResize(evnt, resizeParams)
}
removeClass(tableEl, 'col-drag--resize')
}
updateEvent(evnt)
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
},
handleColResizeDblclickEvent (evnt, params) {
const resizableOpts = computeResizableOpts.value
const { isDblclickAutoWidth } = resizableOpts
const el = refElem.value
if (isDblclickAutoWidth && el) {
evnt.stopPropagation()
evnt.preventDefault()
const { fullColumnIdData } = internalData
const { column } = params
let resizeColumn = column
if (column.children && column.children.length) {
XEUtils.eachTree(column.children, childColumn => {
resizeColumn = childColumn
})
}
const colid = resizeColumn.id
const colRest = fullColumnIdData[colid]
const dragBtnElem = evnt.target as HTMLDivElement
const cell = dragBtnElem.parentNode as HTMLTableCellElement
const cellParams = Object.assign(params, { cell, $table: $xeTable })
const colMinWidth = getColReMinWidth(cellParams)
const colMaxWidth = getColReMaxWidth(cellParams)
el.setAttribute('data-calc-col', 'Y')
let resizeWidth = calcColumnAutoWidth(resizeColumn, el)
el.removeAttribute('data-calc-col')
if (colRest) {
resizeWidth = Math.max(resizeWidth, colRest.width)
}
resizeWidth = Math.max(colMinWidth, resizeWidth)
if (colMaxWidth > 1) {
resizeWidth = Math.min(colMaxWidth, resizeWidth)
}
const resizeParams = { ...params, resizeWidth, resizeColumn }
reactData.isDragResize = false
internalData._lastResizeTime = Date.now()
if ($xeTable.handleColResizeDblclickCellAreaEvent) {
$xeTable.handleColResizeDblclickCellAreaEvent(evnt, resizeParams)
} else {
resizeColumn.resizeWidth = resizeWidth
handleUpdateColResize(evnt, resizeParams)
}
}
},
/**
* @private
*/
handleRowResizeMousedownEvent (evnt, params) {
evnt.stopPropagation()
evnt.preventDefault()
const { row } = params
const { showOverflow } = props
const { overflowX, scrollbarWidth, overflowY, scrollbarHeight } = reactData
const { elemStore, fullAllDataRowIdData } = internalData
const osbWidth = overflowY ? scrollbarWidth : 0
const osbHeight = overflowX ? scrollbarHeight : 0
const scrollbarYToLeft = computeScrollbarYToLeft.value
const resizableOpts = computeResizableOpts.value
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
let tableEl = refElem.value
if ($xeGantt) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
tableEl = ganttContainerElem
}
}
const resizeBarElem = refRowResizeBar.value
if (!resizeBarElem) {
return
}
const { clientY: dragClientY } = evnt
const resizeTipElem = resizeBarElem.firstElementChild as HTMLDivElement
const dragBtnElem = evnt.currentTarget as HTMLDivElement
const tdEl = dragBtnElem.parentNode as HTMLTableCellElement
const trEl = tdEl.parentNode as HTMLTableCellElement
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
if (!bodyScrollElem) {
return
}
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (!rowRest) {
return
}
const defaultRowHeight = computeDefaultRowHeight.value
let currCellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
if (!showOverflow) {
currCellHeight = tdEl.clientHeight
}
const tableRect = tableEl.getBoundingClientRect()
const trRect = trEl.getBoundingClientRect()
const targetOffsetY = dragClientY - trRect.y - trEl.clientHeight
let resizeHeight = currCellHeight
const cellEl = tdEl.querySelector('.vxe-cell')
let cellMinHeight = 0
if (cellEl) {
const cellStyle = getComputedStyle(cellEl)
cellMinHeight = Math.max(1, Math.ceil(XEUtils.toNumber(cellStyle.paddingTop) + XEUtils.toNumber(cellStyle.paddingBottom)))
}
const minTop = trRect.y - tableRect.y + cellMinHeight
// 处理拖动事件
const updateEvent = (evnt: MouseEvent) => {
evnt.stopPropagation()
evnt.preventDefault()
const rtWidth = tableEl.clientWidth - osbWidth
const tableHeight = tableEl.clientHeight - osbHeight
let dragTop = evnt.clientY - tableRect.y - targetOffsetY
if (dragTop < minTop) {
dragTop = minTop
} else {
resizeHeight = Math.max(cellMinHeight, currCellHeight + evnt.clientY - dragClientY)
}
resizeBarElem.style.left = `${scrollbarYToLeft ? osbWidth : 0}px`
resizeBarElem.style.top = `${dragTop}px`
resizeBarElem.style.width = `${rtWidth}px`
if (resizableOpts.showDragTip && resizeTipElem) {
resizeTipElem.textContent = getI18n('vxe.table.resizeRowTip', [resizeHeight])
const resizeTipWidth = resizeTipElem.clientWidth
const resizeTipHeight = resizeTipElem.clientHeight
let resizeBarLeft = Math.max(2, evnt.clientX - tableRect.x)
let resizeBarTop = 0
if (resizeBarLeft + resizeTipWidth >= rtWidth - 2) {
resizeBarLeft = rtWidth - resizeTipWidth - 2
}
if (dragTop + resizeTipHeight >= tableHeight) {
resizeBarTop = tableHeight - (dragTop + resizeTipHeight)
}
resizeTipElem.style.left = `${resizeBarLeft}px`
resizeTipElem.style.top = `${resizeBarTop}px`
}
reactData.isDragResize = true
}
reactData.isDragResize = true
addClass(tableEl, 'row-drag--resize')
resizeBarElem.style.display = 'block'
document.onmousemove = updateEvent
document.onmouseup = function (evnt) {
document.onmousemove = null
document.onmouseup = null
resizeBarElem.style.display = 'none'
internalData._lastResizeTime = Date.now()
setTimeout(() => {
reactData.isDragResize = false
}, 50)
if (resizeHeight !== currCellHeight) {
const resizeParams = { ...params, resizeHeight, resizeRow: row }
internalData.isResizeCellHeight = true
if ($xeTable.handleRowResizeCellAreaEvent) {
$xeTable.handleRowResizeCellAreaEvent(evnt, resizeParams)
} else {
rowRest.resizeHeight = resizeHeight
handleUpdateRowResize(evnt, resizeParams)
updateRowOffsetTop()
}
}
removeClass(tableEl, 'row-drag--resize')
}
updateEvent(evnt)
},
/**
* @private
*/
handleRowResizeDblclickEvent (evnt, params) {
const resizableOpts = computeResizableOpts.value
const { isDblclickAutoHeight } = resizableOpts
const el = refElem.value
if (isDblclickAutoHeight && el) {
evnt.stopPropagation()
evnt.preventDefault()
const { editStore } = reactData
const { fullAllDataRowIdData } = internalData
const { actived } = editStore
const { row } = params
const rowid = getRowid($xeTable, row)
const rowRest = fullAllDataRowIdData[rowid]
if (!rowRest) {
return
}
const handleRsHeight = () => {
el.setAttribute('data-calc-row', 'Y')
const resizeHeight = calcCellAutoHeight(rowid, rowRest, el)
el.removeAttribute('data-calc-row')
const resizeParams = { ...params, resizeHeight, resizeRow: row }
reactData.isDragResize = false
internalData._lastResizeTime = Date.now()
if ($xeTable.handleRowResizeDblclickCellAreaEvent) {
$xeTable.handleRowResizeDblclickCellAreaEvent(evnt, resizeParams)
} else {
rowRest.resizeHeight = resizeHeight
handleUpdateRowResize(evnt, resizeParams)
}
}
if (actived.row || actived.column) {
$xeTable.clearEdit().then(handleRsHeight)
} else {
handleRsHeight()
}
}
},
/**
* @private
*/
saveCustomStore (type) {
const { customConfig } = props
const tableId = computeTableId.value
const customOpts = computeCustomOpts.value
const { updateStore, storage } = customOpts
const { isCustomAlign, isCustomHeaderAlign, isCustomFooterAlign, isCustomResizable, isCustomVisible, isCustomFixed, isCustomSort, isCustomAggGroup, isCustomAggFunc, isCustomPager } = handleCustomStoreConfig($xeTable)
if (type !== 'reset') {
// fix:修复拖动列宽,重置按钮无法点击的问题
reactData.isCustomStatus = true
}
if (storage &&
(customConfig ? isEnableConf(customOpts) : customOpts.enabled) &&
(
isCustomAlign ||
isCustomHeaderAlign ||
isCustomFooterAlign ||
isCustomResizable ||
isCustomVisible ||
isCustomFixed ||
isCustomSort ||
isCustomAggGroup ||
isCustomAggFunc ||
isCustomPager
)
) {
if (!tableId) {
errLog('vxe.error.reqProp', ['id'])
return nextTick()
}
const storeData: VxeTableDefines.CustomStoreData = type === 'reset'
? {
pagerData: {},
alignData: {},
headerAlignData: {},
footerAlignData: {},
resizableData: {},
sortData: [],
visibleData: {},
fixedData: {},
aggGroupData: {},
aggFuncData: {}
}
: $xeTable.getCustomStoreData()
if (updateStore) {
return updateStore({
$table: $xeTable,
id: tableId,
type,
storeData
})
} else {
setCustomStorageMap(tableId, type === 'reset' ? null : storeData)
}
}
return nextTick()
},
/**
* @private
*/
handleCustom () {
const { mouseConfig } = props
if (mouseConfig) {
if ($xeTable.clearSelected) {
$xeTable.clearSelected()
}
if ($xeTable.clearCellAreas) {
$xeTable.clearCellAreas()
$xeTable.clearCopyCellArea()
}
}
$xeTable.analyColumnWidth()
return $xeTable.refreshColumn(true)
},
handleUpdateDataQueue () {
reactData.upDataFlag++
},
handleRefreshColumnQueue () {
reactData.reColumnFlag++
},
handleFilterOptions (column) {
if (column) {
const { filterStore } = reactData
const { filterRender, filters } = column
const filterOptions = filters || []
const compConf = isEnableConf(filterRender) ? renderer.get(filterRender.name) : null
const frMethod = column.filterRecoverMethod || (compConf ? (compConf.tableFilterRecoverMethod || compConf.filterRecoverMethod) : null)
filterStore.column = column
// 复原状态
filterOptions.forEach((option: any) => {
const { _checked, checked } = option
option._checked = checked
if (!checked && _checked !== checked) {
if (frMethod) {
frMethod({ option, column, $table: $xeTable })
}
}
})
$xeTable.checkFilterOptions()
}
},
preventEvent (evnt, type, args, next, end) {
let evntList = interceptor.get(type)
// 兼容老版本
if (!evntList.length && type === 'event.clearEdit') {
evntList = interceptor.get('event.clearActived')
if (evntList.length) {
warnLog('vxe.error.delEvent', ['event.clearActived', 'event.clearEdit'])
}
}
// 兼容老版本
let rest = null
let isStop = false
for (let i = 0; i < evntList.length; i++) {
const func = evntList[i]
const fnRest = func(Object.assign({ $table: $xeTable, $grid: $xeGrid, gantt: $xeGantt, $event: evnt }, args))
if (fnRest === false) {
isStop = true
break
} else if (fnRest && fnRest.status === false) {
rest = fnRest.result
isStop = true
break
}
}
if (!isStop) {
if (next) {
rest = next()
}
}
if (end) {
end()
}
return rest
},
updateCheckboxStatus () {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { afterTreeFullData, afterGroupFullData, selectCheckboxMaps, treeIndeterminateRowMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const checkboxOpts = computeCheckboxOpts.value
const { checkField, indeterminateField, checkStrictly, checkMethod } = checkboxOpts
if (checkStrictly) {
return
}
if (isRowGroupStatus || treeConfig) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const childRowMaps: Record<string, number> = {}
const childRowList: any[][] = []
if (isRowGroupStatus) {
// 行分组
const mapChildrenField = aggregateOpts.mapChildrenField
if (mapChildrenField) {
XEUtils.eachTree(afterGroupFullData, (row) => {
const rowid = handleGetRowId(row)
const childList = row[mapChildrenField]
if (childList && childList.length && !childRowMaps[rowid]) {
childRowMaps[rowid] = 1
childRowList.unshift([row, rowid, childList])
}
}, { children: mapChildrenField })
}
} else if (treeConfig) {
// 树结构
const { transform, mapChildrenField } = treeOpts
XEUtils.eachTree(afterTreeFullData, (row) => {
const rowid = handleGetRowId(row)
const childList = row[transform ? mapChildrenField : childrenField]
if (childList && childList.length && !childRowMaps[rowid]) {
childRowMaps[rowid] = 1
childRowList.unshift([row, rowid, childList])
} else {
if (indeterminateField) {
XEUtils.set(row, indeterminateField, false)
}
}
}, { children: transform ? mapChildrenField : childrenField })
}
childRowList.forEach(vals => {
const row = vals[0]
const rowid: string = vals[1]
const childList: any[] = vals[2]
let sLen = 0 // 已选
let hLen = 0 // 半选
let vLen = 0 // 有效子行
const cLen = childList.length // 子行
childList.forEach(
checkMethod
? (item) => {
const childRowid = handleGetRowId(item)
const isSelect = checkField ? XEUtils.get(item, checkField) : selectCheckboxMaps[childRowid]
if (checkMethod({ $table: $xeTable, row: item })) {
if (isSelect) {
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
hLen++
}
vLen++
} else {
if (isSelect) {
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
hLen++
}
}
}
: item => {
const childRowid = handleGetRowId(item)
const isSelect = checkField ? XEUtils.get(item, checkField) : selectCheckboxMaps[childRowid]
if (isSelect) {
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
hLen++
}
vLen++
}
)
let isSelected = false
if (cLen > 0) {
if (vLen > 0) {
isSelected = (sLen > 0 || hLen > 0) && sLen >= vLen
} else {
// 如果存在子项禁用
if ((sLen > 0 && sLen >= vLen)) {
isSelected = true
} else if (selectCheckboxMaps[rowid]) {
isSelected = true
} else {
isSelected = false
}
}
} else {
// 如果无子项
isSelected = selectCheckboxMaps[rowid]
}
const halfSelect = !isSelected && (sLen > 0 || hLen > 0)
if (checkField) {
XEUtils.set(row, checkField, isSelected)
}
if (indeterminateField) {
XEUtils.set(row, indeterminateField, halfSelect)
}
if (isSelected) {
if (!checkField) {
selectCheckboxMaps[rowid] = row
}
if (treeIndeterminateRowMaps[rowid]) {
delete treeIndeterminateRowMaps[rowid]
}
} else {
if (!checkField) {
if (selectCheckboxMaps[rowid]) {
delete selectCheckboxMaps[rowid]
}
}
if (halfSelect) {
treeIndeterminateRowMaps[rowid] = row
} else {
if (treeIndeterminateRowMaps[rowid]) {
delete treeIndeterminateRowMaps[rowid]
}
}
}
})
}
reactData.updateCheckboxFlag++
},
updateAllCheckboxStatus () {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { afterFullData, afterTreeFullData, afterGroupFullData, checkboxReserveRowMap, selectCheckboxMaps, treeIndeterminateRowMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const { mapChildrenField } = aggregateOpts
const checkboxOpts = computeCheckboxOpts.value
const { checkField, checkMethod, showReserveStatus } = checkboxOpts
const { handleGetRowId } = createHandleGetRowId($xeTable)
let sLen = 0 // 已选
let dsLen = 0 // 禁用的已选
let hLen = 0 // 半选
let dhLen = 0 // 禁用的半选
let vLen = 0 // 有效行
const rootList = (treeConfig ? afterTreeFullData : (isRowGroupStatus ? afterGroupFullData : afterFullData))
rootList.forEach(checkMethod
? row => {
const childRowid = handleGetRowId(row)
const selected = checkField ? XEUtils.get(row, checkField) : selectCheckboxMaps[childRowid]
if (isRowGroupStatus && $xeTable.isAggregateRecord(row)) {
const childList: any[] = row[mapChildrenField || '']
if (selected) {
vLen++
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
vLen++
hLen++
} else if (childList && childList.length && childList.some(item => checkMethod({ $table: $xeTable, row: item }))) {
vLen++
}
} else if (checkMethod({ $table: $xeTable, row })) {
if (selected) {
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
hLen++
}
vLen++
} else {
if (selected) {
dsLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
dhLen++
}
}
}
: row => {
const childRowid = handleGetRowId(row)
const selected = checkField ? XEUtils.get(row, checkField) : selectCheckboxMaps[childRowid]
if (selected) {
sLen++
} else if (treeIndeterminateRowMaps[childRowid]) {
hLen++
}
vLen++
})
const isSelected = rootList.length > 0 ? (vLen > 0 ? (sLen >= vLen) : (sLen >= rootList.length)) : false
let halfSelect = !isSelected && (sLen > 0 || hLen > 0 || dsLen > 0 || dhLen > 0)
// 如果复选框启用保留记录,当保留数据存在时显示半选
if (!isSelected && !halfSelect && showReserveStatus) {
halfSelect = !XEUtils.isEmpty(checkboxReserveRowMap)
}
reactData.isAllSelected = isSelected
reactData.isIndeterminate = halfSelect
},
checkSelectionStatus () {
$xeTable.updateCheckboxStatus()
$xeTable.updateAllCheckboxStatus()
},
/**
* 切换选中
* 多选,行选中事件
*/
handleBatchSelectRows (rows, checked, isForce) {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { selectCheckboxMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const { transform, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const checkboxOpts = computeCheckboxOpts.value
const { checkField, checkStrictly, checkMethod } = checkboxOpts
const { handleGetRowId } = createHandleGetRowId($xeTable)
// indeterminateField 仅支持读取
const indeterminateField = checkboxOpts.indeterminateField || checkboxOpts.halfField
if (checkField) {
// 树结构
if ((treeConfig || isRowGroupStatus) && !checkStrictly) {
// 更新子节点状态
XEUtils.eachTree(rows, (row) => {
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
XEUtils.set(row, checkField, checked)
if (indeterminateField) {
XEUtils.set(row, indeterminateField, false)
}
handleCheckboxReserveRow(row, checked)
}
}, { children: transform ? mapChildrenField : childrenField })
reactData.updateCheckboxFlag++
return
}
// 列表
rows.forEach(row => {
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
XEUtils.set(row, checkField, checked)
handleCheckboxReserveRow(row, checked)
}
})
reactData.updateCheckboxFlag++
return
}
// 树结构
if (!checkStrictly) {
if (isRowGroupStatus) {
// 更新行分组节点状态
XEUtils.eachTree(rows, (row) => {
const rowid = handleGetRowId(row)
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
if (checked) {
selectCheckboxMaps[rowid] = row
} else {
if (selectCheckboxMaps[rowid]) {
delete selectCheckboxMaps[rowid]
}
}
handleCheckboxReserveRow(row, checked)
}
}, { children: aggregateOpts.mapChildrenField })
reactData.updateCheckboxFlag++
return
} else if (treeConfig) {
// 更新子节点状态
XEUtils.eachTree(rows, (row) => {
const rowid = handleGetRowId(row)
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
if (checked) {
selectCheckboxMaps[rowid] = row
} else {
if (selectCheckboxMaps[rowid]) {
delete selectCheckboxMaps[rowid]
}
}
handleCheckboxReserveRow(row, checked)
}
}, { children: transform ? mapChildrenField : childrenField })
reactData.updateCheckboxFlag++
return
}
}
// 列表
rows.forEach(row => {
const rowid = handleGetRowId(row)
if (isForce || (!checkMethod || checkMethod({ $table: $xeTable, row }))) {
if (checked) {
if (!selectCheckboxMaps[rowid]) {
selectCheckboxMaps[rowid] = row
}
} else {
if (selectCheckboxMaps[rowid]) {
delete selectCheckboxMaps[rowid]
}
}
handleCheckboxReserveRow(row, checked)
reactData.updateCheckboxFlag++
}
})
},
/**
* 即将移除
* @deprecated
*/
handleSelectRow ({ row }, checked, isForce) {
$xeTable.handleBatchSelectRows([row], checked, isForce)
},
handleUpdateHeaderMerge () {
const { mergeHeaderList } = internalData
const { mergeMaps, mergeRowMaps, mergeColMaps } = buildMergeData(mergeHeaderList)
internalData.mergeHeaderCellMaps = mergeMaps
internalData.mergeHeaderRowMaps = mergeRowMaps
internalData.mergeHeaderColMaps = mergeColMaps
reactData.mergeHeadFlag++
},
/**
* 处理合并
*/
handleUpdateBodyMerge () {
const { mergeBodyList } = internalData
const { mergeMaps, mergeRowMaps, mergeColMaps } = buildMergeData(mergeBodyList)
internalData.mergeBodyCellMaps = mergeMaps
internalData.mergeBodyRowMaps = mergeRowMaps
internalData.mergeBodyColMaps = mergeColMaps
reactData.mergeBodyFlag++
},
handleUpdateFooterMerge () {
const { mergeFooterList } = internalData
const { mergeMaps, mergeRowMaps, mergeColMaps } = buildMergeData(mergeFooterList)
internalData.mergeFooterCellMaps = mergeMaps
internalData.mergeFooterRowMaps = mergeRowMaps
internalData.mergeFooterColMaps = mergeColMaps
reactData.mergeFootFlag++
},
handleAggregateSummaryData () {
return $xeTable.refreshAggregateCalcValues()
},
triggerHeaderTitleEvent (evnt, iconParams, params) {
const tipContent = iconParams.content || (iconParams as any).message
if (tipContent) {
const { tooltipStore } = reactData
const { column } = params
const content = getFuncText(tipContent)
handleTargetEnterEvent(true)
tooltipStore.row = null
tooltipStore.column = column
tooltipStore.visible = true
tooltipStore.currOpts = iconParams
nextTick(() => {
const $tooltip = refTooltip.value
if ($tooltip && $tooltip.open) {
$tooltip.open(evnt.currentTarget, content)
}
})
}
},
/**
* 触发表头 tooltip 事件
*/
triggerHeaderTooltipEvent (evnt, params) {
const { tooltipStore } = reactData
const headerTooltipOpts = computeHeaderTooltipOpts.value
const { column } = params
handleTargetEnterEvent(true)
const titleElem = evnt.currentTarget as HTMLDivElement
if (!titleElem) {
return
}
const cWrapperEl = titleElem.parentElement as HTMLDivElement
if (!cWrapperEl) {
return
}
const cellEl = cWrapperEl.parentElement as HTMLDivElement
if (!cellEl) {
return
}
const thEl = cellEl.parentElement as HTMLTableCellElement
if (!thEl) {
return
}
if (tooltipStore.column !== column || !tooltipStore.visible) {
const ctEl = thEl.querySelector<HTMLElement>('.vxe-cell--title')
handleTooltip(evnt, headerTooltipOpts, 'header', thEl, (hasClass(thEl, 'col--ellipsis') ? ctEl : cWrapperEl) || cWrapperEl, ctEl || cellEl, params)
}
},
/**
* 触发单元格 tooltip 事件
*/
triggerBodyTooltipEvent (evnt, params) {
const { editConfig } = props
const { editStore } = reactData
const { tooltipStore } = reactData
const tooltipOpts = computeTooltipOpts.value
const editOpts = computeEditOpts.value
const { actived } = editStore
const { row, column } = params
const tdEl = evnt.currentTarget as HTMLTableCellElement
handleTargetEnterEvent(tooltipStore.column !== column || tooltipStore.row !== row)
// 单元格处于编辑状态时不触发提示框
if (column.editRender && isEnableConf(editConfig)) {
// 如果是行编辑模式
if (editOpts.mode === 'row' && actived.row === row) {
return
}
// 如果是单元格编辑模式
if (actived.row === row && actived.column === column) {
return
}
}
if (tooltipStore.column !== column || tooltipStore.row !== row || !tooltipStore.visible) {
const ctEl = tdEl.querySelector<HTMLElement>('.vxe-cell--wrapper')
let ovEl = null
let tipEl = tdEl.querySelector<HTMLElement>(column.type === 'html' ? '.vxe-cell--html' : '.vxe-cell--label')
if (column.treeNode) {
ovEl = tdEl.querySelector<HTMLElement>('.vxe-tree-cell')
}
if (!tipEl) {
tipEl = ctEl
}
handleTooltip(evnt, tooltipOpts, 'body', tdEl, ovEl || ctEl, tipEl, params)
}
},
/**
* 触发表尾 tooltip 事件
*/
triggerFooterTooltipEvent (evnt, params) {
const { column } = params
const { tooltipStore } = reactData
const footerTooltipOpts = computeFooterTooltipOpts.value
const tdEl = evnt.currentTarget as HTMLTableCellElement
handleTargetEnterEvent(tooltipStore.column !== column || !!tooltipStore.row)
if (tooltipStore.column !== column || !tooltipStore.visible) {
const ctEl = tdEl.querySelector<HTMLElement>('.vxe-cell--wrapper')
let ovEl = null
let tipEl = tdEl.querySelector<HTMLElement>(column.type === 'html' ? '.vxe-cell--html' : '.vxe-cell--label')
if (column.type === 'html') {
ovEl = tdEl.querySelector<HTMLElement>('.vxe-cell--html')
}
if (!tipEl) {
tipEl = ctEl
}
handleTooltip(evnt, footerTooltipOpts, 'footer', tdEl, ovEl || ctEl, tipEl, params)
}
},
handleTargetLeaveEvent () {
const tooltipOpts = computeTooltipOpts.value
let $tooltip = refTooltip.value
if ($tooltip && $tooltip.setActived) {
$tooltip.setActived(false)
}
if (tooltipOpts.enterable) {
internalData.tooltipTimeout = setTimeout(() => {
$tooltip = refTooltip.value
if ($tooltip && $tooltip.isActived && !$tooltip.isActived()) {
$xeTable.closeTooltip()
}
}, tooltipOpts.leaveDelay)
} else {
$xeTable.closeTooltip()
}
},
triggerHeaderCellClickEvent (evnt, params) {
const { _lastResizeTime } = internalData
const sortOpts = computeSortOpts.value
const columnOpts = computeColumnOpts.value
const currentColumnOpts = computeCurrentColumnOpts.value
const { column } = params
const cell = evnt.currentTarget
const triggerResizable = _lastResizeTime && _lastResizeTime > Date.now() - 300
const triggerSort = getEventTargetNode(evnt, cell, 'vxe-cell--sort').flag
const triggerFilter = getEventTargetNode(evnt, cell, 'vxe-cell--filter').flag
if (sortOpts.trigger === 'cell' && !(triggerResizable || triggerSort || triggerFilter)) {
$xeTable.triggerSortEvent(evnt, column, getNextSortOrder(column))
}
if ($xeGantt) {
const ganttReactData = $xeGantt.reactData
ganttReactData.activeBarRowid = null
}
dispatchEvent('header-cell-click', Object.assign({ triggerResizable, triggerSort, triggerFilter, cell }, params), evnt)
if ((columnOpts.isCurrent || props.highlightCurrentColumn) && (!currentColumnOpts.trigger || ['header', 'default'].includes(currentColumnOpts.trigger))) {
$xeTable.triggerCurrentColumnEvent(evnt, params)
}
},
triggerHeaderCellDblclickEvent (evnt, params) {
dispatchEvent('header-cell-dblclick', Object.assign({ cell: evnt.currentTarget }, params), evnt)
},
/**
* 列点击事件
* 如果是单击模式,则激活为编辑状态
* 如果是双击模式,则单击后选中状态
*/
triggerCellClickEvent (evnt, params) {
const { treeConfig, highlightCurrentRow, highlightCurrentColumn, editConfig, aggregateConfig, rowGroupConfig } = props
const { editStore, isDragResize, expandColumn, checkboxColumn, radioColumn } = reactData
if (isDragResize) {
return
}
const expandOpts = computeExpandOpts.value
const editOpts = computeEditOpts.value
const treeOpts = computeTreeOpts.value
const radioOpts = computeRadioOpts.value
const checkboxOpts = computeCheckboxOpts.value
const keyboardOpts = computeKeyboardOpts.value
const aggregateOpts = computeAggregateOpts.value
const rowOpts = computeRowOpts.value
const columnOpts = computeColumnOpts.value
const currentColumnOpts = computeCurrentColumnOpts.value
const { actived, focused } = editStore
const { row, column } = params
const { type, treeNode, rowGroupNode } = column
const isRadioType = type === 'radio'
const isCheckboxType = type === 'checkbox'
const isExpandType = type === 'expand'
const cell = evnt.currentTarget as HTMLDivElement
const triggerRadio = isRadioType && getEventTargetNode(evnt, cell, 'vxe-cell--radio').flag
const triggerCheckbox = isCheckboxType && getEventTargetNode(evnt, cell, 'vxe-cell--checkbox').flag
const triggerTreeNode = treeNode && getEventTargetNode(evnt, cell, 'vxe-cell--tree-btn').flag
const triggerExpandNode = isExpandType && getEventTargetNode(evnt, cell, 'vxe-table--expanded').flag
const triggerRowGroupNode = isExpandType && getEventTargetNode(evnt, cell, 'vxe-row-group--node-btn').flag
params = Object.assign({ cell, triggerRadio, triggerCheckbox, triggerTreeNode, triggerExpandNode }, params)
if (!triggerCheckbox && !triggerRadio) {
// 如果是展开行
if (!triggerExpandNode && ((expandColumn && expandOpts.trigger === 'row') || (isExpandType && expandOpts.trigger === 'cell'))) {
$xeTable.triggerRowExpandEvent(evnt, params)
}
// 如果是树形表格
if (treeConfig && (treeOpts.trigger === 'row' || (treeNode && treeOpts.trigger === 'cell'))) {
$xeTable.triggerTreeExpandEvent(evnt, params)
}
// 如果是行分组
if ((aggregateConfig || rowGroupConfig) && (aggregateOpts.trigger === 'row' || (rowGroupNode && aggregateOpts.trigger === 'cell'))) {
$xeTable.triggerRowGroupExpandEvent(evnt, params)
}
}
// 如果点击了树节点
if (!triggerTreeNode) {
if (!triggerExpandNode && !triggerRowGroupNode) {
// 如果是当前行
if (rowOpts.isCurrent || highlightCurrentRow) {
if (!triggerCheckbox && !triggerRadio) {
$xeTable.triggerCurrentRowEvent(evnt, params)
}
}
// 如果是当前列
if ((columnOpts.isCurrent || highlightCurrentColumn) && (!currentColumnOpts.trigger || ['cell', 'default'].includes(currentColumnOpts.trigger))) {
if (!triggerCheckbox && !triggerRadio) {
$xeTable.triggerCurrentColumnEvent(evnt, params)
}
}
// 如果是单选框
if (!triggerRadio && ((radioColumn && radioOpts.trigger === 'row') || (isRadioType && radioOpts.trigger === 'cell'))) {
$xeTable.triggerRadioRowEvent(evnt, params)
}
// 如果是复选框
if (!triggerCheckbox && ((checkboxColumn && checkboxOpts.trigger === 'row') || (isCheckboxType && checkboxOpts.trigger === 'cell'))) {
$xeTable.handleToggleCheckRowEvent(evnt, params)
}
}
// 如果设置了单元格选中功能,则不会使用点击事件去处理(只能支持双击模式)
if (isEnableConf(editConfig)) {
// 记录点击输入框聚焦状态
if (keyboardOpts.arrowCursorLock && evnt && editOpts.mode === 'cell' && evnt.target && /^input|textarea$/i.test((evnt.target as HTMLElement).tagName)) {
focused.column = column
focused.row = row
}
if (editOpts.trigger === 'manual') {
if (actived.args && actived.row === row && column !== actived.column) {
if (editOpts.mode === 'row') {
handleChangeCell(evnt, params)
} else {
checkValidate('blur')
.catch((e: any) => e)
}
}
} else if (!actived.args || row !== actived.row || column !== actived.column) {
if (editOpts.trigger === 'click') {
handleChangeCell(evnt, params)
} else if (editOpts.trigger === 'dblclick') {
if (editOpts.mode === 'row' && actived.row === row) {
handleChangeCell(evnt, params)
}
}
}
}
}
// 如果是双击编辑模式
if (isEnableConf(editConfig) && editOpts.trigger === 'dblclick') {
if (actived.row && actived.column) {
if (editOpts.mode === 'row') {
if (!$xeTable.eqRow(actived.row, row)) {
$xeTable.handleClearEdit(evnt)
}
} else if (editOpts.mode === 'cell') {
if (!$xeTable.eqRow(actived.row, row) || actived.column.id !== column.id) {
$xeTable.handleClearEdit(evnt)
}
}
}
}
if ($xeGantt) {
const ganttReactData = $xeGantt.reactData
ganttReactData.activeBarRowid = null
}
dispatchEvent('cell-click', params, evnt)
},
/**
* 列双击点击事件
* 如果是双击模式,则激活为编辑状态
*/
triggerCellDblclickEvent (evnt, params) {
const { editConfig } = props
const { editStore, isDragResize } = reactData
if (isDragResize) {
return
}
const editOpts = computeEditOpts.value
const { actived } = editStore
const cell = evnt.currentTarget as HTMLDivElement
params = Object.assign({ cell }, params)
if (isEnableConf(editConfig) && editOpts.trigger === 'dblclick') {
if (!actived.args || evnt.currentTarget !== actived.args.cell) {
if (editOpts.mode === 'row') {
checkValidate('blur')
.catch((e) => e)
.then(() => {
$xeTable.handleEdit(params, evnt)
.then(() => checkValidate('change'))
.catch((e) => e)
})
} else if (editOpts.mode === 'cell') {
$xeTable.handleEdit(params, evnt)
.then(() => checkValidate('change'))
.catch((e) => e)
}
}
}
dispatchEvent('cell-dblclick', params, evnt)
},
triggerFooterCellClickEvent (evnt, params) {
const cell = evnt.currentTarget as HTMLDivElement
params = Object.assign({ cell }, params)
if ($xeGantt) {
const ganttReactData = $xeGantt.reactData
ganttReactData.activeBarRowid = null
}
$xeTable.dispatchEvent('footer-cell-click', params, evnt)
},
triggerFooterCellDblclickEvent (evnt, params) {
const cell = evnt.currentTarget as HTMLDivElement
params = Object.assign({ cell }, params)
$xeTable.dispatchEvent('footer-cell-dblclick', params, evnt)
},
handleToggleCheckRowEvent (evnt, params) {
const { selectCheckboxMaps } = internalData
const checkboxOpts = computeCheckboxOpts.value
const { checkField, trigger } = checkboxOpts
const { row } = params
if (trigger === 'manual') {
return
}
let checked = false
if (checkField) {
checked = !XEUtils.get(row, checkField)
} else {
checked = !selectCheckboxMaps[getRowid($xeTable, row)]
}
if (evnt) {
$xeTable.triggerCheckRowEvent(evnt, params, checked)
} else {
$xeTable.handleBatchSelectRows([row], checked)
$xeTable.checkSelectionStatus()
}
},
triggerCheckRowEvent (evnt: MouseEvent, params, checked) {
const { treeConfig } = props
const { row } = params
const { isRowGroupStatus } = reactData
const { fullAllDataRowIdData, afterFullData, afterTreeFullData, afterGroupFullData } = internalData
const checkboxOpts = computeCheckboxOpts.value
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const { checkMethod, trigger, checkStrictly } = checkboxOpts
if (trigger === 'manual') {
return
}
evnt.stopPropagation()
if (checkboxOpts.isShiftKey && evnt.shiftKey) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
const checkboxRecords = $xeTable.getCheckboxRecords()
let currSeq = -1
let currRow: any = null
const currRowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[currRowid]
if (rowRest) {
currRow = rowRest.row
currSeq = rowRest._seq
}
if (currRow && checkboxRecords.length) {
let rangeRows: any[] = []
let firstSeq = -1
let firstRow: any = null
let firstRowid = ''
let lastSeq = -1
let lastRow: any = null
let lastRowid = ''
// 根据序号取出范围最小/最大的行
const selectMaps: Record<string, any> = {}
checkboxRecords.forEach(item => {
const rowid = handleGetRowId(item)
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
if (!firstRow || firstSeq > rowRest._seq) {
firstRowid = rowRest.rowid
firstRow = rowRest.row
firstSeq = rowRest._seq
}
if (!lastRow || lastSeq < rowRest._seq) {
lastRowid = rowRest.rowid
lastRow = rowRest.row
lastSeq = rowRest._seq
}
}
selectMaps[rowid] = item
})
// 如果已选只有一条并选择当前则跳过
if (firstRow && lastRow && (checkboxRecords.length > 1 || currRowid !== firstRowid) && (Math.abs(firstSeq - lastSeq) > 1 || Math.abs(currSeq - firstSeq) > 1 || Math.abs(currSeq - lastSeq) > 1)) {
// 树与分组
if (treeConfig || isRowGroupStatus) {
const { transform } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const iteratorChildField = treeConfig ? (transform ? treeOpts.mapChildrenField : childrenField) : aggregateOpts.mapChildrenField as string
let isMatchFirst = false
let matchStartRowid = ''
let matchEndRowid = ''
const handleGetChildRowid = (rowid: string, isLast: 0 | 1): string => {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const item = rowRest.row
const childList = item[iteratorChildField]
if (childList && childList.length) {
const targetRow = isLast ? XEUtils.last(childList) : XEUtils.first(childList)
return handleGetChildRowid(handleGetRowId(targetRow), isLast)
}
}
return rowid
}
const handleGetSelectParentRowid = (rowid: string): string => {
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
const parentItem = rowRest.parent
if (parentItem) {
const parentRowid = handleGetRowId(parentItem)
if (selectMaps[parentRowid]) {
return handleGetSelectParentRowid(parentRowid)
}
}
}
return rowid
}
const handleGetRootRow = (row: string): VxeTableDefines.RowCacheItem | null => {
const rowid = handleGetRowId(row)
if (rowid) {
const rest = fullAllDataRowIdData[rowid]
if (rest) {
return rest.parent ? handleGetRootRow(rest.parent) : rest
}
}
return null
}
if (currSeq < firstSeq) {
matchStartRowid = handleGetChildRowid(currRowid, 0)
matchEndRowid = handleGetChildRowid(firstRowid, 1)
} else {
if (currSeq > lastSeq) {
matchStartRowid = handleGetSelectParentRowid(lastRowid)
} else {
matchStartRowid = handleGetChildRowid(firstRowid, 0)
}
matchEndRowid = handleGetChildRowid(currRowid, 1)
}
const handleRangeRow = (item: any) => {
const rowid = handleGetRowId(item)
const childList = item[iteratorChildField]
if (!isMatchFirst && rowid === matchStartRowid) {
isMatchFirst = true
}
// 如果是父子关联,则排除父节点
if (isMatchFirst && (checkStrictly || !childList || !childList.length)) {
rangeRows.push(item)
}
// break
return rowid === matchEndRowid
}
if (treeConfig) {
const matchStartRowRest = fullAllDataRowIdData[matchStartRowid]
const firstRootRowRest = matchStartRowRest ? handleGetRootRow(matchStartRowRest.row) : null
if (firstRootRowRest) {
// 优化查找复杂度
XEUtils.findTree(afterTreeFullData.slice(firstRootRowRest.treeIndex), handleRangeRow, { children: iteratorChildField })
} else {
XEUtils.findTree(afterTreeFullData, handleRangeRow, { children: iteratorChildField })
}
} else {
XEUtils.findTree(afterGroupFullData, handleRangeRow, { children: iteratorChildField })
}
} else {
const _currRowIndex = $xeTable.getVTRowIndex(currRow)
const _firstRowIndex = $xeTable.getVTRowIndex(firstRow)
const _lastRowIndex = $xeTable.getVTRowIndex(lastRow)
if (_currRowIndex < _firstRowIndex) {
rangeRows = afterFullData.slice(_currRowIndex, _firstRowIndex + 1)
} else if (_currRowIndex > _lastRowIndex) {
rangeRows = afterFullData.slice(_lastRowIndex, _currRowIndex + 1)
} else {
rangeRows = afterFullData.slice(_firstRowIndex, _currRowIndex + 1)
}
}
$xeTable.setAllCheckboxRow(false)
nextTick(() => {
handleCheckedCheckboxRow(rangeRows, true, false)
})
dispatchEvent('checkbox-range-select', Object.assign({ rangeRecords: rangeRows }, params), evnt)
}
}
}
if (!checkMethod || checkMethod({ $table: $xeTable, row })) {
$xeTable.handleBatchSelectRows([row], checked)
$xeTable.checkSelectionStatus()
dispatchEvent('checkbox-change', Object.assign({
records: () => $xeTable.getCheckboxRecords(),
reserves: () => $xeTable.getCheckboxReserveRecords(),
indeterminates: () => $xeTable.getCheckboxIndeterminateRecords(),
checked
}, params), evnt)
}
},
/**
* 多选,选中所有事件
*/
triggerCheckAllEvent (evnt, value) {
const checkboxOpts = computeCheckboxOpts.value
const { trigger } = checkboxOpts
if (trigger === 'manual') {
return
}
if (evnt) {
evnt.stopPropagation()
}
handleCheckAllEvent(evnt, value)
},
/**
* 单选,行选中事件
*/
triggerRadioRowEvent (evnt, params) {
const { selectRadioRow: oldValue } = reactData
const { row } = params
const radioOpts = computeRadioOpts.value
const { trigger, strict, checkMethod } = radioOpts
if (trigger === 'manual') {
return
}
evnt.stopPropagation()
if (!checkMethod || checkMethod({ $table: $xeTable, row })) {
let newValue = row
let isChange = oldValue !== newValue
if (strict) {
handleCheckedRadioRow(newValue, false)
} else {
if (oldValue === newValue) {
newValue = null
}
isChange = oldValue !== newValue
if (isChange && newValue) {
handleCheckedRadioRow(newValue, false)
} else {
newValue = null
$xeTable.clearRadioRow()
}
}
if (isChange) {
dispatchEvent('radio-change', { oldValue, newValue, selected: !!newValue, ...params }, evnt)
}
}
},
triggerCurrentColumnEvent (evnt, params) {
const { currentColumn: oldValue } = reactData
const columnOpts = computeColumnOpts.value
const currentColumnOpts = computeCurrentColumnOpts.value
const beforeRowMethod = currentColumnOpts.beforeSelectMethod || columnOpts.currentMethod as any
let newValue: VxeTableDefines.ColumnInfo | null = params.column
const { trigger, strict } = currentColumnOpts
if (trigger === 'manual') {
return
}
let isChange = oldValue !== newValue
if (!beforeRowMethod || beforeRowMethod({ column: newValue, $table: $xeTable })) {
if (strict) {
$xeTable.setCurrentColumn(newValue)
} else {
if (oldValue === newValue) {
newValue = null
}
isChange = oldValue !== newValue
if (isChange && newValue) {
$xeTable.setCurrentColumn(newValue)
} else {
newValue = null
$xeTable.clearCurrentColumn()
}
}
if (isChange) {
dispatchEvent('current-column-change', { oldValue, newValue, selected: !!newValue, ...params }, evnt)
}
} else {
dispatchEvent('current-column-disabled', params, evnt)
}
},
triggerCurrentRowEvent (evnt, params) {
const { currentRow: oldValue } = internalData
const rowOpts = computeRowOpts.value
const currentRowOpts = computeCurrentRowOpts.value
const beforeRowMethod = currentRowOpts.beforeSelectMethod || rowOpts.currentMethod as any
let { row: newValue } = params
const { trigger, strict } = currentRowOpts
if (trigger === 'manual') {
return
}
let isChange = oldValue !== newValue
if (!beforeRowMethod || beforeRowMethod({ row: newValue, $table: $xeTable })) {
if (strict) {
$xeTable.setCurrentRow(newValue)
} else {
if (oldValue === newValue) {
newValue = null
}
isChange = oldValue !== newValue
if (isChange && newValue) {
$xeTable.setCurrentRow(newValue)
} else {
newValue = null
$xeTable.clearCurrentRow()
}
}
if (isChange) {
dispatchEvent('current-row-change', { oldValue, newValue, selected: !!newValue, ...params }, evnt)
// 已废弃
dispatchEvent('current-change', { oldValue, newValue, ...params }, evnt)
}
} else {
dispatchEvent('current-row-disabled', params, evnt)
}
},
/**
* 展开行事件
*/
triggerRowExpandEvent (evnt, params) {
const { expandColumn } = reactData
const { rowExpandLazyLoadedMaps } = internalData
const expandOpts = computeExpandOpts.value
const { row } = params
const { lazy, trigger } = expandOpts
if (trigger === 'manual') {
return
}
evnt.stopPropagation()
const rowid = getRowid($xeTable, row)
if (!lazy || !rowExpandLazyLoadedMaps[rowid]) {
const expanded = !$xeTable.isRowExpandByRow(row)
const columnIndex = expandColumn ? $xeTable.getColumnIndex(expandColumn) : -1
const $columnIndex = expandColumn ? $xeTable.getVMColumnIndex(expandColumn) : -1
$xeTable.setRowExpand(row, expanded)
dispatchEvent('toggle-row-expand', {
expanded,
column: expandColumn,
columnIndex,
$columnIndex,
row,
rowIndex: $xeTable.getRowIndex(row),
$rowIndex: $xeTable.getVMRowIndex(row)
}, evnt)
}
},
/**
* 行分组事件
*/
triggerRowGroupExpandEvent (evnt, params) {
const { rowGroupExpandedMaps } = internalData
const aggregateOpts = computeAggregateOpts.value
const { row, column } = params
const { trigger } = aggregateOpts
if (trigger === 'manual') {
return
}
evnt.stopPropagation()
const rowid = getRowid($xeTable, row)
const expanded = !rowGroupExpandedMaps[rowid]
const columnIndex = $xeTable.getColumnIndex(column)
const $columnIndex = $xeTable.getVMColumnIndex(column)
$xeTable.setRowGroupExpand(row, expanded)
dispatchEvent('toggle-row-group-expand', { expanded, column, columnIndex, $columnIndex, row }, evnt)
},
/**
* 展开树节点事件
*/
triggerTreeExpandEvent (evnt, params) {
const { treeExpandLazyLoadedMaps, treeEATime } = internalData
const treeOpts = computeTreeOpts.value
const { row, column } = params
const { lazy, trigger, accordion } = treeOpts
if (trigger === 'manual') {
return
}
evnt.stopPropagation()
const rowid = getRowid($xeTable, row)
if (!lazy || !treeExpandLazyLoadedMaps[rowid]) {
const expanded = !$xeTable.isTreeExpandByRow(row)
const columnIndex = $xeTable.getColumnIndex(column)
const $columnIndex = $xeTable.getVMColumnIndex(column)
if (treeEATime) {
clearTimeout(treeEATime)
}
$xeTable.setTreeExpand(row, expanded).then(() => {
if (accordion) {
internalData.treeEATime = setTimeout(() => {
internalData.treeEATime = undefined
$xeTable.scrollToRow(row)
}, 30)
}
})
dispatchEvent('toggle-tree-expand', { expanded, column, columnIndex, $columnIndex, row }, evnt)
}
},
handleColumnSortEvent (evnt, column) {
const { mouseConfig } = props
const mouseOpts = computeMouseOpts.value
const { field, sortable, order } = column
if (sortable) {
const params = { $table: $xeTable, $event: evnt, column, field, property: field, order, sortList: $xeTable.getSortColumns(), sortTime: column.sortTime }
if (mouseConfig && mouseOpts.area && $xeTable.handleSortEvent) {
$xeTable.handleSortEvent(evnt, params)
}
if (!order) {
dispatchEvent('clear-sort', params, evnt)
}
dispatchEvent('sort-change', params, evnt)
}
},
/**
* 点击排序事件
*/
triggerSortEvent (evnt, column, order) {
const sortOpts = computeSortOpts.value
const { multiple, allowClear } = sortOpts
const { field, sortable } = column
if (sortable) {
if (!order || column.order === order) {
if (allowClear) {
$xeTable.clearSort(multiple ? column : null)
}
} else {
$xeTable.sort({ field, order })
}
$xeTable.handleColumnSortEvent(evnt, column)
}
},
handleCellRuleUpdateStatus (type, cellParams, cellValue) {
const { validStore } = reactData
const { row, column } = cellParams
if ($xeTable.hasCellRules) {
if ($xeTable.hasCellRules(type, row, column)) {
const cell = $xeTable.getCellElement(row, column)
if (cell) {
const customVal = !XEUtils.isUndefined(cellValue)
return $xeTable.validCellRules(type, row, column, cellValue)
.then(() => {
if (customVal && validStore.visible) {
setCellValue(row, column, cellValue)
}
$xeTable.clearValidate(row, column)
})
.catch(({ rule }: any) => {
if (customVal) {
setCellValue(row, column, cellValue)
}
$xeTable.showValidTooltip({ rule, row, column, cell })
})
}
}
}
return nextTick()
},
/**
* 表头单元格按下事件
*/
triggerHeaderCellMousedownEvent (evnt, params) {
const { mouseConfig } = props
const mouseOpts = computeMouseOpts.value
const columnOpts = computeColumnOpts.value
const columnDragOpts = computeColumnDragOpts.value
const { trigger, isCrossDrag, isPeerDrag, disabledMethod } = columnDragOpts
const cell = evnt.currentTarget as HTMLDivElement
const triggerInput = cell && cell.tagName && cell.tagName.toLowerCase() === 'input'
const triggerCheckbox = getEventTargetNode(evnt, cell, 'vxe-cell--checkbox').flag
const triggerSort = getEventTargetNode(evnt, cell, 'vxe-cell--sort').flag
const triggerFilter = getEventTargetNode(evnt, cell, 'vxe-cell--filter').flag
let triggerDrag = false
const isColDragCell = columnOpts.drag && trigger === 'cell'
if (!(triggerInput || triggerCheckbox || triggerSort || triggerFilter)) {
const { column } = params
if (isColDragCell && !column.fixed && (isCrossDrag || isPeerDrag || !column.parentId) && !(disabledMethod && disabledMethod(params))) {
triggerDrag = true
$xeTable.handleHeaderCellDragMousedownEvent(evnt, params)
}
}
if (!triggerDrag && mouseConfig && mouseOpts.area && $xeTable.handleHeaderCellAreaMouseDnEvent) {
$xeTable.handleHeaderCellAreaMouseDnEvent(evnt, Object.assign({ cell, triggerSort, triggerFilter }, params))
}
$xeTable.focus()
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
},
/**
* 单元格按下事件
*/
triggerCellMousedownEvent (evnt, params) {
const { column } = params
const { type, treeNode } = column
const isRadioType = type === 'radio'
const isCheckboxType = type === 'checkbox'
const isExpandType = type === 'expand'
const rowOpts = computeRowOpts.value
const rowDragOpts = computeRowDragOpts.value
const { trigger, isCrossDrag, isPeerDrag, disabledMethod } = rowDragOpts
const cell = evnt.currentTarget as HTMLElement
params.cell = cell
const triggerInput = cell && cell.tagName && cell.tagName.toLowerCase() === 'input'
const triggerRadio = isRadioType && getEventTargetNode(evnt, cell, 'vxe-cell--radio').flag
const triggerCheckbox = isCheckboxType && getEventTargetNode(evnt, cell, 'vxe-cell--checkbox').flag
const triggerTreeNode = treeNode && getEventTargetNode(evnt, cell, 'vxe-cell--tree-btn').flag
const triggerExpandNode = isExpandType && getEventTargetNode(evnt, cell, 'vxe-table--expanded').flag
let isColDragCell = false
if (rowOpts.drag) {
isColDragCell = trigger === 'row' || (column.dragSort && trigger === 'cell')
}
let triggerDrag = false
if (!(triggerInput || triggerRadio || triggerCheckbox || triggerTreeNode || triggerExpandNode)) {
if (isColDragCell && (isCrossDrag || isPeerDrag || !params.level) && !(disabledMethod && disabledMethod(params))) {
triggerDrag = true
$xeTable.handleCellDragMousedownEvent(evnt, params)
}
}
if (!triggerDrag && $xeTable.handleCellMousedownEvent) {
$xeTable.handleCellMousedownEvent(evnt, params)
}
$xeTable.focus()
$xeTable.closeFilter()
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
},
triggerCellMouseupEvent () {
clearDragStatus()
},
/**
* 行拖拽
*/
handleRowDragDragstartEvent (evnt) {
if (evnt.dataTransfer) {
evnt.dataTransfer.setDragImage(getTpImg(), 0, 0)
}
$xeTable.closeTooltip()
},
handleRowDragSwapEvent (evnt, isSyncRow, dragRow, prevDragRow, prevDragPos, prevDragToChild) {
const { treeConfig, dragConfig } = props
const rowDragOpts = computeRowDragOpts.value
const { afterFullData, tableFullData, fullAllDataRowIdData } = internalData
const $xeGanttView = internalData.xeGanttView
const { animation, isPeerDrag, isCrossDrag, isSelfToChildDrag, dragEndMethod, dragToChildMethod } = rowDragOpts
const treeOpts = computeTreeOpts.value
const cellOpts = computeCellOpts.value
const rowOpts = computeRowOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const { transform, rowField, mapChildrenField, parentField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const dEndMethod = dragEndMethod || (dragConfig ? dragConfig.dragEndMethod : null)
const dragOffsetIndex = prevDragPos === 'bottom' ? 1 : 0
const el = refElem.value
const errRest = {
status: false
}
if (!(el && prevDragRow && dragRow)) {
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
// 判断是否有拖动
if (prevDragRow !== dragRow) {
const dragParams = {
oldRow: dragRow,
newRow: prevDragRow,
dragRow,
dragPos: prevDragPos as 'top' | 'bottom',
dragToChild: !!prevDragToChild,
offsetIndex: dragOffsetIndex as 0 | 1
}
const dragRowid = getRowid($xeTable, dragRow)
const dragRowRest = fullAllDataRowIdData[dragRowid] || {}
const _dragRowIndex = dragRowRest._index
let dragRowHeight = 0
let dragOffsetTop = -1
if (animation) {
dragRowHeight = getCellRestHeight(dragRowRest, cellOpts, rowOpts, defaultRowHeight)
const oldTrEl = el.querySelector<HTMLElement>(`.vxe-body--row[rowid="${dragRowid}"]`)
if (oldTrEl) {
dragOffsetTop = oldTrEl.offsetTop
}
}
let oafIndex = -1
let nafIndex = -1
const oldRest = dragRowRest
const newRowid = getRowid($xeTable, prevDragRow)
const newRest = fullAllDataRowIdData[newRowid]
const oldAllMaps: Record<string, any> = {}
let isSelfToChildStatus = false
// 如果为树结构
if (treeConfig) {
if (transform) {
if (oldRest && newRest) {
const { level: oldLevel } = oldRest
const { level: newLevel } = newRest
XEUtils.eachTree([dragRow], item => {
oldAllMaps[getRowid($xeTable, item)] = item
}, { children: mapChildrenField })
if (oldLevel && newLevel) {
// 子到子
if (isPeerDrag && !isCrossDrag) {
if (oldRest.row[parentField] !== newRest.row[parentField]) {
// 非同级
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
} else {
if (!isCrossDrag) {
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
if (oldAllMaps[newRowid]) {
isSelfToChildStatus = true
if (!(isCrossDrag && isSelfToChildDrag)) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.error.treeDragChild')
})
}
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
}
}
} else if (oldLevel) {
// 子到根
if (!isCrossDrag) {
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
} else if (newLevel) {
// 根到子
if (!isCrossDrag) {
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
if (oldAllMaps[newRowid]) {
isSelfToChildStatus = true
if (!(isCrossDrag && isSelfToChildDrag)) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.error.treeDragChild')
})
}
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
}
}
} else {
// 根到根
}
}
}
}
const isDragToChildFlag = isSelfToChildDrag && dragToChildMethod ? dragToChildMethod(dragParams) : prevDragToChild
if (dEndMethod) {
handleRowDragEndClearStatus()
}
return Promise.resolve(dEndMethod ? dEndMethod(dragParams) : true).then((status) => {
if (!status) {
return errRest
}
// 如果为树结构
if (treeConfig) {
if (transform) {
// 移出源位置
if (oldRest && newRest) {
const fullList = XEUtils.toTreeArray(internalData.afterTreeFullData, {
updated: false,
key: rowField,
parentKey: parentField,
children: mapChildrenField
})
// 移出
const otfIndex = $xeTable.findRowIndexOf(fullList, dragRow)
fullList.splice(otfIndex, 1)
// 插入
const ptfIndex = $xeTable.findRowIndexOf(fullList, prevDragRow)
const ntfIndex = ptfIndex + dragOffsetIndex
fullList.splice(ntfIndex, 0, dragRow)
// 改变层级
if (isSelfToChildStatus && (isCrossDrag && isSelfToChildDrag)) {
XEUtils.each(dragRow[childrenField], childRow => {
childRow[parentField] = dragRow[parentField]
})
}
dragRow[parentField] = isDragToChildFlag ? prevDragRow[rowField] : prevDragRow[parentField]
internalData.tableFullTreeData = XEUtils.toArrayTree(fullList, {
key: rowField,
parentKey: parentField,
children: childrenField,
mapChildren: mapChildrenField
})
}
}
} else {
// 移出
oafIndex = $xeTable.findRowIndexOf(afterFullData, dragRow)
const otfIndex = $xeTable.findRowIndexOf(tableFullData, dragRow)
afterFullData.splice(oafIndex, 1)
tableFullData.splice(otfIndex, 1)
// 插入
const pafIndex = $xeTable.findRowIndexOf(afterFullData, prevDragRow)
const ptfIndex = $xeTable.findRowIndexOf(tableFullData, prevDragRow)
nafIndex = pafIndex + dragOffsetIndex
const ntfIndex = ptfIndex + dragOffsetIndex
afterFullData.splice(nafIndex, 0, dragRow)
tableFullData.splice(ntfIndex, 0, dragRow)
}
$xeTable.handleClearStack()
$xeTable.handleTableData(treeConfig && transform)
$xeTable.cacheRowMap(false)
updateScrollYStatus()
if (!(treeConfig && transform)) {
$xeTable.updateAfterDataIndex()
}
$xeTable.checkSelectionStatus()
if (reactData.scrollYLoad) {
$xeTable.updateScrollYSpace()
}
if (evnt) {
dispatchEvent('row-dragend', {
oldRow: dragRow,
newRow: prevDragRow,
dragRow,
dragPos: prevDragPos as any,
dragToChild: isDragToChildFlag,
offsetIndex: dragOffsetIndex,
_index: {
newIndex: nafIndex,
oldIndex: oafIndex
}
}, evnt)
}
return nextTick().then(() => {
if (animation) {
const { tableData } = reactData
const { fullAllDataRowIdData } = internalData
const dragRowRest = fullAllDataRowIdData[dragRowid]
const _newRowIndex = dragRowRest._index
const firstRow = tableData[0]
const firstRowRest = fullAllDataRowIdData[getRowid($xeTable, firstRow)]
let wrapperEl = el
if ($xeGantt && $xeGanttView) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
wrapperEl = ganttContainerElem
}
}
if (firstRowRest) {
const _firstRowIndex = firstRowRest._index
const _lastRowIndex = _firstRowIndex + tableData.length
let rsIndex = -1
let reIndex = -1
let offsetRate = 1
if (_dragRowIndex < _firstRowIndex) {
// 从上往下虚拟拖拽
rsIndex = 0
reIndex = _newRowIndex - _firstRowIndex
} else if (_dragRowIndex > _lastRowIndex) {
// 从下往上虚拟拖拽
const $newRowIndex = dragRowRest.$index
rsIndex = $newRowIndex + 1
reIndex = tableData.length
offsetRate = -1
} else {
if (_newRowIndex > _dragRowIndex) {
// 从上往下拖拽
rsIndex = _dragRowIndex - _firstRowIndex
reIndex = rsIndex + _newRowIndex - _dragRowIndex
} else {
// 从下往上拖拽
rsIndex = _newRowIndex - _firstRowIndex
reIndex = rsIndex + _dragRowIndex - _newRowIndex + 1
offsetRate = -1
}
}
const dragRangeList = tableData.slice(rsIndex, reIndex)
if (dragRangeList.length) {
const dtClss: string[] = []
dragRangeList.forEach(row => {
const rowid = getRowid($xeTable, row)
dtClss.push(`.vxe-body--row[rowid="${rowid}"]`)
if ($xeGantt) {
dtClss.push(`.vxe-gantt-view--body-row[rowid="${rowid}"]`, `.vxe-gantt-view--chart-row[rowid="${rowid}"]`)
}
})
const dtTrList = wrapperEl.querySelectorAll<HTMLElement>(dtClss.join(','))
moveRowAnimateToTb(dtTrList, offsetRate * dragRowHeight)
}
}
const drClss = [`.vxe-body--row[rowid="${dragRowid}"]`]
if ($xeGantt) {
drClss.push(`.vxe-gantt-view--body-row[rowid="${dragRowid}"]`, `.vxe-gantt-view--chart-row[rowid="${dragRowid}"]`)
}
const newDtTrList = wrapperEl.querySelectorAll<HTMLElement>(drClss.join(','))
const newTrEl = newDtTrList[0]
if (dragOffsetTop > -1 && newTrEl) {
moveRowAnimateToTb(newDtTrList, dragOffsetTop - newTrEl.offsetTop)
}
}
updateRowOffsetTop()
updateRowExpandStyle()
$xeTable.updateCellAreas()
$xeTable.recalculate()
}).then(() => {
return {
status: true
}
})
}).catch(() => {
return errRest
}).then((rest) => {
handleRowDragEndClearStatus()
return rest
})
}
handleRowDragEndClearStatus()
return Promise.resolve(errRest)
},
handleCrossTableRowDragCancelEvent () {
handleRowDragEndClearStatus()
},
/**
* 处理跨表拖拽完成
*/
handleCrossTableRowDragFinishEvent (evnt) {
const { tableData } = reactData
const { fullAllDataRowIdData } = internalData
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const rowDragOpts = computeRowDragOpts.value
const { animation, isCrossTableDrag } = rowDragOpts
const treeOpts = computeTreeOpts.value
const { mapChildrenField } = treeOpts
const el = refElem.value
if (isCrossTableDrag && crossTableDragRowObj && crossTableDragRowInfo) {
const { row: dragRow } = crossTableDragRowInfo
if (dragRow) {
const dragRowid = getRowid($xeTable, dragRow)
const dragRowRest = fullAllDataRowIdData[dragRowid]
let dragRowHeight = 0
let rsIndex = -1
if (dragRowRest) {
if (animation) {
dragRowHeight = getCellRestHeight(dragRowRest, cellOpts, rowOpts, defaultRowHeight)
}
rsIndex = dragRowRest.$index
}
const dragRangeList = rsIndex > -1 && rsIndex < tableData.length - 1 ? tableData.slice(rsIndex + 1) : []
const dragList = XEUtils.toTreeArray([dragRow], {
updated: true,
children: mapChildrenField
})
$xeTable.remove(dragList).then(() => {
if (animation && dragRowHeight && dragRangeList.length) {
const $xeGanttView = internalData.xeGanttView
let wrapperEl = el
if ($xeGantt && $xeGanttView) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
wrapperEl = ganttContainerElem
}
}
const dtClss: string[] = []
dragRangeList.forEach(row => {
const rowid = getRowid($xeTable, row)
dtClss.push(`.vxe-body--row[rowid="${rowid}"]`)
if ($xeGantt) {
dtClss.push(`.vxe-gantt-view--body-row[rowid="${rowid}"]`, `.vxe-gantt-view--chart-row[rowid="${rowid}"]`)
}
})
const dtTrList = wrapperEl.querySelectorAll<HTMLElement>(dtClss.join(','))
moveRowAnimateToTb(dtTrList, dragRowHeight)
}
})
dispatchEvent('row-remove-dragend', {
row: dragRow
}, evnt)
handleRowDragEndClearStatus()
}
}
},
/**
* 处理跨表拖至新的空表
*/
handleCrossTableRowDragoverEmptyEvent (evnt) {
const { tableData } = reactData
const rowDragOpts = computeRowDragOpts.value
const { isCrossTableDrag } = rowDragOpts
if (isCrossTableDrag && crossTableDragRowObj && !tableData.length) {
const { $oldTable, $newTable } = crossTableDragRowObj
if ($oldTable) {
const oldTableReactData = $oldTable.reactData
if ($oldTable.xID !== $xeTable.xID) {
if ($newTable && $newTable.xID !== $xeTable.xID) {
$newTable.hideCrossTableRowDropClearStatus()
}
evnt.preventDefault()
$oldTable.hideCrossTableRowDropClearStatus()
crossTableDragRowObj.$newTable = $xeTable
internalData.prevDragRow = null
reactData.dragTipText = oldTableReactData.dragTipText
showDropTip(evnt, evnt.currentTarget as HTMLDivElement, null, true, '')
}
}
}
},
/**
* 处理跨表拖插入
*/
handleCrossTableRowDragInsertEvent (evnt) {
const { treeConfig } = props
const { prevDragRow, prevDragPos, prevDragToChild } = internalData
const rowDragOpts = computeRowDragOpts.value
const { animation, isSelfToChildDrag, isCrossTableDrag, dragEndMethod, dragToChildMethod } = rowDragOpts
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const treeOpts = computeTreeOpts.value
const { parentField, mapChildrenField } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
// 跨表拖拽
if (isCrossTableDrag && crossTableDragRowObj && crossTableDragRowInfo) {
const { row: oldRow } = crossTableDragRowInfo
const { $oldTable } = crossTableDragRowObj
const el = refElem.value
if ($oldTable && oldRow) {
const dragRow = oldRow
let dragOffsetIndex = -1
if (prevDragRow) {
dragOffsetIndex = prevDragPos === 'bottom' ? 1 : 0
}
const dragParams = {
oldRow: dragRow,
newRow: prevDragRow,
dragRow,
dragPos: prevDragPos as 'top' | 'bottom',
dragToChild: !!prevDragToChild,
offsetIndex: dragOffsetIndex as 0 | 1
}
const isDragToChildFlag = isSelfToChildDrag && dragToChildMethod ? dragToChildMethod(dragParams) : prevDragToChild
const errRest = {
status: false
}
if (dragEndMethod) {
handleRowDragEndClearStatus()
}
Promise.resolve(dragEndMethod ? dragEndMethod(dragParams) : true).then((status) => {
if (!status) {
if ($oldTable) {
if ($oldTable.xID !== $xeTable.xID) {
$oldTable.handleCrossTableRowDragCancelEvent(evnt)
}
}
handleRowDragEndClearStatus()
return errRest
}
let insertRest: Promise<any> = Promise.resolve()
if (treeConfig) {
const dragList = XEUtils.toTreeArray([dragRow], {
updated: true,
children: mapChildrenField
})
$oldTable.handleCrossTableRowDragFinishEvent(evnt)
if (prevDragRow) {
dragRow[parentField] = prevDragRow[parentField]
} else {
dragRow[parentField] = null
}
dragList.forEach(row => {
row[childrenField] = undefined
row[mapChildrenField] = undefined
})
if (prevDragRow) {
if (prevDragPos === 'bottom') {
insertRest = $xeTable.insertNextAt(dragList, prevDragRow)
} else {
insertRest = $xeTable.insertAt(dragList, prevDragRow)
}
} else {
insertRest = $xeTable.insert(dragList)
}
} else {
$oldTable.handleCrossTableRowDragFinishEvent(evnt)
if (prevDragRow) {
if (prevDragPos === 'bottom') {
insertRest = $xeTable.insertNextAt(dragRow, prevDragRow)
} else {
insertRest = $xeTable.insertAt(dragRow, prevDragRow)
}
} else {
insertRest = $xeTable.insert(dragRow)
}
}
dispatchEvent('row-insert-dragend', {
oldRow,
newRow: prevDragRow,
dragRow,
dragPos: prevDragPos as any,
dragToChild: isDragToChildFlag,
offsetIndex: dragOffsetIndex
}, evnt)
clearRowDragData()
insertRest.then(() => {
const { tableData } = reactData
const { fullAllDataRowIdData } = internalData
const oldRowid = getRowid($xeTable, dragRow)
const oldRowRest = fullAllDataRowIdData[oldRowid]
let dragRowHeight = 0
let rsIndex = -1
if (oldRowRest) {
if (animation) {
dragRowHeight = getCellRestHeight(oldRowRest, cellOpts, rowOpts, defaultRowHeight)
}
rsIndex = oldRowRest.$index
}
const dragRangeList = rsIndex > -1 ? tableData.slice(rsIndex) : []
if (animation && dragRowHeight && dragRangeList.length) {
const $xeGanttView = internalData.xeGanttView
let wrapperEl = el
if ($xeGantt && $xeGanttView) {
const { refGanttContainerElem } = $xeGantt.getRefMaps()
const ganttContainerElem = refGanttContainerElem.value
if (ganttContainerElem) {
wrapperEl = ganttContainerElem
}
}
const dtClss: string[] = []
dragRangeList.forEach(row => {
const rowid = getRowid($xeTable, row)
dtClss.push(`.vxe-body--row[rowid="${rowid}"]`)
if ($xeGantt) {
dtClss.push(`.vxe-gantt-view--body-row[rowid="${rowid}"]`, `.vxe-gantt-view--chart-row[rowid="${rowid}"]`)
}
})
const dtTrList = wrapperEl.querySelectorAll<HTMLElement>(dtClss.join(','))
moveRowAnimateToTb(dtTrList, -dragRowHeight)
}
})
})
}
}
},
hideCrossTableRowDropClearStatus () {
hideDropTip()
},
handleRowDragDragendEvent (evnt) {
const { treeConfig } = props
const { fullAllDataRowIdData, prevDragToChild } = internalData
const { dragRow } = reactData
const treeOpts = computeTreeOpts.value
const { lazy } = treeOpts
const hasChildField = treeOpts.hasChild || treeOpts.hasChildField
const { prevDragRow, prevDragPos } = internalData
const rowDragOpts = computeRowDragOpts.value
const { isCrossTableDrag } = rowDragOpts
// 跨表拖拽
if (isCrossTableDrag && crossTableDragRowObj) {
const { $newTable } = crossTableDragRowObj
if ($newTable && $newTable.xID !== $xeTable.xID) {
$newTable.handleCrossTableRowDragInsertEvent(evnt)
return
}
}
if (treeConfig && lazy && prevDragToChild) {
// 懒加载
const newRowid = getRowid($xeTable, prevDragRow)
const rowRest = fullAllDataRowIdData[newRowid]
if (prevDragRow[hasChildField]) {
if (rowRest && rowRest.treeLoaded) {
$xeTable.handleRowDragSwapEvent(evnt, true, dragRow, prevDragRow, prevDragPos, prevDragToChild)
}
} else {
$xeTable.handleRowDragSwapEvent(evnt, true, dragRow, prevDragRow, prevDragPos, prevDragToChild)
}
} else {
$xeTable.handleRowDragSwapEvent(evnt, true, dragRow, prevDragRow, prevDragPos, prevDragToChild)
}
},
handleRowDragDragoverEvent (evnt) {
const { treeConfig } = props
const { fullAllDataRowIdData } = internalData
const { dragRow } = reactData
const treeOpts = computeTreeOpts.value
const { lazy, transform, parentField } = treeOpts
const hasChildField = treeOpts.hasChild || treeOpts.hasChildField
const rowDragOpts = computeRowDragOpts.value
const { isPeerDrag, isCrossDrag, isToChildDrag, isCrossTableDrag } = rowDragOpts
if (!dragRow && !(isCrossTableDrag && (!treeConfig || isCrossDrag) && crossTableDragRowObj)) {
evnt.preventDefault()
return
}
const isControlKey = hasControlKey(evnt)
const trEl = evnt.currentTarget as HTMLElement
const rowid = trEl.getAttribute('rowid') || ''
const rowRest = fullAllDataRowIdData[rowid]
if (rowRest) {
evnt.preventDefault()
const row = rowRest.row
const offsetY = evnt.clientY - trEl.getBoundingClientRect().y
const dragPos = offsetY < trEl.clientHeight / 2 ? 'top' : 'bottom'
internalData.prevDragToChild = !!(treeConfig && transform && (isCrossDrag && isToChildDrag) && isControlKey)
internalData.prevDragRow = row
internalData.prevDragPos = dragPos
// 跨表拖拽
if (isCrossTableDrag && (!treeConfig || isCrossDrag) && crossTableDragRowObj) {
const { $oldTable, $newTable } = crossTableDragRowObj
if ($oldTable) {
const oldTableReactData = $oldTable.reactData
if ($oldTable.xID === $xeTable.xID) {
if ($newTable) {
$newTable.hideCrossTableRowDropClearStatus()
}
reactData.isCrossDragRow = false
oldTableReactData.isCrossDragRow = false
crossTableDragRowObj.$newTable = null
} else {
if ($newTable && $newTable.xID !== $xeTable.xID) {
$newTable.hideCrossTableRowDropClearStatus()
}
$oldTable.hideCrossTableRowDropClearStatus()
oldTableReactData.isCrossDragRow = true
reactData.dragTipText = oldTableReactData.dragTipText
crossTableDragRowObj.$newTable = $xeTable
showDropTip(evnt, trEl, null, true, dragPos)
return
}
}
}
if ($xeTable.eqRow(dragRow, row) ||
(isControlKey && treeConfig && lazy && row[hasChildField] && rowRest && !rowRest.treeLoaded) ||
(!isCrossDrag && treeConfig && transform && (isPeerDrag ? dragRow[parentField] !== row[parentField] : rowRest.level))
) {
showDropTip(evnt, trEl, null, false, dragPos)
return
}
showDropTip(evnt, trEl, null, true, dragPos)
dispatchEvent('row-dragover', {
oldRow: dragRow,
targetRow: row,
dragPos
}, evnt)
}
},
handleCellDragMousedownEvent (evnt, params) {
evnt.stopPropagation()
const { dragConfig } = props
const rowDragOpts = computeRowDragOpts.value
const { isCrossTableDrag, trigger, dragStartMethod } = rowDragOpts
const { row } = params
const dragEl = evnt.currentTarget as HTMLElement
const tdEl = trigger === 'cell' || trigger === 'row' ? dragEl : dragEl.parentElement?.parentElement as HTMLElement
const trEl = tdEl.parentElement as HTMLElement
const dStartMethod = dragStartMethod || (dragConfig ? dragConfig.dragStartMethod : null)
clearRowDropOrigin()
if (dStartMethod && !dStartMethod(params)) {
trEl.draggable = false
reactData.dragRow = null
reactData.dragCol = null
clearCrossTableDragStatus()
hideDropTip()
return
}
if (isCrossTableDrag) {
crossTableDragRowInfo.row = row
crossTableDragRowObj = { $oldTable: $xeTable, $newTable: null }
}
reactData.dragRow = row
reactData.isCrossDragRow = false
reactData.dragCol = null
trEl.draggable = true
updateRowDropOrigin(row)
updateRowDropTipContent(tdEl)
dispatchEvent('row-dragstart', params, evnt)
},
handleCellDragMouseupEvent () {
clearDragStatus()
},
/**
* 列拖拽
*/
handleHeaderCellDragDragstartEvent (evnt) {
if (evnt.dataTransfer) {
evnt.dataTransfer.setDragImage(getTpImg(), 0, 0)
}
$xeTable.closeTooltip()
},
handleColDragSwapColumn () {
handleUpdateColumn()
return parseColumns(false).then(() => {
$xeTable.updateCellAreas()
$xeTable.saveCustomStore('update:sort')
})
},
handleColDragSwapEvent (evnt, isSyncColumn, dragCol, prevDragCol, prevDragPos, prevDragToChild) {
const { mouseConfig } = props
const columnDragOpts = computeColumnDragOpts.value
const { animation, isPeerDrag, isCrossDrag, isSelfToChildDrag, isToChildDrag, dragEndMethod, dragToChildMethod } = columnDragOpts
const { collectColumn, fullColumnIdData } = internalData
const el = refElem.value
const dragOffsetIndex = prevDragPos === 'right' ? 1 : 0
const errRest = {
status: false
}
if (!(el && prevDragCol && dragCol)) {
return Promise.resolve(errRest)
}
// 判断是否有拖动
if (prevDragCol !== dragCol) {
const dragColumn = dragCol
const newColumn = prevDragCol
const dragParams = {
oldColumn: dragColumn,
newColumn,
dragColumn,
dragPos: prevDragPos as 'left' | 'right',
dragToChild: !!prevDragToChild,
offsetIndex: dragOffsetIndex as 0 | 1
}
let dragTargetColumn: VxeTableDefines.ColumnInfo | null = null
const dragAllTargetCols: VxeTableDefines.ColumnInfo[] = []
let dragColWidth = 0
if (animation) {
XEUtils.eachTree([dragColumn], column => {
if (!dragTargetColumn && (!column.children || !column.children.length)) {
dragTargetColumn = column
dragColWidth += column.renderWidth
}
dragAllTargetCols.push(column)
})
}
if (!dragTargetColumn) {
dragTargetColumn = dragColumn
}
const dragColRest = fullColumnIdData[dragTargetColumn.id] || {}
const _dragColIndex = dragColRest._index
let dragOffsetLeft = -1
if (animation) {
const oldTrEl = el.querySelector<HTMLElement>(`.vxe-table--column[colid="${dragTargetColumn.id}"]`)
if (oldTrEl) {
dragOffsetLeft = oldTrEl.offsetLeft
}
}
let oafIndex = -1
let nafIndex = -1
const oldAllMaps: Record<string, any> = {}
XEUtils.eachTree([dragColumn], column => {
oldAllMaps[column.id] = column
})
let isSelfToChildStatus = false
if (dragColumn.parentId && newColumn.parentId) {
// 子到子
if (isPeerDrag && !isCrossDrag) {
if (dragColumn.parentId !== newColumn.parentId) {
// 非同级
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
} else {
if (!isCrossDrag) {
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
if (oldAllMaps[newColumn.id]) {
isSelfToChildStatus = true
if (!(isCrossDrag && isSelfToChildDrag)) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.error.treeDragChild')
})
}
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
}
}
} else if (dragColumn.parentId) {
// 子到根
if (!isCrossDrag) {
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
} else if (newColumn.parentId) {
// 根到子
if (!isCrossDrag) {
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
if (oldAllMaps[newColumn.id]) {
isSelfToChildStatus = true
if (!(isCrossDrag && isSelfToChildDrag)) {
if (VxeUI.modal) {
VxeUI.modal.message({
status: 'error',
content: getI18n('vxe.error.treeDragChild')
})
}
handleColDragEndClearStatus()
return Promise.resolve(errRest)
}
}
} else {
// 根到根
}
const isDragToChildFlag = isSelfToChildDrag && dragToChildMethod ? dragToChildMethod(dragParams) : prevDragToChild
return Promise.resolve(dragEndMethod ? dragEndMethod(dragParams) : true).then((status) => {
if (!status) {
return errRest
}
const oldewMatchRest = XEUtils.findTree(collectColumn, item => item.id === dragColumn.id)
// 改变层级
if (isSelfToChildStatus && (isCrossDrag && isSelfToChildDrag)) {
if (oldewMatchRest) {
const { items: oCols, index: oIndex } = oldewMatchRest
const childList = dragColumn.children || []
childList.forEach(column => {
column.parentId = dragColumn.parentId
})
oCols.splice(oIndex, 1, ...childList)
dragColumn.children = []
}
} else {
if (oldewMatchRest) {
const { items: oCols, index: oIndex, parent: oParent } = oldewMatchRest
oCols.splice(oIndex, 1)
if (!oParent) {
oafIndex = oIndex
}
}
}
const newMatchRest = XEUtils.findTree(collectColumn, item => item.id === newColumn.id)
if (newMatchRest) {
const { items: nCols, index: nIndex, parent: nParent } = newMatchRest
// 转子级
if ((isCrossDrag && isToChildDrag) && isDragToChildFlag) {
dragColumn.parentId = newColumn.id
newColumn.children = (newColumn.children || []).concat([dragColumn])
} else {
dragColumn.parentId = newColumn.parentId
nCols.splice(nIndex + dragOffsetIndex, 0, dragColumn)
}
if (!nParent) {
nafIndex = nIndex
}
}
XEUtils.eachTree(collectColumn, (column, index, items, path, parentColumn) => {
if (!parentColumn) {
const sortIndex = index + 1
column.renderSortNumber = sortIndex
}
})
$xeTable.handleClearStack()
if (mouseConfig) {
if ($xeTable.clearSelected) {
$xeTable.clearSelected()
}
if ($xeTable.clearCellAreas) {
$xeTable.clearCellAreas()
$xeTable.clearCopyCellArea()
}
}
if (evnt) {
dispatchEvent('column-dragend', {
oldColumn: dragColumn,
newColumn,
dragColumn,
dragPos: prevDragPos,
dragToChild: isDragToChildFlag,
offsetIndex: dragOffsetIndex,
_index: {
newIndex: nafIndex,
oldIndex: oafIndex
}
}, evnt)
}
return nextTick().then(() => {
if (isSyncColumn) {
return $xeTable.handleColDragSwapColumn()
}
}).then(() => {
if (animation) {
const { tableColumn } = reactData
const { visibleColumn, fullColumnIdData } = internalData
let dragNewColumn: VxeTableDefines.ColumnInfo | null = null
const dragNewColMaps: Record<string, VxeTableDefines.ColumnInfo> = {}
XEUtils.eachTree([dragColumn], column => {
if (!dragNewColumn && (!column.children || !column.children.length)) {
dragNewColumn = column
}
dragNewColMaps[column.id] = column
})
if (!dragNewColumn) {
dragNewColumn = dragColumn
}
if (dragColWidth && dragAllTargetCols.length) {
const _newColIndex = XEUtils.findIndexOf(visibleColumn, column => !!dragNewColumn && column.id === dragNewColumn.id)
const firstCol = tableColumn[0]
const firstColRest = fullColumnIdData[firstCol.id]
if (firstColRest) {
const _firstColIndex = firstColRest._index
const _lastColIndex = _firstColIndex + tableColumn.length
let csIndex = -1
let ceIndex = -1
let offsetRate = 1
if (_dragColIndex < _firstColIndex) {
// 从左往右虚拟拖拽
csIndex = 0
ceIndex = _newColIndex - _firstColIndex
} else if (_dragColIndex > _lastColIndex) {
// 从右往左虚拟拖拽
const $newRowIndex = dragColRest.$index
csIndex = $newRowIndex + 1
ceIndex = tableColumn.length
offsetRate = -1
} else {
if (_newColIndex > _dragColIndex) {
// 从左往右拖拽
csIndex = _dragColIndex - _firstColIndex
ceIndex = csIndex + _newColIndex - _dragColIndex
} else {
// 从右往左拖拽
csIndex = _newColIndex - _firstColIndex + 1
ceIndex = csIndex + _dragColIndex - _newColIndex
offsetRate = -1
}
}
const dragRangeList: VxeTableDefines.ColumnInfo[] = []
const dragRangeMaps: Record<string, VxeTableDefines.ColumnInfo> = {}
for (let i = csIndex; i < ceIndex; i++) {
const column = tableColumn[i]
if (!dragRangeMaps[column.id] && !dragNewColMaps[column.id]) {
dragRangeMaps[column.id] = column
dragRangeList.push(column)
}
}
XEUtils.eachTree([newColumn], column => {
if (!dragRangeMaps[column.id]) {
dragRangeMaps[column.id] = column
dragRangeList.push(column)
}
})
if (dragRangeList.length) {
const dtTrList = el.querySelectorAll<HTMLElement>(dragRangeList.map(column => `.vxe-table--column[colid="${column.id}"]`).join(','))
moveColAnimateToLr(dtTrList, offsetRate * dragColWidth)
}
}
const newTrList = el.querySelectorAll<HTMLElement>(dragAllTargetCols.map(column => `.vxe-table--column[colid="${column.id}"]`).join(','))
const newTdEl = newTrList[0]
if (dragOffsetLeft > -1 && newTdEl) {
moveColAnimateToLr(newTrList, dragOffsetLeft - newTdEl.offsetLeft)
}
}
}
updateColumnOffsetLeft()
loadScrollXData()
$xeTable.updateCellAreas()
return {
status: true
}
})
}).catch(() => {
return errRest
}).then((rest) => {
handleColDragEndClearStatus()
return rest
})
}
handleColDragEndClearStatus()
return Promise.resolve(errRest)
},
handleHeaderCellDragDragendEvent (evnt) {
const { dragCol } = reactData
const { prevDragCol, prevDragPos, prevDragToChild } = internalData
$xeTable.handleColDragSwapEvent(evnt, true, dragCol, prevDragCol, prevDragPos, prevDragToChild)
},
handleHeaderCellDragDragoverEvent (evnt) {
const { dragCol } = reactData
const columnDragOpts = computeColumnDragOpts.value
const { isToChildDrag, isPeerDrag, isCrossDrag } = columnDragOpts
if (!dragCol) {
evnt.preventDefault()
return
}
const isControlKey = hasControlKey(evnt)
const thEl = evnt.currentTarget as HTMLElement
const colid = thEl.getAttribute('colid')
const column = $xeTable.getColumnById(colid)
if (column) {
evnt.preventDefault()
const { clientX } = evnt
const offsetX = clientX - thEl.getBoundingClientRect().x
const dragPos = offsetX < thEl.clientWidth / 2 ? 'left' : 'right'
internalData.prevDragToChild = !!((isCrossDrag && isToChildDrag) && isControlKey)
internalData.prevDragCol = column
internalData.prevDragPos = dragPos
if (column.fixed ||
(dragCol && dragCol.id === column.id) ||
(!isCrossDrag && (isPeerDrag ? dragCol.parentId !== column.parentId : column.parentId))
) {
showDropTip(evnt, null, thEl, false, dragPos)
return
}
showDropTip(evnt, null, thEl, true, dragPos)
dispatchEvent('column-dragover', {
oldColumn: dragCol,
targetColumn: column,
dragPos
}, evnt)
// 边缘滚动
const el = refElem.value
if (!el) {
return
}
const xHandleEl = refScrollXHandleElem.value
const tableBody = refTableBody.value
const tableBodyElem = tableBody ? tableBody.$el as HTMLDivElement : null
const scrollTargetEl = xHandleEl || tableBodyElem
if (scrollTargetEl) {
const wrapperRect = el.getBoundingClientRect()
const tableWrapperWidth = el.clientWidth
const leftContainerElem = refLeftContainer.value
const leftContainerWidth = leftContainerElem ? leftContainerElem.clientWidth : 0
const rightContainerElem = refRightContainer.value
const rightContainerWidth = rightContainerElem ? rightContainerElem.clientWidth : 0
const srartX = wrapperRect.x + leftContainerWidth
const endX = wrapperRect.x + tableWrapperWidth - rightContainerWidth
const distSize = 28
const startDistSize = clientX - srartX
const endDistSize = endX - clientX
if (startDistSize > 0 && startDistSize <= distSize) {
const scrollRatio = Math.floor(tableWrapperWidth / (startDistSize > distSize / 2 ? 240 : 120))
scrollTargetEl.scrollLeft -= scrollRatio * (distSize - startDistSize)
} else if (endDistSize > 0 && endDistSize <= distSize) {
const scrollRatio = Math.floor(tableWrapperWidth / (endDistSize > distSize / 2 ? 240 : 120))
scrollTargetEl.scrollLeft += scrollRatio * (distSize - endDistSize)
}
}
}
},
handleHeaderCellDragMousedownEvent (evnt, params) {
evnt.stopPropagation()
const columnDragOpts = computeColumnDragOpts.value
const { trigger, dragStartMethod } = columnDragOpts
const { column } = params
const dragEl = evnt.currentTarget as HTMLElement
const thEl = trigger === 'cell' ? dragEl : dragEl.parentElement?.parentElement as HTMLElement
clearColDropOrigin()
if (dragStartMethod && !dragStartMethod(params)) {
thEl.draggable = false
reactData.dragRow = null
reactData.dragCol = null
clearCrossTableDragStatus()
hideDropTip()
return
}
reactData.dragCol = column
reactData.dragRow = null
thEl.draggable = true
clearCrossTableDragStatus()
updateColDropOrigin(column)
updateColDropTipContent(thEl)
dispatchEvent('column-dragstart', params, evnt)
},
handleHeaderCellDragMouseupEvent () {
clearColDropOrigin()
hideDropTip()
clearCrossTableDragStatus()
reactData.dragRow = null
reactData.dragCol = null
},
handleScrollEvent (evnt, isRollY, isRollX, scrollTop, scrollLeft, params) {
const { highlightHoverRow } = props
const { lastScrollLeft, lastScrollTop } = internalData
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
if (!xHandleEl || !yHandleEl) {
return
}
const rowOpts = computeRowOpts.value
const validTip = refValidTooltip.value
const tooltip = refTooltip.value
const bodyHeight = yHandleEl.clientHeight
const bodyWidth = xHandleEl.clientWidth
const scrollHeight = yHandleEl.scrollHeight
const scrollWidth = xHandleEl.scrollWidth
let isTop = false
let isBottom = false
let isLeft = false
let isRight = false
let direction = ''
let isTopBoundary = false
let isBottomBoundary = false
let isLeftBoundary = false
let isRightBoundary = false
if (isRollX) {
const xThreshold = computeScrollXThreshold.value
isLeft = scrollLeft <= 0
if (!isLeft) {
isRight = scrollLeft + bodyWidth >= scrollWidth
}
if (scrollLeft > lastScrollLeft) {
direction = 'right'
if (scrollLeft + bodyWidth >= scrollWidth - xThreshold) {
isRightBoundary = true
}
} else {
direction = 'left'
if (scrollLeft <= xThreshold) {
isLeftBoundary = true
}
}
$xeTable.checkScrolling()
internalData.lastScrollLeft = scrollLeft
}
if (isRollY) {
const yThreshold = computeScrollYThreshold.value
isTop = scrollTop <= 1
if (!isTop) {
isBottom = scrollTop + bodyHeight >= scrollHeight - 1
}
if (scrollTop > lastScrollTop) {
direction = 'bottom'
if (scrollTop + bodyHeight >= scrollHeight - yThreshold) {
isBottomBoundary = true
}
} else {
direction = 'top'
if (scrollTop <= yThreshold) {
isTopBoundary = true
}
}
internalData.lastScrollTop = scrollTop
}
internalData.lastSTime = Date.now()
const evntParams = {
source: sourceType,
scrollTop,
scrollLeft,
bodyHeight,
bodyWidth,
scrollHeight,
scrollWidth,
isX: isRollX,
isY: isRollY,
isTop,
isBottom,
isLeft,
isRight,
direction,
...params
}
updateRowExpandStyle()
checkLastSyncScroll(isRollX, isRollY)
if (isRollX) {
$xeTable.closeFilter()
}
if (rowOpts.isHover || highlightHoverRow) {
$xeTable.clearHoverRow()
}
if (validTip && validTip.reactData.visible) {
validTip.close()
}
if (tooltip && tooltip.reactData.visible) {
tooltip.close()
}
if (isBottomBoundary || isTopBoundary || isRightBoundary || isLeftBoundary) {
dispatchEvent('scroll-boundary', evntParams, evnt)
}
dispatchEvent('scroll', evntParams, evnt)
},
/**
* 横向 X 可视渲染事件处理
*/
triggerScrollXEvent () {
const virtualXOpts = computeVirtualXOpts.value
if (virtualXOpts.immediate) {
loadScrollXData()
} else {
lazyScrollXData()
}
},
/**
* 纵向 Y 可视渲染事件处理
*/
triggerScrollYEvent () {
const virtualYOpts = computeVirtualYOpts.value
if (virtualYOpts.immediate) {
loadScrollYData()
} else {
lazyScrollYData()
}
},
triggerBodyScrollEvent (evnt, fixedType) {
const { scrollYLoad, scrollXLoad } = reactData
const { elemStore, intoRunScroll, lastScrollTop, lastScrollLeft, inWheelScroll, inVirtualScroll, inHeaderScroll, inBodyScroll, scrollRenderType, inFooterScroll } = internalData
if (inWheelScroll || inVirtualScroll || inHeaderScroll || inFooterScroll) {
return
}
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const rowExpandEl = refRowExpandElem.value
if (intoRunScroll) {
return
}
if (!bodyScrollElem) {
return
}
if (!xHandleEl) {
return
}
if (!yHandleEl) {
return
}
if (inBodyScroll) {
if (scrollRenderType !== fixedType) {
return
}
}
let scrollTop = yHandleEl.scrollTop
let scrollLeft = xHandleEl.scrollLeft
if (leftScrollElem && fixedType === 'left') {
scrollTop = leftScrollElem.scrollTop
} else if (rightScrollElem && fixedType === 'right') {
scrollTop = rightScrollElem.scrollTop
} else {
scrollTop = bodyScrollElem.scrollTop
scrollLeft = bodyScrollElem.scrollLeft
}
const isRollX = scrollLeft !== lastScrollLeft
const isRollY = scrollTop !== lastScrollTop
internalData.inBodyScroll = true
internalData.scrollRenderType = fixedType
if (isRollY) {
if (fixedType === 'left') {
setScrollTop(bodyScrollElem, scrollTop)
setScrollTop(rightScrollElem, scrollTop)
} else if (fixedType === 'right') {
setScrollTop(bodyScrollElem, scrollTop)
setScrollTop(leftScrollElem, scrollTop)
} else {
setScrollTop(leftScrollElem, scrollTop)
setScrollTop(rightScrollElem, scrollTop)
}
setScrollTop(yHandleEl, scrollTop)
setScrollTop(rowExpandEl, scrollTop)
syncGanttScrollTop(scrollTop)
if (scrollYLoad) {
$xeTable.triggerScrollYEvent(evnt)
}
}
if (isRollX) {
setScrollLeft(xHandleEl, scrollLeft)
setScrollLeft(headerScrollElem, scrollLeft)
setScrollLeft(footerScrollElem, scrollLeft)
if (scrollXLoad) {
$xeTable.triggerScrollXEvent(evnt)
}
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, scrollTop, scrollLeft, {
type: 'body',
fixed: fixedType
})
},
triggerHeaderScrollEvent (evnt, fixedType) {
const { scrollXLoad } = reactData
const { elemStore, intoRunScroll, inWheelScroll, inVirtualScroll, inBodyScroll, inFooterScroll } = internalData
if (inWheelScroll || inVirtualScroll || inBodyScroll || inFooterScroll) {
return
}
const yHandleEl = refScrollYHandleElem.value
const xHandleEl = refScrollXHandleElem.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
if (intoRunScroll) {
return
}
if (!headerScrollElem) {
return
}
if (!xHandleEl) {
return
}
if (!yHandleEl) {
return
}
const scrollTop = yHandleEl.scrollTop
const scrollLeft = headerScrollElem.scrollLeft
const isRollX = true
const isRollY = false
internalData.inHeaderScroll = true
setScrollLeft(xHandleEl, scrollLeft)
setScrollLeft(footerScrollElem, scrollLeft)
setScrollLeft(bodyScrollElem, scrollLeft)
if (scrollXLoad) {
$xeTable.triggerScrollXEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, scrollTop, scrollLeft, {
type: 'header',
fixed: fixedType
})
},
triggerFooterScrollEvent (evnt, fixedType) {
const { scrollXLoad } = reactData
const { elemStore, intoRunScroll, inWheelScroll, inVirtualScroll, inHeaderScroll, inBodyScroll } = internalData
if (inWheelScroll || inVirtualScroll || inHeaderScroll || inBodyScroll) {
return
}
const yHandleEl = refScrollYHandleElem.value
const xHandleEl = refScrollXHandleElem.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
if (intoRunScroll) {
return
}
if (!footerScrollElem) {
return
}
if (!xHandleEl) {
return
}
if (!yHandleEl) {
return
}
const scrollTop = yHandleEl.scrollTop
const scrollLeft = footerScrollElem.scrollLeft
const isRollX = true
const isRollY = false
internalData.inFooterScroll = true
setScrollLeft(xHandleEl, scrollLeft)
setScrollLeft(headerScrollElem, scrollLeft)
setScrollLeft(bodyScrollElem, scrollLeft)
if (scrollXLoad) {
$xeTable.triggerScrollXEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, scrollTop, scrollLeft, {
type: 'footer',
fixed: fixedType
})
},
triggerBodyWheelEvent (evnt) {
const { target, deltaY, deltaX, shiftKey } = evnt
if (target && /^textarea$/i.test((target as HTMLElement).tagName)) {
return
}
// 如果滚轮未移动或者触摸板未变化位置
if (!deltaY && !deltaX) {
return
}
const { highlightHoverRow } = tableProps
const { scrollXLoad, scrollYLoad, overflowX, overflowY, expandColumn } = reactData
const leftFixedWidth = computeLeftFixedWidth.value
const rightFixedWidth = computeRightFixedWidth.value
const { elemStore, lastScrollTop, lastScrollLeft } = internalData
const rowOpts = computeRowOpts.value
const scrollbarXOpts = computeScrollbarXOpts.value
const scrollbarYOpts = computeScrollbarYOpts.value
const xHandleEl = refScrollXHandleElem.value
const yHandleEl = refScrollYHandleElem.value
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const rowExpandEl = refRowExpandElem.value
if (!bodyScrollElem) {
return
}
const wheelSpeed = getWheelSpeed(internalData.lastSTime)
const deltaTop = shiftKey ? 0 : (deltaY * wheelSpeed)
const deltaLeft = (shiftKey ? (deltaX || deltaY) : deltaX) * wheelSpeed
const currScrollTop = bodyScrollElem.scrollTop
const currScrollLeft = bodyScrollElem.scrollLeft
const scrollTop = Math.max(0, currScrollTop + deltaTop)
const scrollLeft = Math.max(0, currScrollLeft + deltaLeft)
const isRollX = overflowX && scrollLeft !== lastScrollLeft
const isRollY = overflowY && scrollTop !== lastScrollTop
if (isRollX) {
// 如果禁用滚动
if (scrollbarXOpts.visible === 'hidden') {
evnt.preventDefault()
return
}
}
if (isRollY) {
// 如果禁用滚动
if (scrollbarYOpts.visible === 'hidden') {
evnt.preventDefault()
return
}
const isTopWheel = deltaTop < 0
// 如果滚动位置已经是顶部或底部,则不需要触发
if (isTopWheel ? currScrollTop <= 0 : currScrollTop >= bodyScrollElem.scrollHeight - bodyScrollElem.clientHeight) {
return
}
}
// 展开行处理,如果展开行嵌入表格中
if ($xeParentTable) {
if (isRollY) {
if (checkTargetElement(evnt.target, [leftScrollElem, bodyScrollElem, rightScrollElem], evnt.currentTarget)) {
evnt.stopPropagation()
return
}
}
if (isRollX) {
if (checkTargetElement(evnt.target, [headerScrollElem, bodyScrollElem, footerScrollElem], evnt.currentTarget)) {
evnt.stopPropagation()
return
}
}
}
if (!(isRollX || leftFixedWidth || rightFixedWidth || expandColumn)) {
return
}
if (rowOpts.isHover || highlightHoverRow) {
$xeTable.clearHoverRow()
}
// 用于鼠标纵向滚轮处理
if (isRollX) {
evnt.preventDefault()
internalData.inWheelScroll = true
wheelScrollLeftTo(scrollLeft, (offsetLeft: number) => {
internalData.inWheelScroll = true
const currLeftNum = offsetLeft
setScrollLeft(xHandleEl, currLeftNum)
setScrollLeft(bodyScrollElem, currLeftNum)
setScrollLeft(headerScrollElem, currLeftNum)
setScrollLeft(footerScrollElem, currLeftNum)
if (scrollXLoad) {
$xeTable.triggerScrollXEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, bodyScrollElem.scrollTop, currLeftNum, {
type: 'table',
fixed: ''
})
})
}
if (isRollY) {
evnt.preventDefault()
internalData.inWheelScroll = true
wheelScrollTopTo(scrollTop, (offsetTop: number) => {
internalData.inWheelScroll = true
const currTopNum = offsetTop
setScrollTop(yHandleEl, currTopNum)
setScrollTop(bodyScrollElem, currTopNum)
setScrollTop(leftScrollElem, currTopNum)
setScrollTop(rightScrollElem, currTopNum)
setScrollTop(rowExpandEl, currTopNum)
syncGanttScrollTop(currTopNum)
if (scrollYLoad) {
$xeTable.triggerScrollYEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, offsetTop, bodyScrollElem.scrollLeft, {
type: 'table',
fixed: ''
})
})
}
},
triggerVirtualScrollXEvent (evnt) {
const { scrollXLoad } = reactData
const { elemStore, inWheelScroll, lastScrollTop, inHeaderScroll, inBodyScroll, inFooterScroll } = internalData
if (inHeaderScroll || inBodyScroll || inFooterScroll) {
return
}
if (inWheelScroll) {
return
}
const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const footerScrollElem = getRefElem(elemStore['main-footer-scroll'])
const yHandleEl = refScrollYHandleElem.value
const wrapperEl = evnt.currentTarget as HTMLDivElement
const { scrollLeft } = wrapperEl
const yBodyEl = yHandleEl || bodyScrollElem
let scrollTop = 0
if (yBodyEl) {
scrollTop = yBodyEl.scrollTop
}
const isRollX = true
const isRollY = scrollTop !== lastScrollTop
internalData.inVirtualScroll = true
setScrollLeft(bodyScrollElem, scrollLeft)
setScrollLeft(headerScrollElem, scrollLeft)
setScrollLeft(footerScrollElem, scrollLeft)
if (scrollXLoad) {
$xeTable.triggerScrollXEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, scrollTop, scrollLeft, {
type: 'table',
fixed: ''
})
},
triggerVirtualScrollYEvent (evnt) {
const { scrollYLoad } = reactData
const { elemStore, inWheelScroll, lastScrollLeft, inHeaderScroll, inBodyScroll, inFooterScroll } = internalData
if (inHeaderScroll || inBodyScroll || inFooterScroll) {
return
}
if (inWheelScroll) {
return
}
const leftScrollElem = getRefElem(elemStore['left-body-scroll'])
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const rightScrollElem = getRefElem(elemStore['right-body-scroll'])
const rowExpandEl = refRowExpandElem.value
const xHandleEl = refScrollXHandleElem.value
const wrapperEl = evnt.currentTarget as HTMLDivElement
const { scrollTop } = wrapperEl
const xBodyEl = xHandleEl || bodyScrollElem
let scrollLeft = 0
if (xBodyEl) {
scrollLeft = xBodyEl.scrollLeft
}
const isRollX = scrollLeft !== lastScrollLeft
const isRollY = true
internalData.inVirtualScroll = true
setScrollTop(bodyScrollElem, scrollTop)
setScrollTop(leftScrollElem, scrollTop)
setScrollTop(rightScrollElem, scrollTop)
setScrollTop(rowExpandEl, scrollTop)
syncGanttScrollTop(scrollTop)
if (scrollYLoad) {
$xeTable.triggerScrollYEvent(evnt)
}
$xeTable.handleScrollEvent(evnt, isRollY, isRollX, scrollTop, scrollLeft, {
type: 'table',
fixed: ''
})
},
/**
* 对于树形结构中,可以直接滚动到指定深层节点中
* 对于某些特定的场景可能会用到,比如定位到某一节点
* @param {Row} row 行对象
*/
scrollToTreeRow (row) {
const { treeConfig } = props
const { isRowGroupStatus } = reactData
const { tableFullData } = internalData
const rests: Promise<any>[] = []
if (treeConfig || isRowGroupStatus) {
const aggregateOpts = computeAggregateOpts.value
const treeOpts = computeTreeOpts.value
const childrenField = treeOpts.children || treeOpts.childrenField
const matchObj = XEUtils.findTree(tableFullData, item => $xeTable.eqRow(item, row), { children: isRowGroupStatus ? aggregateOpts.mapChildrenField : childrenField })
if (matchObj) {
const nodes = matchObj.nodes
nodes.forEach((row, index) => {
if (index < nodes.length - 1 && !$xeTable.isTreeExpandByRow(row)) {
rests.push($xeTable.setTreeExpand(row, true))
}
})
}
}
return Promise.all(rests).then(() => rowToVisible($xeTable, row))
},
updateScrollYStatus,
// 更新横向 X 可视渲染上下剩余空间大小
updateScrollXSpace () {
const { scrollXLoad, overflowX, scrollXWidth } = reactData
const { visibleColumn, scrollXStore, elemStore, fullColumnIdData } = internalData
const $xeGanttView = internalData.xeGanttView
const mouseOpts = computeMouseOpts.value
const tableBody = refTableBody.value
const tableBodyElem = tableBody ? tableBody.$el as HTMLDivElement : null
if (tableBodyElem) {
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const bodyTableElem = getRefElem(elemStore['main-body-table'])
const headerTableElem = getRefElem(elemStore['main-header-table'])
const footerTableElem = getRefElem(elemStore['main-footer-table'])
let xSpaceLeft = 0
const firstColumn = visibleColumn[scrollXStore.startIndex]
if (firstColumn) {
const colRest = fullColumnIdData[firstColumn.id] || {}
xSpaceLeft = colRest.oLeft
}
let clientWidth = 0
if (bodyScrollElem) {
clientWidth = bodyScrollElem.clientWidth
}
// 虚拟渲染
let isScrollXBig = false
let ySpaceWidth = scrollXWidth
if (scrollXWidth > maxXWidth) {
// 触右
if (bodyScrollElem && bodyTableElem && bodyScrollElem.scrollLeft + clientWidth >= maxXWidth) {
xSpaceLeft = maxXWidth - bodyTableElem.clientWidth
} else {
xSpaceLeft = (maxXWidth - clientWidth) * (xSpaceLeft / (scrollXWidth - clientWidth))
}
ySpaceWidth = maxXWidth
isScrollXBig = true
}
if (!(scrollXLoad && overflowX)) {
xSpaceLeft = 0
}
if (getConfig().scrollMarginStyle) {
// 已废弃方式
if (headerTableElem) {
headerTableElem.style.marginLeft = headerTableElem.getAttribute('xvm') ? `${xSpaceLeft}px` : ''
}
if (bodyTableElem) {
bodyTableElem.style.marginLeft = `${xSpaceLeft}px`
}
if (footerTableElem) {
footerTableElem.style.marginLeft = footerTableElem.getAttribute('xvm') ? `${xSpaceLeft}px` : ''
}
} else {
if (headerTableElem) {
headerTableElem.style.transform = headerTableElem.getAttribute('xvm') ? `translate(${xSpaceLeft}px, 0px)` : ''
}
if (bodyTableElem) {
bodyTableElem.style.transform = `translate(${xSpaceLeft}px, ${reactData.scrollYTop || 0}px)`
}
if (footerTableElem) {
footerTableElem.style.transform = footerTableElem.getAttribute('xvm') ? `translate(${xSpaceLeft}px, 0px)` : ''
}
}
const containerList = ['main']
containerList.forEach(name => {
const layoutList = ['header', 'body', 'footer']
layoutList.forEach(layout => {
const xSpaceElem = getRefElem(elemStore[`${name}-${layout}-xSpace`])
if (xSpaceElem) {
xSpaceElem.style.width = scrollXLoad ? `${ySpaceWidth}px` : ''
}
})
})
reactData.scrollXLeft = xSpaceLeft
reactData.scrollXWidth = ySpaceWidth
reactData.isScrollXBig = isScrollXBig
const scrollXSpaceEl = refScrollXSpaceElem.value
if (scrollXSpaceEl) {
scrollXSpaceEl.style.width = `${ySpaceWidth}px`
}
calcScrollbar()
if (isScrollXBig && mouseOpts.area) {
errLog('vxe.error.notProp', ['mouse-config.area'])
}
if ($xeGanttView && $xeGanttView.handleUpdateSXSpace) {
$xeGanttView.handleUpdateSXSpace()
}
return nextTick().then(() => {
updateStyle()
})
}
},
// 更新纵向 Y 可视渲染上下剩余空间大小
updateScrollYSpace () {
const { isAllOverflow, overflowY, scrollYLoad, scrollYHeight, expandColumn } = reactData
const { scrollYStore, elemStore, isResizeCellHeight, afterFullData, fullAllDataRowIdData, rowExpandedMaps } = internalData
const $xeGanttView = internalData.xeGanttView
const { startIndex } = scrollYStore
const mouseOpts = computeMouseOpts.value
const expandOpts = computeExpandOpts.value
const rowOpts = computeRowOpts.value
const cellOpts = computeCellOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const bodyTableElem = getRefElem(elemStore['main-body-table'])
const leftBodyTableElem = getRefElem(elemStore['left-body-table'])
const rightbodyTableElem = getRefElem(elemStore['right-body-table'])
const containerList = ['main', 'left', 'right']
let ySpaceTop = 0
let sYHeight = scrollYHeight
let isScrollYBig = false
if (scrollYLoad) {
const isCustomCellHeight = isResizeCellHeight || cellOpts.height || rowOpts.height
if (!isCustomCellHeight && !expandColumn && isAllOverflow) {
sYHeight = afterFullData.length * defaultRowHeight
if (sYHeight > maxYHeight) {
isScrollYBig = true
}
ySpaceTop = Math.max(0, startIndex * defaultRowHeight)
} else {
const firstRow = afterFullData[startIndex]
if (firstRow) {
let rowid = getRowid($xeTable, firstRow)
let rowRest = fullAllDataRowIdData[rowid] || {}
ySpaceTop = (rowRest.oTop || 0)
const lastRow = afterFullData[afterFullData.length - 1]
rowid = getRowid($xeTable, lastRow)
rowRest = fullAllDataRowIdData[rowid] || {}
if (rowRest) {
const rHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
// 如果为空时还没计算完数据,保持原高度不变
if (rHeight) {
sYHeight = (rowRest.oTop || 0) + rHeight
// 是否展开行
if (expandColumn && rowExpandedMaps[rowid]) {
sYHeight += rowRest.expandHeight || expandOpts.height || 0
}
}
}
if (sYHeight > maxYHeight) {
isScrollYBig = true
}
} else {
sYHeight = bodyTableElem ? bodyTableElem.clientHeight : 0
}
}
} else {
if (bodyTableElem) {
sYHeight = bodyTableElem.clientHeight
}
}
let clientHeight = 0
if (bodyScrollElem) {
clientHeight = bodyScrollElem.clientHeight
}
// 虚拟渲染
let ySpaceHeight = sYHeight
let scrollYTop = ySpaceTop
if (isScrollYBig) {
// 触底
if (bodyScrollElem && bodyTableElem && bodyScrollElem.scrollTop + clientHeight >= maxYHeight) {
scrollYTop = maxYHeight - bodyTableElem.clientHeight
} else {
scrollYTop = (maxYHeight - clientHeight) * (ySpaceTop / (scrollYHeight - clientHeight))
}
ySpaceHeight = maxYHeight
}
if (!(scrollYLoad && overflowY)) {
scrollYTop = 0
}
if (getConfig().scrollMarginStyle) {
if (leftBodyTableElem) {
leftBodyTableElem.style.marginTop = `${scrollYTop}px`
}
if (bodyTableElem) {
bodyTableElem.style.marginTop = `${scrollYTop}px`
}
if (rightbodyTableElem) {
rightbodyTableElem.style.marginTop = `${scrollYTop}px`
}
} else {
if (leftBodyTableElem) {
leftBodyTableElem.style.transform = `translate(0px, ${scrollYTop}px)`
}
if (bodyTableElem) {
bodyTableElem.style.transform = `translate(${reactData.scrollXLeft || 0}px, ${scrollYTop}px)`
}
if (rightbodyTableElem) {
rightbodyTableElem.style.transform = `translate(0px, ${scrollYTop}px)`
}
}
containerList.forEach(name => {
const layoutList = ['header', 'body', 'footer']
layoutList.forEach(layout => {
const ySpaceElem = getRefElem(elemStore[`${name}-${layout}-ySpace`])
if (ySpaceElem) {
ySpaceElem.style.height = ySpaceHeight ? `${ySpaceHeight}px` : ''
}
})
})
const scrollYSpaceEl = refScrollYSpaceElem.value
if (scrollYSpaceEl) {
scrollYSpaceEl.style.height = ySpaceHeight ? `${ySpaceHeight}px` : ''
}
const rowExpandYSpaceEl = refRowExpandYSpaceElem.value
if (rowExpandYSpaceEl) {
rowExpandYSpaceEl.style.height = ySpaceHeight ? `${ySpaceHeight}px` : ''
}
reactData.scrollYTop = scrollYTop
reactData.scrollYHeight = isScrollYBig ? sYHeight : ySpaceHeight
reactData.isScrollYBig = isScrollYBig
calcScrollbar()
if (isScrollYBig && mouseOpts.area) {
errLog('vxe.error.notProp', ['mouse-config.area'])
}
if ($xeGanttView && $xeGanttView.handleUpdateSYSpace) {
$xeGanttView.handleUpdateSYSpace()
}
return nextTick().then(() => {
return updateStyle()
})
},
updateScrollXData () {
const { isAllOverflow } = reactData
handleTableColumn()
$xeTable.updateScrollXSpace()
return nextTick().then(() => {
handleTableColumn()
$xeTable.updateScrollXSpace()
if (!isAllOverflow) {
$xeTable.updateScrollYSpace()
}
})
},
updateScrollYData () {
$xeTable.handleTableData()
$xeTable.updateScrollYSpace()
return nextTick().then(() => {
$xeTable.handleTableData()
$xeTable.updateScrollYSpace()
})
},
/**
* 处理固定列的显示状态
*/
checkScrolling () {
const { elemStore } = internalData
const bodyScrollElem = getRefElem(elemStore['main-body-scroll'])
const leftContainerElem = refLeftContainer.value
const rightContainerElem = refRightContainer.value
const xHandleEl = refScrollXHandleElem.value
const bodtTargetEl = xHandleEl || bodyScrollElem
if (bodtTargetEl) {
if (leftContainerElem) {
if (bodtTargetEl.scrollLeft > 0) {
addClass(leftContainerElem, 'scrolling--middle')
} else {
removeClass(leftContainerElem, 'scrolling--middle')
}
}
if (rightContainerElem) {
if (bodtTargetEl.clientWidth < bodtTargetEl.scrollWidth - Math.ceil(bodtTargetEl.scrollLeft)) {
addClass(rightContainerElem, 'scrolling--middle')
} else {
removeClass(rightContainerElem, 'scrolling--middle')
}
}
}
},
handleUpdateAggData () {
return loadTableData(internalData.tableSynchData, false, true)
},
handlePushStack () {
const keyboardOpts = computeKeyboardOpts.value
if (!keyboardOpts.isUndoRedo) {
return
}
const { stackHistoryStore } = internalData
const stackObj = getSnapshotStackData()
stackHistoryStore.undoStacks.push(stackObj)
stackHistoryStore.redoStacks = []
truncateStackHistory()
},
handleUndoStackEvent (evnt) {
const { stackHistoryStore } = internalData
const { undoStacks, redoStacks } = stackHistoryStore
if (undoStacks.length) {
const stackObj = getSnapshotStackData()
redoStacks.push(stackObj)
handleUpdateSnapshotStackData(undoStacks.pop())
if (evnt) {
dispatchEvent('undo', {}, evnt)
}
return nextTick(() => {
return { status: true }
})
}
return Promise.resolve({ status: false })
},
handleRedoStackEvent (evnt) {
const { stackHistoryStore } = internalData
const { undoStacks, redoStacks } = stackHistoryStore
if (redoStacks.length) {
const stackObj = getSnapshotStackData()
undoStacks.push(stackObj)
handleUpdateSnapshotStackData(redoStacks.pop())
if (evnt) {
dispatchEvent('redo', {}, evnt)
}
return nextTick(() => {
return { status: true }
})
}
return Promise.resolve({ status: false })
},
handleClearStack () {
const { stackHistoryStore } = internalData
stackHistoryStore.undoStacks = []
stackHistoryStore.redoStacks = []
return nextTick()
},
updateZindex () {
if (props.zIndex) {
internalData.tZindex = props.zIndex
} else if (internalData.tZindex < getLastZIndex()) {
internalData.tZindex = nextZIndex()
}
},
handleCheckedCheckboxRow,
/**
* 行 hover 事件
*/
triggerHoverEvent (evnt, { row }) {
$xeTable.setHoverRow(row)
},
setHoverRow (row) {
const $xeGanttView = internalData.xeGanttView
const rowid = getRowid($xeTable, row)
const el = refElem.value
$xeTable.clearHoverRow()
if (el) {
XEUtils.arrayEach(el.querySelectorAll(`.vxe-body--row[rowid="${rowid}"]`), elem => addClass(elem, 'row--hover'))
}
internalData.hoverRow = row
if ($xeGanttView && $xeGanttView.handleUpdateHoverRow) {
$xeGanttView.handleUpdateHoverRow(row)
}
},
clearHoverRow () {
const $xeGanttView = internalData.xeGanttView
const el = refElem.value
if (el) {
XEUtils.arrayEach(el.querySelectorAll('.vxe-body--row.row--hover'), elem => removeClass(elem, 'row--hover'))
}
internalData.hoverRow = null
if ($xeGanttView && $xeGanttView.handleUpdateHoverRow) {
$xeGanttView.handleUpdateHoverRow()
}
},
/**
* 已废弃,被 getCellElement 替换
* @deprecated
*/
getCell (row, column) {
return $xeTable.getCellElement(row, column)
},
findRowIndexOf (list, row) {
return row ? XEUtils.findIndexOf(list, item => $xeTable.eqRow(item, row)) : -1
},
eqRow (row1, row2) {
if (row1 && row2) {
if (row1 === row2) {
return true
}
return getRowid($xeTable, row1) === getRowid($xeTable, row2)
}
return false
},
handleConnectGanttView ($ganttView) {
if ($ganttView && $ganttView.connectUpdate) {
$ganttView.connectUpdate({ $table: $xeTable })
internalData.xeGanttView = $ganttView as any
}
return nextTick()
}
}
// 检测对应模块是否安装
// 'openExport,openPrint,exportData,openImport,importData,saveFile,readFile,importByFile,print'.split(',').forEach(name => {
// ($xeTable as any)[name] = function () {
// errLog('vxe.error.reqModule', ['Export'])
// }
// })
// 'clearValidate,fullValidate,validate'.split(',').forEach(name => {
// ($xeTable as any)[name] = function () {
// errLog('vxe.error.reqModule', ['Validator'])
// }
// })
Object.assign($xeTable, tableMethods, tablePrivateMethods)
/**
* 渲染浮固定列
* 分别渲染左边固定列和右边固定列
* 如果宽度足够情况下,则不需要渲染固定列
* @param {String} fixedType 固定列类型
*/
const renderViewFixed = (fixedType: 'left' | 'right') => {
const { showHeader, showFooter } = props
const { tableData, tableColumn, tableGroupColumn, columnStore, footerTableData } = reactData
const scrollbarOpts = computeScrollbarOpts.value
const scrollbarXOpts = computeScrollbarXOpts.value
const scrollbarYOpts = computeScrollbarYOpts.value
const { overscrollBehavior: overscrollXBehavior } = scrollbarXOpts
const { overscrollBehavior: overscrollYBehavior } = scrollbarYOpts
const isFixedLeft = fixedType === 'left'
const fixedColumn = isFixedLeft ? columnStore.leftList : columnStore.rightList
const osXBehavior = XEUtils.eqNull(overscrollXBehavior) ? scrollbarOpts.overscrollBehavior : overscrollXBehavior
const osYBehavior = XEUtils.eqNull(overscrollYBehavior) ? scrollbarOpts.overscrollBehavior : overscrollYBehavior
return h('div', {
ref: isFixedLeft ? refLeftContainer : refRightContainer,
class: [`vxe-table--fixed-${fixedType}-wrapper`, `sx--${scrollbarXOpts.visible}`, `sy--${scrollbarYOpts.visible}`, {
[`x-ob--${osXBehavior}`]: osXBehavior,
[`y-ob--${osYBehavior}`]: osYBehavior
}]
}, [
showHeader
? h(TableHeaderComponent, {
ref: isFixedLeft ? refTableLeftHeader : refTableRightHeader,
fixedType,
tableData,
tableColumn,
tableGroupColumn,
fixedColumn
})
: renderEmptyElement($xeTable),
h(TableBodyComponent, {
ref: isFixedLeft ? refTableLeftBody : refTableRightBody,
fixedType,
tableData,
tableColumn,
fixedColumn
}),
showFooter
? h(TableFooterComponent, {
ref: isFixedLeft ? refTableLeftFooter : refTableRightFooter,
footerTableData,
tableColumn,
fixedColumn,
fixedType
})
: renderEmptyElement($xeTable)
])
}
const renderEmptyBody = () => {
const emptyOpts = computeEmptyOpts.value
const emptySlot = slots.empty
const emptyParams = { $table: $xeTable, $grid: $xeGrid, gantt: $xeGantt }
if (emptySlot) {
return emptySlot(emptyParams)
} else {
const compConf = emptyOpts.name ? renderer.get(emptyOpts.name) : null
const rtEmptyView = compConf ? (compConf.renderTableEmpty || compConf.renderTableEmptyView || compConf.renderEmpty) : null
if (rtEmptyView) {
return getSlotVNs(rtEmptyView(emptyOpts, emptyParams))
}
}
return getFuncText(props.emptyText) || getI18n('vxe.table.emptyText')
}
const renderDragTipContents = () => {
const { dragConfig } = props
const { dragRow, dragCol, dragTipText } = reactData
const columnDragOpts = computeColumnDragOpts.value
const rowDragOpts = computeRowDragOpts.value
const rowDragSlots = rowDragOpts.slots || {}
const rTipSlot = rowDragSlots.tip || (dragConfig && dragConfig.slots ? dragConfig.slots.rowTip : null)
const columnDragSlots = columnDragOpts.slots || {}
const cTipSlot = columnDragSlots.tip
const dRow = dragRow || (rowDragOpts.isCrossTableDrag ? crossTableDragRowInfo.row : null)
if (dRow && rTipSlot) {
return callSlot(rTipSlot, { row: dRow })
}
if (dragCol && cTipSlot) {
return callSlot(cTipSlot, { column: dragCol })
}
return [h('span', dragTipText)]
}
const renderDragTip = () => {
const { dragRow, dragCol } = reactData
const rowOpts = computeRowOpts.value
const columnOpts = computeColumnOpts.value
const rowDragOpts = computeRowDragOpts.value
const columnDragOpts = computeColumnDragOpts.value
const dRow = dragRow || (rowDragOpts.isCrossTableDrag ? crossTableDragRowInfo.row : null)
if (rowOpts.drag || columnOpts.drag) {
return h('div', {
class: 'vxe-table--drag-wrapper'
}, [
h('div', {
ref: refDragRowLineElem,
class: ['vxe-table--drag-row-line', {
'is--guides': rowDragOpts.showGuidesStatus
}]
}),
h('div', {
ref: refDragColLineElem,
class: ['vxe-table--drag-col-line', {
'is--guides': columnDragOpts.showGuidesStatus
}]
}),
(dRow && rowDragOpts.showDragTip) || (dragCol && columnDragOpts.showDragTip)
? h('div', {
ref: refDragTipElem,
class: 'vxe-table--drag-sort-tip'
}, [
h('div', {
class: 'vxe-table--drag-sort-tip-wrapper'
}, [
h('div', {
class: 'vxe-table--drag-sort-tip-status'
}, [
h('span', {
class: ['vxe-table--drag-sort-tip-normal-status', dRow ? getIcon().TABLE_DRAG_STATUS_ROW : getIcon().TABLE_DRAG_STATUS_COLUMN]
}),
h('span', {
class: ['vxe-table--drag-sort-tip-sub-status', getIcon().TABLE_DRAG_STATUS_SUB_ROW]
}),
h('span', {
class: ['vxe-table--drag-sort-tip-disabled-status', getIcon().TABLE_DRAG_DISABLED]
})
]),
h('div', {
class: 'vxe-table--drag-sort-tip-content'
}, renderDragTipContents())
])
])
: renderEmptyElement($xeTable)
])
}
return renderEmptyElement($xeTable)
}
const renderRowExpandedVNs = () => {
const { treeConfig } = props
const { expandColumn, isRowGroupStatus } = reactData
const tableRowExpandedList = computeTableRowExpandedList.value
const expandOpts = computeExpandOpts.value
const { mode } = expandOpts
if (mode !== 'fixed') {
return renderEmptyElement($xeTable)
}
const expandVNs = [
h('div', {
key: 'repY',
ref: refRowExpandYSpaceElem
})
]
if (expandColumn) {
const { handleGetRowId } = createHandleGetRowId($xeTable)
tableRowExpandedList.forEach((row) => {
const expandOpts = computeExpandOpts.value
const { height: expandHeight, padding, indent } = expandOpts
const { fullAllDataRowIdData, fullColumnIdData } = internalData
const treeOpts = computeTreeOpts.value
const { transform, seqMode } = treeOpts
const cellStyle: Record<string, string> = {}
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid]
const colid = expandColumn.id
const colRest = fullColumnIdData[colid] || {}
let rowLevel = 0
let seq: string | number = -1
let _rowIndex = -1
let rowIndex = -1
let $rowIndex = -1
if (rowRest) {
rowIndex = rowRest.index
$rowIndex = rowRest.$index
_rowIndex = rowRest._index
rowLevel = rowRest.level
seq = rowRest.seq
if (isRowGroupStatus || (treeConfig && transform && seqMode === 'increasing')) {
seq = rowRest._index + 1
} else if ((treeConfig && seqMode === 'fixed')) {
seq = rowRest._tIndex + 1
}
}
if (expandHeight) {
cellStyle.height = `${expandHeight}px`
}
if (isRowGroupStatus || treeConfig) {
cellStyle.paddingLeft = `${(rowLevel * (XEUtils.isNumber(indent) ? indent : treeOpts.indent)) + 30}px`
}
let columnIndex = -1
let $columnIndex = -1
let _columnIndex = -1
if (colRest) {
columnIndex = colRest.index
$columnIndex = colRest.$index
_columnIndex = colRest._index
}
const expandParams: VxeTableDefines.CellRenderDataParams = {
$table: $xeTable,
$grid: $xeGrid,
$gantt: $xeGantt,
seq,
column: expandColumn,
columnIndex,
$columnIndex,
_columnIndex,
fixed: '',
source: 'table',
type: 'body',
level: rowLevel,
rowid,
row,
rowIndex,
$rowIndex,
_rowIndex,
isHidden: false,
isEdit: false,
visibleData: [],
data: [],
items: []
}
expandVNs.push(
h('div', {
key: rowid,
class: ['vxe-body--row-expanded-cell', {
'is--padding': padding,
'is--ellipsis': expandHeight
}],
rowid,
style: cellStyle
}, [
h('div', {
class: 'vxe-body--row-expanded-content'
}, expandColumn.renderData(expandParams))
])
)
})
}
return h('div', {
ref: refRowExpandElem,
class: 'vxe-table--row-expanded-wrapper'
}, expandVNs)
}
const renderScrollX = () => {
return h('div', {
key: 'vsx',
ref: refScrollXVirtualElem,
class: 'vxe-table--scroll-x-virtual'
}, [
h('div', {
ref: refScrollXLeftCornerElem,
class: 'vxe-table--scroll-x-left-corner'
}),
h('div', {
ref: refScrollXWrapperElem,
class: 'vxe-table--scroll-x-wrapper'
}, [
h('div', {
ref: refScrollXHandleElem,
class: 'vxe-table--scroll-x-handle',
onScroll: $xeTable.triggerVirtualScrollXEvent
}, [
h('div', {
ref: refScrollXSpaceElem,
class: 'vxe-table--scroll-x-space'
})
]),
h('div', {
class: 'vxe-table--scroll-x-handle-appearance'
})
]),
h('div', {
ref: refScrollXRightCornerElem,
class: 'vxe-table--scroll-x-right-corner'
})
])
}
const renderScrollY = () => {
return h('div', {
ref: refScrollYVirtualElem,
class: 'vxe-table--scroll-y-virtual'
}, [
h('div', {
ref: refScrollYTopCornerElem,
class: 'vxe-table--scroll-y-top-corner'
}),
h('div', {
ref: refScrollYWrapperElem,
class: 'vxe-table--scroll-y-wrapper'
}, [
h('div', {
ref: refScrollYHandleElem,
class: 'vxe-table--scroll-y-handle',
onScroll: $xeTable.triggerVirtualScrollYEvent
}, [
h('div', {
ref: refScrollYSpaceElem,
class: 'vxe-table--scroll-y-space'
})
]),
h('div', {
class: 'vxe-table--scroll-y-handle-appearance'
})
]),
h('div', {
ref: refScrollYBottomCornerElem,
class: 'vxe-table--scroll-y-bottom-corner'
})
])
}
const renderViewport = () => {
const { showHeader, showFooter } = props
const { overflowX, tableData, tableColumn, tableGroupColumn, footerTableData, columnStore } = reactData
const scrollbarOpts = computeScrollbarOpts.value
const scrollbarXOpts = computeScrollbarXOpts.value
const scrollbarYOpts = computeScrollbarYOpts.value
const { overscrollBehavior: overscrollXBehavior } = scrollbarXOpts
const { overscrollBehavior: overscrollYBehavior } = scrollbarYOpts
const { leftList, rightList } = columnStore
const osXBehavior = XEUtils.eqNull(overscrollXBehavior) ? scrollbarOpts.overscrollBehavior : overscrollXBehavior
const osYBehavior = XEUtils.eqNull(overscrollYBehavior) ? scrollbarOpts.overscrollBehavior : overscrollYBehavior
return h('div', {
ref: refTableViewportElem,
class: ['vxe-table--viewport-wrapper', {
[`x-ob--${osXBehavior}`]: osXBehavior,
[`y-ob--${osYBehavior}`]: osYBehavior
}]
}, [
h('div', {
class: ['vxe-table--main-wrapper', `sx--${scrollbarXOpts.visible}`, `sy--${scrollbarYOpts.visible}`]
}, [
/**
* 表头
*/
showHeader
? h(TableHeaderComponent, {
ref: refTableHeader,
tableData,
tableColumn,
tableGroupColumn
})
: renderEmptyElement($xeTable),
/**
* 表体
*/
h(TableBodyComponent, {
ref: refTableBody,
tableData,
tableColumn
}),
/**
* 表尾
*/
showFooter
? h(TableFooterComponent, {
ref: refTableFooter,
footerTableData,
tableColumn
})
: renderEmptyElement($xeTable)
]),
h('div', {
class: 'vxe-table--fixed-wrapper'
}, [
leftList && leftList.length && overflowX ? renderViewFixed('left') : renderEmptyElement($xeTable),
rightList && rightList.length && overflowX ? renderViewFixed('right') : renderEmptyElement($xeTable)
]),
renderRowExpandedVNs()
])
}
const renderBody = () => {
const scrollbarYToLeft = computeScrollbarYToLeft.value
return h('div', {
class: 'vxe-table--layout-wrapper'
}, scrollbarYToLeft
? [
renderScrollY(),
renderViewport()
]
: [
renderViewport(),
renderScrollY()
])
}
const renderVN = () => {
const { loading, stripe, showHeader, height, treeConfig, mouseConfig, showFooter, highlightCell, highlightHoverRow, highlightHoverColumn, editConfig, editRules } = props
const { isGroup, overflowX, overflowY, scrollXLoad, scrollYLoad, tableData, initStore, isRowGroupStatus, columnStore, filterStore, customStore, tooltipStore } = reactData
const { teleportToWrapperElem, popupToWrapperElem, customPopupToElem } = internalData
const { leftList, rightList } = columnStore
const loadingSlot = slots.loading
const tipSlots = {
header: slots.headerTooltip || slots['header-tooltip'],
body: slots.tooltip,
footer: slots.footerTooltip || slots['footer-tooltip']
}
const currTooltipSlot = tooltipStore.visible && tooltipStore.type ? tipSlots[tooltipStore.type] : null
const tableStyle = computeTableStyle.value
const rowDragOpts = computeRowDragOpts.value
const tableTipConfig = computeTableTipConfig.value
const validTipConfig = computeValidTipConfig.value
const validOpts = computeValidOpts.value
const checkboxOpts = computeCheckboxOpts.value
const treeOpts = computeTreeOpts.value
const rowOpts = computeRowOpts.value
const columnOpts = computeColumnOpts.value
const vSize = computeSize.value
const tableBorder = computeTableBorder.value
const mouseOpts = computeMouseOpts.value
const areaOpts = computeAreaOpts.value
const loadingOpts = computeLoadingOpts.value
const isContentMenu = computeIsContentMenu.value
const currLoading = reactData.isColLoading || reactData.isRowLoading || loading
const resizableOpts = computeResizableOpts.value
const isArea = mouseConfig && mouseOpts.area
const columnDragOpts = computeColumnDragOpts.value
const scrollbarXToTop = computeScrollbarXToTop.value
const scrollbarYToLeft = computeScrollbarYToLeft.value
const { isCrossTableDrag } = rowDragOpts
const tbOns: {
onContextmenu: (...args: any[]) => void
onKeydown: (...args: any[]) => void
onDragover?: (...args: any[]) => void
} = {
onKeydown: keydownEvent,
onContextmenu: contextMenuEvent
}
if (isCrossTableDrag && !tableData.length) {
tbOns.onDragover = $xeTable.handleCrossTableRowDragoverEmptyEvent
}
return h('div', {
ref: refElem,
class: ['vxe-table', 'vxe-table--render-default', `tid_${xID}`, `border--${tableBorder}`, `sx-pos--${scrollbarXToTop ? 'top' : 'bottom'}`, `sy-pos--${scrollbarYToLeft ? 'left' : 'right'}`, {
[`size--${vSize}`]: vSize,
[`valid-msg--${validOpts.msgMode}`]: !!editRules,
'vxe-editable': !!editConfig,
'old-cell-valid': editRules && getConfig().cellVaildMode === 'obsolete',
'cell--highlight': highlightCell,
'cell--selected': mouseConfig && mouseOpts.selected,
'cell--area': isArea,
'header-cell--area': isArea && areaOpts.selectCellByHeader,
'body-cell--area': isArea && areaOpts.selectCellByBody,
'row--highlight': rowOpts.isHover || highlightHoverRow,
'column--highlight': columnOpts.isHover || highlightHoverColumn,
'checkbox--range': checkboxOpts.range,
'col--drag-cell': columnOpts.drag && columnDragOpts.trigger === 'cell',
'is--header': showHeader,
'not--header': !showHeader,
'is--footer': showFooter,
'not--footer': !showFooter,
'is--group': isGroup,
'is-row-group': isRowGroupStatus,
'is--tree-line': treeConfig && (treeOpts.showLine || treeOpts.line),
'is--fixed-left': leftList.length,
'is--fixed-right': rightList.length,
'is--animat': !!props.animat,
'is--round': props.round,
'is--stripe': !treeConfig && stripe,
'is--loading': currLoading,
'is--empty': !currLoading && !tableData.length,
'is--scroll-y': overflowY,
'not--scroll-y': !overflowY,
'is--scroll-x': overflowX,
'not--scroll-x': !overflowX,
'is--virtual-x': scrollXLoad,
'is--virtual-y': scrollYLoad
}],
style: tableStyle,
spellcheck: false,
...tbOns
}, [
/**
* 隐藏列
*/
h('div', {
class: 'vxe-table-slots'
}, slots.default ? slots.default({}) : []),
h('div', {
ref: refVarElem,
class: 'vxe-table-vars'
}, [
h('div', {
class: 'vxe-table-var-default'
}),
h('div', {
class: 'vxe-table-var-medium'
}),
h('div', {
class: 'vxe-table-var-small'
}),
h('div', {
class: 'vxe-table-var-mini'
})
]),
h('div', {
key: 'tw',
class: 'vxe-table--render-wrapper'
}, scrollbarXToTop
? [
renderScrollX(),
renderBody()
]
: [
renderBody(),
renderScrollX()
]),
/**
* 空数据
*/
h('div', {
key: 'tn',
ref: refEmptyPlaceholder,
class: 'vxe-table--empty-place-wrapper',
xid: xID
}, [
h('div', {
class: 'vxe-table--empty-placeholder'
}, [
h('div', {
class: 'vxe-table--empty-content'
}, renderEmptyBody())
])
]),
/**
* 边框线
*/
h('div', {
key: 'tl',
class: 'vxe-table--border-line'
}),
/**
* 列宽线
*/
h('div', {
key: 'tcl',
ref: refColResizeBar,
class: 'vxe-table--resizable-col-bar'
}, resizableOpts.showDragTip
? [
h('div', {
class: 'vxe-table--resizable-number-tip'
})
]
: []),
h('div', {
key: 'ttw'
}, [
h(Teleport, {
to: teleportToWrapperElem,
disabled: !($xeGantt && teleportToWrapperElem)
}, [
h('div', {
ref: refTeleportWrapper
}, [
/**
* 行高线
*/
h('div', {
key: 'trl',
ref: refRowResizeBar,
class: 'vxe-table--resizable-row-bar'
}, resizableOpts.showDragTip
? [
h('div', {
class: 'vxe-table--resizable-number-tip'
})
]
: []),
/**
* 加载中
*/
VxeUILoadingComponent
? h(VxeUILoadingComponent, {
key: 'lg',
class: 'vxe-table--loading',
modelValue: currLoading,
icon: loadingOpts.icon,
text: loadingOpts.text
}, loadingSlot
? {
default: () => callSlot(loadingSlot, { $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, loading: currLoading })
}
: {})
: loadingSlot
? h('div', {
class: ['vxe-loading--custom-wrapper', {
'is--visible': currLoading
}]
}, callSlot(loadingSlot, { $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, loading: currLoading }))
: renderEmptyElement($xeTable),
/**
* 拖拽排序提示
*/
renderDragTip()
])
])
]),
h('div', {
key: 'fpw',
ref: refCustomContainerElem
}, [
h(Teleport, {
to: customPopupToElem,
disabled: !!reactData.ctPopupFlag && !customPopupToElem
}, [
/**
* 自定义列
*/
initStore.custom
? h(TableCustomPanelComponent, {
key: 'cs',
ref: refTableCustom,
customStore
})
: renderEmptyElement($xeTable)
])
]),
h('div', {
key: 'tpw'
}, [
h(Teleport, {
to: popupToWrapperElem,
disabled: !($xeGGWrapper && popupToWrapperElem)
}, [
h('div', {
ref: refPopupWrapperElem
}, [
/**
* 筛选
*/
initStore.filter
? h(TableFilterPanelComponent, {
key: 'tf',
ref: refTableFilter,
filterStore
})
: renderEmptyElement($xeTable),
/**
* 快捷菜单
*/
isContentMenu
? h(TableMenuPanelComponent, {
key: 'tm',
ref: refTableMenu
})
: renderEmptyElement($xeTable)
])
])
]),
/**
* 导入
*/
initStore.import && props.importConfig
? h(TableImportPanelComponent, {
key: 'it',
defaultOptions: reactData.importParams,
storeData: reactData.importStore
})
: renderEmptyElement($xeTable),
/**
* 导出
*/
initStore.export && (props.exportConfig || props.printConfig)
? h(TableExportPanelComponent, {
key: 'et',
defaultOptions: reactData.exportParams,
storeData: reactData.exportStore
})
: renderEmptyElement($xeTable),
/**
* 提示相关
*/
VxeUITooltipComponent
? h('div', {}, [
/**
* 通用提示
*/
h(VxeUITooltipComponent, {
key: 'ctp',
ref: refCommTooltip,
isArrow: false,
enterable: false
}),
/**
* 工具提示
*/
h(VxeUITooltipComponent, {
key: 'btp',
ref: refTooltip,
theme: tableTipConfig.theme,
enterable: tableTipConfig.enterable,
enterDelay: tableTipConfig.enterDelay,
leaveDelay: tableTipConfig.leaveDelay,
useHtml: XEUtils.isBoolean(tableTipConfig.useHtml) ? tableTipConfig.useHtml : undefined,
// 已废弃
useHTML: XEUtils.isBoolean(tableTipConfig.useHTML) ? tableTipConfig.useHTML : undefined,
width: tableTipConfig.width,
height: tableTipConfig.height,
minWidth: tableTipConfig.minWidth,
minHeight: tableTipConfig.minHeight,
maxWidth: tableTipConfig.maxWidth,
maxHeight: tableTipConfig.maxHeight,
placement: tableTipConfig.placement,
defaultPlacement: tableTipConfig.defaultPlacement,
popupClassName: tableTipConfig.popupClassName
}, currTooltipSlot
? {
content: () => {
const { type, row, column, content: tooltipContent } = tooltipStore
if (currTooltipSlot) {
if (column && type === 'header') {
return h('div', {
key: type
}, currTooltipSlot({ column, tooltipContent, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }))
}
if (row && column && type === 'body') {
return h('div', {
key: type
}, currTooltipSlot({ row, column, tooltipContent, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }))
}
if (row && column && type === 'footer') {
return h('div', {
key: type
}, currTooltipSlot({ row, column, tooltipContent, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt }))
}
}
return renderEmptyElement($xeTable)
}
}
: {}),
/**
* 校验提示
*/
props.editRules && validOpts.showErrorMessage && (validOpts.message === 'default' ? !height : validOpts.message === 'tooltip')
? h(VxeUITooltipComponent, {
key: 'vtp',
ref: refValidTooltip,
class: [{
'old-cell-valid': editRules && getConfig().cellVaildMode === 'obsolete'
}, 'vxe-table--valid-error'],
theme: validTipConfig.theme,
enterable: validTipConfig.enterable,
enterDelay: validTipConfig.enterDelay,
leaveDelay: validTipConfig.leaveDelay
})
: renderEmptyElement($xeTable)
])
: renderEmptyElement($xeTable)
])
}
const dataFlag = ref(0)
watch(() => props.data ? props.data.length : -1, () => {
dataFlag.value++
})
watch(() => props.data, () => {
dataFlag.value++
})
watch(dataFlag, () => {
const { initStatus } = internalData
const value = props.data || []
if (value && value.length >= 20000) {
warnLog('vxe.error.errLargeData', ['loadData(data), reloadData(data)'])
}
loadTableData(value, false, true).then(() => {
const { scrollXLoad, scrollYLoad, expandColumn } = reactData
const expandOpts = computeExpandOpts.value
internalData.inited = true
internalData.initStatus = true
if (!initStatus) {
handleLoadDefaults()
}
// const checkboxOpts = computeCheckboxOpts.value
// const checkboxColumn = internalData.tableFullColumn.find(column => column.type === 'checkbox')
// if (checkboxColumn && internalData.tableFullData.length > 300 && !checkboxOpts.checkField) {
// warnLog('vxe.error.checkProp', ['checkbox-config.checkField'])
// }
if ((scrollXLoad || scrollYLoad) && (expandColumn && expandOpts.mode !== 'fixed')) {
warnLog('vxe.error.scrollErrProp', ['column.type=expand'])
}
return $xeTable.recalculate()
})
})
const staticColumnFlag = ref(0)
watch(() => reactData.staticColumns.length, () => {
staticColumnFlag.value++
})
watch(() => reactData.staticColumns, () => {
staticColumnFlag.value++
})
watch(staticColumnFlag, () => {
nextTick(() => handleInitColumn(XEUtils.clone(reactData.staticColumns)))
})
const tableColumnFlag = ref(0)
watch(() => reactData.tableColumn.length, () => {
tableColumnFlag.value++
})
watch(() => reactData.tableColumn, () => {
tableColumnFlag.value++
})
watch(tableColumnFlag, () => {
$xeTable.analyColumnWidth()
})
watch(() => reactData.upDataFlag, () => {
nextTick(() => {
$xeTable.updateData()
})
})
watch(() => reactData.reColumnFlag, () => {
nextTick(() => {
$xeTable.refreshColumn()
})
})
const reLayoutFlag = ref(0)
watch(computeSize, () => {
reLayoutFlag.value++
})
watch(() => props.showHeader, () => {
reLayoutFlag.value++
})
watch(() => props.showFooter, () => {
reLayoutFlag.value++
})
watch(() => props.showHeaderOverflow, () => {
reLayoutFlag.value++
})
watch(() => props.showOverflow, () => {
updateColumnAllOverflow()
reLayoutFlag.value++
})
watch(() => props.showFooterOverflow, () => {
reLayoutFlag.value++
})
watch(() => reactData.overflowX, () => {
reLayoutFlag.value++
})
watch(() => reactData.overflowY, () => {
reLayoutFlag.value++
})
watch(() => props.height, () => {
reLayoutFlag.value++
})
watch(() => props.maxHeight, () => {
reLayoutFlag.value++
})
watch(computeScrollbarXToTop, () => {
reLayoutFlag.value++
})
watch(computeScrollbarYToLeft, () => {
reLayoutFlag.value++
})
watch(() => VxeUI.getLanguage(), () => {
reLayoutFlag.value++
})
watch(() => {
const scrollbarXOpts = computeScrollbarXOpts.value
return scrollbarXOpts.visible
}, () => {
reLayoutFlag.value++
})
watch(() => {
const scrollbarYOpts = computeScrollbarYOpts.value
return scrollbarYOpts.visible
}, () => {
reLayoutFlag.value++
})
watch(() => {
const cellOpts = computeCellOpts.value
return cellOpts.height
}, () => {
reLayoutFlag.value++
})
watch(reLayoutFlag, () => {
$xeTable.recalculate(true)
})
const footFlag = ref(0)
watch(() => props.footerData ? props.footerData.length : -1, () => {
footFlag.value++
})
watch(() => props.footerData, () => {
footFlag.value++
})
watch(footFlag, () => {
internalData.footerFullDataRowData = {}
$xeTable.updateFooter()
})
watch(() => props.syncResize, (value) => {
if (value) {
handleUpdateResize()
nextTick(() => {
handleUpdateResize()
setTimeout(() => handleUpdateResize())
})
}
})
const mergeCellFlag = ref(0)
watch(() => props.mergeCells ? props.mergeCells.length : -1, () => {
mergeCellFlag.value++
})
watch(() => props.mergeCells, () => {
mergeCellFlag.value++
})
watch(mergeCellFlag, () => {
handleUpdateMergeBodyCells(props.mergeCells || [])
})
const mergeHeaderItemFlag = ref(0)
watch(() => props.mergeHeaderCells ? props.mergeHeaderCells.length : -1, () => {
mergeHeaderItemFlag.value++
})
watch(() => props.mergeHeaderCells, () => {
mergeHeaderItemFlag.value++
})
watch(mergeHeaderItemFlag, () => {
handleUpdateMergeHeaderCells(props.mergeHeaderCells || [])
})
const mergeFooteCellFlag = ref(0)
watch(() => props.mergeFooterCells ? props.mergeFooterCells.length : -1, () => {
mergeFooteCellFlag.value++
})
watch(() => props.mergeFooterCells, () => {
mergeFooteCellFlag.value++
})
watch(() => props.mergeFooterItems ? props.mergeFooterItems.length : -1, () => {
mergeFooteCellFlag.value++
})
watch(() => props.mergeFooterItems, () => {
mergeFooteCellFlag.value++
})
watch(mergeFooteCellFlag, () => {
const mFooterCells = props.mergeFooterCells || props.mergeFooterItems
handleUpdateMergeFooterCells(mFooterCells || [])
})
watch(computeRowGroupFields, (val) => {
handleUpdateRowGroup(val)
})
watch(computeRowField, () => {
const { inited, tableFullData } = internalData
// 行主键被改变,重载表格
if (inited) {
handleKeyField()
reactData.tableData = []
nextTick(() => {
$xeTable.reloadData(tableFullData)
})
}
})
const kfFlag = ref(0)
watch(() => reactData.updateColFlag, () => {
kfFlag.value++
})
watch(computeKeepFields, () => {
kfFlag.value++
})
watch(kfFlag, () => {
const keepFields = computeKeepFields.value
const kpfMaps: Record<string, number> = {}
keepFields.forEach(field => {
kpfMaps[field] = 1
})
internalData.keepUpdateFieldMaps = kpfMaps
})
watch(computeAggregateMode, () => {
if (reactData.isRowGroupStatus) {
loadTableData(internalData.tableSynchData, false, true)
}
})
const layoutReFlag = ref(0)
watch(combineTabsResizeFlag, () => {
layoutReFlag.value++
})
watch(combineModalResizeFlag, () => {
layoutReFlag.value++
})
watch(combineSplitterResizeFlag, () => {
layoutReFlag.value++
})
watch(layoutReFlag, () => {
handleGlobalResizeEvent()
})
handleKeyField()
hooks.forEach((options) => {
const { setupTable } = options
if (setupTable) {
const hookRest = setupTable($xeTable)
if (hookRest && XEUtils.isObject(hookRest)) {
Object.assign($xeTable, hookRest)
}
}
})
$xeTable.preventEvent(null, 'created', { $table: $xeTable })
let resizeObserver: ResizeObserver
onActivated(() => {
$xeTable.recalculate().then(() => $xeTable.refreshScroll())
$xeTable.preventEvent(null, 'activated', { $table: $xeTable })
})
onDeactivated(() => {
const { filterStore } = reactData
if (filterStore.visible) {
$xeTable.clearFilter()
}
$xeTable.closeTooltip()
internalData.isActivated = false
$xeTable.preventEvent(null, 'deactivated', { $table: $xeTable })
})
onMounted(() => {
const { editConfig, exportConfig, importConfig, treeConfig, minHeight, keepSource } = props
const { scrollXStore, scrollYStore } = internalData
const columnOpts = computeColumnOpts.value
const columnDragOpts = computeColumnDragOpts.value
const aggregateOpts = computeAggregateOpts.value
const virtualYOpts = computeVirtualYOpts.value
const editOpts = computeEditOpts.value
const treeOpts = computeTreeOpts.value
const radioOpts = computeRadioOpts.value
const checkboxOpts = computeCheckboxOpts.value
const expandOpts = computeExpandOpts.value
const rowOpts = computeRowOpts.value
const customOpts = computeCustomOpts.value
const mouseOpts = computeMouseOpts.value
const exportOpts = computeExportOpts.value
const importOpts = computeImportOpts.value
// const currentRowOpts = computeCurrentRowOpts.value
// const currentColumnOpts = computeCurrentColumnOpts.value
// const keyboardOpts = computeKeyboardOpts.value
const rowDragOpts = computeRowDragOpts.value
const areaOpts = computeAreaOpts.value
const sortOpts = computeSortOpts.value
const filterOpts = computeFilterOpts.value
const { groupFields } = aggregateOpts
if ($xeGantt) {
const { refClassifyWrapperElem } = $xeGantt.getRefMaps()
const classifyWrapperEl = refClassifyWrapperElem.value
if (classifyWrapperEl) {
internalData.teleportToWrapperElem = classifyWrapperEl
}
}
if ($xeGGWrapper) {
const { refPopupContainerElem } = $xeGGWrapper.getRefMaps()
const popupContainerEl = refPopupContainerElem.value
if (popupContainerEl) {
internalData.popupToWrapperElem = popupContainerEl
}
}
if (columnOpts.drag || rowOpts.drag || customOpts.allowSort) {
initTpImg()
}
if (props.rowId) {
warnLog('vxe.error.delProp', ['row-id', 'row-config.keyField'])
}
if (props.rowKey) {
warnLog('vxe.error.delProp', ['row-key', 'row-config.useKey'])
}
if (props.columnKey) {
warnLog('vxe.error.delProp', ['column-id', 'column-config.useKey'])
}
if (!(props.rowId || rowOpts.keyField) && (checkboxOpts.reserve || checkboxOpts.checkRowKeys || radioOpts.reserve || radioOpts.checkRowKey || expandOpts.expandRowKeys || treeOpts.expandRowKeys)) {
warnLog('vxe.error.reqProp', ['row-config.keyField'])
}
if (editConfig) {
if (!keepSource) {
if (editOpts.showStatus || editOpts.showUpdateStatus || editOpts.showInsertStatus) {
warnLog('vxe.error.reqSupportProp', ['edit-config.showStatus | showUpdateStatus | showInsertStatus', 'keep-source'])
}
// if (keyboardOpts.isUndoRedo) {
// warnLog('vxe.error.reqSupportProp', ['keyboard-config.isUndoRedo', 'keep-source'])
// }
}
}
// if (treeConfig && (treeOpts.showLine || treeOpts.line) && !showOverflow) {
// warnLog('vxe.error.reqProp', ['show-overflow'])
// }
// if (treeConfig && !treeOpts.transform && props.stripe) {
// warnLog('vxe.error.noTree', ['stripe'])
// }
if (treeConfig && !treeOpts.transform) {
if (sortOpts.isDeep) {
warnLog('vxe.error.reqSupportProp', ['transform=false', 'sort-config.isDeep=false'])
}
if (filterOpts.isDeep) {
warnLog('vxe.error.reqSupportProp', ['transform=false', 'filter-config.isDeep=false'])
}
}
if (props.showFooter && !(props.footerMethod || props.footerData)) {
warnLog('vxe.error.reqProp', ['footer-data | footer-method'])
}
if (rowOpts.height) {
warnLog('vxe.error.delProp', ['row-config.height', 'cell-config.height'])
}
if (props.highlightCurrentRow) {
warnLog('vxe.error.delProp', ['highlight-current-row', 'row-config.isCurrent'])
}
if (props.highlightHoverRow) {
warnLog('vxe.error.delProp', ['highlight-hover-row', 'row-config.isHover'])
}
if (props.highlightCurrentColumn) {
warnLog('vxe.error.delProp', ['highlight-current-column', 'column-config.isCurrent'])
}
if (props.highlightHoverColumn) {
warnLog('vxe.error.delProp', ['highlight-hover-column', 'column-config.isHover'])
}
if (props.resizable) {
warnLog('vxe.error.delProp', ['resizable', 'column-config.resizable'])
}
// if (props.virtualXConfig && props.scrollX) {
// warnLog('vxe.error.notSupportProp', ['virtual-x-config', 'scroll-x', 'scroll-x=null'])
// }
// if (props.virtualYConfig && props.scrollY) {
// warnLog('vxe.error.notSupportProp', ['virtual-y-config', 'scroll-y', 'scroll-y=null'])
// }
if (props.aggregateConfig && props.rowGroupConfig) {
warnLog('vxe.error.notSupportProp', ['aggregate-config', 'row-group-config', 'row-group-config=null'])
}
// if (props.scrollY) {
// warnLog('vxe.error.delProp', ['scroll-y', 'virtual-y-config'])
// }
// if (props.scrollX) {
// warnLog('vxe.error.delProp', ['scroll-x', 'virtual-x-config'])
// }
// 检查导入导出类型,如果自定义导入导出方法,则不校验类型
if (importConfig && importOpts.types && !importOpts.importMethod && !XEUtils.includeArrays(XEUtils.keys(importOpts._typeMaps), importOpts.types)) {
warnLog('vxe.error.errProp', [`export-config.types=${importOpts.types.join(',')}`, importOpts.types.filter((type) => XEUtils.includes(XEUtils.keys(importOpts._typeMaps), type)).join(',') || XEUtils.keys(importOpts._typeMaps).join(',')])
}
if (exportConfig && exportOpts.types && !exportOpts.exportMethod && !XEUtils.includeArrays(XEUtils.keys(exportOpts._typeMaps), exportOpts.types)) {
warnLog('vxe.error.errProp', [`export-config.types=${exportOpts.types.join(',')}`, exportOpts.types.filter((type) => XEUtils.includes(XEUtils.keys(exportOpts._typeMaps), type)).join(',') || XEUtils.keys(exportOpts._typeMaps).join(',')])
}
if (exportConfig && XEUtils.isBoolean((exportOpts as any).isAllExpand)) {
warnLog('vxe.error.delProp', ['isAllExpand', 'isTreeAllExpanded'])
}
if (!props.id) {
if ((props.customConfig ? isEnableConf(customOpts) : customOpts.enabled) && customOpts.storage) {
errLog('vxe.error.reqProp', ['id'])
}
}
if (props.treeConfig && checkboxOpts.range) {
errLog('vxe.error.noTree', ['checkbox-config.range'])
}
if (rowOpts.height && !props.showOverflow) {
warnLog('vxe.error.notProp', ['table.show-overflow'])
}
if (!$xeTable.triggerClAreaMnEvent) {
if (props.areaConfig) {
warnLog('vxe.error.notProp', ['area-config'])
}
if (props.clipConfig) {
warnLog('vxe.error.notProp', ['clip-config'])
}
if (props.fnrConfig) {
warnLog('vxe.error.notProp', ['fnr-config'])
}
if (mouseOpts.area) {
errLog('vxe.error.notProp', ['mouse-config.area'])
return
}
if (mouseOpts.area && areaOpts.selectCellByHeader && columnOpts.drag && columnDragOpts.trigger === 'cell') {
errLog('vxe.error.notSupportProp', ['area-config.selectCellByHeader & column-config.drag', 'column-drag-config.trigger=cell', 'column-drag-config.trigger=default | area-config.selectCellByHeader=false'])
}
}
if (!$xeTable.handlePivotTableAggData) {
if (customOpts.allowGroup) {
errLog('vxe.error.notProp', ['custom-config.allowGroup'])
return
}
if (customOpts.allowValues) {
errLog('vxe.error.notProp', ['custom-config.allowValues'])
return
}
}
if (treeConfig && rowOpts.drag && !treeOpts.transform) {
errLog('vxe.error.notSupportProp', ['row-config.drag', 'tree-config.transform=false', 'tree-config.transform=true'])
}
if (treeConfig && rowDragOpts.isCrossTableDrag && !rowDragOpts.isCrossDrag) {
errLog('vxe.error.reqSupportProp', ['tree-config & row-drag-config.isCrossTableDrag', 'row-drag-config.isCrossDrag'])
}
if (props.dragConfig) {
warnLog('vxe.error.delProp', ['drag-config', 'row-drag-config'])
}
if (props.rowGroupConfig) {
warnLog('vxe.error.delProp', ['row-group-config', 'aggregate-config'])
}
if (aggregateOpts.countFields) {
warnLog('vxe.error.delProp', ['row-group-config.countFields', 'column.agg-func'])
}
if (aggregateOpts.aggregateMethod) {
warnLog('vxe.error.delProp', ['row-group-config.aggregateMethod', 'aggregate-config.calcValuesMethod'])
}
if (aggregateOpts.countMethod) {
warnLog('vxe.error.delProp', ['aggregate-config.countMethod', 'aggregate-config.calcValuesMethod'])
}
if (props.treeConfig && treeOpts.children) {
warnLog('vxe.error.delProp', ['tree-config.children', 'tree-config.childrenField'])
}
if (props.treeConfig && treeOpts.line) {
warnLog('vxe.error.delProp', ['tree-config.line', 'tree-config.showLine'])
}
if (mouseOpts.area && mouseOpts.selected) {
warnLog('vxe.error.errConflicts', ['mouse-config.area', 'mouse-config.selected'])
}
if (mouseOpts.area && (props.treeConfig && !treeOpts.transform)) {
errLog('vxe.error.notSupportProp', ['mouse-config.area', 'tree-config.transform=false', 'tree-config.transform=true'])
}
if (props.editConfig && editOpts.activeMethod) {
warnLog('vxe.error.delProp', ['edit-config.activeMethod', 'edit-config.beforeEditMethod'])
}
if (checkboxOpts.halfField) {
warnLog('vxe.error.delProp', ['checkbox-config.halfField', 'checkbox-config.indeterminateField'])
}
if (props.editConfig && isEnableConf(editOpts) && props.editRules && (minHeight === 0 || minHeight === '0')) {
warnLog('vxe.error.reqSupportProp', ['edit-config & edit-rules', 'min-height'])
}
if (treeConfig) {
XEUtils.arrayEach(['rowField', 'parentField', 'childrenField', 'hasChildField', 'mapChildrenField'], key => {
const val = treeOpts[key as 'rowField']
if (val && val.indexOf('.') > -1) {
errLog('vxe.error.errProp', [`${key}=${val}`, `${key}=${val.split('.')[0]}`])
}
})
}
if (rowOpts.currentMethod) {
warnLog('vxe.error.delProp', ['row-config.currentMethod', 'current-row-config.beforeSelectMethod'])
}
if (columnOpts.currentMethod) {
warnLog('vxe.error.delProp', ['row-config.currentMethod', 'current-column-config.beforeSelectMethod'])
}
// if ((rowOpts.isCurrent || highlightCurrentRow) && props.keyboardConfig && keyboardOpts.isArrow && !XEUtils.isBoolean(currentRowOpts.isFollowSelected)) {
// warnLog('vxe.error.notConflictProp', ['row-config.isCurrent & keyboard-config.isArrow', 'current-row-config.isFollowSelected'])
// }
// if ((columnOpts.isCurrent || highlightCurrentColumn) && props.keyboardConfig && keyboardOpts.isArrow && !XEUtils.isBoolean(currentColumnOpts.isFollowSelected)) {
// warnLog('vxe.error.notConflictProp', ['column-config.isCurrent & keyboard-config.isArrow', 'current-column-config.isFollowSelected'])
// }
// 如果不支持虚拟滚动
// if (props.spanMethod) {
// if (virtualXOpts.enabled) {
// warnLog('vxe.error.notConflictProp', ['span-method', 'virtual-x-config.enabled=false'])
// }
// if (virtualYOpts.enabled) {
// warnLog('vxe.error.notConflictProp', ['span-method', 'virtual-y-config.enabled=false'])
// }
// }
// if (props.footerSpanMethod) {
// if (virtualXOpts.enabled) {
// warnLog('vxe.error.notConflictProp', ['footer-span-method', 'virtual-x-config.enabled=false'])
// }
// }
// 检查是否有安装需要的模块
// if (props.editConfig && !$xeTable.insert) {
// errLog('vxe.error.reqModule', ['Edit'])
// }
// if (props.editRules && !$xeTable.validate) {
// errLog('vxe.error.reqModule', ['Validator'])
// }
// if ((checkboxOpts.range || props.keyboardConfig || props.mouseConfig) && !$xeTable.handleCellMousedownEvent) {
// errLog('vxe.error.reqModule', ['Keyboard'])
// }
// if ((props.printConfig || props.importConfig || props.exportConfig) && !$xeTable.exportData) {
// errLog('vxe.error.reqModule', ['Export'])
// }
Object.assign(scrollYStore, {
startIndex: 0,
endIndex: 0,
visibleSize: 0
})
Object.assign(scrollXStore, {
startIndex: 0,
endIndex: 0,
visibleSize: 0
})
handleUpdateRowGroup(groupFields)
initData()
nextTick(() => {
if (props.autoResize) {
const el = refElem.value
const parentEl = $xeTable.getParentElem()
resizeObserver = globalResize.create(() => {
if (props.autoResize) {
handleResizeEvent()
}
})
if (el) {
resizeObserver.observe(el)
}
if (parentEl) {
resizeObserver.observe(parentEl)
}
}
})
if (virtualYOpts.mode !== 'scroll') {
const tableViewportEl = refTableViewportElem.value
if (tableViewportEl) {
tableViewportEl.addEventListener('wheel', $xeTable.triggerBodyWheelEvent, { passive: false })
}
}
globalEvents.on($xeTable, 'paste', handleGlobalPasteEvent)
globalEvents.on($xeTable, 'copy', handleGlobalCopyEvent)
globalEvents.on($xeTable, 'cut', handleGlobalCutEvent)
globalEvents.on($xeTable, 'mousedown', handleGlobalMousedownEvent)
globalEvents.on($xeTable, 'blur', handleGlobalBlurEvent)
globalEvents.on($xeTable, 'mousewheel', handleGlobalMousewheelEvent)
globalEvents.on($xeTable, 'keydown', handleGlobalKeydownEvent)
globalEvents.on($xeTable, 'resize', handleGlobalResizeEvent)
globalEvents.on($xeTable, 'scroll', handleGlobalScrollEvent)
globalEvents.on($xeTable, 'contextmenu', $xeTable.handleGlobalContextmenuEvent)
$xeTable.preventEvent(null, 'mounted', { $table: $xeTable })
})
onBeforeUnmount(() => {
const { _sToTime } = internalData
if (_sToTime) {
clearTimeout(_sToTime)
}
const tableViewportEl = refTableViewportElem.value
if (tableViewportEl) {
tableViewportEl.removeEventListener('wheel', $xeTable.triggerBodyWheelEvent)
}
internalData.cvCacheMaps = {}
internalData.prevDragRow = null
internalData.prevDragCol = null
if (resizeObserver) {
resizeObserver.disconnect()
}
$xeTable.closeTooltip()
$xeTable.closeFilter()
if ($xeTable.closeMenu) {
$xeTable.closeMenu()
}
globalEvents.off($xeTable, 'paste')
globalEvents.off($xeTable, 'copy')
globalEvents.off($xeTable, 'cut')
globalEvents.off($xeTable, 'mousedown')
globalEvents.off($xeTable, 'blur')
globalEvents.off($xeTable, 'mousewheel')
globalEvents.off($xeTable, 'keydown')
globalEvents.off($xeTable, 'resize')
globalEvents.off($xeTable, 'scroll')
globalEvents.off($xeTable, 'contextmenu')
$xeTable.preventEvent(null, 'beforeUnmount', { $table: $xeTable })
XEUtils.assign(reactData, createReactData())
XEUtils.assign(internalData, createInternalData())
})
onUnmounted(() => {
$xeTable.preventEvent(null, 'unmounted', { $table: $xeTable })
})
nextTick(() => {
const { showOverflow, showHeaderOverflow, showFooterOverflow } = props
const tooltipOpts = computeTooltipOpts.value
const headerTooltipOpts = computeHeaderTooltipOpts.value
const footerTooltipOpts = computeFooterTooltipOpts.value
if (props.loading) {
if (!VxeUILoadingComponent && !slots.loading) {
errLog('vxe.error.reqComp', ['vxe-loading'])
errLog('vxe.error.errProp', ['loading=true', 'loading=false | <template #loading>...</template>'])
}
}
if (!VxeUITooltipComponent) {
let useOvTip = false
if (showOverflow === 'tooltip') {
useOvTip = true
errLog('vxe.error.errProp', ['show-overflow=' + showOverflow, 'show-overflow=false,title,ellipsis'])
}
if (showOverflow === true && tooltipOpts.mode === 'tooltip') {
useOvTip = true
errLog('vxe.error.notSupportProp', ['show-overflow=' + showOverflow, 'tooltip-config.mode=tooltip', 'tooltip-config.mode=title,ellipsis'])
}
if (showHeaderOverflow === 'tooltip') {
useOvTip = true
errLog('vxe.error.errProp', ['show-header-overflow=' + showHeaderOverflow, 'show-header-overflow=false,title,ellipsis'])
}
if (showHeaderOverflow === true && headerTooltipOpts.mode === 'tooltip') {
useOvTip = true
errLog('vxe.error.notSupportProp', ['show-header-overflow=' + showHeaderOverflow, 'header-tooltip-config.mode=tooltip', 'header-tooltip-config.mode=title,ellipsis'])
}
if (showFooterOverflow === 'tooltip') {
useOvTip = true
errLog('vxe.error.errProp', ['show-footer-overflow=' + showFooterOverflow, 'show-footer-overflow=false,title,ellipsis'])
}
if (showFooterOverflow === true && footerTooltipOpts.mode === 'tooltip') {
useOvTip = true
errLog('vxe.error.notSupportProp', ['show-footer-overflow=' + showFooterOverflow, 'footer-tooltip-config.mode=tooltip', 'footer-tooltip-config.mode=title,ellipsis'])
}
if (useOvTip) {
errLog('vxe.error.reqComp', ['vxe-tooltip'])
}
}
})
provide('$xeColgroup', null)
provide('$xeTable', $xeTable)
$xeTable.renderVN = renderVN
return $xeTable
},
render () {
return this.renderVN()
}
})