原文链接:两棵el-tree的节点跨树拖拽实现
参照这篇文章,把它做成组件,新增左侧树(可拖出)被拖节点变灰提示;
拖拽中:
拖拽后:
TreeDragComponent.vue
<template><!-- 两棵el-tree的节点跨树拖拽实现 --><div class="tree-drag"><!-- 左侧树(可拖出) --><el-tree:data="treeData1"ref="tree1"class="tree" node-key="id"draggabledefault-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"draggabledefault-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且子节点加入目标节点里