前言
ant-design-vue1x官方文档推荐集成vue-draggable-resizable来实现表格的可伸缩列,但是官方demo有bug,且使用起来很麻烦,所以自己动手在ant-design-vue 1x表格组件的基础上实现列伸缩功能。
实现列伸缩功能的思路
在每个th里添加一个元素用来左右拖动列,通过absolute定位将拖动元素其定位到th的右边界处。给拖动元素绑定mousedown事件,在mousedown时开始给document绑定move事件监测左右拖动的距离,根据拖动距离设置当前拖动元素对应的th的宽度,设置th列宽可通过设置colgroup里的col的宽度来实现。
关键代码
表格使用的是template风格的API。
<a-table-column
v-for="(column, columnIdx) in columnsMap"
:key="column.dataIndex"
:align="column.align"
:dataIndex="column.dataIndex"
:width="column.width"
:fixed="column.fixed">
<!-- 表格 thead -->
<template #title>
<div class="column-title">
<div @mousedown="e => handleColumnResize(e, columnIdx, column.dataIndex)" class="column-resize"></div>
</div>
</template>
</a-table-column>
鼠标拖动事件处理
handleColumnResize(clickE, index, dataIndex) {
this.movingColumnKey = dataIndex
let originX = clickE.pageX
let moveX = 0
document.onmousemove = e => {
// 在拖动列时保持鼠标指针的样式
document.body.style.cursor = 'col-resize'
moveX = e.pageX - originX
if (moveX != 0) {
let colIndex = index
let length = 0
const refContainer = this.$refs.table
if (this.elColgroupList?.length > 0) {
length = this.elColgroupList.length
} else {
// 列拉伸收缩-获取所有col
this.elColgroupList = refContainer.getElementsByTagName('colgroup')
}
let computedMoveX = 0 // 实际的伸缩量
let finalWidth = 0 // 最终宽度
for (let i=0 ; i<length; i++) {
const group = this.elColgroupList[i]
if (group?.childNodes?.[colIndex]){
const target = group.childNodes[colIndex]
let currentW = target.style.width // 样式设置的宽度
const viewWidth = target.getBoundingClientRect().width // 实际视图的宽度
if (currentW) {
// 去除单位
currentW = parseInt(currentW.slice(0, -2))
} else {
currentW = viewWidth
}
// columnMinWidth为定义的最小列宽,防止列宽过小
if ((viewWidth + moveX) <= this.columnMinWidth) {
target.style.width = `${this.columnMinWidth}px`
target.style.minWidth = `${this.columnMinWidth}px`
target.width = this.columnMinWidth
computedMoveX = this.columnMinWidth - currentW
finalWidth = this.columnMinWidth
} else {
const movedW = currentW + moveX
target.style.width = `${movedW}px`
target.style.minWidth = `${movedW}px`
target.width = movedW
computedMoveX = moveX
finalWidth = movedW
}
}
}
// 如果列宽变化,同步更改表格宽度,不压缩其他列
if (this.elScrollTableList?.length === 0) {
const elScroll = refContainer.getElementsByClassName('ant-table-scroll')?.[0]
if (elScroll) {
this.elScrollTableList = elScroll.getElementsByTagName('table')
}
}
const tabLength = this.elScrollTableList.length
for (let i=0 ; i<tabLength; i++) {
const table = this.elScrollTableList[i]
const widthStr = table?.style?.width || ''
const len = widthStr.length
if (len > 2) {
const width = parseInt(widthStr.slice(0, len - 2))
table.style.width = `${width + computedMoveX}px`
}
}
const myEvent = new Event('resize')
setTimeout(() => {
window.dispatchEvent(myEvent)
}, 10)
originX = e.pageX
}
}
// mouseup或mouseleave事件,取消拖动列
document.onmouseup = e => {
document.body.style.cursor = 'auto'
this.movingColumnKey = ''
document.onmousemove = null
document.onmouseup = null
}
document.onmouseleave = e => {
document.body.style.cursor = 'auto'
document.onmousemove = null
document.onmouseleave = null
}
},
总结
上面提到的列伸缩是在每个th内部右边界处添加一个元素用于拖动,拖动时只是增大或减小当前th的宽度,最右一列的宽度伸缩也是在右边界处拖动。固定列需要固定宽度,没有支持伸缩。