本组件是基于Vue2
的自定义tree组件,参考了weibangtuo的实现,实现了以下功能:
使用
代码
1.基础使用:给s-tree
组件传递tree-data
即可,treeData
中单个节点的数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { name: 'Node Name', title: 'Node Tag title attr', isParent: true, isOpen: false, icon: 'fa fa-folder', openedIcon: 'fa fa-folder-open', closedIcon: 'fa fa-folder', children: [], buttons: [ { title: 'icon button tag title attr', icon: 'fa fa-edit', click: function (node) { } } ], }
|
本例所使用的json数据
2.节点单击事件:监听node-click
,此方法接受了node
参数(点击的节点对象)
3.节点后的按钮事件: buttons
下click
的值可以是自定义方法,也可以默认方法,默认有addNode
、delNode
、editNode
字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <div class="tree-demo"> <s-tree :tree-data = "treeData" @node-click="nodeClick"></s-tree> </div> </template> <script> import sTree from "src/common/component/tree"; let treeData = require("./tree.json");
treeData[0].children[1].buttons[0].click=function(node){ alert('自定义添加事件'); } export default { data(){ return { treeData: treeData } }, components:{ sTree }, methods:{ nodeClick(node){ console.log(node); } } } </script>
|
效果图
组件原理
下面是一部分需要注意的地方讲解,其他的请查看底部的源代码链接
使用递归组件
因为不确定树有多少层,所以必须得使用递归组件,递归组件最重要的是什么时候跳出递归,本例使用了如下条件:
tree.vue
1 2 3 4 5 6 7 8
| <template> <ul class="s-tree"> <s-tree-item v-for="node in innerTreeData" :key="node.name" :node="node"> <s-tree v-if="node.isOpen && node.children" :tree-data="node.children"></s-tree> </s-tree-item> </ul> </template>
|
node-click事件实现
由于组件是递归的,无法直接$emit
事件给外部钩子函数,所以循环获取到了最外层的s-tree
vue实例,通过此实例$emit
事件暴漏给外部钩子函数
tree-item.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default { methods:{ nodeClick(){ let _this = this; while(isNotTree(_this.$parent)){ _this = _this.$parent; } _this.$emit('node-click',this.node); } } } function isNotTree(vm){ let classStr = vm.$el.className; if(classStr.indexOf('s-tree')!==-1){ return true; } return false; }
|
单击节点选中效果实现
s-tree-item
如果是单个循环的组件,不是递归的,那么直接在外层的s-tree
创建变量储存选中的值,在s-tree-item
内部与该值判断来实现选中效果就可以了,但是递归组件中s-tree
也可能生成了多个,所以这个值不能储存在s-tree
里,只能储存在一个单例里,这儿我使用了事件中心
,代码如下:
tree-item.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <template> <li class="s-tree-item" @click.stop="nodeClick"> <i :class="[statusIconClass]"></i> <a :class="{'is-active': activeValue === node.name}"> <i :class="[node.icon,nodeIconClass]"></i> {{node.name}} <i v-if="node.buttons" v-for="button in node.buttons" class="iButton" :class="[button.icon]" :title="button.title"></i> </a> <slot></slot> </li> </template> <script> import Bus from './bus.vue'; export default { computed:{ activeValue(){ return Bus.activeValue; } }, methods:{ nodeClick(){ Bus.$emit('active',this.node.name); } } } </script>
|
bus.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script> import Vue from 'vue'; export default new Vue({ name: 'bus', data(){ return { activeValue: '' } }, created(){ this.$on('active',this.active); }, methods:{ active(value){ this.activeValue = value; } } }); </script>
|
对了,这儿使用vuex也是可以的。
结语