1.实现如下图所示的简单Tree
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 鼠标在指定元素上才允许拖动
具体文档:
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>