elementUI-checkbox官网:https://element.eleme.cn/#/zh-CN/component/checkbox
在这里插入图片描述

一、要做上面这种效果,首先要了解全选框中indeterminate 状态和v-model的变量的关系 参考 -Dayer-
<el-checkbox :indeterminate="isIndeterminate" v-model="selected" @change="handleCheckAllChange($event, 'hd')">华东</el-checkbox>

对于indeterminate和v-model绑定的isIndeterminate和selected的值分别为:

isIndeterminateselected描述结果
truetrue或false样式为 -,不确定状态
falsetrue样式为 √,被选中状态
falsefalse不勾,没有被选中

总结:isIndeterminate 在全选和未被全选状态下都是 false,其他状态下是 trueselected 只有全选状态下是 true,其他都是 false(或者你也可以设置成:只要子级被选中,那么selected=true,子级一个都没有被选,selected=false。我在这里用的是第一个)。


二、具体案例参考:https://www.jb51.net/article/148826.htm

1. 做这种有多个全选复选框的,最好是让后台帮忙加一下字段,不然就跟官网上的案例一样,每个一级菜单下添加一个数组变量用来存放数据。
字段isIndeterminate(全选复选框显示状态),selected(CheckBox选中状态,是否被选中)。

[
  {isIndeterminate:false, list:null, menuId:2, menuName:"行业管理", selected:false},
  {isIndeterminate:false, list:[
    {list:null, menuId:4, menuName:"企业管理", selected:false},
    {list:null, menuId:5, menuName:"数据生成", selected:false},
    {list:null, menuId:6, menuName:"数据下载", selected:false},
  ], menuId:3, menuName:"企业管理", selected:false},
  {isIndeterminate:false, list:[
    {list:null, menuId:8, menuName:"信息导入", selected:false},
    {list:null, menuId:9, menuName:"物流单据信息查询", selected:false},
    {list:null, menuId:10, menuName:"其他单据信息查询", selected:false},
  ], menuId:7, menuName:"单据管理", selected:false},
]

在一级菜单下添加:isIndeterminateselected,二级菜单下每条数据只要添加 selected 就行了。

2. 思路
在这里插入图片描述
注释部分有很多是废话,方便理解,开发时可以删除,留下自己认为比较重要的注释就可以啦~

3. 具体代码
(1)模板部分代码:

<el-dialog :title="dialogTitle" :visible.sync="isDialog" width="620px" v-if="isDialog">
<el-form :inline="true" :model="addForm" :rules="rules" ref="addForm" size="mini" class="myStyle" label-width="100px">
  <el-form-item label="角色菜单" prop="roleName">
    <el-input v-model="addForm.roleName" required placeholder="请输入角色菜单" class="nobr"></el-input>
  </el-form-item>
  <el-form-item label="角色描述">
    <el-input type="textarea" :rows="2" resize="none" v-model="addForm.roleDesc" placeholder="请输入角色描述" class="nobr"></el-input>
  </el-form-item>
  <el-form-item label="角色菜单">
    <div v-for="(selectMenu,selectMenuId) in selectMenuArr" :key="selectMenuId" style="padding-left:34px">
      <el-checkbox :indeterminate="selectMenu.isIndeterminate" v-model="selectMenu.selected" class="selectAll" 
      				@change="allCheckboxGroup($event, selectMenu, selectMenu.list)">
      				{{selectMenu.menuName}}
      </el-checkbox>
        <el-checkbox v-model="menuChild.selected" v-for="(menuChild,menuChildId) in selectMenu.list" :key="menuChildId" 
        			@change="checkOneBox(selectMenu, selectMenu.list)">
          			{{menuChild.menuName}}
        </el-checkbox>
    </div>
  </el-form-item>  
</el-form>
<div slot="footer" class="dialog-footer">
  <el-button @click="isDialog=false;" class="myStyle" size="mini">取 消</el-button>
  <el-button type="primary" @click="onSubmit(this.openDialogName)" class="myStyle" size="mini">确 定</el-button>
</div>
</el-dialog>
        
