原文链接:两棵el-tree的节点跨树拖拽实现
参照这篇文章,把它做成组件,新增左侧树(可拖出)被拖节点变灰提示;
拖拽中:
拖拽后:
TreeDragComponent.vue
<template>
<!-- 两棵el-tree的节点跨树拖拽实现 -->
<div class="tree-drag">
<!-- 左侧树(可拖出) -->
<el-tree
:data="treeData1"
ref="tree1"
class="tree"
node-key="id"
draggable
default-expand-all
:allow-drop="returnFalse"
:props="defaultProps"
@node-drag-start="handleDragstart"
@node-drag-end="handleDragend"
>
<!-- 使用作用域插槽自定义节点样式 -->
<span slot-scope="{ node, data }" :class="{ 'custom-red': data.isHighlighted }">
{{ node.label }}
</span>
</el-tree>
<!-- 右侧树(可拖入) -->
<el-tree
:data="treeData2"
ref="tree2"
class="tree"
node-key="id"
draggable
default-expand-all
:props="defaultProps"
:allow-drop="returnTrue"
></el-tree>
</div>
</template>
<script>
export default {
name: "TreeDrag",
props:['treeData1','treeData2'],
data() {
return {
defaultProps: {
children: 'children',
label: 'name'
},
};
},
methods: {
// (1) 手动触发:该节点向父组件通知拖拽开始(只不过目标树是右侧树)
// 经过这个移花接木的操作,右侧的树会误以为是自身的节点触发了tree-node-drag-start事件,它会将被拖拽节点保存在组件内
handleDragstart (node, event) {
this.$refs.tree2.$emit('tree-node-drag-start', event, {node: node});
},
// (2) 鼠标滑过阶段
// 当鼠标拖拽着左侧树上的节点从右侧树上的节点划过(也就是触发dragover事件)时,右侧树会误以为是在拖拽自己的节点,因此会在tree-node-drag-over事件中自动执行位置计算,所以这一阶段无需我们干预。
// (3)鼠标释放阶段
// 尽管此时右侧树已经误以为被拖拽的是自身节点,但被拖拽的节点此时仍然是左侧树的子组件,所以当鼠标释放时,它只能向左侧树(即它的父组件)触发tree-node-drag-end事件。由于左侧树不允许释放,所以此时节点并没有发生移动。
// 为了让右侧树收到鼠标释放的通知,我们开始进行第二次移花接木,即把左侧树上发生的tree-node-drag-end事件以同样的方式触发给右侧树
handleDragend (draggingNode, endNode, position, event) {
//不只是做节点移动,而是拖拽复制,也就是要保留左侧树上的原节点
//真正要保留原节点很难实现,所以选择在移动后恢复左侧树上的节点
// 插入一个空节点用于占位
let emptyData = {id: (+new Date), children: []};
this.$refs.tree1.insertBefore(emptyData, draggingNode);
this.$refs.tree2.$emit('tree-node-drag-end', event);
this.$nextTick(() => {
// 如果是移动到了当前树上,需要清掉空节点
if (this.$refs.tree1.getNode(draggingNode.data)) {
this.$refs.tree1.remove(emptyData);
} else {
// 如果移动到了别的树上,需要恢复该节点,并清掉空节点
let data = JSON.parse(JSON.stringify(draggingNode.data));
this.createHighlightedNode(data); // 添加标记字段
this.$refs.tree1.insertAfter(data, this.$refs.tree1.getNode(emptyData));
this.$refs.tree1.remove(emptyData);
}
})
},
// 递归创建带高亮标记的节点
createHighlightedNode(data) {
// 给当前节点添加 isHighlighted 属性
data.isHighlighted = true;
// 检查是否有子节点
if (data.children && Array.isArray(data.children)) {
// 递归处理每个子节点
data.children.forEach(child => {
this.createHighlightedNode(child);
});
}
return data;
},
// 允许内部拖拽
returnTrue () {
// 可传参数:draggingNode, dropNode, type
// 校验所有拖拽节点是否允许放入(如只能放入同级或特定父级)
// return this.draggingNodes.every(node =>
// node.level <= 2 && // 限制最大层级
// node.type !== 'system' // 限制类型
// )
return true;
},
// 禁止内部拖拽
returnFalse () {
return false;
}
}
};
</script>
<style lang="scss" scoped>
.tree-drag {
display: flex;
justify-content: space-between;
.tree {
flex-grow: 1;
.custom-red {
color: #E4E4E4 !important; /* 红色背景 */
font-size: 14px !important;
}
}
}
</style>
Parent.vue
<TreeDrag :treeData1="treeDataLeft" :treeData2="treeDataRight"></TreeDrag>
element-ui源码中用于定义树节点的element-ui\packages\tree\src\tree-node.vue组件
下一篇:在此基础上加功能,el-tree拖拽事件,限制同级拖拽,获取拖拽后节点的前后节点,同级拖拽合并父节点name且子节点加入目标节点里