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.
905 lines (868 loc) • 37.3 kB
text/typescript
import { h, ref, Ref, PropType, inject, nextTick, onMounted, onUnmounted } from 'vue'
import { defineVxeComponent } from '../../ui/src/comp'
import XEUtils from 'xe-utils'
import { VxeUI } from '../../ui'
import { getOffsetSize, calcTreeLine, getRowid, createHandleGetRowId, getCellRestHeight } from './util'
import { updateCellTitle, getPropClass } from '../../ui/src/dom'
import { isEnableConf } from '../../ui/src/utils'
import { getSlotVNs } from '../../ui/src/vn'
import type { VxeTablePrivateMethods, VxeTableConstructor, VxeTableDefines, VxeComponentSlotType } from '../../../types'
const { getI18n, renderer, renderEmptyElement } = VxeUI
const renderType = 'body'
export default defineVxeComponent({
name: 'VxeTableBody',
props: {
tableData: Array as PropType<any[]>,
tableColumn: Array as PropType<VxeTableDefines.ColumnInfo[]>,
fixedColumn: Array as PropType<VxeTableDefines.ColumnInfo[]>,
fixedType: {
type: String as PropType<'right' | 'left' | ''>,
default: ''
}
},
setup (props) {
const $xeTable = inject('$xeTable', {} as VxeTableConstructor & VxeTablePrivateMethods)
const { xID, props: tableProps, context: tableContext, reactData: tableReactData, internalData: tableInternalData } = $xeTable
const { computeEditOpts, computeMouseOpts, computeCellOffsetWidth, computeAreaOpts, computeDefaultRowHeight, computeEmptyOpts, computeTooltipOpts, computeRadioOpts, computeExpandOpts, computeTreeOpts, computeCheckboxOpts, computeCellOpts, computeValidOpts, computeRowOpts, computeColumnOpts, computeRowDragOpts, computeResizableOpts, computeVirtualXOpts, computeVirtualYOpts } = $xeTable.getComputeMaps()
const refElem = ref() as Ref<HTMLDivElement>
const refBodyScroll = ref() as Ref<HTMLDivElement>
const refBodyTable = ref() as Ref<HTMLTableElement>
const refBodyColgroup = ref() as Ref<HTMLTableColElement>
const refBodyTBody = ref() as Ref<HTMLTableSectionElement>
const refBodyXSpace = ref() as Ref<HTMLDivElement>
const refBodyYSpace = ref() as Ref<HTMLDivElement>
const refBodyEmptyBlock = ref() as Ref<HTMLDivElement>
// 滚动、拖动过程中不需要触发
const isVMScrollProcess = () => {
const { delayHover } = tableProps
const { lastScrollTime, isDragResize } = tableReactData
return !!(isDragResize || (lastScrollTime && Date.now() < lastScrollTime + (delayHover as number)))
}
const renderLine = (rowid: string, params: VxeTableDefines.CellRenderBodyParams, cellHeight: number): VxeComponentSlotType[] => {
const { column } = params
const { afterFullData } = tableInternalData
const { treeConfig } = tableProps
const treeOpts = computeTreeOpts.value
const { slots, treeNode } = column
const { fullAllDataRowIdData } = tableInternalData
if (slots && (slots as any).line) {
return $xeTable.callSlot((slots as any).line, params)
}
const rowRest = fullAllDataRowIdData[rowid]
let rLevel = 0
let prevRow = null
if (rowRest) {
rLevel = rowRest.level
prevRow = rowRest.items[rowRest.treeIndex - 1]
}
if (treeConfig && treeNode && (treeOpts.showLine || treeOpts.line)) {
return [
h('div', {
key: 'tl',
class: 'vxe-tree--line-wrapper'
}, [
h('div', {
class: 'vxe-tree--line',
style: {
height: `${getRowid($xeTable, afterFullData[0]) === rowid ? 1 : calcTreeLine(params, prevRow)}px`,
bottom: `-${Math.floor(cellHeight / 2)}px`,
left: `${(rLevel * treeOpts.indent) + (rLevel ? 2 - getOffsetSize($xeTable) : 0) + 16}px`
}
})
])
]
}
return []
}
/**
* 渲染列
*/
const renderTdColumn = (
seq: number | string,
rowid: string,
fixedType: 'left' | 'right' | '',
isOptimizeMode: boolean,
rowLevel: number,
row: any,
rowIndex: number,
$rowIndex: number,
_rowIndex: number,
column: VxeTableDefines.ColumnInfo,
$columnIndex: number,
columns: VxeTableDefines.ColumnInfo[],
items: any[]
) => {
const $xeGrid = $xeTable.xeGrid
const { columnKey, resizable: allResizable, showOverflow: allShowOverflow, border, height, treeConfig, cellClassName: allCellClassName, cellStyle, align: allAlign, spanMethod, mouseConfig, editConfig, editRules, tooltipConfig, padding: allPadding } = tableProps
const { tableData, tableColumn, dragRow, overflowX, overflowY, currentColumn, scrollXLoad, scrollYLoad, mergeBodyFlag, calcCellHeightFlag, resizeHeightFlag, resizeWidthFlag, editStore, isAllOverflow, validErrorMaps } = tableReactData
const { fullAllDataRowIdData, fullColumnIdData, mergeBodyCellMaps, visibleColumn, afterFullData, mergeBodyList, scrollXStore, scrollYStore } = tableInternalData
const cellOpts = computeCellOpts.value
const validOpts = computeValidOpts.value
const checkboxOpts = computeCheckboxOpts.value
const editOpts = computeEditOpts.value
const tooltipOpts = computeTooltipOpts.value
const resizableOpts = computeResizableOpts.value
const virtualXOpts = computeVirtualXOpts.value
const virtualYOpts = computeVirtualYOpts.value
const { isAllColumnDrag, isAllRowDrag } = resizableOpts
const rowOpts = computeRowOpts.value
const rowDragOpts = computeRowDragOpts.value
const defaultRowHeight = computeDefaultRowHeight.value
const customCellHeight = calcCellHeightFlag ? (cellOpts.height || rowOpts.height) : 0
const { disabledMethod: dragDisabledMethod, isCrossDrag, isPeerDrag } = rowDragOpts
const columnOpts = computeColumnOpts.value
const mouseOpts = computeMouseOpts.value
const areaOpts = computeAreaOpts.value
const cellOffsetWidth = computeCellOffsetWidth.value
const { selectCellToRow } = areaOpts
const { type, cellRender, editRender, align, showOverflow, className, treeNode, rowResize, padding, verticalAlign, slots } = column
const { verticalAlign: allVerticalAlign } = cellOpts
const { actived } = editStore
const rowRest = fullAllDataRowIdData[rowid] || {}
const colid = column.id
const colRest = fullColumnIdData[colid] || {}
const renderOpts = editRender || cellRender
const compConf = renderOpts ? renderer.get(renderOpts.name) : null
const compCellClassName = compConf ? (compConf.tableCellClassName || compConf.cellClassName) : null
const compCellStyle = compConf ? (compConf.tableCellStyle || compConf.cellStyle) : ''
const showAllTip = tooltipOpts.showAll
const columnIndex = colRest.index
const _columnIndex = colRest._index
const isEdit = isEnableConf(editRender)
const resizeHeight = resizeHeightFlag ? rowRest.resizeHeight : 0
let fixedHiddenColumn = overflowX && (fixedType ? column.fixed !== fixedType : !!column.fixed)
const isCellPadding = XEUtils.eqNull(padding) ? (allPadding === null ? cellOpts.padding : allPadding) : padding
const cellOverflow = XEUtils.eqNull(showOverflow) ? allShowOverflow : showOverflow
const showEllipsis = cellOverflow === 'ellipsis'
const showTitle = cellOverflow === 'title'
const showTooltip = cellOverflow === true || cellOverflow === 'tooltip'
const hasEllipsis = isAllOverflow || showTitle || showTooltip || showEllipsis
const showResizable = (XEUtils.isBoolean(column.resizable) ? column.resizable : (columnOpts.resizable || allResizable))
const isCsHeight = !!customCellHeight
const isRsHeight = resizeHeight > 0
let isDirty
const tdOns: any = {}
const cellAlign = align || (compConf ? compConf.tableCellAlign : '') || allAlign
const cellVerticalAlign = XEUtils.eqNull(verticalAlign) ? allVerticalAlign : verticalAlign
const errorValidItem = validErrorMaps[`${rowid}:${colid}`]
const showValidTip = editRules && validOpts.showMessage && (validOpts.message === 'default' ? (height || tableData.length > 1) : validOpts.message === 'inline')
const tdAttrs: any = { colid }
const cellParams: VxeTableDefines.CellRenderBodyParams & {
$table: VxeTableConstructor<any> & VxeTablePrivateMethods
} = {
$table: $xeTable,
$grid: $xeGrid,
isEdit: false,
seq,
rowid,
row,
rowIndex,
$rowIndex,
_rowIndex,
column,
columnIndex,
$columnIndex,
_columnIndex,
fixed: fixedType,
type: renderType,
isHidden: !!fixedHiddenColumn,
level: rowLevel,
visibleData: afterFullData,
data: tableData,
items
}
let isRowDragCell = false
let isDisabledDrag = false
if (rowOpts.drag) {
isRowDragCell = rowDragOpts.trigger === 'row' || (column.dragSort && rowDragOpts.trigger === 'cell')
}
if (isRowDragCell) {
isDisabledDrag = !!(dragDisabledMethod && dragDisabledMethod(cellParams))
}
// hover 进入事件
if (showTitle || showTooltip || showAllTip || tooltipConfig) {
tdOns.onMouseenter = (evnt: MouseEvent) => {
if (isVMScrollProcess()) {
return
}
if (showTitle) {
updateCellTitle(evnt.currentTarget, column)
} else if (showTooltip || showAllTip) {
// 如果配置了显示 tooltip
$xeTable.triggerBodyTooltipEvent(evnt, cellParams)
}
$xeTable.dispatchEvent('cell-mouseenter', Object.assign({ cell: evnt.currentTarget }, cellParams), evnt)
}
}
// hover 退出事件
if (showTooltip || showAllTip || tooltipConfig) {
tdOns.onMouseleave = (evnt: MouseEvent) => {
if (isVMScrollProcess()) {
return
}
if (showTooltip || showAllTip) {
$xeTable.handleTargetLeaveEvent(evnt)
}
$xeTable.dispatchEvent('cell-mouseleave', Object.assign({ cell: evnt.currentTarget }, cellParams), evnt)
}
}
// 按下事件处理
if (isRowDragCell || checkboxOpts.range || mouseConfig) {
tdOns.onMousedown = (evnt: MouseEvent) => {
$xeTable.triggerCellMousedownEvent(evnt, cellParams)
}
}
// 拖拽列事件
if (isRowDragCell) {
tdOns.onMouseup = $xeTable.triggerCellMouseupEvent
}
// 点击事件处理
tdOns.onClick = (evnt: MouseEvent) => {
$xeTable.triggerCellClickEvent(evnt, cellParams)
}
// 双击事件处理
tdOns.onDblclick = (evnt: MouseEvent) => {
$xeTable.triggerCellDblclickEvent(evnt, cellParams)
}
let isMergeCell = false
let mergeColspan = 1
let mergeRowspan = 1
// 合并行或列
if (mergeBodyFlag && mergeBodyList.length) {
const spanRest = mergeBodyCellMaps[`${_rowIndex}:${_columnIndex}`]
if (spanRest) {
const { rowspan, colspan } = spanRest
if (!rowspan || !colspan) {
return renderEmptyElement($xeTable)
}
if (rowspan > 1) {
isMergeCell = true
mergeRowspan = rowspan
tdAttrs.rowspan = rowspan
}
if (colspan > 1) {
isMergeCell = true
mergeColspan = colspan
tdAttrs.colspan = colspan
}
}
} else if (spanMethod) {
// 自定义合并行或列的方法
const { rowspan = 1, colspan = 1 } = spanMethod(cellParams) || {}
if (!rowspan || !colspan) {
return renderEmptyElement($xeTable)
}
if (rowspan > 1) {
isMergeCell = true
mergeRowspan = rowspan
tdAttrs.rowspan = rowspan
}
if (colspan > 1) {
isMergeCell = true
mergeColspan = colspan
tdAttrs.colspan = colspan
}
}
// 如果被合并不可隐藏
if (fixedHiddenColumn && isMergeCell) {
if (tdAttrs.colspan > 1 || tdAttrs.rowspan > 1) {
fixedHiddenColumn = false
}
}
// 如果编辑列开启显示状态
if (!fixedHiddenColumn && editConfig && (editRender || cellRender) && (editOpts.showStatus || editOpts.showUpdateStatus)) {
isDirty = $xeTable.isUpdateByRow(row, column.field)
}
const isVNAutoHeight = !hasEllipsis && (scrollYLoad || scrollXLoad)
let cellHeight = getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
const isLastColumn = $columnIndex === columns.length - 1
const isAutoCellWidth = !column.resizeWidth && (column.minWidth === 'auto' || column.width === 'auto')
let isVNPreEmptyStatus = false
if (!isMergeCell) {
if (!dragRow || getRowid($xeTable, dragRow) !== rowid) {
if (overflowY && scrollYLoad && tableData.length > 16 && !treeConfig && !virtualYOpts.immediate && (_rowIndex < scrollYStore.visibleStartIndex - scrollYStore.preloadSize || _rowIndex > scrollYStore.visibleEndIndex + scrollYStore.preloadSize)) {
isVNPreEmptyStatus = true
} else if (overflowX && scrollXLoad && tableColumn.length > 10 && !virtualXOpts.immediate && !column.fixed && (_columnIndex < scrollXStore.visibleStartIndex - scrollXStore.preloadSize || _columnIndex > scrollXStore.visibleEndIndex + scrollXStore.preloadSize)) {
isVNPreEmptyStatus = true
}
}
}
if (mergeRowspan > 1) {
const mEndRow = afterFullData[_rowIndex + mergeRowspan - 1]
if (mEndRow) {
const meRowRest = fullAllDataRowIdData[getRowid($xeTable, mEndRow)]
if (meRowRest) {
cellHeight += meRowRest.oTop + getCellRestHeight(meRowRest, cellOpts, rowOpts, defaultRowHeight) - rowRest.oTop - getCellRestHeight(rowRest, cellOpts, rowOpts, defaultRowHeight)
}
}
}
const tcStyle: Record<string, string> = {}
if (hasEllipsis && resizeWidthFlag) {
let mergeColWidth = 0
if (mergeColspan > 1) {
for (let index = 1; index < mergeColspan; index++) {
const nextColumn = visibleColumn[columnIndex + index]
if (nextColumn) {
mergeColWidth += nextColumn.renderWidth
}
}
}
tcStyle.width = `${column.renderWidth + mergeColWidth - cellOffsetWidth}px`
}
if (scrollYLoad || scrollXLoad || hasEllipsis || isCsHeight || isRsHeight) {
tcStyle.height = `${cellHeight}px`
} else {
tcStyle.minHeight = `${cellHeight}px`
}
// console.log(lastScrollTime)
const tdVNs: VxeComponentSlotType[] = []
if (fixedHiddenColumn && isAllOverflow) {
tdVNs.push(
h('div', {
key: 'tc',
class: ['vxe-cell', {
'c--title': showTitle,
'c--tooltip': showTooltip,
'c--ellipsis': showEllipsis
}],
style: tcStyle
})
)
} else {
// 渲染单元格
if (treeConfig) {
tdVNs.push(...renderLine(rowid, cellParams, cellHeight))
}
tdVNs.push(
h('div', {
key: 'tc',
class: ['vxe-cell', {
'c--title': showTitle,
'c--tooltip': showTooltip,
'c--ellipsis': showEllipsis
}],
style: tcStyle,
title: showTitle ? $xeTable.getCellLabel(row, column) : null
}, isVNPreEmptyStatus
? []
: [
h('div', {
colid,
rowid,
class: 'vxe-cell--wrapper vxe-body-cell--wrapper'
}, column.renderCell(cellParams))
])
)
if (showValidTip && errorValidItem) {
const errRule = errorValidItem.rule
const validSlot = slots ? slots.valid : null
const validParams = { ...cellParams, ...errorValidItem, rule: errorValidItem }
tdVNs.push(
h('div', {
key: 'tcv',
class: ['vxe-cell--valid-error-tip', getPropClass(validOpts.className, validParams)],
style: errRule && errRule.maxWidth
? {
width: `${errRule.maxWidth}px`
}
: null
}, [
h('div', {
class: `vxe-cell--valid-error-wrapper vxe-cell--valid-error-theme-${validOpts.theme || 'normal'}`
}, [
validSlot
? $xeTable.callSlot(validSlot, validParams)
: [
h('span', {
class: 'vxe-cell--valid-error-msg'
}, errorValidItem.content)
]
])
])
)
}
}
let showAreaRowStatus = false
if (mouseConfig && mouseOpts.area && !_columnIndex && selectCellToRow) {
showAreaRowStatus = true
}
if (!fixedHiddenColumn && showResizable && isAllColumnDrag) {
tdVNs.push(
h('div', {
key: 'tcc',
class: ['vxe-cell--col-resizable', {
'is--line': !border || border === 'none'
}],
onMousedown: (evnt: MouseEvent) => $xeTable.handleColResizeMousedownEvent(evnt, fixedType, cellParams),
onDblclick: (evnt: MouseEvent) => $xeTable.handleColResizeDblclickEvent(evnt, cellParams)
})
)
}
if ((rowResize || isAllRowDrag) && rowOpts.resizable) {
tdVNs.push(
h('div', {
key: 'tcr',
class: 'vxe-cell--row-resizable',
onMousedown: (evnt: MouseEvent) => $xeTable.handleRowResizeMousedownEvent(evnt, cellParams),
onDblclick: (evnt: MouseEvent) => $xeTable.handleRowResizeDblclickEvent(evnt, cellParams)
})
)
}
return h('td', {
class: [
'vxe-table--column vxe-body--column',
colid,
cellVerticalAlign ? `col--vertical-${cellVerticalAlign}` : '',
cellAlign ? `col--${cellAlign}` : '',
type ? `col--${type}` : '',
{
'col--last': isLastColumn,
'col--tree-node': treeNode,
'col--edit': isEdit,
'col--ellipsis': hasEllipsis,
'col--cs-height': isCsHeight,
'col--rs-height': isRsHeight,
'col--to-row': showAreaRowStatus,
'col--auto-height': isVNAutoHeight,
'fixed--width': !isAutoCellWidth,
'fixed--hidden': fixedHiddenColumn,
'is--padding': isCellPadding,
'is--progress': (fixedHiddenColumn && isAllOverflow) || isVNPreEmptyStatus,
'is--drag-cell': isRowDragCell && (isCrossDrag || isPeerDrag || !rowLevel),
'is--drag-disabled': isDisabledDrag,
'col--dirty': isDirty,
'col--active': editConfig && isEdit && (actived.row === row && (actived.column === column || editOpts.mode === 'row')),
'col--valid-error': !!errorValidItem,
'col--current': currentColumn === column
},
getPropClass(compCellClassName, cellParams),
getPropClass(className, cellParams),
getPropClass(allCellClassName, cellParams)
],
key: columnKey || scrollXLoad || scrollYLoad || columnOpts.useKey || rowOpts.useKey || columnOpts.drag ? colid : $columnIndex,
...tdAttrs,
style: Object.assign({}, XEUtils.isFunction(compCellStyle) ? compCellStyle(cellParams) : compCellStyle, XEUtils.isFunction(cellStyle) ? cellStyle(cellParams) : cellStyle),
...tdOns
}, isOptimizeMode && fixedHiddenColumn ? [] : tdVNs)
}
const renderRows = (fixedType: 'left' | 'right' | '', isOptimizeMode: boolean, tableData: any[], tableColumn: VxeTableDefines.ColumnInfo[]) => {
const $xeGrid = $xeTable.xeGrid
const { stripe, rowKey, highlightHoverRow, rowClassName, rowStyle, editConfig, treeConfig } = tableProps
const { hasFixedColumn, treeExpandedFlag, scrollXLoad, scrollYLoad, isAllOverflow, rowExpandedFlag, expandColumn, selectRadioRow, pendingRowFlag, rowExpandHeightFlag, isRowGroupStatus } = tableReactData
const { fullAllDataRowIdData, fullColumnIdData, treeExpandedMaps, pendingRowMaps, rowExpandedMaps } = tableInternalData
const checkboxOpts = computeCheckboxOpts.value
const radioOpts = computeRadioOpts.value
const treeOpts = computeTreeOpts.value
const editOpts = computeEditOpts.value
const rowOpts = computeRowOpts.value
const columnOpts = computeColumnOpts.value
const { transform, seqMode } = treeOpts
const childrenField = treeOpts.children || treeOpts.childrenField
const rows: any[] = []
const { handleGetRowId } = createHandleGetRowId($xeTable)
const isDeepRow = treeConfig || isRowGroupStatus
tableData.forEach((row, $rowIndex) => {
const rowid = handleGetRowId(row)
const rowRest = fullAllDataRowIdData[rowid] || {}
let rowIndex = $rowIndex
let rowLevel = 0
let seq: string | number = -1
let _rowIndex = -1
const hasRowGroupAggregate = isRowGroupStatus && row.isAggregate
const trOn: Record<string, any> = {}
// 当前行事件
if (rowOpts.isHover || highlightHoverRow) {
trOn.onMouseenter = (evnt: MouseEvent) => {
if (isVMScrollProcess()) {
return
}
$xeTable.triggerHoverEvent(evnt, { row, rowIndex })
}
trOn.onMouseleave = () => {
if (isVMScrollProcess()) {
return
}
$xeTable.clearHoverRow()
}
}
if (rowRest) {
rowIndex = rowRest.index
_rowIndex = rowRest._index
rowLevel = rowRest.level
seq = rowRest.seq
if (hasRowGroupAggregate || (treeConfig && transform && seqMode === 'increasing')) {
seq = rowRest._index + 1
} else if ((treeConfig && seqMode === 'fixed')) {
seq = rowRest._tIndex + 1
}
}
const params = { $table: $xeTable, seq, rowid, fixed: fixedType, type: renderType, level: rowLevel, row, rowIndex, $rowIndex, _rowIndex }
// 行是否被展开
const isExpandRow = expandColumn && !!rowExpandedFlag && !!rowExpandedMaps[rowid]
// 树节点是否被展开
let isExpandTree = false
let rowChildren = []
let isNewRow = false
if (editConfig) {
isNewRow = $xeTable.isInsertByRow(row)
}
if (treeConfig && !scrollYLoad && !transform) {
rowChildren = row[childrenField]
isExpandTree = !!treeExpandedFlag && rowChildren && rowChildren.length > 0 && !!treeExpandedMaps[rowid]
}
// 拖拽行事件
if (rowOpts.drag && !isRowGroupStatus && (!treeConfig || transform)) {
trOn.onDragstart = $xeTable.handleRowDragDragstartEvent
trOn.onDragend = $xeTable.handleRowDragDragendEvent
trOn.onDragover = $xeTable.handleRowDragDragoverEvent
}
const trClass = [
'vxe-body--row',
isDeepRow ? `row--level-${rowLevel}` : '',
{
'row--stripe': stripe && (_rowIndex + 1) % 2 === 0,
'is--new': isNewRow,
'is--expand-row': isExpandRow,
'is--expand-tree': isExpandTree,
'row--new': isNewRow && (editOpts.showStatus || editOpts.showInsertStatus),
'row--radio': radioOpts.highlight && $xeTable.eqRow(selectRadioRow, row),
'row--checked': checkboxOpts.highlight && $xeTable.isCheckedByCheckboxRow(row),
'row--pending': !!pendingRowFlag && !!pendingRowMaps[rowid],
'row--group': hasRowGroupAggregate
},
getPropClass(rowClassName, params)
]
const tdVNs = tableColumn.map((column, $columnIndex) => {
return renderTdColumn(seq, rowid, fixedType, isOptimizeMode, rowLevel, row, rowIndex, $rowIndex, _rowIndex, column, $columnIndex, tableColumn, tableData)
})
rows.push(
h('tr', {
class: trClass,
rowid: rowid,
style: rowStyle ? (XEUtils.isFunction(rowStyle) ? rowStyle(params) : rowStyle) : null,
key: rowKey || scrollXLoad || scrollYLoad || rowOpts.useKey || rowOpts.drag || columnOpts.drag || isRowGroupStatus || treeConfig ? rowid : $rowIndex,
...trOn
}, tdVNs)
)
// 如果行被展开了
if (isExpandRow) {
const expandOpts = computeExpandOpts.value
const { height: expandHeight, padding, mode: expandMode } = expandOpts
if (expandMode === 'fixed') {
rows.push(
h('tr', {
class: 'vxe-body--row-expanded-place',
key: `expand_${rowid}`,
rowid
}, [
h('td', {
class: 'vxe-body--row-expanded-place-column',
colspan: tableColumn.length,
style: {
height: `${rowExpandHeightFlag ? (rowRest.expandHeight || expandHeight) : 0}px`
}
})
])
)
} else {
const cellStyle: any = {}
if (expandHeight) {
cellStyle.height = `${expandHeight}px`
}
if (treeConfig) {
cellStyle.paddingLeft = `${(rowLevel * treeOpts.indent) + 30}px`
}
const { showOverflow } = expandColumn || {}
const colid = expandColumn.id
const colRest = fullColumnIdData[colid] || {}
const hasEllipsis = XEUtils.eqNull(showOverflow) ? isAllOverflow : showOverflow
let columnIndex = -1
let $columnIndex = -1
let _columnIndex = -1
if (colRest) {
columnIndex = colRest.index
$columnIndex = colRest.$index
_columnIndex = colRest._index
}
const expandParams: VxeTableDefines.CellRenderDataParams = {
$grid: $xeGrid,
$table: $xeTable,
seq,
column: expandColumn as VxeTableDefines.ColumnInfo,
columnIndex,
$columnIndex,
_columnIndex,
fixed: fixedType,
type: renderType,
level: rowLevel,
row,
rowid,
rowIndex,
$rowIndex,
_rowIndex,
isHidden: false,
isEdit: false,
visibleData: [],
data: [],
items: []
}
rows.push(
h('tr', {
class: ['vxe-body--expanded-row', {
'is--padding': padding
}],
key: `expand_${rowid}`
}, [
h('td', {
class: ['vxe-body--expanded-column', {
'fixed--hidden': fixedType && !hasFixedColumn,
'col--ellipsis': hasEllipsis
}],
colspan: tableColumn.length
}, [
h('div', {
class: ['vxe-body--expanded-cell', {
'is--ellipsis': expandHeight
}],
style: cellStyle
}, [
expandColumn.renderData(expandParams)
])
])
])
)
}
}
// 如果是树形表格
if (isExpandTree) {
rows.push(...renderRows(fixedType, isOptimizeMode, rowChildren, tableColumn))
}
})
return rows
}
onMounted(() => {
nextTick(() => {
const { fixedType } = props
const { elemStore } = tableInternalData
const prefix = `${fixedType || 'main'}-body-`
elemStore[`${prefix}wrapper`] = refElem
elemStore[`${prefix}scroll`] = refBodyScroll
elemStore[`${prefix}table`] = refBodyTable
elemStore[`${prefix}colgroup`] = refBodyColgroup
elemStore[`${prefix}list`] = refBodyTBody
elemStore[`${prefix}xSpace`] = refBodyXSpace
elemStore[`${prefix}ySpace`] = refBodyYSpace
elemStore[`${prefix}emptyBlock`] = refBodyEmptyBlock
})
})
onUnmounted(() => {
const { fixedType } = props
const { elemStore } = tableInternalData
const prefix = `${fixedType || 'main'}-body-`
elemStore[`${prefix}wrapper`] = null
elemStore[`${prefix}scroll`] = null
elemStore[`${prefix}table`] = null
elemStore[`${prefix}colgroup`] = null
elemStore[`${prefix}list`] = null
elemStore[`${prefix}xSpace`] = null
elemStore[`${prefix}ySpace`] = null
elemStore[`${prefix}emptyBlock`] = null
})
const renderVN = () => {
const { slots } = tableContext
const $xeGrid = $xeTable.xeGrid
const { fixedColumn, fixedType, tableColumn } = props
const { spanMethod, footerSpanMethod, mouseConfig } = tableProps
const { isGroup, tableData, isColLoading, overflowX, scrollXLoad, scrollYLoad, isAllOverflow, expandColumn, dragRow, dragCol } = tableReactData
const { visibleColumn, fullAllDataRowIdData, fullColumnIdData } = tableInternalData
const emptyOpts = computeEmptyOpts.value
const mouseOpts = computeMouseOpts.value
const expandOpts = computeExpandOpts.value
let renderDataList = tableData
let renderColumnList = tableColumn as VxeTableDefines.ColumnInfo[]
let isOptimizeMode = false
// 如果是使用优化模式
if (scrollXLoad || scrollYLoad || isAllOverflow) {
if ((expandColumn && expandOpts.mode !== 'fixed') || spanMethod || footerSpanMethod) {
// 如果不支持优化模式
} else {
isOptimizeMode = true
}
}
if (!isColLoading && (fixedType || !overflowX)) {
renderColumnList = visibleColumn
}
if (fixedType) {
if (isOptimizeMode) {
renderColumnList = fixedColumn || []
}
}
// 行拖拽
if (scrollYLoad && dragRow) {
if (renderDataList.length > 2) {
const dRowRest = fullAllDataRowIdData[getRowid($xeTable, dragRow)]
if (dRowRest) {
const drIndex = dRowRest._index
const firstRow = renderDataList[0]
const lastRow = renderDataList[renderDataList.length - 1]
const firstRowRest = fullAllDataRowIdData[getRowid($xeTable, firstRow)]
const lastRowRest = fullAllDataRowIdData[getRowid($xeTable, lastRow)]
if (firstRowRest && lastRowRest) {
const frIndex = firstRowRest._index
const lrIndex = lastRowRest._index
if (drIndex < frIndex) {
renderDataList = [dragRow].concat(renderDataList)
} else if (drIndex > lrIndex) {
renderDataList = renderDataList.concat([dragRow])
}
}
}
}
}
if (!fixedType && !isGroup) {
// 列拖拽
if (scrollXLoad && dragCol) {
if (renderColumnList.length > 2) {
const dCowRest = fullColumnIdData[dragCol.id]
if (dCowRest) {
const dcIndex = dCowRest._index
const firstCol = renderColumnList[0]
const lastCol = renderColumnList[renderColumnList.length - 1]
const firstColRest = fullColumnIdData[firstCol.id]
const lastColRest = fullColumnIdData[lastCol.id]
if (firstColRest && lastColRest) {
const fcIndex = firstColRest._index
const lcIndex = lastColRest._index
if (dcIndex < fcIndex) {
renderColumnList = [dragCol].concat(renderColumnList)
} else if (dcIndex > lcIndex) {
renderColumnList = renderColumnList.concat([dragCol])
}
}
}
}
}
}
let emptyContent: string | VxeComponentSlotType | VxeComponentSlotType[]
const emptySlot = slots ? slots.empty : null
const emptyParams = { $table: $xeTable, $grid: $xeGrid }
if (emptySlot) {
emptyContent = $xeTable.callSlot(emptySlot, emptyParams)
} else {
const compConf = emptyOpts.name ? renderer.get(emptyOpts.name) : null
const rtEmptyView = compConf ? (compConf.renderTableEmpty || compConf.renderTableEmptyView || compConf.renderEmpty) : null
if (rtEmptyView) {
emptyContent = getSlotVNs(rtEmptyView(emptyOpts, emptyParams))
} else {
emptyContent = tableProps.emptyText || getI18n('vxe.table.emptyText')
}
}
const ons: Record<string, any> = {
onScroll (evnt: Event) {
$xeTable.triggerBodyScrollEvent(evnt, fixedType)
}
}
return h('div', {
ref: refElem,
class: ['vxe-table--body-wrapper', fixedType ? `fixed-${fixedType}--wrapper` : 'body--wrapper'],
xid: xID
}, [
h('div', {
ref: refBodyScroll,
class: 'vxe-table--body-inner-wrapper',
...ons
}, [
fixedType
? renderEmptyElement($xeTable)
: h('div', {
ref: refBodyXSpace,
class: 'vxe-body--x-space'
}),
h('div', {
ref: refBodyYSpace,
class: 'vxe-body--y-space'
}),
h('table', {
ref: refBodyTable,
class: 'vxe-table--body',
xid: xID,
cellspacing: 0,
cellpadding: 0,
border: 0,
xvm: isOptimizeMode ? '1' : null
}, [
/**
* 列宽
*/
h('colgroup', {
ref: refBodyColgroup
}, renderColumnList.map((column, $columnIndex) => {
return h('col', {
name: column.id,
key: $columnIndex,
style: {
width: `${column.renderWidth}px`
}
})
})),
/**
* 内容
*/
h('tbody', {
ref: refBodyTBody
}, renderRows(fixedType, isOptimizeMode, renderDataList, renderColumnList))
]),
h('div', {
class: 'vxe-table--checkbox-range'
}),
mouseConfig && mouseOpts.area
? h('div', {
class: 'vxe-table--cell-area'
}, [
h('span', {
class: 'vxe-table--cell-main-area'
}, mouseOpts.extension
? [
h('span', {
class: 'vxe-table--cell-main-area-btn',
onMousedown (evnt: MouseEvent) {
if ($xeTable.triggerCellAreaExtendMousedownEvent) {
$xeTable.triggerCellAreaExtendMousedownEvent(evnt, { $table: $xeTable, fixed: fixedType, type: renderType })
}
}
})
]
: []),
h('span', {
class: 'vxe-table--cell-clip-area'
}),
h('span', {
class: 'vxe-table--cell-extend-area'
}),
h('span', {
class: 'vxe-table--cell-multi-area'
}),
h('span', {
class: 'vxe-table--cell-active-area'
}),
h('span', {
class: 'vxe-table--cell-row-status-area'
})
])
: renderEmptyElement($xeTable),
!fixedType
? h('div', {
class: 'vxe-table--empty-block',
ref: refBodyEmptyBlock
}, [
h('div', {
class: 'vxe-table--empty-content'
}, emptyContent)
])
: renderEmptyElement($xeTable)
])
])
}
return renderVN
}
})