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>
原文地址:https://www.cnblogs.com/dianzan/p/13397389.html