vue使用拖拽方式创建结构树

 更新时间:2021年10月12日 10:03:48   作者:javscp_q  
这篇文章主要为大家详细介绍了vue使用拖拽方式创建结构树,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了vue使用拖拽方式创建结构树的具体代码,供大家参考,具体内容如下

在页面中拖拽虚线框中的节点,创建向右的结构树,如下图

记录实现思路:

vueTree.vue

<template>
  <div class="container">
    <div class="node-container">
      <div v-for="(item, index) in nodeList"
           :key="index"
           class="source-node"
           draggable="true"
           @dragstart="dragStart(item)">
        {{ item }}
      </div>
    </div>
    <div class="tree-container"
         @dragover="allowDrop"
         @drop="handleDrop">
      <tree-node v-if="nodeData"
                 ref="node"
                 :nodeData="nodeData"
                 @delete-node="deleteTree" />
    </div>
  </div>
</template>
<script>
import TreeNode from './treeNode.vue'
import { Node } from './config.js'
export default {
  name: 'vue-tree',
  components: {
    TreeNode
  },
  // 后代节点无法获取节点数据,即无法独立创建节点,所以将祖先节点的创建节点方法暴露给后代节点
  provide () {
    return {
      createNode: this.createNode
    }
  },
  data () {
    return {
      nodeList: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
      currNode: null,
      nodeData: null
    }
  },
  methods: {
    // 开始拖拽,获取节点数据
    dragStart (item) {
      this.currNode = item
    },
    // 若未生成跟节点,则允许拖拽
    allowDrop (event) {
      if (!this.nodeData) {
        event.preventDefault()
      }
    },
    // 拖拽结束,生成节点
    handleDrop () {
      if (!this.nodeData) {
        this.nodeData = this.createNode()
      }
    },
    createNode () {
      let node = new Node(this.currNode)
      return node
    },
    // 根节点删除,删除整个树
    deleteTree () {
      this.nodeData = null
    }
  }
}
</script>
<style lang="scss" scoped>
.container {
  padding: 20px;
  width: calc(100% - 40px);
  height: calc(100% - 40px);
  .node-container {
    height: 100px;
    border: 1px dashed red;
    display: flex;
    .source-node {
      width: 50px;
      height: 30px;
      background: #fff;
      border: 1px solid blue;
      text-align: center;
      line-height: 30px;
      margin: 10px;
      cursor: pointer;
    }
  }
  .tree-container {
    height: calc(100% - 122px);
    margin-top: 20px;
  }
}
</style>

config,js

export class Node{
  constructor(name){
    this.name = name,
    this.children = []
  }
}

treeNode.vue

<template>
  <!-- 
    结构:最外层是node-inner,每个node-inner中有一个node与node-box,node存放当前节点,node-box存放当前节点的全部子节点,当前节点有几个子节点则node-box中就会有几个node-inner,以此循环
      <node-inner>
        <node></node>
        <node-box>
          <node-inner>
            <node></node>
            <node-box>...</node-box>
          </node-inner>
          <node-inner>
            <node></node>
            <node-box>...</node-box>
          </node-inner>
          ...
        </node-box>
      </node-inner>
   -->
  <div class="node-inner">
    <div class="node"
         :class="{ 'drag-over-node': isDragover }"
         @dragover="dragOver"
         @dragleave="dragLeave"
         @drop="nodeDrop">
      <span class="name">{{nodeData.name}}</span>
      <span class="del"
            @click="deleteNode">删除</span>
    </div>
    <div v-show="nodeData.children.length > 0"
         class="node-box">
      <tree-node v-for="(item,index) in nodeData.children"
                 :key="index"
                 :nodeData="item"
                 @delete-node="deleteChild(index)" />
    </div>
  </div>
