vxe-table-ro-test
Version:
一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、列拖拽,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...
311 lines (298 loc) • 13.2 kB
text/typescript
import { createCommentVNode, defineComponent, h, ref, Ref, PropType, inject, nextTick, onMounted, onUnmounted } from 'vue'
import XEUtils from 'xe-utils'
import { VxeUI } from '../../ui'
import { updateCellTitle, getPropClass } from '../../ui/src/dom'
import type { VxeTablePrivateMethods, VxeTableConstructor, VxeTableMethods, VxeColumnPropTypes, VxeTableDefines } from '../../../types'
const { renderer } = VxeUI
const renderType = 'footer'
function mergeFooterMethod (mergeFooterList: VxeTableDefines.MergeItem[], _rowIndex: number, _columnIndex: number) {
for (let mIndex = 0; mIndex < mergeFooterList.length; mIndex++) {
const { row: mergeRowIndex, col: mergeColIndex, rowspan: mergeRowspan, colspan: mergeColspan } = mergeFooterList[mIndex]
if (mergeColIndex > -1 && mergeRowIndex > -1 && mergeRowspan && mergeColspan) {
if (mergeRowIndex === _rowIndex && mergeColIndex === _columnIndex) {
return { rowspan: mergeRowspan, colspan: mergeColspan }
}
if (_rowIndex >= mergeRowIndex && _rowIndex < mergeRowIndex + mergeRowspan && _columnIndex >= mergeColIndex && _columnIndex < mergeColIndex + mergeColspan) {
return { rowspan: 0, colspan: 0 }
}
}
}
}
export default defineComponent({
name: 'VxeTableFooter',
props: {
footerTableData: {
type: Array as PropType<any[][]>,
default: () => []
},
tableColumn: {
type: Array as PropType<VxeTableDefines.ColumnInfo[]>,
default: () => []
},
fixedColumn: {
type: Array as PropType<VxeTableDefines.ColumnInfo[]>,
default: () => []
},
fixedType: {
type: String as PropType<VxeColumnPropTypes.Fixed>,
default: null
}
},
setup (props) {
const $xeTable = inject('$xeTable', {} as VxeTableConstructor & VxeTableMethods & VxeTablePrivateMethods)
const { xID, props: tableProps, reactData: tableReactData, internalData: tableInternalData } = $xeTable
const { refTableHeader, refTableBody, refValidTooltip } = $xeTable.getRefMaps()
const { computeTooltipOpts, computeColumnOpts } = $xeTable.getComputeMaps()
const refElem = ref() as Ref<HTMLDivElement>
const refFooterTable = ref() as Ref<HTMLTableElement>
const refFooterColgroup = ref() as Ref<HTMLTableColElement>
const refFooterTFoot = ref() as Ref<HTMLTableSectionElement>
const refFooterXSpace = ref() as Ref<HTMLDivElement>
/**
* 滚动处理
* 如果存在列固定左侧,同步更新滚动状态
* 如果存在列固定右侧,同步更新滚动状态
*/
const scrollEvent = (evnt: MouseEvent) => {
const { fixedType } = props
const { scrollXLoad } = tableReactData
const { lastScrollLeft } = tableInternalData
const validTip = refValidTooltip.value
const tableHeader = refTableHeader.value
const tableBody = refTableBody.value
const headerElem = tableHeader ? tableHeader.$el as HTMLDivElement : null
const footerElem = refElem.value
const bodyElem = tableBody.$el as HTMLDivElement
const scrollLeft = footerElem.scrollLeft
const isX = scrollLeft !== lastScrollLeft
tableInternalData.lastScrollLeft = scrollLeft
tableReactData.lastScrollTime = Date.now()
if (headerElem) {
headerElem.scrollLeft = scrollLeft
}
if (bodyElem) {
bodyElem.scrollLeft = scrollLeft
}
if (scrollXLoad && isX) {
$xeTable.triggerScrollXEvent(evnt)
}
if (isX && validTip && validTip.reactData.visible) {
validTip.updatePlacement()
}
$xeTable.dispatchEvent('scroll', { type: renderType, fixed: fixedType, scrollTop: bodyElem.scrollTop, scrollLeft, isX, isY: false }, evnt)
}
onMounted(() => {
nextTick(() => {
const { fixedType } = props
const { elemStore } = tableInternalData
const prefix = `${fixedType || 'main'}-footer-`
elemStore[`${prefix}wrapper`] = refElem
elemStore[`${prefix}table`] = refFooterTable
elemStore[`${prefix}colgroup`] = refFooterColgroup
elemStore[`${prefix}list`] = refFooterTFoot
elemStore[`${prefix}xSpace`] = refFooterXSpace
})
})
onUnmounted(() => {
const { fixedType } = props
const { elemStore } = tableInternalData
const prefix = `${fixedType || 'main'}-footer-`
elemStore[`${prefix}wrapper`] = null
elemStore[`${prefix}table`] = null
elemStore[`${prefix}colgroup`] = null
elemStore[`${prefix}list`] = null
elemStore[`${prefix}xSpace`] = null
})
const renderVN = () => {
let { fixedType, fixedColumn, tableColumn, footerTableData } = props
const { footerRowClassName, footerCellClassName, footerRowStyle, footerCellStyle, footerAlign: allFooterAlign, footerSpanMethod, align: allAlign, columnKey, showFooterOverflow: allColumnFooterOverflow } = tableProps
const { visibleColumn } = tableInternalData
const { scrollXLoad, overflowX, scrollbarWidth, currentColumn, mergeFooterList } = tableReactData
const tooltipOpts = computeTooltipOpts.value
const columnOpts = computeColumnOpts.value
// 如果是使用优化模式
if (fixedType) {
// 如果存在展开行使用全量渲染
if (!tableReactData.expandColumn && (scrollXLoad || allColumnFooterOverflow)) {
if (!mergeFooterList.length || !footerSpanMethod) {
tableColumn = fixedColumn
} else {
tableColumn = visibleColumn
}
} else {
tableColumn = visibleColumn
}
}
return h('div', {
ref: refElem,
class: ['vxe-table--footer-wrapper', fixedType ? `fixed-${fixedType}--wrapper` : 'body--wrapper'],
xid: xID,
onScroll: scrollEvent
}, [
fixedType
? createCommentVNode()
: h('div', {
ref: refFooterXSpace,
class: 'vxe-body--x-space'
}),
h('table', {
ref: refFooterTable,
class: 'vxe-table--footer',
xid: xID,
cellspacing: 0,
cellpadding: 0,
border: 0
}, [
/**
* 列宽
*/
h('colgroup', {
ref: refFooterColgroup
}, tableColumn.map((column, $columnIndex) => {
return h('col', {
name: column.id,
key: $columnIndex
})
}).concat(scrollbarWidth
? [
h('col', {
name: 'col_gutter'
})
]
: [])),
/**
* 底部
*/
h('tfoot', {
ref: refFooterTFoot
}, footerTableData.map((list, _rowIndex) => {
const $rowIndex = _rowIndex
const rowParams = { $table: $xeTable, row: list, _rowIndex, $rowIndex, fixed: fixedType, type: renderType }
return h('tr', {
class: ['vxe-footer--row', footerRowClassName ? XEUtils.isFunction(footerRowClassName) ? footerRowClassName(rowParams) : footerRowClassName : ''],
style: footerRowStyle ? (XEUtils.isFunction(footerRowStyle) ? footerRowStyle(rowParams) : footerRowStyle) : null
}, tableColumn.map((column, $columnIndex) => {
const { type, showFooterOverflow, footerAlign, align, footerClassName, editRender, cellRender } = column
const renderOpts = editRender || cellRender
const compConf = renderOpts ? renderer.get(renderOpts.name) : null
const showAllTip = tooltipOpts.showAll
const isColGroup = column.children && column.children.length
const fixedHiddenColumn = fixedType ? column.fixed !== fixedType && !isColGroup : column.fixed && overflowX
const footOverflow = XEUtils.eqNull(showFooterOverflow) ? allColumnFooterOverflow : showFooterOverflow
const footAlign = footerAlign || (compConf ? compConf.tableFooterCellAlign : '') || allFooterAlign || align || (compConf ? compConf.tableCellAlign : '') || allAlign
let showEllipsis = footOverflow === 'ellipsis'
const showTitle = footOverflow === 'title'
const showTooltip = footOverflow === true || footOverflow === 'tooltip'
let hasEllipsis = showTitle || showTooltip || showEllipsis
const attrs: any = { colid: column.id }
const tfOns: any = {}
const columnIndex = $xeTable.getColumnIndex(column)
const _columnIndex = $xeTable.getVTColumnIndex(column)
const itemIndex = _columnIndex
const cellParams: VxeTableDefines.CellRenderFooterParams = {
$table: $xeTable,
$grid: $xeTable.xegrid,
row: list,
rowIndex: _rowIndex,
_rowIndex,
$rowIndex,
column,
columnIndex,
$columnIndex,
_columnIndex,
itemIndex,
items: list,
fixed: fixedType,
type: renderType,
data: footerTableData
}
// 纵向虚拟滚动不支持动态行高
if (scrollXLoad && !hasEllipsis) {
showEllipsis = hasEllipsis = true
}
if (showTitle || showTooltip || showAllTip) {
tfOns.onMouseenter = (evnt: MouseEvent) => {
if (showTitle) {
updateCellTitle(evnt.currentTarget, column)
} else if (showTooltip || showAllTip) {
$xeTable.triggerFooterTooltipEvent(evnt, cellParams)
}
}
}
if (showTooltip || showAllTip) {
tfOns.onMouseleave = (evnt: MouseEvent) => {
if (showTooltip || showAllTip) {
$xeTable.handleTargetLeaveEvent(evnt)
}
}
}
tfOns.onClick = (evnt: MouseEvent) => {
$xeTable.dispatchEvent('footer-cell-click', Object.assign({ cell: evnt.currentTarget }, cellParams), evnt)
}
tfOns.onDblclick = (evnt: MouseEvent) => {
$xeTable.dispatchEvent('footer-cell-dblclick', Object.assign({ cell: evnt.currentTarget }, cellParams), evnt)
}
// 合并行或列
if (mergeFooterList.length) {
const spanRest = mergeFooterMethod(mergeFooterList, _rowIndex, _columnIndex)
if (spanRest) {
const { rowspan, colspan } = spanRest
if (!rowspan || !colspan) {
return null
}
if (rowspan > 1) {
attrs.rowspan = rowspan
}
if (colspan > 1) {
attrs.colspan = colspan
}
}
} else if (footerSpanMethod) {
// 自定义合并方法
const { rowspan = 1, colspan = 1 } = footerSpanMethod(cellParams) || {}
if (!rowspan || !colspan) {
return null
}
if (rowspan > 1) {
attrs.rowspan = rowspan
}
if (colspan > 1) {
attrs.colspan = colspan
}
}
return h('td', {
class: ['vxe-footer--column', column.id, {
[`col--${footAlign}`]: footAlign,
[`col--${type}`]: type,
'col--last': $columnIndex === tableColumn.length - 1,
'fixed--hidden': fixedHiddenColumn,
'col--ellipsis': hasEllipsis,
'col--current': currentColumn === column
}, getPropClass(footerClassName, cellParams), getPropClass(footerCellClassName, cellParams)],
...attrs,
style: footerCellStyle ? (XEUtils.isFunction(footerCellStyle) ? footerCellStyle(cellParams) : footerCellStyle) : null,
...tfOns,
key: columnKey || columnOpts.useKey ? column.id : $columnIndex
}, [
h('div', {
class: ['vxe-cell', {
'c--title': showTitle,
'c--tooltip': showTooltip,
'c--ellipsis': showEllipsis
}]
}, column.renderFooter(cellParams))
])
}).concat(scrollbarWidth
? [
h('td', {
class: 'vxe-footer--gutter col--gutter'
})
]
: []))
}))
])
])
}
return renderVN
}
})