[场景实现]:多选框与树形结构递归

一、场景描述

实现一个分配权限的页面,最左侧是大的权限模块的名称,左右侧是控制其是否勾选的多选框。中间部分是一级权限模块下的子权限名称及多选框。

请求此权限模块数据的接口返回的是树形结构 对象数组。
主要属性为menuName表示权限名,isHas表示是否拥有此权限,1或0字符串,parentMenuId为0表示一级,children表示子项,id唯一标识。

展示在界面的数据为一级和没有子项的末级。对象数组项中,可能没有children子项,即权限一级二级三级四级都可能出现。多选框标识是否是否有此权限。并且存在依赖关系。当界面上某模块所有子项勾选,一级多选框自动勾选,一个子项未勾选,一级多选框自动不勾选。一级多选框的勾选与否同时也能控制此模块的所有子项的勾选。
当选定权限保存时向后端传递的是界面上所有勾选项。

[场景实现]:多选框与树形结构递归_第1张图片

二、实现

2.1 数据渲染

el-checkbox的v-model属性为选中项绑定值,可以是布尔、字符串或数字类型。用true-label和false-label标识选中和未选中值即可。
但是当使用的是否以为只能是布尔类型,就把返回的数据依赖isHas转化一下渲染.

// PermissionsType.value是被赋值的数据
for (let i = 0; i < PermissionsType.value.length; i++) {
     addIsCheckedProperty(PermissionsType.value[i]);
}
//增加isChecked标识选中未选中
function addIsCheckedProperty(menu) {
     if (menu.isHas === '1') {
        menu.isChecked = true;
      } else {
        menu.isChecked = false;
      }
      if (menu.children && menu.children.length > 0) {
          for (let i = 0; i < menu.children.length; i++) {
              addIsCheckedProperty(menu.children[i]);
           }
      }
}

2.2 多选框的关联关系

为el-checkbox绑定change事件,传递当前的项作为参数。
当parentMenuId === 0即为一级数据,检测到一级就根据一级数据更新其下所有子项isChecked 。
若为子项,则根据所有子项的isChecked状态改变一级项的isChecked 状态

//el-checkbox的change事件
const handleCheckboxChange = (item) => {
     //点击一级
     if (item.parentMenuId === 0) {
        updateChildrenCheckedStatus(item, item.isChecked);
     }
     updateParentCheckedStatus(PermissionsType.value);
};
// 递归更新子菜单状态
const updateChildrenCheckedStatus = (menu, isChecked) => {
     if (menu.children) {
        menu.children.forEach((child) => {
             child.isChecked = isChecked;
             updateChildrenCheckedStatus(child, isChecked); // 继续更新孙菜单
        });
     }
};
// 检查并更新父菜单状态
const updateParentCheckedStatus = (menu) => {
     menu.forEach((menuItem) => {
          if (menuItem.children && menuItem.children.length > 0) {
              menuItem.isChecked = areAllChildrenChecked(menuItem.children);
          } else if (!menuItem.isChecked) {
              menuItem.isChecked = false; // 只有一级的isChecked置为false
          }
     });
};
const areAllChildrenChecked = (children) => {
     for (const child of children) {
         if (child.children && child.children.length > 0) {
             if (!areAllChildrenChecked(child.children)) {
                 return false;
             }
         } else {
               if (!child.isChecked) {
                   return false;
               }
         }
     }
     return true;
};

2.3 只传递界面上勾选项的id

遍历数据中所有被选择项的id得到数组。
遍历数据,只保留一级数据的id和末级数据的id,得到数组。
数组去重。

for (let i = 0; i < PermissionsType.value.length; i++) {
    getCheckedIds(PermissionsType.value[i]);
}
selectIgnore();
filteredMenuIds.value = selectedMenuIds.value.filter((item) => isIgnore.value.indexOf(item) === -1);

//递归遍历被选中权限id
function getCheckedIds(menu) {
    if (menu.isChecked === true) {
       selectedMenuIds.value.push(menu.id);
    }
    if (menu.children && menu.children.length > 0) {
        for (let i = 0; i < menu.children.length; i++) {
           getCheckedIds(menu.children[i]);
        }
    }
}
//有三级项时忽略掉二级项 有四级项时忽略三级项
const selectIgnore = () => {
      PermissionsType.value.forEach((item) => {
          if (item.children) {
              item.children.forEach((child) => {
                   if (child.children) {
                       isIgnore.value.push(child.id);
                       child.children.forEach((childThird) => {
                        	if (childThird.children) {
                           		isIgnore.value.push(childThird.id);
                        	}
	                    });
                   }
              });
          }
     });
};

你可能感兴趣的:(前端,场景,前端,javascript,elementui)