isDialog: false,
openDialogName: "",
dialogTitle: "",
selectMenuArr: [],//这个是复选框绑定值
storageArr: [], //暂存数组。selectMenuArr修改过了,但是想要原来没被修改过的数据
menuIdsArr:[], //储存表中的角色菜单(用于修改弹框)可以按需求定义,无影响。不过如果这里没有定义,那么在打开弹框操作时要记得定义。
//因为我们的需求是要判断提交修改结果是否与原来表中的数据相等,所以在这里定义了menuIdsArr

(2)全选复选框,和单个选择复选框的方法:

// 是否全选
allCheckboxGroup(e,preList,sonList){
  console.log(e , preList, sonList)
  if( e ){ //这里的e就是一级菜单的selected
  	if( sonList ){
  	  //不论子菜单是否被选中,只要一级菜单被选中,让该子菜单的selected都=true;
  	  for(let i = 0 ;i < sonList.length; i++){
    	sonList[i].selected = true;
      }
      //因为在选中部分子菜单的情况下,再点击全选按钮。这种情况也能使一级菜单的selected=true,
      //此时一级菜单的isIndeterminate=true(且这种情况只有 子菜单存在 才能发生)。
      //但是只要被全选,一级菜单的isIndeterminate就要=false。
  	  preList.isIndeterminate = false; 
  	}
  } else {
  //因为让一级菜单的selected=false只有在全部都被选中的情况下发生,所以一级菜单的isIndetermainate是一直=false的
    if( sonList ) {
      for(let i = 0 ;i < sonList.length; i++){
    	sonList[i].selected = false;
      }
    }
  }
  //要知道,让一级菜单的isIndeterminate=true,都是必须要操作子菜单才能做到的
},

//单个选择
checkOneBox(preList,sonList){
  //tickCount选了的总量,unTickCount没选数量 这里的 e就是当前复选框的selected值
  var tickCount = 0, unTickCount = 0, len = sonList.length
  for(let i=0; i<len; i++){
    if( sonList[i].selected == true) tickCount++;
    if( sonList[i].selected == false) unTickCount++;
  }
  if(tickCount == len){ //选中数量=子级总数,全选
    preList.selected = true;
    preList.isIndeterminate = false;
  } else if ( unTickCount == len ){ //未选中数量=子级总数,未全选
    preList.selected = false;
    preList.isIndeterminate = false;
  } else { //其他情况是不确定状态
    preList.selected = false;
    preList.isIndeterminate = true;
  }
},

(3) 因为我这是在弹框中写的,这里将打开弹框和关闭弹框时的操作贴一下:
我这边的需求是,只要选了子菜单,那么该子菜单的父级菜单(一级菜单)也要被添加。

//打开弹框清空表单,并将数据保存到弹框中的 updateForm 中
openDialog(formName = "addForm", data) {
  this.$nextTick(() => {
    if(this.$refs.addForm){
      this.$refs.addForm.clearValidate();
    } 
  })
  for(let key in this.addForm){
    this.addForm[key]="";
  }
  delete this.addForm.id;  
  this.isDialog = true;
  this.openDialogName = formName;

  //selectMenuArr是绑定所有复选框的数据,打开弹框时,让数据恢复成初始值。
  this.selectMenuArr = JSON.parse(JSON.stringify(this.storageArr)); //深浅拷贝问题

  if (formName === "addForm") {  
    this.dialogTitle = '新增角色信息';

  } else if (formName === "updateForm") {
    this.dialogTitle = '修改角色信息';
    let { id, roleName, roleDesc, menuIds } = data; 
    this.addForm = { id, roleName, roleDesc, menuIds };
    
    this.menuIdsArr = data.menuIds !="" ? data.menuIds.split(",").map(Number) : []; //将表中的数据存到menuIdsArr中
    
    //以下部分是在打开修改弹框时,根据表格中的数据来让复选框对应显示
    //1. 比较menuIds的值和selectMenuArr中的menuId
    for(let i=0; i<this.selectMenuArr.length;i++){
      //2. 如果相等,考虑有无子级。
      if(this.menuIdsArr.indexOf(this.selectMenuArr[i].menuId)>=0){
      	//2.1 有子:可能全选,可能选了一部分。计算该子菜单id和menuIds中相等个数,并将相等的selected=true。
        let count = 0; 
        if(this.selectMenuArr[i].list){
          for(let x=0; x<this.selectMenuArr[i].list.length; x++){
            for(let y=0; y<this.menuIdsArr.length; y++){
              if( this.selectMenuArr[i].list[x].menuId == this.menuIdsArr[y] ){
                this.selectMenuArr[i].list[x].selected = true;
                count++;
              }
            }
          }
          
          //3. 判断相等个数是否=子菜单长度,从而判断该子菜单的一级菜单全选状态。
          if(count == this.selectMenuArr[i].list.length){
            this.selectMenuArr[i].selected = true;
            this.selectMenuArr[i].isIndeterminate = false;
          } else {
            this.selectMenuArr[i].selected = false;
            this.selectMenuArr[i].isIndeterminate = true;
          }
        } else {   
        //2.2 无子:selected=true,isIndeterminate=false。           
          this.selectMenuArr[i].selected = true;
          this.selectMenuArr[i].isIndeterminate = false;
        }
      } //没有相同的部分,就不用管了(原数据的值都是false)
    }     

  }
},

