Vue编写的树形tree组件
近期在进行vue的学习,自研的vue项目需要一个tree组件。由于在学习阶段,不想‘拿来主义’,拿别人写好的组件使用,对于处于学习阶段的开发者来说不是一件好事,虽然拿别人开发好的东西过来用可以快速进行业务开发,但阻绝了学习者锻炼自身开发技能的机会。‘拿来主义’的学习方式,与其说是学习一门语言,不如说是学习一种成熟组件的使用方式。自己动手写一写还是非常有利于自身水平提升的。于是,开始动手自己写vue
·
近期在进行vue的学习,自研的vue项目需要一个tree组件。由于在学习阶段,不想‘拿来主义’,拿别人写好的组件使用,对于处于学习阶段的开发者来说不是一件好事,虽然拿别人开发好的东西过来用可以快速进行业务开发,但阻绝了学习者锻炼自身开发技能的机会。‘拿来主义’的学习方式,与其说是学习一门语言,不如说是学习一种成熟组件的使用方式。自己动手写一写还是非常有利于自身水平提升的。于是,开始动手自己写vue-tree组件。
代码:
<template>
<div class='cus_vtree_wrap' @click.capture='clickNodeWrap'>
<v-tree-item :treeData='treeData' @clickNodeCom='clickNode' :checkBox='checkBox' :class='{tree_root_lonely:treeData.length === 1}' @toggleCheckBox="checkBoxFun"></v-tree-item>
</div>
</template>
<script>
export default{
name:'vue-tree',
props:['treeData','checkBox'],
data(){
return {
//userData:this.treeData
}
},
mounted(){
this.initData(true,true,true);
},
updated(){
this.initData(true,true,true);
},
methods:{
initData(expandInit,activeInit,checkedInit){
var self = this;
var modifyDataFun = function(datas){
if(datas){
datas.forEach( (m,index) => {
if(expandInit){
self.$set(m,'expand',true);
}
if(activeInit){
self.$set(m,'active',false);
}
if(checkedInit){
self.$set(m,'checked',false);
self.$set(m,'partchecked',false);
}
if(index === datas.length - 1){
self.$set(m,'last',true);
}
if(m.children){
modifyDataFun(m.children);
}
});
}
}
modifyDataFun(this.treeData);
},
clickNode(data){
this.$emit('clickNode',data);
},
clickNodeWrap(){
this.initData(false,true,false);
},
checkBoxFun(item){
},
getCheckedNodes(){
var resultArr = [];
var getCheckedNodesFun = (datas) => {
if(datas){
datas.forEach((m)=>{
if(m.checked === true){
resultArr.push(m);
}
if(m.children){
getCheckedNodesFun(m.children);
}
})
}
};
getCheckedNodesFun(this.treeData);
return resultArr;
},
getRoot(){
return this.treeData[0];
},
findNode(selectedId){
var result;
var findNodeFun = (datas) => {
if(datas){
try{
datas.forEach((m) => {
if(m.id === selectedId){
result = m;
throw new Error('stop');
}
if(m.children){
findNodeFun(m.children);
}
});
}catch(e){
}
}
};
findNodeFun(this.treeData);
return result;
},
setSelectedNode(node,clickDiv){
var select;
var that = this;
var setSelectedFun = (datas) => {
if(datas){
datas.forEach((m) => {
if(m.id === node.id){
m.active = true;
select = m;
if(clickDiv) {
var dom = document.getElementById(m.id);
if(dom && dom.className.indexOf('cus_item_content') > -1){
dom.click();
}else {
var doms = document.getElementsByClassName('cus_item_content');
for(var y = 0 ; y < doms.length ;y ++){
if(doms[y].id === m.id){
doms[y].click();
break;
}
}
}
}
}else {
m.active = false;
}
if(m.children){
setSelectedFun(m.children);
}
});
}
};
setSelectedFun(this.treeData);
return select;
},
getSelectedNode(){
var resultNode = null;
var getSelectedFun = (datas) => {
if(datas){
datas.forEach((m) => {
if(m.active === true){
resultNode = m;
return;
}
if(m.children){
getSelectedFun(m.children)
}
})
}
};
getSelectedFun(this.treeData);
return resultNode;
},
getParentNode(node){
var resultNode = null;
var getParentNode = (datas,parent) => {
if(datas){
try{
datas.forEach( (m) => {
if(node && m.id === node.id){
resultNode = parent;
throw new Error("Stop");
}
if(!resultNode && m.children){
getParentNode(m.children,m);
}
} );
}catch(e){
}
}
}
getParentNode(this.treeData,null);
return resultNode;
},
getParentNodesArr(node){
var resultArr = [];
var parentNode ;
var getParent = (target) => {
parentNode = this.getParentNode(target);
if(parentNode){
resultArr.push(parentNode);
getParent(parentNode);
}
};
getParent(node);
return resultArr;
}
},
components:{'v-tree-item':{
name:'v-tree-item',
template:'<ul class="cus_tree_ul" :class="{cus_tree_ulLine:(treeData && treeData.length)}"><li v-for="item in treeData"><div class="cus_item_content" :title="item.text" @click="clickNode(item)" :id= item.id :class="{active:item.active}">'+
'<span class="treeExpandBtn" @click.stop="toggleNode(item)" :class="{butopen:item.expand && item.children,btnclose:!item.expand && item.children,line: !item.last && !item.children,lastLine:item.last&&!item.children}"></span><span class="tree-icon" :class="item.icon"></span>'+
'<span v-if="checkBox" @click="checkBoxClick(item)" class="cus_chekcbox" :class="{cus_chekcbox_checked:item.checked,cus_chekcbox_part_checked:item.partchecked}"></span>{{item.text}}</div>'+
'<v-tree-item :treeData="item.children" v-if="item.expand" @clickNodeCom="clickNodeCom" :checkBox="checkBox" :class="{cus_checkbox_allchecked:item.checked}" @toggleCheckBox="checkBoxFun(item)" ></v-tree-item> </li></ul>',
methods:{
clickNode(item){
item.active = true;
this.$emit('clickNodeCom',item);
},
toggleNode(item){
item.expand = !item.expand;
item.active = true;
},
clickNodeCom(data){
this.$emit('clickNodeCom',data);
},
checkBoxClick(item){
item.partchecked = false;
item.checked = !item.checked;
//设置子元素是否勾选
var checkChildFun = (childrenDatas) => {
childrenDatas.forEach((m) => {
m.checked = item.checked;
if(m.children){
checkChildFun(m.children);
}
})
}
if(item.children){
checkChildFun(item.children);
}
this.$emit('toggleCheckBox');
},
checkBoxFun(item){
var checkedNum = 0;
var partCheckedNum = 0;
item.children.forEach((li) => {
if(li.checked === true){
checkedNum++;
}else if(li.partchecked === true){
partCheckedNum++;
}
})
if(checkedNum === item.children.length){ //全选
item.checked = true;
item.partchecked = false;
}else if(checkedNum > 0 || partCheckedNum > 0){ //部分勾选
item.checked = false;
item.partchecked = true;
}else{ //没有勾选
item.checked = false;
item.partchecked = false;
}
this.$emit('toggleCheckBox')
},
},
props:['treeData','checkBox']
}}
}
</script>
<style>
.cus_vtree_wrap>ul:first-child{
background-image:none;
}
ul.cus_tree_ul{
padding-left:16px;
background-image:url('../images/index.gif');
background-position:4px 10px;
background-repeat:repeat-y;
}
li:last-child>ul.cus_tree_ul:last-child{
background-image:none;
}
ul.cus_tree_ul li{
text-align:left;
cursor:pointer;
list-style:none;
}
.cus_item_content span.treeExpandBtn{
display:inline-block;
width:18px;
height:18px;
margin-right:6px;
background-image:url('../images/zTreeStandard.png');
}
.cus_vtree_wrap>ul> li:first-child>.cus_item_content>span.treeExpandBtn.butopen{
background-position:-92px 0px
}
li .cus_item_content span.treeExpandBtn.butopen{
background-position:-92px -18px
}
li:last-child .cus_item_content span.treeExpandBtn.butopen{
background-position:-92px -36px
}
.cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.butopen{
background-position:-92px -54px
}
.cus_vtree_wrap>ul> li:first-child>.cus_item_content>span.treeExpandBtn.btnclose{
background-position:-74px -0px
}
li .cus_item_content span.treeExpandBtn.btnclose{
background-position:-74px -18px
}
li:last-child .cus_item_content span.treeExpandBtn.btnclose{
background-position:-74px -36px
}
.cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.btnclose{
background-position:-74px -54px
}
.cus_item_content span.treeExpandBtn.lastLine{
background-position:-56px -36px
}
.cus_item_content span.treeExpandBtn.line{
background-position:-56px -18px
}
.cus_item_content .cus_chekcbox{
display:inline-block;
width:14px;
height:14px;
margin-right:6px;
background-image:url('../images/zTreeStandard.png');
background-position:-0px -0px
}
.cus_item_content .cus_chekcbox.cus_chekcbox_checked{
background-position:-14px -0px
}
.cus_item_content .cus_chekcbox.cus_chekcbox_part_checked{
background-position:0px -42px
}
ul.cus_tree_ul li .cus_item_content{
padding:4px;
white-space: nowrap;
overflow-x:hidden;
}
ul.cus_tree_ul li .cus_item_content:hover{
background:#8f83e1;
color:white
}
ul.cus_tree_ul li .cus_item_content.active{
background:#7663f8;
color:white;
}
.folder{
display:inline-block;
width:16px;
height:16px;
margin-right:6px;
background-image:url('../images/zTreeStandard.png');
background-position:-110px -0px
}
.file{
display:inline-block;
width:18px;
height:18px;
margin-right:6px;
background-image:url('../images/zTreeStandard.png');
background-position:-110px -30px
}
</style>
效果图:
使用方式:
安装:npm install vue-tree-jf --save
引用:
import vueTree from 'vue-tree-jf'
Vue.use(vueTree);
.....
<vue-tree :treeData='datas' ref='vtree' @clickNode ='clickNode' ></vue-tree>
初始化参数
参数 | 类型 | 默认值 | 描述 |
treeData | Array | 数据源,包含'id','text','icon','children'属性。 示例:[ {text:'xiaoming', id:'1', icon:'folder', children:[ {text:'1-1', id:'1-1', icon:'folder', children:[ {text:'1-1-1', id:'1-1-1', icon:'file', },{text:'1-1-2', id:'1-1-2', icon:'file' }] },{ text:'1-2', id:'1-2', icon:'folder', }] } ] | |
checkBox | Boolen | false | 是否显示checkbox |
transitionTime | Number | 200 | 折叠动画时间,单位ms |
expand | Boolean | true | 初始化时是否展开节点 |
treeData参数
参数名称 | 类型 | 默认值 | 描述 |
text | String | 节点名称 | |
id | String | 节点id | |
icon | String | 节点图标 | |
children | Array | 节点的子节点 |
方法
方法名称 | 参数 | 返回值 | 描述 |
getSelectedNode | - | 返回选中的tree节点 | |
getCheckedNodes | - | 返回多选的tree节点 | 在多选属性checkbox为true时有效。 |
getRoot | - | 获取树根节点。 | |
findNode | nodeId | 节点id | 获根据id获取选中节点数据。 |
setSelectedNode | node,ifClick | node:节点数据,包含id字段即可。 ifClick:是否触发点击事件 | 设置树节点选中。 |
getParentNode | node | node:节点对象(包含id属性即可) | 获取父节点数据。 |
getParentNodesArr | node | node:节点对象(包含id属性即可) | 获取所有祖先节点数据数组。 |
事件说明
事件名称 | 参数 | 参数说明 | 描述 |
clickNode | node | 当前点击节点的数据 | 节点点击时触发 |
文件更新(2020/12/2)
更新版本(1.0.7):
1.修改选中样式;
2.新增折叠动画特效,通过transitionTime控制折叠动画时间
完整项目github:GitHub - jianfeng418/vue-tree: vue vue-tree vue-checkbox-tree
更多推荐
已为社区贡献5条内容
所有评论(0)