Ant Design table实现表头拖拽 官方 vue-draggable-resizable的魔改版
这里顺便也说一下使用官方vue-draggable-resizable组件,可能也有人不喜欢魔改版呢,我是在这里学习官方组件的使用的
官方组件的表头宽度问题大家应该要注意一下,就是全部表头的宽度不大于表格的宽度的话就会出现错乱,解决办法我就写在我的魔改版里面了。
这个魔改版是由于自己使用官方提供的 vue-draggable-resizable 出现卡顿的情况,而自己看了ElementUI的拖拽发现是真的丝滑,所以就尝试模仿一下,尝试丝滑的味道!!!
最终实现的效果如下图:
这个魔改版是用不到官方的vue-draggable-resizable组件的,就是一个div标签搞定了。
多看注释,我基本都是写在注释里面!重要:每个表头必须带dataIndex这个属性。
开始魔改:
先把主要代码放出来,在进行详细解析解析!像表头这些变量名你们看一下应该就看得懂了,我也没有大改这些名字,我就不写出来了,因为你们肯定是用过ant得表格再来的。
//这是表头从新渲染的函数
//从这里开始
(h, props, children) => {
let thDom = null;
let resize = false
if (!vm.visibleColumns || vm.visibleColumns[vm.visibleColumns.length-1].title == '')resize = true//这个是由于我的问题导致渲染报错,大家可以注释掉或者根据自己的情况改改
const { key, ...restProps } = props;
if(typeof key == 'number')return
let col
if (key === 'selection-column') {
col = {};
} else {
col = vm.visibleColumns.find((item) => {
const k = item.dataIndex || item.key;
return k === key;
});
}
if (!col || !col.width) {
return <th {...restProps} >{children}</th>;
}
//到这里都是经过上面那篇文章学习的原代码,支持多选滴!!!
//下面开始就是我自己的代码啦!
let start = false
let startX = 0;
let move = 0;
let touch = null
//鼠标移动函数
const onmousemove =(e) =>{
if (start){
move = e.clientX-startX
touch.target.style.transform=`translateX(${move}px)`
if (col.width+move < 40){//这里限制40是因为表头最小不想让它小于40
onmouseup()
}
}
}
//按下函数,记录下按下的位置
const moveStart =(e)=>{
start = true
startX = e.clientX
touch = e//把按下的目标存好,因为移出表格之后这个事件对象会变的。
touch.target.classList.add('border_right')//这个是我添加给它移动的时候的样式而已。
document.addEventListener('selectstart', stopSelect);//给document添加一个不能够选中文本的事件,不然会出现边拉边选中了一堆文字内容。
}
let stopSelect = (e) =>{
e.preventDefault();
}
//这是移出目标的时候依然可以拖拉的函数
let moveout = (e) =>{
if (start){
move = e.clientX-startX
touch.target.style.transform=`translateX(${move}px)`
if (col.width+move < 40){onmouseup(e)}
}
}
//在目标外按键弹起把之前的事件清除掉
let clear = (e)=>{
if (start){
onmouseup(e)
touch = null
start = false
document.removeEventListener('mousemove',moveout)
}
}
let outEvent = () =>{
if (start){
document.addEventListener('mousemove',moveout)
document.addEventListener('mouseup',clear)
}
}
//这是在目标内按键弹起的函数
const onmouseup = (e) =>{
document.removeEventListener('selectstart', stopSelect)
document.removeEventListener('mousemove',moveout)
start = false
touch.target.classList.remove('border_right')
let allWidth=0;//这里需要先计算好表头的宽度,
vm.visibleColumns.forEach((item, index)=>{
if (item.hasOwnProperty('width') && item.dataIndex !== 'fill'){
allWidth += item.width
}
})
let movedDistance = col.width+move;
let temArr = vm.visibleColumns.find(item =>{
return item.title == col.title
})
if (movedDistance<=40){
allWidth = allWidth - col.width + 40
}else {
allWidth = allWidth - col.width + movedDistance
}
let tem = document.getElementById('standard-table').getBoundingClientRect().width - 18
if(vm.visibleColumns[vm.visibleColumns.length-1].dataIndex !== 'fill'){
vm.visibleColumns.push({
title:'',
width:1,
dataIndex:'fill'
})
}
if (allWidth<tem){
vm.visibleColumns[vm.visibleColumns.length-1].width = tem-allWidth-(!this.rowSelection?0:41)//41是表头多选框的宽度
}else{
vm.visibleColumns[vm.visibleColumns.length-1].width = 1
}
col.width = movedDistance <=40?40:movedDistance
touch.target.style.transform='translateX(0)'
move=0
touch = null
this.$refs.actionColumns.saveVisualBlock('fa')
}
return (
<th {...restProps} v-ant-ref={r => (thDom = r)} width={col.width} class="resize-table-th" >
{children}
<div
class='table-draggable-handle'
onmousedown={moveStart}
onmousemove={onmousemove}
onmouseup={onmouseup}
onmouseleave={outEvent}//这是鼠标移出目标区域的时候依然支持拖拉
> </div>
</th>
)
详细解析开始:
1、保持前面的代码
let thDom = null;
let resize = false
if (!vm.visibleColumns || vm.visibleColumns[vm.visibleColumns.length-1].title == '')resize = true//这个是由于我的问题导致渲染报错,大家可以注释掉或者根据自己的情况改改
const { key, ...restProps } = props;
if(typeof key == 'number')return
let col
if (key === 'selection-column') {
col = {};
} else {
col = vm.visibleColumns.find((item) => {
const k = item.dataIndex || item.key;
return k === key;
});
}
if (!col || !col.width) {
return <th {...restProps} >{children}</th>;
}
2、修改官方组件
直接删除,改成div标签。
return (
<th {...restProps} v-ant-ref={r => (thDom = r)} width={col.width} class="resize-table-th" >
{children}
<div
class='table-draggable-handle'//保持样式,但是样式有一点要改。
onmousedown={moveStart}
onmousemove={onmousemove}
onmouseup={onmouseup}
onmouseleave={outEvent}//这是鼠标移出目标区域的时候依然支持拖拉
> </div>
</th>
)
看了这些代码一眼就舒服,div中有个class是使用原来的样式,但是有些要修改的,我就直接上最终的CSS代码。一共需要绑定四个事件,
- onmousedown
const moveStart =(e)=>{
start = true//可移动标志位改变
startX = e.clientX//记录开始位置的X点
touch = e//把按下的目标存好,因为移出表格之后这个事件对象会变的。
touch.target.classList.add('border_right')//这个是我添加给它移动的时候的样式而已。
document.addEventListener('selectstart', stopSelect);//给document添加一个不能够选中文本的事件,不然会出现边拉边选中了一堆文字内容。
}
点击的时候记录开始点击的位置以及修改可进入移动的状态的标志位:start
2. onmousemove
const onmousemove =(e) =>{
if (start){
move = e.clientX-startX//计算移动的距离
touch.target.style.transform=`translateX(${move}px)`//使用transform来实现移动的效果,这样就不会出现卡顿
if (col.width+move < 40){//这里限制40是因为表头最小不想让它小于40
onmouseup()//小于40就直接结束移动
}
}
}
移动的时候不修改原表头的宽度这些,使用transform来实现移动的效果
4. onmouseup
const onmouseup = (e) =>{
document.removeEventListener('selectstart', stopSelect)//去除禁止选中文本事件
document.removeEventListener('mousemove',moveout)//去除在目标外的移动事件
start = false//改变移动的标志位,不可进行移动
touch.target.classList.remove('border_right')
let allWidth=0;//这里需要先计算好表头的宽度,
//先计算完在把原表头宽度赋值的原因是:会出现表格错乱,
//1、先计算好全部表格的宽度
vm.visibleColumns.forEach((item, index)=>{
if (item.hasOwnProperty('width') && item.dataIndex !== 'fill'){
allWidth += item.width
}
})
//2、计算好最终目标表头宽度
let movedDistance = col.width+move;
//3、把总宽度计算好
if (movedDistance<=40){
allWidth = allWidth - col.width + 40
}else {
allWidth = allWidth - col.width + movedDistance
}
//4、这是获取表格组件的宽度,18是边距,你们要看看你们的,不然可能会出现一点错位
let tem = document.getElementById('standard-table').getBoundingClientRect().width - 18
if(vm.visibleColumns[vm.visibleColumns.length-1].dataIndex !== 'fill'){
//5、大坑的填补方法,使用一个空表头来填剩下来的位置,就给表头最后添加一个空表头
vm.visibleColumns.push({
title:'',
width:1,
dataIndex:'fill'
})
}
//6、这里比较计算后总表头的宽度和表格组件的宽度比较,大于的话就把填坑大哥的宽度设为1,
//否则就计算:把表格宽度减去总表头的宽度,有选择的话就需要减去那个按钮的宽度,最后赋值到填坑大哥那里去
if (allWidth<tem){
vm.visibleColumns[vm.visibleColumns.length-1].width = tem-allWidth-(!this.rowSelection?0:41)//41是表头多选框的宽度
}else{
vm.visibleColumns[vm.visibleColumns.length-1].width = 1
}
//7、最后就把之前的东西都清空
col.width = movedDistance <=40?40:movedDistance
touch.target.style.transform='translateX(0)'
move=0
touch = null
}
把最终位置的X点和开始的X点进行计算表头是加宽了还是变窄了,然后再改变原表头的宽度
6. onmouseleave
//离开目标对象的时候要添加移动事件和弹起事件。
let outEvent = () =>{
if (start){
document.addEventListener('mousemove',moveout)
document.addEventListener('mouseup',clear)
}
}
//在目标对象外结束的时候清空那些东西
let clear = (e)=>{
if (start){
onmouseup(e)
touch = null
start = false
document.removeEventListener('mousemove',moveout)
}
}
let moveout = (e) =>{
if (start){
move = e.clientX-startX
touch.target.style.transform=`translateX(${move}px)`
if (col.width+move < 40){onmouseup(e)}
}
}
超出点击目标的时候依然可以移动,并且也给document添加mouseup事件,弹起的时候就进行上一个函数一样的计算点的位置
3、修改CSS样式
/deep/ .resize-table-th {
position: relative;
.table-draggable-handle {
/*transform: none !important;这个要删除是因为使用transform的话就很丝滑*/
position: absolute;
height: 100% !important;
bottom: 0;
left: auto !important;
right: -10px;
cursor: col-resize;
touch-action: none;
width: 20px;
}
.table-draggable-handle:hover{
background-image: linear-gradient(to right,#FAFAFA 40%,#36A3FF 50%,#FAFAFA 60%);
opacity: 0.7;
}
}
这些不多讲自己看一下注释,只有一个重点。
4、修改一个比较坑的地方
刚开始说的总的表头宽度比表格的宽度小的话就会出现表格内容错乱。比如:表格的宽度是1000px,而你只有2个表头,一个100px,那总表头宽度是200px,就会有800px的空缺,如果不用一个空表头去填这个坑的话表格就会出现错乱。所以在上面的代码就会出现push一个title是空的表头信息,目前我想到的解决办法就是这个。