vue实现树状结构+拖拽

1.首先说树状结构的实现
我直接推一手链接,直接用他的最终源码就可以
HTML&CSS中的树形结构图_html树状结构图_|木子李|的博客-CSDN博客

然后,我这里有一些改动

menudata结构如下

  menudata: [

        {

          name: "1213",

          id: 1,

        },

        {

          name: "12312",

          id: 2,

        }

]

相关方法如下
 

   onMenuClick(name) {
      console.log(name);
    },
    dragStart(ev) {
      this.$bus.$emit("dragStart", ev);
    },
    dragEnd(event) {
      this.$bus.$emit("dragEnd", event);
    },
    ondrag(e) {
      this.$bus.$emit("ondrag", e);
    },
    transferData(event) {
      const name = event.target.innerText;
      console.log("++++++++++");
      console.log(name);
      pubsub.publish("name", name);
    },
  },

我这里用到了pubsub.js,相关内容小伙伴可以自行百度
然后树组件和树组件对应的拖拽都完成了,该进行拖拽区域的设置了
 

  <div class="drap-container">
          <div>
        <div
          class="top-content"
          id="region_id"
          @drop.prevent="drop"
          @dragover.prevent
          ref="moveArea"
        >
         
          <div
            class="top-content-fu"
            :style="{ 
              
              left: info.x - 150 + 'px', top: info.y - 100 + 'px' 
              
              
              }"
            v-for="(info, index) in InfoList"
            :key="index"
            v-bind:id="info.id"
            draggable="true"
            @dblclick="doubleClick(info.id)"
            @mouseenter="enterInto"
          >
            <div class="top-content-fu-text">
            
              <label style="margin: auto">{{ info.name }}</label>
            </div>
            <div class="top-content-son">
              <div
                class="top-content-son-img"
                :style="{ backgroundImage: 'url(' + info.avatar + ')' }"
              ></div>
              <div style="width: 100%; height: 20px; margin-top: 8px">
                <div class="top-content-son-pd"></div>
                <div
                  class="top-content-son-ss"
                  @click="deleteTag($event)"
                ></div>
              </div>
            </div>
          </div>
        </div>
        <div class="dexter-content" id="exportation"></div>
        <!-- <div id="alartline"><el-divider></el-divider></div> -->
      </div>
        </div>

