话不多说,直接上代码
tree.vue:
<template>
<ul class="tree__ul">
<treeItem
v-for="(treeData,index) in treeDatas"
:treeData="treeData"
:key="index"
@nodeItemClick="nodeClick"
:options="options"
:level="1"></treeItem>
</ul>
</template>
<script>
export default {
data(){
return{
}
},
props:{
treeDatas:Array,
options:Object,
},
methods:{
nodeClick(value){
if(this._events["nodeClick"]){
this.$emit('nodeClick', value)
}
},
getSelectNode(){
let nodes=[];
nodes=this.getChildrenSelect(this.treeDatas);
return nodes;
},
getChildrenSelect(children){
let nodes=[];
children.forEach((item,index)=>{
if(item.children&&item.children.length){
let childNodes=this.getChildrenSelect(item.children);
childNodes.forEach((childItem,childIndex)=>{
nodes.push(childItem);
})
}else if(item.checked){
nodes.push(item);
}
});
return nodes;
}
},
components:{
treeItem:{
name:"treeItem",
data(){
return{
isOpen:false,
isUnAllCheck:false
}
},
props:{
treeData:Object,
options:Object,
level:Number,
},
computed:{
isFolder(){
return this.treeData.children&&this.treeData.children.length;
}
},
created(){
//默认全选
if(this.options.isSelected){
this.treeData.checked=true;
}
},
mounted(){
this.isOpen=this.options.expandNum>=this.level;
},
methods:{
changeType(){
if(this.isFolder){
this.isOpen=!this.isOpen;
//异步树展开节点的时候调用回调
if(this.isOpen&&this._events["expandNode"]){
this.$emit('expandNode', this.treeData)
}
}
},
nodeClick(value,level){
if(level){
this.currentNodeClick();
}else{
this.parentNodeState();
}
//是否有回调函数
if(this._events["nodeItemClick"]){
this.$emit('nodeItemClick', value)
}
},
currentNodeClick(){
this.isUnAllCheck=false;
this.treeData.checked=!this.treeData.checked;
let state=this.treeData.checked;
if(this.treeData.children&&this.treeData.children){
this.childStateChange(this.treeData.children,state);
}
},
childStateChange(children,state){
if(children&&children.length){
children.forEach((item,index)=>{
item.checked=state;
if(item.children&&item.children){
this.childStateChange(item.children,state);
}
});
}
},
parentNodeState(){
let state=this.getChildrenState(this.treeData.children);
if(state===null){
this.isUnAllCheck=true;
}else{
this.isUnAllCheck=false;
this.treeData.checked=state;
}
},
getChildrenState(children){
var state=false;
if(children&&children.length>0){
for(let i=0;i<children.length;i++){
let item=children[i];
//子节点没有选全
if(state!=item.checked&&i>0){
return null;
}
state=item.checked;
if(item.children&&item.children.length){
let childState=this.getChildrenState(item.children);
//子节点没有选全
if(childState==null||state!=childState)
return null;
}
}
}
return state;
}
},
template:`<li class="tree__li">
<div :class="['tree__item--arrow',isOpen?'tree__item--arrow-down':'']" @click="changeType" v-if="isFolder"><i></i></div>
<div class="tree__item" @click="nodeClick(treeData,level)">
<span :class="['tree-icon', isFolder&&isOpen?'tree-folder_open':isFolder?'tree-folder':'tree-file']" v-if="options.hasicon"></span>
<span :class="['tree-checkbox', isUnAllCheck?'tree-checkbox_unallchecked':treeData.checked?'tree-checkbox_checked':'tree-checkbox_unchecked']"></span>
<span class="tree-text" >{{treeData.name}}</span>
<span title="删除" class="glyphicon glyphicon-remove tree-delete" v-if="treeData.candelete">x</span>
</div>
<ul class="tree__ul" v-show="isOpen" v-if="isFolder">
<treeItem v-for="(item,index) in treeData.children"
:treeData="item"
:key="index"
:options="options"
:level="level+1"
@nodeItemClick="nodeClick"
></treeItem>
</ul>
</li>`
}
}
}
</script>
css:
body{
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
.header{
height: 50px;
border-bottom: 1px solid hsla(0,0%,100%,.15);
background-color: #373d41;
}
.body{
position: absolute;
top:50px;
left: 220px;
right:0;
bottom: 0;
background-color: #fff;
}
.menu{
position: absolute;
top:50px;
left: 0;
bottom: 0;
width: 220px;
}
.tree{
position: relative;
width: 100%;
height: 100%;
padding: 15px 10px;
overflow-x: hidden;
overflow-y: auto;
}
.tree__ul{
margin: 0;
padding: 0;
list-style: none;
}
.tree__li{
position: relative;
padding-left: 12px;
}
.tree__item{
margin: 0;
padding: 2px 12px 2px 6px;
line-height: 20px;
font-size: 12px;
color: #222;
background-color: transparent;
cursor: pointer;
}
.tree__item:hover{
background-color: #F0F8FD;
}
.tree__item_selected{
background-color: #F0F8FD;
}
.tree__item--arrow{
display: inline-block;
position: absolute;
top: 0;
left: 2px;
width: 12px;
height: 24px;
overflow: hidden;
cursor: pointer;
}
.tree__item--arrow i{
display: inline-block;
position: absolute;
top: 7px;
left: 4px;
border-width: 5px;
border-style: dashed dashed dashed solid;
border-color: transparent transparent transparent #222;
font-size: 0;
line-height: 0;
cursor: pointer;
}
.tree__item--arrow:hover i{
border-color: transparent transparent transparent #999;
}
.tree__item--arrow-down{
}
.tree__item--arrow-down i{
display: inline-block;
position: absolute;
top: 10px;
left: 0px;
border-width: 5px;
border-style: solid dashed dashed dashed;
border-color: #222 transparent transparent transparent;
font-size: 0;
line-height: 0;
}
.tree__item--arrow-down:hover i{
border-color: #999 transparent transparent transparent;
}
.tree__item--arrow + .tree__ul{
display: none;
}
.tree__item--arrow-down + .tree__ul{
display: block;
}
/*树样式*/
.tree-icon,.tree-checkbox {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: top;
overflow: hidden;
}
.tree-icon{
background-image: url('../img/treeicon.png');
}
.tree-folder {
background-position: 52px -3px;
}
.tree-folder_open {
background-position: 52px 47px;
}
.tree-file {
background-position: 52px 21px;
}
.tree-checkbox {
background-image: url('../img/treeicon.png');
}
.tree-checkbox_checked {
background-position: 25px 47px;
}
.tree-checkbox_unchecked {
background-position: 25px -2px;
}
.tree-checkbox_unallchecked {
background-position: 25px 22px;
}
.tree-text {
display: inline-block;
vertical-align: top;
}
.tree-delete {
padding-left: 10px;
color: red;
}
样式图标:
调用:
<tree
ref="testTree"
:treeDatas="treeDatas"
@nodeClick="nodeClick"
@expandNode="expandClick"
:options="options"></tree>
数据格式:
var options = {
expandNum: 4,
isAllexpand: true,
hascheckbox: true,
hasicon: true,
folderClass: "",
folderopenClass: "",
fileClass: "",
candelete: true,
checkedCallback: false,
removeCallback: false,
checkedSelectChild: true,
};
var treeDatas = [{
"id": 4,
"value": "省XX",
"name": "省XX",
"checked": false,
"children": [{"id": 1026, "value": "chenXX", "name": "陈XX","checked": false}, {
"id": 2427,
"value": "zhuangXX",
"name": "庄XX", "checked": false
}, {"id": 100730, "value": "dwliuXX", "name": "刘XX", "checked": false}, {
"id": 100906,
"value": "linXX",
"name": "林XX", "checked": true
}, {"id": 212, "value": "zhangXX", "name": "张XX", "checked": false}, {
"id": 214,
"value": "wangXX",
"name": "王XX", "checked": false
}, {"id": 236, "value": "huangXX", "name": "黄XX", "checked": false}, {
"id": 265,
"value": "caihouen",
"name": "蔡XX", "checked": false,
"children":[{"id": 1026, "value": "chenXX", "name": "陈XX", "checked": true}, {
"id": 212,
"value": "zhangXX",
"name": "张XX","checked": true
}, {"id": 531, "value": "liuXX", "name": "刘XX", "checked": true}, {
"id": 4477,
"value": "liXX",
"name": "李XX",
"checked": true
}]
}]
}, {
"id": 55,
"value": "省XX分析组",
"name": "省XX分析组",
"checked": true,
"children": [
{"id": 1026, "value": "chenXX", "name": "陈XX", "checked": true}, {
"id": 212,
"value": "zhangXX",
"name": "张XX","checked": true
}, {"id": 531, "value": "liuXX", "name": "刘XX", "checked": true}, {
"id": 4477,
"value": "liXX",
"name": "李XX",
"checked": true
}]
}];
获取树数据的方法:
//数结构的回调
nodeClick(value) {
console.log(value);
},
expandClick(value){
console.log(value);
},
getSelectNode(){
let nodes=this.$refs.testTree.getSelectNode();
console.log(nodes);
}
以上,一个功能还不够完善的树组件(一直没时间完善功能)