</template>
<script>
export default {
  name: 'tree-node',
  props: {
    nodeData: {
      type: Object,
      default: () => { }
    }
  },
  // 获取祖先节点传递的数据
  inject: ['createNode'],
  data () {
    return {
      isDragover: false
    }
  },
  methods: {
    // 节点允许拖拽添加子节点
    dragOver (event) {
      event.preventDefault()
      if (!this.isDragover) {
        this.isDragover = true
      }
    },
    dragLeave () {
      if (this.isDragover) {
        this.isDragover = false
      }
    },
    // 为节点添加子节点
    nodeDrop () {
      let node = this.createNode()
      this.nodeData.children.push(node)
      this.isDragover = false
    },
    // 删除当前节点,本质是交给父级删除子节点
    deleteNode () {
      this.$emit("delete-node")
    },
    // 接收删除子节点的指令并执行删除功能
    deleteChild (index) {
      this.nodeData.children.splice(index, 1)
    }
  }
}
</script>
<style lang="scss" scoped>
.node {
  border: 1px solid orange;
  border-radius: 4px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  background-color: #fff;
  height: 36px;
  padding: 0 12px 0 16px;
  line-height: 36px;
  margin-bottom: 10px;
  .name {
    font-size: 16px;
    margin-right: 12px;
  }
  .del {
    color: red;
    font-size: 12px;
    cursor: pointer;
  }
  &.drag-over-node {
    box-shadow: 6px 6px 12px rgba(106, 20, 134, 0.15);
  }
}
.node-box {
  display: inline-flex;
  flex-direction: column;
  .node-inner {
    margin-left: 80px;
    // 连接竖条
    &:not(:last-child):before {
      position: absolute;
      left: -70px;
      top: 22px;
      border: 1px solid orange;
      content: "";
      width: 8px;
      background-color: #fff;
      border-bottom-color: #fff;
      height: 100%;
      border-top-color: #fff;
      z-index: 3;
    }
    // 连接横条
    &:after {
      left: -61px;
      width: 60px;
      content: "";
      position: absolute;
      top: 14px;
      height: 8px;
      border: 1px solid orange;
      content: "";
      background-color: #fff;
      border-right-color: #fff;
      border-left-color: #fff;
      z-index: 3;
    }
    // 最后一个竖条圆滑拐角
    &:nth-last-child(2):before {
      border-bottom-left-radius: 6px;
      border-bottom-color: orange;
    }
    // 第一个横条拉长
    &:first-child:after {
      left: -81px;
      width: 80px;
      z-index: 2;
    }
  }
}
.node-inner {
  position: relative;
}
</style>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Vue动态添加class可能带来的问题解读(被覆盖)

    Vue动态添加class可能带来的问题解读(被覆盖)

    文章讨论了在使用Vue.js时,通过动态class修改元素样式时可能会遇到的问题,当通过JavaScript动态添加类时,Vue的动态class会覆盖掉通过JavaScript添加的类,导致样式丢失,这个问题在实际开发中可能会遇到,尤其是在使用第三方框架
    2024-12-12
  • vue2组件进阶与插槽详解(推荐!)

    vue2组件进阶与插槽详解(推荐!)

    插槽(slot)作用是让父组件可以往子组件指定位置插入 html 结构,也是组件的一种通信方式,下面这篇文章主要给大家介绍了关于vue2组件进阶与插槽的相关资料,需要的朋友可以参考下
    2023-02-02
  • 一次在vue中使用post进行excel表下载的实战记录

    一次在vue中使用post进行excel表下载的实战记录

    最近遇到了需求,觉着有必要给大家总结下,这篇文章主要给大家介绍了关于一次在vue中使用post进行excel表下载的实战记录,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Vue自定义指令详解

    Vue自定义指令详解

    这篇文章主要为大家介绍了Vue自定义指令,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • Vue3+ElementPlus el-date-picker设置可选时间范围的示例代码

    Vue3+ElementPlus el-date-picker设置可选时间范围的示例代码

    在Vue3中使用Element Plus的el-date-picker组件设置可选时间范围,你可以使用disabled-date属性,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-07-07
  • vue实现消息向上无缝滚动效果

    vue实现消息向上无缝滚动效果

    这篇文章主要为大家详细介绍了vue实现消息向上无缝滚动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • vue 实现弹窗关闭后刷新效果

    vue 实现弹窗关闭后刷新效果

    这篇文章主要介绍了vue 实现弹窗关闭后刷新效果,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue-resource拦截器设置头信息的实例

    vue-resource拦截器设置头信息的实例

    下面小编就为大家带来一篇vue-resource拦截器设置头信息的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • 详解vue-cli 脚手架项目-package.json

    详解vue-cli 脚手架项目-package.json

    本篇文章主要介绍了详解vue-cli 脚手架项目-package.json,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Vue中mixins的使用方法详解

    Vue中mixins的使用方法详解

    mixins是一种分发 Vue 组件中可复用功能的使用方式,它是一个 js 对象,包含我们组件script中的任意功能选项,下面就跟随小编一起来看看它的具体使用吧
    2024-03-03

最新评论