Vue实现简单Tree与handle拖拽

1.实现如下图所示的简单Tree

图1
SimpleTree.vue

<template>
  <div >
    <div v-for="item in treeDataTemp" :key="item.id">

      <span v-if="item.children && item.children.length">
        <a-icon type="caret-down"/>
      </span>
      <span>{{item.title}}</span>

      <div class="item-children">
      	<!--递归自身-->
        <simple-tree :treeData="item.children"/>
      </div>

    </div>
  </div>

</template>

<script>
export default {
  name: "SimpleTree",
  components: {
  },
  props: {
    treeData: {
      type: Array,
      default: () => []
    },
  },
  data() {
    return {
      treeDataTemp: this.treeData,
    }
  },
  watch: {
  	// 动态删除或增加节点,重新赋值
    treeData (val) {
      this.treeDataTemp = val
    }
  },
}
</script>

<style scoped lang="less">
.item-children {
  padding-left: 20px
}
</style>

Index.vue

<template>
  <div style="width: 80%;padding: 20px">

    <simple-tree :treeData="treeData"></simple-tree>

  </div>
</template>

<script>
import simpleTree from './SimpleTree.vue';

const treeData = [
  {
    id: '1',
    title: '1-0-0',
    children: [
      {
        id: '11',
        title: '1-1-0',
        children: [
          {  id: '111',  title: '1-1-1',  },
          {  id: '112',  title: '1-1-2', }
        ]
      },
      {   id: '12',  title: '1-2-0',  }
    ]
  },
  {
    id: '2',
    title: '2-0-0',
    children: [
      { id: '21',  title: '2-1-0', },
      { id: '22',  title: '2-2-0', }
    ]
  }
]
export default {
  name: "TreeModal",
  components: {
    simpleTree
  },
  data () {
    return {
      visible: true,
      treeData,
    }
  }
}
</script>

<style scoped>

</style>

2.点击展开子节点

这里实现的是点击icon三角箭头展开
在这里插入图片描述
SimpleTree.vue

<template>
  <div >
    <div v-for="item in treeDataTemp" :key="item.id">

      <span v-if="item.children && item.children.length">
        <!--点击icon,根据展开或未展开确定icon类型-->
        <a-icon :type="isOpen(item.id) ? 'caret-down' : 'caret-right'"
                @click="toggle(item)"/>
      </span>

      <span>{{item.title}}</span>

      <div class="item-children" v-show="isOpen(item.id)">
      	<!--递归自身-->
        <simple-tree :treeData="item.children" />
      </div>

    </div>
  </div>

</template>

<script>
import dragIcon from './DragIcon.vue';

export default {
  name: "SimpleTree",
  components: {
    dragIcon
  },
  props: {
    treeData: {
      type: Array,
      default: () => []
    },
  },
  data() {
    return {
      treeDataTemp: this.treeData,
      expandedKeys: [], // 展开的节点id数组
    }
  },
  watch: {
  	// 动态删除或增加节点,重新赋值
    treeData (val) {
      this.treeDataTemp = val
    }
  },
  computed: {
    isOpen() {
      return function (id) {
        // 判断节点是否展开
        return this.expandedKeys.includes(id)
      }
    },
  },
  methods: {
  	// 切换
    toggle (item) {
      if (item.children && item.children.length) {
        let index = this.expandedKeys.indexOf(item.id)
        if (index > -1) {
          // 收起
          this.expandedKeys.splice(index, 1)

        } else {
          // 展开
          this.expandedKeys.push(item.id)
        }
      }
    }
  }
}
</script>

<style scoped lang="less">
.item-children {
  padding-left: 20px
}
</style>

3.选中节点

加一个行点击选中事件,设置选中样式
在这里插入图片描述
SimpleTree.vue

<template>
  <div>
    <div v-for="item in treeDataTemp" :key="item.id">

      <div class="item-title" :class="{'item-title-selected': item.id === current}"
           @click="itemNodeClick(item)">

         <span v-if="item.children && item.children.length" >

            <!--点击icon-->
           <a-icon :type="isOpen(item.id) ? 'caret-down' : 'caret-right'"
                   @click="toggle(item)"/>

         </span>

        <span>{{item.title}}</span>

      </div>

      <div class="item-children" v-show="isOpen(item.id)">
        <!--递归自身-->
        <!--更深层级的节点需要一层一层$emit出来-->
        <simple-tree :treeData="item.children"
                     @tree-node-click="$emit('tree-node-click', $event)"
                     :selectedKey="current"
        />
      </div>

    </div>
  </div>

</template>

<script>
import dragIcon from './DragIcon.vue';

export default {
  name: "SimpleTree",
  components: {
    dragIcon
  },
  props: {
    treeData: {
      type: Array,
      default: () => []
    },
    selectedKey: String, // 选中的节点id
  },
  data() {
    return {
      treeDataTemp: this.treeData,
      expandedKeys: [], // 展开的节点id数组
      current: null,
    }
  },
  watch: {
    // 动态删除或增加节点,重新赋值
    treeData (val) {
      this.treeDataTemp = val
    },

    // 监听当前点击节点的id
    selectedKey: {
      handler(num) {
        this.current = num
      },
      deep: true,
      immediate: true
    },
  },
  computed: {
    isOpen() {
      return function (id) {
        // 判断节点是否展开
        return this.expandedKeys.includes(id)
      }
    },
  },
  methods: {
    // 切换
    toggle (item) {
      if (item.children && item.children.length) {
        let index = this.expandedKeys.indexOf(item.id)
        if (index > -1) {
          // 收起
          this.expandedKeys.splice(index, 1)

        } else {
          // 展开
          this.expandedKeys.push(item.id)
        }
      }
    },

    // 节点点击
    itemNodeClick (item) {
      this.$emit('tree-node-click', item)
    },
  }
}
</script>

