三级checkbox菜单权限勾选与半勾选

实现功能:

 思路:

1、tab切换为一级菜单栏,根据点击不同的tab获取该底下的二三级菜单,实际操作为二三级,但最终传值,如果一级底下有勾选项也要将一级id传给后台

2、实现勾选与半勾选,用于回显页面的数据结构为树结构

全部源码:
<template>
  <div class="newSubPage warp">
    <div class="bg-f2 bg-fff" style="padding-bottom: 45px">
      <el-form ref="addRoleForm" class="pAll" style="padding-bottom: 60px !important" 
        :rules="addRoleRules" :model="addRole"  label-width="80px">
        <el-form-item label="分配权限:" class="distribute">
          <!-- 一级菜单tab -->
          <el-tabs v-model="activeRoleId" type="card" @tab-click="handleClick">
            <el-tab-pane v-for="item in firstmenuList" :key="item.id"
              :label="item.menuName" :name="item.id.toString()">
            </el-tab-pane>
          </el-tabs>
          <div class="table-body" v-for="secondMenu in roleList" :key="secondMenu.id">
            <!-- 二级菜单 -->
            <el-checkbox :indeterminate="secondMenu.indeterminate" 
              v-model="secondMenu.selected" @change="handleTwoLevelMenuhange(secondMenu, $event)">
              {{ secondMenu.menuName }}
            </el-checkbox>

            <div v-for="thirdMenu in secondMenu.childList" :key="thirdMenu.id" class="juxtaposition">
              <!-- 三级菜单 -->
              <el-checkbox :indeterminate="thirdMenu.indeterminate" v-model="thirdMenu.selected"
                @change="handleThreeLevelMenuhange(thirdMenu, secondMenu, $event, true)" class="thirdLeve">
                {{ thirdMenu.menuName }}
              </el-checkbox>

              <div class="thirdLeveLable">
                <!-- 四级菜单 -->
                <el-checkbox :indeterminate="fourthMenu.indeterminate" v-model="fourthMenu.selected"
                  @change="handleFourLevelMenuhange(fourthMenu, thirdMenu, secondMenu, $event)"
                  v-for="fourthMenu in thirdMenu.childList" :key="fourthMenu.id">
                  {{ fourthMenu.menuName }}
                </el-checkbox>
              </div>
            </div>
          </div>
        </el-form-item>
      </el-form>
      <el-button @click="savaBtnSubmit()" type="primary" class="btnSizeStyle">确定</el-button>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      title: '', // 编辑新增标题
      addRole: {},
      addRoleRules: { },
      activeRoleId: '1', // 运维概况Id为1
      roleLoading: false, // 权限列表加载
      firstmenuList: [], // 一级菜单查询
      roleList: [], // 二三级数据源
      checkbox: [], // 勾选数据源
      checkModelList: [], // 储存每个模块化的勾选id
    }
  },
  created() {
    this.getFirstLevelMenu() // 一级菜单查询
  },
  watch: {},
  methods: {

    // 一级菜单查询, 顶部横向排序
    getFirstLevelMenu() {
      this.firstmenuList = [
        {id: 1, menuName: '运维概况'},
        {id: 2, menuName: '巡检中心'},
      ]
      this.getRoleMenu()
    },

    // 二三四级菜单查询
    getRoleMenu() {
      let list = []
      if (this.activeRoleId == 1) {
        list = [
          {id: 3, menuName: '主机监视', parentId: 1, childList: [
            {id: 21, menuName: '主机列表', parentId: 3, childList: [
              {id: 52, menuName: '查看主机列表', parentId: 21},
              {id: 53, menuName: '删除', parentId: 21},
            ]}
          ]},
          {id: 4, menuName: '资产分布', parentId: 1, childList: [
            {id: 22, menuName: '点位分布概览', parentId: 4, childList: [
              {id: 61, menuName: '查看点位分布概览', parentId: 22},
            ]},
            {id: 23, menuName: '资产分布概览', parentId: 4, childList: [
              {id: 71, menuName: '查看点位分布概览', parentId: 22},
              {id: 72, menuName: '查看资产分布概览', parentId: 23},
            ]}
          ]}
        ]
      } else {
        list = [
          {id: 5, menuName: '人工巡检', parentId: 2, childList: [
            {id: 8, menuName: '机房巡检', parentId: 5, childList: [
              {id: 82, menuName: '查看', parentId: 8},
              {id: 83, menuName: '添加', parentId: 8},
              {id: 84, menuName: '编辑', parentId: 8},
              {id: 85, menuName: '详情', parentId: 8},
              {id: 86, menuName: '导出', parentId: 8},
              {id: 87, menuName: '删除', parentId: 8},
            ]}
          ]},
        ]
      }
      this.roleList = this.getProductType(list)
    },
    
    // 根据勾选数据获得默认勾选状态
    getProductType(data, type) {
      data.forEach(parent1 => {
        let allSelected1 = true;
        let someSelected1 = false;

        parent1.childList.forEach(parent2 => {
          let allSelected2 = true // 默认全勾选
          let someSelected2 = false

          parent2.childList.forEach(child => {
            console.log(this.checkModelList.indexOf(child.id) != -1, this.checkModelList,'---', child.id)
            if (this.checkModelList.indexOf(child.id) != -1) { // 如果包含该四级菜单
              child.selected = true // 该四级菜单状态为勾选
            } else { // 否则不勾选
              child.selected = false
            }

            if (!child.selected) { // 如果四级菜单有一个没勾
              allSelected2 = false // 三级菜单全勾状态为false
            } else {
              someSelected2 = true // 三级菜单全勾状态改变,代表勾选一部分
            }
            parent2.selected = allSelected2 // 赋值三级菜单全勾状态
            if (someSelected2 && !allSelected2) { // 勾选三级菜单一部分
              parent2.indeterminate = true // 赋值三级菜单办勾状态
            } else {
              parent2.indeterminate = false
            }

            if (!parent2.selected) {  // 如果三级菜单有一个没勾
              allSelected1 = false // 二级菜单全勾状态为false
            } else {
              someSelected1 = true // 二级菜单全勾状态改变,代表勾选一部分
            }
          })
          parent1.selected = allSelected1 // 赋值二级菜单全勾状态

          if (someSelected1 && !allSelected1) { // 勾选二级菜单一部分
            parent1.indeterminate = true // 赋值二级菜单办勾状态
          } else {
            parent1.indeterminate = false
          }
        })
      })
      return data
    },

    // 二级菜单点击
    handleTwoLevelMenuhange(secondMenu, e) {
      secondMenu.indeterminate = false // 去掉二级不确定状态
      secondMenu.selected = e // 二级勾选后,全部勾选或者取消

      if(secondMenu.childList) {
        for(let item of secondMenu.childList) {
          if (e) item.indeterminate = false // 取消半勾选换成全勾选
          item.selected = e // 三级全部勾选或者取消
          if (item.childList)
            for(let el of item.childList)
            el.selected = e // 四级全部勾选或取消
        }
      }
      this.getCheckData() // 得到勾选数据
    },

    // 三级菜单点击
    handleThreeLevelMenuhange(thirdMenu, secondMenu, e, isFourClick){
      let twoData = secondMenu.childList // 二级下的所有三级
      let tickCount = 0, unTickCount = 0
      for (let i of twoData) {
        // 四级勾选已在四级进行了操作,所以点击四级不进这里
        if(thirdMenu.id == i.id && isFourClick) i.selected = e
        if(i.selected) { // 勾选操作
          i.indeterminate = false
          tickCount++
        }
        else if (!i.indeterminate && !i.selected) { // 不勾选
          unTickCount++
        }
      }
      if(tickCount == twoData.length) { // 三级级全勾选
        secondMenu.selected = true 
        secondMenu.indeterminate = false
      } else if(unTickCount == twoData.length) { // 三级级全不勾选
        secondMenu.selected = false 
        secondMenu.indeterminate = false
      } else {
        secondMenu.selected = false
        secondMenu.indeterminate = true // 添加三级不确定状态
      }
      if(thirdMenu.childList && isFourClick) { // 四级渲染时不走这里
        for(let item of thirdMenu.childList) {
          item.selected = e // 四级全部勾选或者取消
        }
      }
      this.getCheckData() // 得到勾选数据
      this.$forceUpdate() // 强制更新
    },

    
    // 四级菜单点击
    handleFourLevelMenuhange(fourthMenu, thirdMenu, secondMenu, e){
      let threeData = thirdMenu.childList // 三级下所有的四级
      let tickCount = 0, unTickCount = 0
      for (let i of threeData) {
        if(fourthMenu.id == i.id) i.selected = e
        if(i.selected) tickCount++ // 勾选
        if(!i.selected) unTickCount++ // 不勾选
      }

      if(tickCount == threeData.length) { // 三级级全勾选
        thirdMenu.selected = true 
        thirdMenu.indeterminate = false
      } else if(unTickCount == threeData.length) { // 三级级全不勾选
        thirdMenu.selected = false 
        thirdMenu.indeterminate = false
      } else {
        thirdMenu.selected = false
        thirdMenu.indeterminate = true // 添加三级不确定状态
      }

      // 关联三级点击--二级勾选
      this.handleThreeLevelMenuhange(thirdMenu, secondMenu, e, false)
      this.$forceUpdate() // 强制更新
    },
    
    // 得到勾选的所有数据,如果一级下的子级有勾选,一级也要储存
    getCheckData() {
      this.checkbox = []
      this.traverseData(this.roleList)
      
      // 合并数组,checkbox有个操作会清空勾选数据
      let allId = this.checkModelList.concat(this.checkbox) 
      let uniqueSelected2 = new Set(allId) // 去重
      this.checkModelList = [...uniqueSelected2]
    },
    
    // 根据是否勾选将添加到数据
    traverseData(data) {
      for (let item of data) {
        if (item.selected || item.indeterminate) {
          this.checkbox.push(item.id)
          this.checkbox.push(item.parentId) // 主要是将第一级id添加进去
        } else if (!item.selected && !item.indeterminate) {
          this.checkModelList.filter((el, index) => {
            if (el === item.id || el === item.parentId) { // 同时将自己与父级删除
              this.checkModelList.splice(index, 1)
              index--
            }
          })
        }
        if (item.childList) this.traverseData(item.childList)
      }
      return data
    },

    // tab切换获取新的数据
    handleClick(event) {
      this.activeRoleId = event._props.name
      this.getRoleMenu()
    },

    // 递归方法,将所有数据赋值新字段
    circulateCheck(data) {
      for (let item of data) {
        item.selected = false
        if (item.childList) {
          this.circulateCheck(item.childList)
        }
      }
      return data
    },

    // 确定按钮提交事件
    async savaBtnSubmit() {
      console.log('勾选数据源', this.checkModelList)
    },
  }
}
</script>
<style lang="scss" scoped>
.table-body {
  border: 1px solid #dfe4ed;
  margin-bottom: 10px;
  padding: 0 10px;
}
.juxtaposition {
  display: flex;
  border-bottom: 1px solid #dfe4ed;
  padding: 5px 0px;
  .thirdLeve {
    flex-shrink: 0; // 当前元素就不会被其他元素挤压。
    width: 165px !important;
  }
  .thirdLeveLable {
    padding-left: 10px;
    border-left: 1px solid #1890ff;
  }
}
::v-deep .juxtaposition .el-checkbox__label {
  font-weight: normal !important;
}
::v-deep .el-tabs--card > .el-tabs__header {
  height: 40px;
  border-bottom: none;
}
</style>
总结:

1、getProductType函数是用于编辑页面回显的问题,不需要可注释

2、勾选级数大的,级数小的总是和级数大的状态一致

3、勾选级数小的,判断父级的子级长度,再根据tickCount和unTickCount这两个纸判断父级是全勾选还是半勾选。如果为全勾选,半勾选状态一定要为false,不然会出先显示不正确的问题。勾选四级要判断三级和二级,往上推

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值