vue可拖拽树
注:自用,一些样式其他人可能没有
<!-- Vue SFC --> <template> <div class="container bxs"> <div class="ct-inner bxs"> <div class="ct-inner-fiexd w"> <el-input placeholder="请输入关键词" suffix-icon="el-icon-search" v-model="filterText"></el-input> <div class="w clearfix tc mt10"> <el-button class="fll" type="primary" plain size="mini">刷新</el-button> <el-button type="primary" plain size="mini" @click="unFoldAll">全部展开</el-button> <el-button class="flr" type="primary" plain size="mini" @click="collapseAll">全部收起</el-button> </div> </div> <div class="ct-inner-content h"> <!-- show-checkbox 是否展示多选框 --> <!-- :check-on-click-node="true" 点击文本内容是否选中 --> <el-tree v-if="openOrNot" :data="treeData" node-key="id" :default-expand-all="defaultExpand" :expand-on-click-node="false" @node-click="handleLeftclick" @node-drag-start="handleDragStart" @node-drag-enter="handleDragEnter" @node-drag-leave="handleDragLeave" @node-drag-over="handleDragOver" @node-drag-end="handleDragEnd" @node-drop="handleDrop" @node-contextmenu="rightClick" :filter-node-method="filterNode" draggable :allow-drop="allowDrop" :allow-drag="allowDrag" ref="tree" > <span class="slot-t-node span-ellipsis" slot-scope="{ node, data }"> <span class="span-ellipsis-inner" v-show="!data.isEdit"> <!-- <el-tooltip class="item" effect="dark" :content="node.label" placement="right"> --> <span class="span-ellipsis"> <span class="span-ellipsis" :class="[(selectTreeData&&data.id==selectTreeData.id)? 'slot-t-node--label' : '']" style="vertical-align:middle;" > <i class="iconfont jxintegral-fill"></i> {{node.label}} </span> </span> <!-- </el-tooltip> --> </span> <!-- autofocus --> <span v-show="data.isEdit"> <el-input class="slot-t-input" style="height:28px;" size="mini" v-model="data.label" :ref="'slotTreeInput'+data.id" @blur.stop="NodeBlur(node,data)" @keydown.native.enter="NodeBlur(node,data)" ></el-input> </span> </span> </el-tree> <el-card class="box-card" ref="card" v-show="menuVisible"> <div @click="addSameLevelNode()" v-show="firstLevel"> <i class="el-icon-circle-plus-outline"></i> <span class="ml10">同级增加</span> </div> <div class="add" @click="addChildNode()"> <i class="el-icon-circle-plus-outline"></i> <span class="ml10">子级增加</span> </div> <div class="delete" @click="deleteNode()"> <i class="el-icon-remove-outline"></i> <span class="ml10">删除节点</span> </div> <div class="edit" @click="editNode()"> <i class="el-icon-edit"></i> <span class="ml10">修改节点</span> </div> </el-card> </div> </div> </div> </template> <script> import axios from "axios"; export default { name: "PersonManageLf", components: {}, data() { return { openOrNot: true, //展开收缩dom挂载切换 defaultExpand: true, //默认全部展开 eleId: "", isShow: false, currentData: "", currentNode: "", menuVisible: false, firstLevel: false, filterText: "", maxexpandId: 4, treeData: [ { id: 1, label: "一级 1", isEdit: false, children: [ { id: 4, label: "二级 1-1", isEdit: false, children: [ { id: 9, label: "三级 1-1-1呜呜呜呜呜呜呜呜无无无无无无无无无无无无无无无无无", isEdit: false, }, { id: 10, label: "三级 1-1-2", isEdit: false, }, ], }, ], }, { id: 2, label: "一级 2", isEdit: false, children: [ { id: 5, label: "二级 2-1", isEdit: false, }, { id: 6, label: "二级 2-2", isEdit: false, }, ], }, { id: 3, label: "一级 3", isEdit: false, children: [ { id: 7, label: "二级 3-1", isEdit: false, }, { id: 8, label: "二级 3-2", isEdit: false, children: [ { id: 11, label: "三级 3-2-1", isEdit: false, }, { id: 12, label: "三级 3-2-2", isEdit: false, }, { id: 13, label: "三级 3-2-3", isEdit: false, }, ], }, ], }, ], treeDataCopy: "", // 拖拽结束后验证后不可拖拽 defaultProps: { children: "children", label: "label", }, selectTreeNode: "", //选中的树节点,内含父节点 selectTreeData: "", //选中的树data newAddTreeObjs: {}, //新增节点储存对象 nowAddTreeNodeid: "", //当前新增节点id }; }, methods: { // 全部展开 unFoldAll() { let self = this; // 将没有转换成树的原数据 let list = this.treeData; for (let i = 0; i < list.length; i++) { // 将没有转换成树的原数据设置key为... 的展开 self.$refs.tree.store.nodesMap[list[i].id].expanded = true; } }, // 全部折叠 collapseAll() { let self = this; // 将没有转换成树的原数据 let list = this.treeData; for (let i = 0; i < list.length; i++) { self.$refs.tree.store.nodesMap[list[i].id].expanded = false; } }, renderContent(h, { node, data, store }) { console.log(node.label); console.log(data); console.log(store); return ( <div style="display:inline-block;"> <span style="">{node.label}</span> </div> ); }, open() { this.defaultExpand = !this.defaultExpand; this.openOrNot = false; setTimeout(() => { this.openOrNot = true; }, 10); }, NodeBlur(Node, data) { // debugger; console.log(Node, data); if (data.label.length === 0) { this.$message.error("菜单名不可为空!"); return false; } else { if (data.isEdit) { this.$set(data, "isEdit", false); console.log(data.isEdit); } this.$nextTick(() => { this.$refs["slotTreeInput" + data.id].$refs.input.focus(); }); } }, // 查询 filterNode(value, data) { if (!value) return true; return data.label.indexOf(value) !== -1; }, handleDragStart(node, ev) { console.log(this.treeData, "树"); console.log("drag start", node); // 如果要阻止拖拽 // if(node.childNodes.length>0){ // console.log("有子节点不能移动") // ev.preventDefault(); // return; // } }, handleDragEnter(draggingNode, dropNode, ev) { console.log("tree drag enter: ", dropNode.label); }, handleDragLeave(draggingNode, dropNode, ev) { console.log("tree drag leave: ", dropNode.label); }, handleDragOver(draggingNode, dropNode, ev) { console.log("tree drag over: ", dropNode.label); }, handleDragEnd(draggingNode, dropNode, dropType, ev) { console.log("tree drag end: ", dropNode && dropNode.label, dropType); }, handleDrop(draggingNode, dropNode, dropType, ev) { console.log("tree drop: ", dropNode.label, dropType); // 拖拽结束后验证后不可拖拽 // console.log(this.treeData,'树') // this.treeData=JSON.parse(JSON.stringify(this.treeDataCopy)); }, allowDrop(draggingNode, dropNode, type) { console.log(dropNode.data) if (dropNode.data.label === "二级 3-1") { return type !== "inner"; } else { return true; } }, allowDrag(draggingNode) { return draggingNode.data.label.indexOf("三级 3-2-2") === -1; }, // 鼠标右击事件 rightClick(MouseEvent, object, Node, element) { // debugger; console.log(Node); this.currentData = object; this.currentNode = Node; if (Node.level === 1) { this.firstLevel = true; } else { this.firstLevel = false; } this.menuVisible = true; // let menu = document.querySelector('#card') // /* 菜单定位基于鼠标点击位置 */ // menu.style.left = event.clientX + 'px' // menu.style.top = event.clientY + 'px' document.addEventListener("click", this.foo); // console.log(event.clientY); this.$refs.card.$el.style.left = event.clientX + 40 - 250 + "px"; this.$refs.card.$el.style.top = event.clientY + 10 - 165 + "px"; }, // 鼠标左击事件 handleLeftclick(data, node) { // console.log(data,'选中的节点') this.selectTreeNode = node; this.selectTreeData = data; this.foo(data.id, true); }, //判断是否为新增id,且新增id是否为真id sureNewOrOld(id) { //1 表明是旧id,2表示新id,3表示id未返回 id = id + ""; let arrs = id.split("-"); let newAddTreeObjs = this.newAddTreeObjs; if (arrs[0] == "xzjd") { console.log(id); console.log(newAddTreeObjs); console.log(newAddTreeObjs[id]); let idData = newAddTreeObjs[id]; if (idData.id != id) { return 2; } else { // 新增部门,请刷新列表获取信息! this.$message.warning("新增部门,请稍后操作!"); this.selectTreeNode = ""; this.selectTreeData = ""; return 3; } } else { return 1; } }, // 取消鼠标监听事件 菜单栏 foo(id, havesure) { // havesure判断是否为左键点击 let newAddTreeObjs = this.newAddTreeObjs; this.menuVisible = false; // 要及时关掉监听,不关掉的是一个坑,不信你试试,虽然前台显示的时候没有啥毛病,加一个alert你就知道了 document.removeEventListener("click", this.foo); if (this.nowAddTreeNodeid) { this.nowAddTreeNodeid = ""; return; } console.log("真正选中的节点"); if (!havesure) { return; } let oldSure = this.sureNewOrOld(id); console.log(oldSure); if (oldSure == 2) { } else if (oldSure == 3) { return; } else { this.changeRightMess(id); } console.log(); }, // 刷新右侧信息 changeRightMess(id) { this.$emit("sentFather", { id: id }); }, // 增加同级节点事件 addSameLevelNode() { let id = Math.ceil(Math.random() * 1000); let timestamp = Date.parse(new Date()); id = "xzjd-" + id + "-" + timestamp; console.log(id); var data = { id: id, label: "新增节点" }; this.newAddTreeObjs[id] = data; this.nowAddTreeNodeid = id; this.$refs.tree.append(data, this.currentNode.parent); }, // 增加子级节点事件 addChildNode() { console.log(this.currentData); console.log(this.currentNode); // if (this.currentNode.level >= 3) { // this.$message.error("最多只支持三级!"); // return false; // } let id = Math.ceil(Math.random() * 1000); let timestamp = Date.parse(new Date()); id = "xzjd-" + id + "-" + timestamp; var data = { id: id, label: "新增节点" }; this.newAddTreeObjs[id] = data; this.nowAddTreeNodeid = id; this.$refs.tree.append(data, this.currentNode); }, // 删除节点 deleteNode() { this.$refs.tree.remove(this.currentNode); }, // 编辑节点 editNode() { // debugger; if (!this.currentData.isEdit) { this.$set(this.currentData, "isEdit", true); } }, }, watch: { filterText(val) { this.$refs.tree.filter(val); }, }, mounted() { // this.test(); // 拖拽结束后验证后不可拖拽 // this.treeDataCopy=JSON.parse(JSON.stringify(this.treeData)) }, }; </script> <style lang="scss" scoped> .container { height: 95%; .ct-inner { height: 100%; position: relative; padding-top: 84px; .ct-inner-fiexd { position: absolute; left: 0; top: 0; } .ct-inner-content { // overflow: hidden; .text { font-size: 14px; } .add { cursor: pointer; margin-top: 10px; } .delete { margin: 10px 0; cursor: pointer; } .edit { margin-bottom: 10px; cursor: pointer; } .box-card { width: 140px; position: absolute; z-index: 1000; } .slot-t-node--label { background-color: #f5f7fa; } } } } .span-ellipsis { width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; display: inline-block; } </style> <style > .el-tree { width: 100%; max-height: 95%; padding-bottom: 20px; overflow: auto; } .el-tree > .el-tree-node { display: inline-block; min-width: 100%; } .el-tree-node__content { height: 30px; } </style>