用到了事件总线总归是有些麻烦的
 

 methods: {
    //删除模块
    deleteTag(event) {
      //将this赋值给thar,这里涉及到this指向问题,所以这里会将this赋值给thar。在需要this的时候,使用thar进行代替。避免了出现函数,变量找不到问题
      var thar = this;
      var id = event.currentTarget.parentElement.parentElement.parentElement.id;
      var ids = id.split("-");
      // jsPlumb.remove(id); //移除节点
      var deleteID = document.getElementById(id);
      var r = confirm('确定将"' + uncompileStr(ids[0]) + '"模块删除?');
      if (r == true) {
        //点击删除dom
        deleteID.addEventListener("click", function () {
          thar.plumbIns.deleteConnectionsForElement(deleteID.id);
          thar.plumbIns.removeAllEndpoints(deleteID.id); //删除divID所有端点
          thar.plumbIns.remove(deleteID.id); //移除节点
        });
      }
    },

    //拖拽事件
    dragStart(ev) {
      console.log("dragstart拖拽开始事件,绑定于被拖拽元素上", ev);
      let info = { id: ev.target.id, isDrop: true };
      ev.dataTransfer.setData("Text", JSON.stringify(info));
      // 鼠标相对于被选中元素的位置
      this.offsetX = ev.offsetX;
      this.offsetY = ev.offsetY;
      //获取标签文本
      this.dragName = ev.target.innerText;
    },
    dragEnd(event) {
      event.dataTransfer.clearData();
    },
    drop(e) {
      let info = JSON.parse(e.dataTransfer.getData("Text"));
      if (info.isDrop) {
        info.x = e.clientX - this.offsetX -300 ;
        info.y = e.clientY - this.offsetY -10 ;
        // 将加密后的字符串作为 id值
        info.id = compileStr(this.dragName) + "-" + Math.random();
        //拼接图片地址
        info.avatar = require("../../assets/images/dragModule/" +
          this.dragName +
          ".png");
        info.name = this.dragName;
        this.InfoList.push(info);
        //为 div绑定节点
        this.$nextTick(() => {
          
          this.addOneAnchor(info.id);
        });
      }
    },
    //双击模块,通知popup组件加载对应的组件
    doubleClick(id) {
      this.identification = id;
      this.showAlertModal = true;
      this.idarr = id.split("-");
      this.componentName = uncompileStr(this.idarr[0]);
    },
    enterInto(event) {
      /// currentTarget 获取绑定点击事件的节点  firstElementChild获取绑定事件节点的第一个子节点
      let labelNodeText =
        event.currentTarget.firstElementChild.firstElementChild.innerText;
      //发布消息
      pubsub.publish("name", labelNodeText);
    },
  },
 mounted() {
    this.plumbIns = this.$jsPlumb.getInstance();
    //将this赋值给thar
    var thar = this;
    //出口节点
    this.plumbIns.addEndpoint(
      "exportation",
      { anchor: [0, 0.1] },
      exportationStyle
    );
    this.$nextTick(() => {
      /*info.sourceId: 连接的源元素id
          info.targetId: 连接的目标元素id
          info.source: 连接的源元素
          info.target: 连接的目标元素
          info.sourceEndpoint: 连接的源端点
          info.targetEndpoint: 连接的目标端点*/
      thar.plumbIns.ready(() => {
        //点击删除连接线
        thar.plumbIns.bind("click", function (info) {
          thar.plumbIns.deleteConnection(info);
        });
      });
      //当链接建立前   检查是否已经建立连接
      thar.plumbIns.bind("beforeDrop", function (info) {
        let targetIdArray = thar.dataCache.get(info.sourceId);
        //避免源点与目标点在用一处上
        //短路运算符&&和||
        if (
          info.sourceId === info.targetId ||
          (Array.isArray(targetIdArray) &&
            targetIdArray.indexOf(info.targetId) > -1) ||
          targetIdArray === info.targetId
        ) {
          return false; // 链接不会建立
        } else {
          return true; // 链接会自动建立
        }
      });
      //连接事件
      thar.plumbIns.bind("connection", function (info) {
        //判断是否与exportation节点建立连接
        if (info.targetId == "exportation") {
          exportation = true;
        }
        //将源元素与目标元素id存入到数组中
        let array = [info.sourceId, info.targetId];
        //调用store中的ad方法,进行添加操作
        thar.$store.dispatch("ad", array);
        //获取state中的数据
        thar.dataCache = thar.$store.state.dataCache;
      });
      //断开连接
      thar.plumbIns.bind("connectionDetached", function (info) {
        //断开连接时,将exportation的值修改为false
        if (info.targetId == "exportation") {
          exportation = false;
        }
        //将源元素id与目标元素id存入到数组中
        let array = [info.sourceId, info.targetId];
        //调用storte中dd方法,进行删除操作
        thar.$store.dispatch("dd", array);
      });
    });
    //为每一个模块添加对应的节点。
    this.addOneAnchor = function (idStr) {
      var ids = idStr.split("-");
      var decode = uncompileStr(ids[0]);
      //添加端点
      var getFirstChildId = document.getElementById("region_id").children[0].id;
      if (getFirstChildId == idStr) {
        /*addEndpoint(参数1,参数2,参数3)
              参数1:所需要添加节点元素的id值
              参数2:节点位置,jsPlumb有九个默认锚点位置(Top、Right、Bottom、Left、Center...)。数组的语法[x,y]x-相对该锚点在x轴坐标比例(最大1)y-相对该锚点y轴坐标比例(最大1)
              参数3:节点样式
              */
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.3] },
          rightAnchorStyle
        );
        //判断 值是否存在数组中
      } else if (leftTwo.includes(decode)) {
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.4] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.6] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: "Right" }, rightAnchorStyle);
      } else if (rightghtTwo.includes(decode)) {
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.4] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.6] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(idStr, { anchor: "Left" }, leftAnchorStyle);
      } else if (leftTwo_rightThree.includes(decode)) {
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.4] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.6] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.3] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.5] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.7] },
          rightAnchorStyle
        );
      } else if (leftTwo_rightTwo.includes(decode)) {
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.4] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.6] }, leftAnchorStyle);
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.4] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.6] },
          rightAnchorStyle
        );
      } else if (rightThree.includes(decode)) {
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.3] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.5] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.7] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.5] }, leftAnchorStyle);
      } else if (rightOne.includes(decode)) {
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.5] },
          rightAnchorStyle
        );
      } else if (leftOne.includes(decode)) {
        this.plumbIns.addEndpoint(idStr, { anchor: [0, 0.5] }, leftAnchorStyle);
      } else if (classify.includes(decode)) {
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.3] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.5] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.7] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(
          idStr,
          { anchor: [1, 0.9] },
          rightAnchorStyle
        );
        this.plumbIns.addEndpoint(idStr, { anchor: "Right" }, rightAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: "Left" }, leftAnchorStyle);
      } else {
        this.plumbIns.addEndpoint(idStr, { anchor: "Right" }, rightAnchorStyle);
        this.plumbIns.addEndpoint(idStr, { anchor: "Left" }, leftAnchorStyle);
      }
      //设置可移动区域
      this.plumbIns.draggable(idStr, { containment: this.regionId });
    };
    //消息订阅
    this.closeMoreNodeDescribe = pubsub.subscribe(
      "closeMoreNodeDescribe",
      (msgName, data) => {
        this.moreNodeDescribe = data;
      }
    );
    //消息订阅
    this.pid = pubsub.subscribe("showAlertModal", (msgName, data) => {
      this.showAlertModal = data;
    });
    //全局总线
    this.$bus.$on("dragStart", this.dragStart);
    this.$bus.$on("dragEnd", this.dragEnd);
    //this.$bus.$on("ondrag", this.ondrag);
  },

