1. 问题
今天在写需求的时候遇到一个问题,本来好好的页面在我设置了固定列并且滚动到最后一行的时候表格数据发生了错位。原来是vxe-table在出现汇总页和左右侧固定列的时候,最后一行数据会出现错位。 如下图所示
2. 解决过程
- 通过分析样式发现左侧固定列和右侧固定列的max-height为610px,但是中间主体内容的max-height为610px-58px(底部汇总行的高度)=552px,我想可能是vxe-table作者在写这个库的时候忘记减去这个高度了。对于这个原因,我采用了三种方法解决。
- 既然是两边长了,那我就放一个元素下去把它垫起来。于是我只要在"vxe-table--checkbox-range"这个class的前面加一个高度为58px的元素,就能让错位复原。
代码如下
this.$nextTick(() => {
if (this.isFirstRender) {
const p = document.querySelector('.vxe-table--fixed-left-wrapper .vxe-table--body-wrapper')
const div = document.createElement('div')
div.style.height = '58px'
p.insertBefore(div, p.lastChild)
this.isFirstRender = false
}
})
实现的效果如下
这样实现有一个缺点,在拖到滚动调快速滑到底部的时候,还是会错位
- 既然上面的方法不是太好,并且我认为这样实现也不太优雅,行吧,去它的issues上看看,有没有大佬遇到同样的问题,可惜,没有找到类似的问题。没办法了,磕源码去。找呀找,挖呀挖,计算MaxHeight方法大概就是这一行
在vxe-table有fixed列的时候customMaxHeight-headerHeight,在显示合计行的时候,前面相减的部分减去的是0,这里应该是底部合计行的高度。所以这里只要再减去底部合计行的高度就没问题了。又到了给组件打补丁的时候了,源码里面的methods.js文件是直接引入到table.js文件并且直接合并的,只不过这里采用的是render函数的渲染方式。所以我们继承Table组件,重写它方法,应该就能达到想要的结果。
直接上代码了
<script>
import DomTools, { browse } from 'vxe-table/packages/tools/dom'
import { Table } from 'vxe-table'
import XEUtils from 'xe-utils'
export default {
extends: Table,
methods: {
updateStyle() {
const { isNodeElement } = DomTools
let {
$refs,
isGroup,
fullColumnIdData,
tableColumn,
customHeight,
customMaxHeight,
border,
headerHeight,
showFooter,
showOverflow: allColumnOverflow,
showHeaderOverflow: allColumnHeaderOverflow,
showFooterOverflow: allColumnFooterOverflow,
footerHeight,
tableHeight,
tableWidth,
scrollbarHeight,
scrollbarWidth,
scrollXLoad,
scrollYLoad,
cellOffsetWidth,
columnStore,
elemStore,
editStore,
currentRow,
mouseConfig,
keyboardConfig,
keyboardOpts,
spanMethod,
mergeList,
mergeFooterList,
footerSpanMethod,
isAllOverflow,
visibleColumn,
} = this
const containerList = ['main', 'left', 'right']
const emptyPlaceholderElem = $refs.emptyPlaceholder
const bodyWrapperElem = elemStore['main-body-wrapper']
if (emptyPlaceholderElem) {
emptyPlaceholderElem.style.top = `${headerHeight}px`
emptyPlaceholderElem.style.height = bodyWrapperElem ? `${bodyWrapperElem.offsetHeight - scrollbarHeight}px` : ''
}
if (customHeight > 0) {
if (showFooter) {
customHeight += scrollbarHeight
}
}
containerList.forEach((name, index) => {
const fixedType = index > 0 ? name : ''
const layoutList = ['header', 'body', 'footer']
const fixedColumn = columnStore[`${fixedType}List`]
const fixedWrapperElem = $refs[`${fixedType}Container`]
layoutList.forEach(layout => {
const wrapperElem = elemStore[`${name}-${layout}-wrapper`]
const tableElem = elemStore[`${name}-${layout}-table`]
if (layout === 'header') {
// 表头体样式处理
// 横向滚动渲染
let tWidth = tableWidth
let renderColumnList = tableColumn
if (isGroup) {
renderColumnList = visibleColumn
} else {
// 如果是使用优化模式
if (fixedType) {
if (scrollXLoad || allColumnHeaderOverflow) {
renderColumnList = fixedColumn
}
}
}
tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth + scrollbarWidth}px` : ''
// 修复 IE 中高度无法自适应问题
if (browse.msie) {
XEUtils.arrayEach(tableElem.querySelectorAll('.vxe-resizable'), resizeElem => {
resizeElem.style.height = `${resizeElem.parentNode.offsetHeight}px`
})
}
}
const repairElem = elemStore[`${name}-${layout}-repair`]
if (repairElem) {
repairElem.style.width = `${tableWidth}px`
}
const listElem = elemStore[`${name}-${layout}-list`]
if (isGroup && listElem) {
XEUtils.arrayEach(listElem.querySelectorAll('.col--group'), thElem => {
const colNode = this.getColumnNode(thElem)
if (colNode) {
const column = colNode.item
const { showHeaderOverflow } = column
const cellOverflow = XEUtils.isBoolean(showHeaderOverflow) ? showHeaderOverflow : allColumnHeaderOverflow
const showEllipsis = cellOverflow === 'ellipsis'
const showTitle = cellOverflow === 'title'
const showTooltip = cellOverflow === true || cellOverflow === 'tooltip'
const hasEllipsis = showTitle || showTooltip || showEllipsis
let childWidth = 0
let countChild = 0
if (hasEllipsis) {
XEUtils.eachTree(column.children, item => {
if (!item.children || !column.children.length) {
countChild++
}
childWidth += item.renderWidth
})
}
thElem.style.width = hasEllipsis ? `${childWidth - countChild - (border ? 2 : 0)}px` : ''
}
})
}
} else if (layout === 'body') {
const emptyBlockElem = elemStore[`${name}-${layout}-emptyBlock`]
if (isNodeElement(wrapperElem)) {
if (customMaxHeight) {
// 多减去footerHeight
wrapperElem.style.maxHeight = `${fixedType ? customMaxHeight - headerHeight - (showFooter ? footerHeight : scrollbarHeight) : customMaxHeight - headerHeight - footerHeight}px`
} else {
if (customHeight > 0) {
wrapperElem.style.height = `${fixedType ? (customHeight > 0 ? customHeight - headerHeight - footerHeight : tableHeight) - (showFooter ? 0 : scrollbarHeight) : customHeight - headerHeight - footerHeight}px`
} else {
wrapperElem.style.height = ''
}
}
}
// 如果是固定列
if (fixedWrapperElem) {
const isRightFixed = fixedType === 'right'
const fixedColumn = columnStore[`${fixedType}List`]
if (isNodeElement(wrapperElem)) {
wrapperElem.style.top = `${headerHeight}px`
}
fixedWrapperElem.style.height = `${(customHeight > 0 ? customHeight - headerHeight - footerHeight : tableHeight) + headerHeight + footerHeight - scrollbarHeight * (showFooter ? 2 : 1)}px`
fixedWrapperElem.style.width = `${fixedColumn.reduce((previous, column) => previous + column.renderWidth, isRightFixed ? scrollbarWidth : 0)}px`
}
let tWidth = tableWidth
let renderColumnList = tableColumn
// 如果是使用优化模式
if (fixedType) {
// 如果存在展开行使用全量渲染
if (!this.expandColumn && (scrollXLoad || scrollYLoad || (allColumnOverflow ? isAllOverflow : allColumnOverflow))) {
if (!mergeList.length && !spanMethod && !(keyboardConfig && keyboardOpts.isMerge)) {
renderColumnList = fixedColumn
} else {
renderColumnList = visibleColumn
}
} else {
renderColumnList = visibleColumn
}
}
tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth}px` : ''
// 兼容性处理
tableElem.style.paddingRight = scrollbarWidth && fixedType && (browse['-moz'] || browse.safari) ? `${scrollbarWidth}px` : ''
}
if (emptyBlockElem) {
emptyBlockElem.style.width = tWidth ? `${tWidth}px` : ''
}
} else if (layout === 'footer') {
let tWidth = tableWidth
let renderColumnList = tableColumn
// 如果是使用优化模式
if (fixedType) {
// 如果存在展开行使用全量渲染
if (!this.expandColumn && (scrollXLoad || allColumnFooterOverflow)) {
if (!mergeFooterList.length || !footerSpanMethod) {
renderColumnList = fixedColumn
} else {
renderColumnList = visibleColumn
}
} else {
renderColumnList = visibleColumn
}
}
tWidth = renderColumnList.reduce((previous, column) => previous + column.renderWidth, 0)
if (isNodeElement(wrapperElem)) {
// 如果是固定列
if (fixedWrapperElem) {
wrapperElem.style.top = `${customHeight > 0 ? customHeight - footerHeight : tableHeight + headerHeight}px`
}
wrapperElem.style.marginTop = `${-scrollbarHeight}px`
}
if (tableElem) {
tableElem.style.width = tWidth ? `${tWidth + scrollbarWidth}px` : ''
}
}
const colgroupElem = elemStore[`${name}-${layout}-colgroup`]
if (colgroupElem) {
XEUtils.arrayEach(colgroupElem.children, colElem => {
const colid = colElem.getAttribute('name')
if (colid === 'col_gutter') {
colElem.style.width = `${scrollbarWidth}px`
}
if (fullColumnIdData[colid]) {
const column = fullColumnIdData[colid].column
const { showHeaderOverflow, showFooterOverflow, showOverflow } = column
let cellOverflow
colElem.style.width = `${column.renderWidth}px`
if (layout === 'header') {
cellOverflow = XEUtils.isUndefined(showHeaderOverflow) || XEUtils.isNull(showHeaderOverflow) ? allColumnHeaderOverflow : showHeaderOverflow
} else if (layout === 'footer') {
cellOverflow = XEUtils.isUndefined(showFooterOverflow) || XEUtils.isNull(showFooterOverflow) ? allColumnFooterOverflow : showFooterOverflow
} else {
cellOverflow = XEUtils.isUndefined(showOverflow) || XEUtils.isNull(showOverflow) ? allColumnOverflow : showOverflow
}
const showEllipsis = cellOverflow === 'ellipsis'
const showTitle = cellOverflow === 'title'
const showTooltip = cellOverflow === true || cellOverflow === 'tooltip'
let hasEllipsis = showTitle || showTooltip || showEllipsis
const listElem = elemStore[`${name}-${layout}-list`]
// 滚动的渲染不支持动态行高
if (layout === 'header' || layout === 'footer') {
if (scrollXLoad && !hasEllipsis) {
hasEllipsis = true
}
} else {
if ((scrollXLoad || scrollYLoad) && !hasEllipsis) {
hasEllipsis = true
}
}
if (listElem) {
XEUtils.arrayEach(listElem.querySelectorAll(`.${column.id}`), elem => {
const colspan = parseInt(elem.getAttribute('colspan') || 1)
const cellElem = elem.querySelector('.vxe-cell')
let colWidth = column.renderWidth
if (cellElem) {
if (colspan > 1) {
const columnIndex = this.getColumnIndex(column)
for (let index = 1; index < colspan; index++) {
const nextColumn = this.getColumns(columnIndex + index)
if (nextColumn) {
colWidth += nextColumn.renderWidth
}
}
}
cellElem.style.width = hasEllipsis ? `${colWidth - (cellOffsetWidth * colspan)}px` : ''
}
})
}
}
})
}
})
})
if (currentRow) {
this.setCurrentRow(currentRow)
}
if (mouseConfig && mouseConfig.selected && editStore.selected.row && editStore.selected.column) {
this.addColSdCls()
}
return this.$nextTick()
},
},
}
</script>
这样就比较完美的解决了这个问题
- 还有一个方法就是,我的伙伴发现,如果是设置height就没有这样的问题,那如果我们还想达到max-height的效果就得在获取到列表的时候自己去算表格的高度,同时,每行的高度得固定。