当前位置: 首页 > Web前端 > vue.js

一个拖拽的表格满足需求

时间:2023-04-01 00:21:23 vue.js

公司PM又来捣乱了。我必须制作一个拖放表格。拖放表头可以调整位置,也可以调整宽度。。。市面上现成的轮子,或者拖拽位置,或者拖拽宽度,随便搜一下,或者自己造轮子,怎么样你想自己造轮子。刚开始需求的时候,按照开源大佬的提示,写了这么一个拖拽式的,高高兴兴的交给PM看,却被狠狠的拒绝了,“我希望能够负担得起的头可以放下”:PM这么说。好吧,好吧,我写,我写。需求写完了,我来梳理一下思路。看起来很难,但是写出一个很难实际完成而不觉得难的需求是很难的。还是花样不少。首先要明确需求,表格形式,要能够把当前点击的表头挑出来,然后拖动。当它经过左右任意一个header的一半宽度时,在header边的左右添加一个2像素的图标,松开鼠标,将拾取的header放在竖线上,即标题的拖动。其次,表头的宽度可以任意拖动。关键是两端必须有固定的柱子。需求很明确,那我们就开始梳理思路,把事情做起来吧。饿了么的表格有现成的拖动宽度,固定列,那就用饿了么的表格组件,emmm,组长说了,自己用div布局,元素太多展开不了,会有使用饿了么组件的限制。好吧好吧,我自己写的!首先确定表头的数据格式。这里还是需要向饿了么学习。做妓女一时的爽,一直做妓女的快乐~tableHeader[{label:'表头名称',prop:'name',//当前列数据对应的字段很重要width:''//当前宽度}]创建一个数组来存储表头(行),调整数组的顺序来调整列表的排序,通过数组{{col.label}}

{{draggingText}}
这样我们就渲染了一个表头。至于方法,我们会说classNamecolumn_是用来区分各个表头的。毕竟操作表头的宽度肯定会操作dom。mousedown方法是当前鼠标点击事件,即用户操作宽度或调整位置的触发点。mousemove用于记录鼠标移动。这里的mousemove主要是用来拖动表头的宽度,鼠标样式的效果。接下来是拖动表头的宽度。之所以先写到这里,是因为整个拖拖拉拉的想法都是白搭。你饿了吗?表格宽度调整首先我们需要梳理一下拖拽需要哪些元素。这里应用了GetBoundingClientRect。FunctionconsttableEl=this.$refs.dTable//获取外层父实例consttableLeft=tableEl.getBoundingClientRect().left//父实例到屏幕左侧的距离constcolumnEl=this.$el.querySelector('.column_'+i)//获取当前点击的元素domconstcolumnRect=columnEl.getBoundingClientRect()constminLeft=columnRect.left-tableLeft+30//为什么要加30?this.dragState={startMouseLeft:event.clientX,//鼠标当前的x坐标,设为起点startLeft:columnRect.right-tableLeft,//从左边开始的初始值//距离表头tableLeft左侧的距离}然后我们设置鼠标经过时的样式变化,在鼠标经过的元素右侧8个像素处将鼠标指针更改为col-resize,并将当前点的拖动锁定设置为拖动宽度。//鼠标移动事件,添加鼠标样式handleMouseMove(event,i){lettarget=event.target;如果(!this.dragging){让rect=target.getBoundingClientRect();constbodyStyle=document.body.style;如果(rect.width>12&&rect.right-event.pageX<8){bodyStyle.cursor='col-resize';target.style.cursor='col-resize';this.draggingColumn=true}elseif(!this.dragging){bodyStyle.cursor='';target.style.cursor='移动';this.draggingColumn=null;}}},然后开始写鼠标拖动表头宽度,我们先准备必要的元素this.dragging=true;//启用拖动this.resizeProxyVisible=true;//显示拖线实例constresizeProxy=this.$refs.resizeProxy;//获取实例resizeProxy.style.left=this.dragState.startLeft+'px';修改左边距document.onselectstart=function(){returnfalse;};//禁止文本被选中document.ondragstart=function(){returnfalse;};//禁止拖动//添加监听事件监听鼠标移动,引发事件document.addEventListener('mousemove',handleMouseMove);document.addEventListener('mouseup',handleMouseUp);然后我们设置鼠标移动效果consthandleMouseMove=(event)=>{constdeltaLeft=event.clientX-this.dragState.startMouseLeft;//获取鼠标实际滚动的距离constproxyLeft=this.dragState.startLeft+deltaLeft;//当前位置resizeProxy.style.left=Math.max(minLeft,proxyLeft)+'px';//设置竖线位置};现在我们设置鼠标弹起事件consthandleMouseUp=()=>{if(this.dragging){const{startColumnLeft,startLeft}=this.dragState;constfinalLeft=parseInt(resizeProxy.style.left,10);让columnWidth=finalLeft-startColumnLeft;if(columnWidth<68)columnWidth=68//最小拖动你可以根据需要定义宽度。this.tableHeader[i].width=columnWidthconstparams=JSON.stringify(this.tableHeader)//这里执行请求事件,将表头以json格式发送给后端,并保存当前设置的当前宽度完成后设置元素columnEl.style.width=columnWidth+'px'document.body.style.cursor='';this.dragging=false;this.draggingColumn=null;this.dragState={};this.resizeProxyVisible=false;}//移除监听事件document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);文档.onselectstart=null;文档。ondragstart=null;};这样,拖动宽度的方法就完成了。主要参考饿了么表格宽度调整。拖放标题。下面我们开始编写拖放表头元素。这个想法与拖动宽度相同。我们创建一个dom(draggingDom)作为被拾取的元素,然后当我们移动超过左右元素宽度的一半时,我们设置前(后)元素的边框。刚开始写到这里的时候,陷入了一个思路的误区,卡了很久。感谢好朋友晴美志尚提供中线思路,不然我还傻乎乎的动态计算元素宽度呢。我们将整个header元素模拟成一个坐标轴,鼠标当前点击的节点为(0,0),向左为负(dragging_left),向右为正(dragging_right)。同样的,我们先准备好需要的元素,先计算出每个header结构的中线位置,时间紧,这里有两种方法计算,等有空再review再修改。(如果哪位大佬看完有更好的方法,请联系我,我请你喝咖啡,我太懒了。。。)//左中线数组getPreLine(i){letarr=[]let_this=thisconstsetPreWith=(n)=>{if(!n)returnfalseletlen=this.getDomsWidth(n,i)+_this.tableHeader[n-1].width/2arr.push(-len)setPreWith(n-1)}setPreWith(i)constnewArr=arr.reverse()returnnewArr},//获取相邻宽度集合getDomsWidth(k,j){let_this=thisletlen=0for(leti=k;i{//MoveconstdeltaLeft=event.clientX-this.dragState.startMouseLeft;//实际位移距离constproxyLeft=deltaLeft+columnRect.left-20//位移后到左边的距离this.$refs.draggingDom.style.left=proxyLeft+'px';//生成初始位置//点击为原点,向左为负,向右为正,我们只需要比较前后两个dom的中线即可if(deltaLeft<0){this.dragState.direction='left'//如果移动距离小于上一个dom中线if(deltaLeft=lineArr[end]){removeClass(this.$el.querySelector('.column_'+end),'dragging_left')removeClass(this.$el.querySelector('.column_'+end),'dragging_right')++endaddClass(this.$el.querySelector('.column_'+end),'dragging_'+this.dragState.direction)}}elseif((deltaLeft>0)){this.dragState.direction='right'if(deltaLeft>lineArr[end+1]){removeClass(this.$el.querySelector('.column_'+end),'dragging_left')removeClass(this.$el.querySelector('.column_'+end),'dragging_right')++endaddClass(this.$el.querySelector('.column_'+end),'dragging_'+this.dragState.direction)}elseif(deltaLeft<=lineArr[end]){removeClass(this.$el.querySelector('.column_'+end),'dragging_left')removeClass(this.$el.querySelector('.column_'+end),'dragging_right')--endaddClass(this.$el.querySelector('.column_'+end),'dragging_'+this.dragState.direction)}}};最后我们看一下鼠标抬起时的事件consthandleMouseUp=(event)=>{//当鼠标抬起时,我们先移动移除当前边removeClass(this.$el.querySelector('.column_'+end),'dragging_left')removeClass(this.$el.querySelector('.column_'+end),'dragging_right')end!==this.activeIndex&&this.setThDom(end)//这里可以将修改后的值传给后端this.draggingDomVisible=falsethis.draggingcell=falsethis.dragState={};document.removeEventListener('mousemove',handleMouseMove);document.removeEventListener('mouseup',handleMouseUp);文档.onselectstart=null;document.ondragstart=null;}和拖动宽度一样,我们也需要监听鼠标事件好了,拖动表格和拖动宽度就完成了。