介绍
本文将介绍如何使用 Vue.js 和 Element-UI 实现可拖拽的树形结构组件。树形结构组件是 Web 开发中常用的组件之一,它可以用于展示层级结构的数据,比如文件夹结构、部门结构等。在实际开发中,我们经常需要对树形结构进行拖拽排序、拖拽移动、拖拽复制等操作。本文将通过示例代码,演示如何使用 Vue.js 和 Element-UI 实现这些操作。
操作演示
在开始介绍代码实现之前,我们先来看一下最终实现的效果。

如上图所示,我们可以通过拖拽节点,实现对树形结构的排序、移动和复制。
实现步骤
安装依赖
首先,我们需要安装 Vue.js 和 Element-UI。
# 安装 Vue.js npm install vue # 安装 Element-UI npm install element-ui
创建组件
接下来,我们创建一个树形结构组件 Tree.vue,并引入 Element-UI 的 el-tree 组件。
// www.javascriptcn.com code example
<template>
<el-tree
:data="data"
:props="defaultProps"
:default-expand-all="true"
:draggable="true"
@node-drag-start="handleDragStart"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
></el-tree>
</template>
<script>
import { ElTree } from 'element-ui';
export default {
name: 'Tree',
components: { ElTree },
props: {
data: {
type: Array,
default: () => [],
},
},
data() {
return {
defaultProps: {
children: 'children',
label: 'label',
},
};
},
methods: {
handleDragStart(draggingNode, e) {
// TODO: 处理拖拽开始事件
},
handleDragOver(draggingNode, dropNode, dropType, e) {
// TODO: 处理拖拽过程中事件
},
handleDragEnd(draggingNode, dropNode, dropType, e) {
// TODO: 处理拖拽结束事件
},
},
};
</script>在上面的代码中,我们使用了 Element-UI 的 el-tree 组件,并通过 props 属性传入了树形结构的数据。同时,我们设置了 draggable 属性为 true,启用了拖拽功能。在 methods 中,我们定义了三个拖拽事件处理函数,分别是 handleDragStart、handleDragOver 和 handleDragEnd。
处理拖拽开始事件
在 handleDragStart 中,我们需要处理拖拽开始事件。在这个事件中,我们需要获取拖拽节点的信息,并设置一些拖拽相关的样式。
// www.javascriptcn.com code example
handleDragStart(draggingNode, e) {
// 获取拖拽节点的信息
this.draggingNode = draggingNode;
this.draggingNodeEl = e.target;
this.draggingNodeRect = this.draggingNodeEl.getBoundingClientRect();
// 设置拖拽节点的样式
this.draggingNodeEl.style.position = 'absolute';
this.draggingNodeEl.style.zIndex = '9999';
this.draggingNodeEl.style.opacity = '0.7';
// 创建占位节点
this.placeholderNodeEl = document.createElement('div');
this.placeholderNodeEl.style.height = `${this.draggingNodeRect.height}px`;
this.draggingNodeEl.parentNode.insertBefore(this.placeholderNodeEl, this.draggingNodeEl.nextSibling);
},在上面的代码中,我们通过 e.target 获取到拖拽节点的 DOM 元素,并通过 getBoundingClientRect() 获取到拖拽节点的位置和尺寸信息。然后,我们设置了拖拽节点的样式,包括设置 position 为 absolute、z-index 为 9999、opacity 为 0.7。接着,我们创建了一个占位节点 placeholderNodeEl,并将其插入到拖拽节点的后面。
处理拖拽过程中事件
在 handleDragOver 中,我们需要处理拖拽过程中的事件。在这个事件中,我们需要判断拖拽节点是否可以放置到当前节点的位置,并设置一些拖拽相关的样式。
// www.javascriptcn.com code example
handleDragOver(draggingNode, dropNode, dropType, e) {
// 判断拖拽节点是否可以放置到当前节点的位置
if (!dropNode || !this.draggingNodeEl || dropNode === this.draggingNode || dropNode.parent === this.draggingNode) {
return;
}
// 获取当前节点的信息
const dropNodeEl = e.target;
const dropNodeRect = dropNodeEl.getBoundingClientRect();
const isAbove = e.clientY < dropNodeRect.top + dropNodeRect.height / 2;
// 设置拖拽节点和占位节点的样式
this.draggingNodeEl.style.top = `${dropNodeRect.top - this.draggingNodeRect.height / 2}px`;
this.draggingNodeEl.style.left = `${dropNodeRect.left}px`;
this.placeholderNodeEl.style.display = 'block';
// 判断拖拽节点是在当前节点的上方还是下方
if (isAbove) {
dropNodeEl.parentNode.insertBefore(this.placeholderNodeEl, dropNodeEl);
} else {
dropNodeEl.parentNode.insertBefore(this.placeholderNodeEl, dropNodeEl.nextSibling);
}
},在上面的代码中,我们首先判断拖拽节点是否可以放置到当前节点的位置,如果不行则直接返回。接着,我们获取当前节点的 DOM 元素、位置和尺寸信息,并判断拖拽节点是在当前节点的上方还是下方。然后,我们设置拖拽节点和占位节点的样式,包括设置拖拽节点的 top 和 left,以及占位节点的 display。最后,我们根据拖拽节点是在当前节点的上方还是下方,将占位节点插入到对应的位置。
处理拖拽结束事件
在 handleDragEnd 中,我们需要处理拖拽结束事件。在这个事件中,我们需要将拖拽节点插入到目标节点的位置,并清除一些拖拽相关的样式。
// www.javascriptcn.com code example
handleDragEnd(draggingNode, dropNode, dropType, e) {
// 将拖拽节点插入到目标节点的位置
const parent = dropNode ? dropNode.parent : null;
const index = parent ? parent.children.indexOf(dropNode) : this.data.indexOf(dropNode);
parent ? parent.children.splice(index, 0, this.draggingNode) : this.data.splice(index, 0, this.draggingNode);
// 清除拖拽节点和占位节点的样式
this.draggingNodeEl.style.position = '';
this.draggingNodeEl.style.zIndex = '';
this.draggingNodeEl.style.opacity = '';
this.draggingNodeEl.style.top = '';
this.draggingNodeEl.style.left = '';
this.placeholderNodeEl.parentNode.removeChild(this.placeholderNodeEl);
// 重置拖拽节点的信息
this.draggingNode = null;
this.draggingNodeEl = null;
this.draggingNodeRect = null;
this.placeholderNodeEl = null;
},在上面的代码中,我们首先将拖拽节点插入到目标节点的位置,具体实现方式是通过 splice() 方法将拖拽节点插入到目标节点的前面。然后,我们清除拖拽节点和占位节点的样式,包括设置拖拽节点的 position、z-index、opacity、top 和 left,以及移除占位节点。最后,我们重置了拖拽节点的信息。
示例代码
完整的示例代码如下所示。你可以将以下代码复制到一个名为 Tree.vue 的文件中,然后在其他组件中引入和使用。
// www.javascriptcn.com code example
<template>
<el-tree
:data="data"
:props="defaultProps"
:default-expand-all="true"
:draggable="true"
@node-drag-start="handleDragStart"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
></el-tree>
</template>
<script>
import { ElTree } from 'element-ui';
export default {
name: 'Tree',
components: { ElTree },
props: {
data: {
type: Array,
default: () => [],
},
},
data() {
return {
defaultProps: {
children: 'children',
label: 'label',
},
draggingNode: null,
draggingNodeEl: null,
draggingNodeRect: null,
placeholderNodeEl: null,
};
},
methods: {
handleDragStart(draggingNode, e) {
// 获取拖拽节点的信息
this.draggingNode = draggingNode;
this.draggingNodeEl = e.target;
this.draggingNodeRect = this.draggingNodeEl.getBoundingClientRect();
// 设置拖拽节点的样式
this.draggingNodeEl.style.position = 'absolute';
this.draggingNodeEl.style.zIndex = '9999';
this.draggingNodeEl.style.opacity = '0.7';
// 创建占位节点
this.placeholderNodeEl = document.createElement('div');
this.placeholderNodeEl.style.height = `${this.draggingNodeRect.height}px`;
this.draggingNodeEl.parentNode.insertBefore(this.placeholderNodeEl, this.draggingNodeEl.nextSibling);
},
handleDragOver(draggingNode, dropNode, dropType, e) {
// 判断拖拽节点是否可以放置到当前节点的位置
if (!dropNode || !this.draggingNodeEl || dropNode === this.draggingNode || dropNode.parent === this.draggingNode) {
return;
}
// 获取当前节点的信息
const dropNodeEl = e.target;
const dropNodeRect = dropNodeEl.getBoundingClientRect();
const isAbove = e.clientY < dropNodeRect.top + dropNodeRect.height / 2;
// 设置拖拽节点和占位节点的样式
this.draggingNodeEl.style.top = `${dropNodeRect.top - this.draggingNodeRect.height / 2}px`;
this.draggingNodeEl.style.left = `${dropNodeRect.left}px`;
this.placeholderNodeEl.style.display = 'block';
// 判断拖拽节点是在当前节点的上方还是下方
if (isAbove) {
dropNodeEl.parentNode.insertBefore(this.placeholderNodeEl, dropNodeEl);
} else {
dropNodeEl.parentNode.insertBefore(this.placeholderNodeEl, dropNodeEl.nextSibling);
}
},
handleDragEnd(draggingNode, dropNode, dropType, e) {
// 将拖拽节点插入到目标节点的位置
const parent = dropNode ? dropNode.parent : null;
const index = parent ? parent.children.indexOf(dropNode) : this.data.indexOf(dropNode);
parent ? parent.children.splice(index, 0, this.draggingNode) : this.data.splice(index, 0, this.draggingNode);
// 清除拖拽节点和占位节点的样式
this.draggingNodeEl.style.position = '';
this.draggingNodeEl.style.zIndex = '';
this.draggingNodeEl.style.opacity = '';
this.draggingNodeEl.style.top = '';
this.draggingNodeEl.style.left = '';
this.placeholderNodeEl.parentNode.removeChild(this.placeholderNodeEl);
// 重置拖拽节点的信息
this.draggingNode = null;
this.draggingNodeEl = null;
this.draggingNodeRect = null;
this.placeholderNodeEl = null;
},
},
};
</script>结语
在本文中,我们介绍了如何使用 Vue.js 和 Element-UI 实现可拖拽的树形结构组件。通过示例代码,我们演示了如何处理拖拽开始、拖拽过程中和拖拽结束事件,并实现了排序、移动和复制等功能。希望这篇文章对你有所帮助,欢迎留言讨论。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d26ec0a941bf713449139c