打开elementUI弹框这一部分可以看我另一篇博客 elementUI清空弹框中的表单数据

(4)提交数据

//提交查询、添加、修改
onSubmit(formName) {
  this.$refs.addForm.validate(valid => {
    if (valid) {
      this.isDialog = false;
      
      //将已经选中的复选框的值处理一下,放到arr中
      let arr = [];
      for(let i =0 ;i<this.selectMenuArr.length; i++){
        if(this.selectMenuArr[i].selected){ //selected=true.有子说明全选,无子说明只有一个菜单且被选中
          if(this.selectMenuArr[i].list){
            for(let j=0; j<this.selectMenuArr[i].list.length; j++){
              arr.push(this.selectMenuArr[i].list[j].menuId);
            }
          }
          arr.push(this.selectMenuArr[i].menuId);
        } else {
          if(this.selectMenuArr[i].isIndeterminate){//isIndeterminate=true.说明一定有子,且选了一部分
            for(let j=0; j<this.selectMenuArr[i].list.length; j++){
              if(this.selectMenuArr[i].list[j].selected){
                arr.push(this.selectMenuArr[i].list[j].menuId);
              }
            }
            arr.push(this.selectMenuArr[i].menuId);
          }
        }
      }
        
      //添加 
      if (formName === 'addForm') {   
        //因为我这边后台要求传字符串,且修改时,要角色菜单为发生变化就传 null。
        this.addForm.menuIds = arr.length == 0 ? "" : arr.sort(function(a,b){return a-b;}).join();
        //------添加接口-------
      }
      //修改
      else if (formName === 'updateForm') {
        //判断角色是否修改,没修改传 null,修改了传字符            
        this.addForm.menuIds = this.menuIdsArr.sort(function(a,b){return a-b;}).join() == arr.sort(function(a,b){return a-b;}).join() ? null : arr.sort(function(a,b){return a-b;}).join();
        //------修改接口-------
      }

    } else {
      return false;
    }
  });
},

(5)获取复选框的动态数据:

selectRoleMenus(){
  this.$axios.post("接口").then(res => {
    this.storageArr = res.list;
    //因为在选择复选框时会操作到selectMenuArr ,而我们打开添加弹框时需要初始数据,所以将初始数据保留一份。
    this.selectMenuArr = res.list;     
  })
}
//记得在created中使用方法。

补充:

1. 比较两个数组是否相等 是否拥有相同元素:

<script type="text/javascript">
    alert([1,2,3].toString()== [3,2,1].toString());
    alert([1,2,3].sort(function(a,b){return a-b;}).toString()== [3,2,1].sort(function(a,b){return a-b;}).toString());
</script>

2. 从接口获取的数据 同时赋值给两个变量,改变其中一个变量时,另一个变量的值也会改变:
引用数据类型赋值时只是给变量保存一个指针,指向存储在堆中的对象,所以两个变量实际上是指向的同一个地方。

this.labelListData = JSON.parse(JSON.stringify(this.editLabelList));

解决方法是进行深度复制,因为在拷贝字符串时会开辟新的存储地址,这样就切断了该对象的指针与其指向地址的联系。( 听说如果拷贝对象包含正则表达式,函数,或者undefined等值时,深度烤贝会失效 )

3. 根据某个特定的值删除数组中的指定元素:

var arr = [2,3,5,9,54,21];
arr.splice( arr.indexOf(5), 1 ); //删除数组中的5
Logo

前往低代码交流专区

更多推荐