假定,我们要实现的权限组件效果是这样的:
要实现点击系统,下面的都全选,点击基础功能,用户管理半选,系统半选。点击新增组织,如果基础功能没有选中,则基础功能改为选中。
<template> <div class="permit_cont"> <div v-if="datas.childResoList.length>0" :class="{'top_check':datas.parentId==0}"> <el-checkbox :indeterminate="isIndeterminate" v-model="datas.checkState" @change="handleCheckAllChange">{{datas.title}}</el-checkbox> </div> <el-checkbox v-else v-model="datas.checkState">{{datas.title}}</el-checkbox> <div v-if="datas.childResoList.length>0"> <el-checkbox v-model="based" v-if='datas.parentId!=0' @change='basecheck'>基础功能</el-checkbox> <template v-for="d in datas.childResoList"> <permitnode :datas="d" v-if="d.childResoList.length>0" @pchange="permitCheck" :key="d.id"></permitnode> <el-checkbox v-else v-model="d.checkState" @change="leafcheck">{{d.title}}</el-checkbox> <div v-if="datas.parentId==0" class="permit_hr"></div> </template> </div> </div> </template> <script> export default{ name:"permitnode", props:{ datas:{ type:Object, default:{} } }, data(){ return { isIndeterminate:false, childClick:true, based:true }; }, mounted(){ this.based=this.datas.checkState; let count=0; //初始化时判断自身时全选还是半选 this.datas.childResoList.forEach(it=>{ if(it.checkState){ count++; } }); if(count>0&&count<this.datas.childResoList.length){ this.isIndeterminate=true; this.datas.indeter=true; } if(count==0){ this.isIndeterminate=false; this.datas.indeter=false; } if(count==this.datas.childResoList.length){ this.isIndeterminate=false; this.datas.indeter=false; } }, //watch是监听数据的变化,所以change引起树的下级变化, //下级变化调用leafcheck方法改变上层数据时,上层数据的值没有变,所以不会死循环 watch:{ "datas.checkState":function(val){ //如果是半选状态,则表明是由子组件引起的变化,不进行全选操作 if(this.datas.indeter==true){ return ; } //递归watch过来时,取消本身的半选状态。 this.isIndeterminate=false; this.based=val; //遍历全选子组件数据,子组件watch到变化全选算子组件 for(let i=0;i<this.datas.childResoList.length;i++){ let tmp=this.datas.childResoList[i]; tmp.checkState=this.datas.checkState?true:false; if(tmp.childResoList&&tmp.childResoList.length>0){ tmp.indeter=false; //全选时改变半选状态 } this.datas.childResoList.splice(i,1,tmp); } } }, methods:{ handleCheckAllChange(val){ //点击上面的全选按钮时,改变全选状态,出发watch变化 this.isIndeterminate=false;//取消自身的半选状态 this.datas.indeter=false; //向上触发事件,改变上层checkbox的变化 this.$emit("pchange"); }, //子组件变化时触发的pchange事件 permitCheck(){ this.leafcheck(); }, //基础权限选中,则父权限选中,基础权限取消,则全部取消选中。 basecheck(){ if(this.based==false){ this.datas.checkState=false; this.isIndeterminate=false; this.datas.indeter=false; }else{ this.datas.checkState=true; this.isIndeterminate=true; this.datas.indeter=true; } this.$emit("pchange"); }, //叶子checkebox变化时,包括子组件变化 leafcheck(val){ let count=0; let hasIndeter=false; //计算选中的数值,是不是达成了全选的状态 this.datas.childResoList.forEach(it=>{ if(it.checkState){ //判断其中是否有半选的 if(it.indeter!=undefined){ if(it.indeter==true){ hasIndeter=true; } } count++; } }); //parentId!=0不是第一级权限 if(this.datas.parentId!=0){ if(val&&this.based==false){ this.based=true; this.datas.checkState=true; } //当checkbox选中时,这时候based必为true if(count<this.datas.childResoList.length){ //this.datas.checkState=true; //indeter用来表示数据是否是显示为半选状态 this.datas.indeter=true; this.isIndeterminate=true; }else{ if(hasIndeter){ this.datas.indeter=true; this.isIndeterminate=true; }else{ this.datas.indeter=false; this.isIndeterminate=false; } } }else{ console.log("count:"+count); if(count>0&&count<this.datas.childResoList.length){ this.datas.indeter=true; this.isIndeterminate=true; this.datas.checkState=true; //indeter用来表示数据是否是显示为半选状态 } //基础权限也没选中时 if(count==0){ this.datas.indeter=false; this.isIndeterminate=false; this.datas.checkState=false; } //因为选中和半选都是选中状态,所以要做一下半选显示状态的区分 //全部选中,且没有半选显示状态的。 if(count==this.datas.childResoList.length&&hasIndeter==false){ this.datas.indeter=false; this.isIndeterminate=false; this.datas.checkState=true; } //全部选中,但是有是半选显示状态的 if(count==this.datas.childResoList.length&&hasIndeter==true){ this.datas.indeter=true; this.isIndeterminate=true; this.datas.checkState=true; } } //向上层反馈变化 this.$emit("pchange"); } } } </script> <style scoped> .permit_cont{color:#fff;font-size:12px;line-height: 2;} .permit_hr{border-bottom: 1px solid #2F3B52;margin:10px 0;} .top_check{margin-bottom:20px;} </style>
之后需要在这个组件外面套一层组件,提供一个方法来获取选中的checkbox
<template> <div> <permitNode v-if="c.id==rootId" :datas="c" v-for='c in chess' :key="c.id"></permitNode> </div> </template> <script> import permitNode from '@/components/public/permit_node' export default{ props:{ chess:{ type:Array, default:[] }, rootId:{ type:Number, defualt:0 } }, components:{ permitNode }, data(){ return { }; }, methods:{ //遍历获取树型数据中选中的checkbox的id getCheckedKey(){ let arr=[]; let keyarr=this.getCheckedByCircle(this.chess,arr); return keyarr; }, getCheckedByCircle(keys,arr){ for(let i=0;i<keys.length;i++){ let tmp=keys[i]; if(tmp.checkState){ arr.push(tmp.id); } if(tmp.childResoList.length>0){ this.getCheckedByCircle(tmp.childResoList,arr); } } return arr; } } } </script>
第二个组件上面的v-for是因为权限要像tab页签一样分多个模块,根据模块的选中状态展现不同的权限选项。