一:背景
数据量较大时往往最采用虚拟树形,但是element的虚拟树形并不支持拖拽功能。
根据当前技术栈的选择以及改动成本的大小得出以下几种方案,并进行一一尝试。
方案一:采用vxe-table组件
此组件本身就支持拖动功能,如下图:

优点:
- 现场的拖动功能
- 项目中以及引用了此npm依赖包
缺点:
- 需改动ui风格,成本较大
- 当前树形组件都是基于element ui封装起来的,,两个组件库API方法不一
方案二:基于当前element 树形组件手写拖动功能
优点:
- ui风格与当前系统一致
- API保持一致
缺点:
- 需自己添加拖动功能
- 拖动效果达不到预期
2.1 拖动功能实现
2.1.1 借助soetablejs三方库


优点:
- 现成的拖拽逻辑以及效果
缺点:
- 效果和预期不一样,且不知能否自定义
2.1.2 原生方式
原生js中存在拖动的APIdrag
,但是拖动过程中经过的地方需要展示可放下的效果,如下图
此效果当前的实现思路是进行计算,但是实时计算耗能较大,且容易不精准,既然element存在此功能,边打开控制台查看下能否寻到灵感。
发现了如下打印:

分析关键字:enter
、over
、leave
,然我网上查询资料得到以下API:
网站:developer.mozilla.org/zh-CN/docs/...

由此可见拖动过程的开始拖动 、移入 、移除 、结束拖动等事件都是有原生API的,借助此类API便可实现想要的效果
拖动功能初步实现进行以下操作即可
- 元素开启
deaggable
属性

2.2 移入效果实现
思路:
deagenter
移入事件记录当前鼠标移入的元素


-
动态类决定当前是否显示移入效果
- 再已有基础上增加了禁止拖入的红色效果


2.3 拖动效果自定义
经资料查询,原生API有一个event.dataTransfer.serDragImage
的方法可以设置拖动的样式,最开始以为只能传图片,但是图片不能满足效果,又尝试了拖动过程中实时根据鼠标位置进行悬浮dom定位,但是此方式性能较差,移动快了容易跟随慢一拍。再经常尝试,其实原生api支持dom传入。最终实现如下:
-
拖动开始借助
dragStart
方法根据当前拖动的节点进行拖动样式的变化 -
自定义样式使用一个新的tree组件,保持拖动效果同当前一致
- 拖动样式进行优化,最多显示三个子级
dom增加事件回调

js逻辑处理

自定义样式dom
效果:

2.4 拖动过程中事件增加
拖动过程中判断能否拖入的逻辑需要再此处处理,长远考虑拖动功能可能其他模块也要用,所以拖动功能便和已有的树形组件封装再一起,然后通过传参的方式进行开启,相关事件均和普通树形API以及使用方式 保持一致,此方法要在移入 、结束拖拽事件中都生效,所以实现如下:
-
enter中触发此方法
-
end中触发此方法
当然如果没有此方法则代表都可拖入,优先才有proop传下来的方法

2.5 占位符效果实现
思路如下:
- 获取当前点击的node节点数据
- 添加新的节点
- 展开当前节点(可能是折叠状态)
- 滚动到新添加的节点位置
代码如下
代码如下:

注意事项:
占位符添加时机 ,要再内容区滚动结束后再调用,否则会影响右侧内容定位的精准度,因为右侧滚动条定位是需要再浏览器当前帧绘制完成后才滚动的,若绘制过程中左侧树形又发生绘制,则会导致右侧内容区定位不准。
3、问题优化
3.1、 滚动条拖拽问题优化:
当前问题:鼠标拖动滚动条时整体发生偏移,且影响后续滚动事件,如下图:

原因:对组件整体开启了deaggable
属性
解决方案:修改传参

3.2 新增时定位优化
当前定位时右侧内容区不能精准的滚动到对应位置。
解决思路如下:
- 只要是新增,右侧内容区便采用分页逻辑,重新对数据进行截取
- 截取完成后添加新节点
- 滚动到新节点位置,使其置顶
- 其余步骤保持一致

至此虚拟树形的拖拽效果以及使用方式以及和element 普通树形的效果以及使用方式保持一致,并且再某些效果上更佳。