就这样吧,拍拍手,下播

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue 是一种流行的 JavaScript 前端框架,它可以通过组件化的方式快速构建应用程序。要实现思维导图,可以按照以下思路进行: 1. 数据结构设计:思维导图可以看作是一个树状结构,每个节点表示一个思维节点,包含内容、子节点等信息。可以使用一个型数据结构来表示思维导图,每个节点可以是一个对象,包含相应的属性。 2. 组件设计:根据数据结构设计好的型数据,可以创建一个形组件来展示思维导图。可以使用 Vue 的组件化特性,将思维导图拆分成不同的组件,方便管理和复用。 3. 数据绑定:通过 Vue 的响应式数据绑定机制,将思维导图的数据与组件进行关联。可以通过在组件中使用 Vue 的 data 属性来保存思维导图的数据,并在模板中根据数据动态渲染组件。 4. 组件交互:为了实现思维导图的交互功能,可以通过 Vue 的事件机制来实现。例如,可以为节点添加点击事件,实现节点的展开和收起功能。可以为节点添加拖拽事件,支持节点的移动和调整。 5. 样式设计:思维导图通常需要一定的样式设计来美化界面。可以使用 Vue 的样式绑定功能,通过绑定类名或行内样式的方式来实现样式设计。可以为组件添加相应的样式属性,或者使用 CSS 预处理器来提高样式管理的可维护性。 总结,通过设计好的型数据结构,使用 Vue 的组件化特性、数据绑定、事件机制和样式绑定等功能,可以较好地实现思维导图。这样结构清晰、交互丰富、样式美观的思维导图可以有效帮助用户整理和展示思维。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值