<style scoped lang="less">
.item-children {
  padding-left: 20px
}

.item-title {
  cursor: pointer;
}

.item-title-selected {
  background: #87abff;
  color: #FFFFFF;
}
</style>

Index.vue

<template>
  <div style="width: 80%;padding: 20px">

    <simple-tree :treeData="treeData"
                 @tree-node-click="nodeClick"
                 :selectedKey="treeSelectedKey">

    </simple-tree>

  </div>
</template>

<script>
import simpleTree from './SimpleTree.vue';

const treeData = [
  {
    id: '1',
    title: '1-0-0',
    children: [
      {
        id: '11',
        title: '1-1-0',
        children: [
          {
            id: '111',
            title: '1-1-1',
          },
          {
            id: '112',
            title: '1-1-2',
          }
        ]
      },
      {
        id: '12',
        title: '1-2-0',
      }
    ]
  },
  {
    id: '2',
    title: '2-0-0',
    children: [
      {
        id: '21',
        title: '2-1-0',
      },
      {
        id: '22',
        title: '2-2-0',
      }
    ]
  }
]
export default {
  name: "TreeModal",
  components: {
    simpleTree
  },
  data () {
    return {
      visible: true,
      treeData,
      treeSelectedItem: {}, // 选中的节点
      treeSelectedKey: null, // 选中的节点的id
    }
  },
  methods: {
    // 获得被点击的节点
    nodeClick (node) {
      this.treeSelectedItem = node
      this.treeSelectedKey = node.id
    }
  }
}
</script>

<style scoped>

</style>

4.使用draggable实现handle拖拽

handle 鼠标在指定元素上才允许拖动

具体文档:

vue.draggable中文文档

在这里插入图片描述
在这里插入图片描述

SimpleTree.vue

<template>
  <div>
    <draggable handle=".mover" v-model="treeDataTemp" animation="300" @start="onStart" @end="onEnd">
      <transition-group>

        <div v-for="item in treeDataTemp" :key="item.id">

          <div class="item-title" :class="{'item-title-selected': item.id === current}"
               @click="itemNodeClick(item)">

            <!--允许拖拽的handle-->
            <span class="mover">
                <a-icon type="drag" />
            </span>


            <span v-if="item.children && item.children.length" >

              <!--点击icon-->
              <a-icon :type="isOpen(item.id) ? 'caret-down' : 'caret-right'"
                      @click="toggle(item)"/>

            </span>

            <span>{{item.title}}</span>

          </div>

          <div class="item-children" v-show="isOpen(item.id)">
            <!--递归自身-->
            <!--更深层级的节点需要一层一层$emit出来-->
            <simple-tree :treeData="item.children"
                         @tree-node-click="$emit('tree-node-click', $event)"
                         :selectedKey="current"
            />
          </div>

        </div>

      </transition-group>
    </draggable>
  </div>

</template>

<script>
import dragIcon from './DragIcon.vue';
import draggable from 'vuedraggable'

export default {
  name: "SimpleTree",
  components: {
    dragIcon,
    draggable
  },
  props: {
    treeData: {
      type: Array,
      default: () => []
    },
    selectedKey: String, // 选中的节点id
  },
  data() {
    return {
      treeDataTemp: this.treeData,
      expandedKeys: [], // 展开的节点id数组
      current: null,

      drag: false, // 拖拽
    }
  },
  watch: {
    // 动态删除或增加节点,重新赋值
    treeData (val) {
      this.treeDataTemp = val
    },

    // 监听当前点击节点的id
    selectedKey: {
      handler(num) {
        this.current = num
      },
      deep: true,
      immediate: true
    },
  },
  computed: {
    isOpen() {
      return function (id) {
        // 判断节点是否展开
        return this.expandedKeys.includes(id)
      }
    },
  },
  methods: {
    // 切换
    toggle (item) {
      if (item.children && item.children.length) {
        let index = this.expandedKeys.indexOf(item.id)
        if (index > -1) {
          // 收起
          this.expandedKeys.splice(index, 1)

        } else {
          // 展开
          this.expandedKeys.push(item.id)
        }
      }
    },

    // 节点点击
    itemNodeClick (item) {
      this.$emit('tree-node-click', item)
    },

    // 开始拖拽事件
    onStart() {
      this.drag = true;
    },

    // 拖拽结束事件
    onEnd() {
      this.drag = false;
    },
  }
}
</script>

<style scoped lang="less">
.item-children {
  padding-left: 20px
}

.item-title {
  cursor: pointer;
}

.item-title-selected {
  background: #87abff;
  color: #FFFFFF;
}

.mover {
  cursor: move;
}
</style>
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值