iview自定义下拉树菜单

最近由于项目需要,封装了第二种下拉树,带checkBox并且可以支持快速选择的下拉树。样式如下


自定义下拉树菜单.png

所实现交互功能如下:

1.点击对应的层级按钮,自动勾选出对应的所有同级节点。

2.点击自定义,清空所有勾选的节点。

3.勾选中不同层级的节点或同层级节点并没有全部选中,自动切换到自定义节点,并保存之前勾选的节点不清空。

4.在自定义中,若勾选的节点是同一个层级且此层级节点全部被选中,自动切换到对应层级按钮。

下面开始贴代码( 由于我将这种下拉树和之前普通的下拉树 是封装到同一个组件中的,所以只贴跟这次关联度较高的代码,之前的线性数组递归成树不再重复展示)

复制代码
<RadioGroup v-model="radioValue" v-if="showCheckBox" @on-change="selectedRadioHandle">
        <Radio
          style="margin-left: 10px"
          :label="item.value"
          v-for="item in radioArray"
          :key="item.value"
        >{{item.label}}</Radio>
</RadioGroup>
<Tree
        ref="tree"
        :data="suitableTreeData"
        @on-check-change="handleCheckedChange"
        check-strictly
        check-directly
        show-checkbox
        v-if="showCheckBox"
      ></Tree>
      <Button style="margin-left: 300px;" v-if="showCheckBox" @click="handleSubmit" type="primary" ghost>确定</Button>
      <Button v-if="showCheckBox" type="primary" ghost style="margin-left: 8px" @click="cancel">取消</Button>

首先讲解tree组件上面的参数:

1.check-strictly 在iview的api中是"在显示复选框的情况下,是否严格的遵循父子不互相关联的做法",简单的说就是父子节点不关联。

2.check-directly 在iview的api中是"开启后,在 show-checkbox 模式下,select 的交互也将转为 check"。

3.show-checkbox是支持多选。

接下来就是实现上面交互所需要的逻辑和代码

1.要实现第一个"点击对应的层级按钮,自动勾选出对应得所有同级节点"这个比较简单。将radioGroup里面的按钮value值根据节点层级设为对应的2,3,4,5,0。0代表自定义节点,用v-model双向绑定一个radioValue值,当点击radio按钮时将此变量作为参数传入一个找出所有同层级节点方法中。方法代码如下:

复制代码
 findLevelNode(tree, nodeLevel) {
      //找出某层级所有节点
      const _this = this;
      // console.log("nodeLevel",nodeLevel);
      tree.forEach(item => {
        if (item.level == nodeLevel) {
          _this.checkedNodeArr.push(item);
        } else {
          if (item.children) {
            _this.findLevelNode(item.children, nodeLevel);
          }else { return}
        }
      });
      _this.checkedNodeArr = _this.checkedNodeArr.filter(item => {
        //这里用filter是因为递归出来的数组中会存放上一次选择的所有节点,所以过滤出当前层级的节点
          if (item.level == nodeLevel) {return item}
        }
      );
      return _this.checkedNodeArr;
    },

2.实现第二个"点击自定义,清空所有勾选的节点"的交互也比较简单,也是用上面的方法,当选择自定义的时候,会返回一个空数组,所以不再赘述。

3.第三个需求中"勾选中不同层级的节点或同层级节点并没有全部选中,自动切换到自定义按钮,并保存之前勾选的节点不清空"。

这个交互分为两个步骤,第一个是"勾选中不同层级的节点或同层级节点并没有全部选中,自动切换到自定义节点"。要实现这个效果,首先要获取到所勾选的节点,并判断勾选的节点是否全部属于同一个层级,如果不是切换到自定义*( 获取勾选的节点可以用this.$refs.tree.getCheckedNodes()来实现 )* 。然后要判断当前选中层级的所有节点中有没有checked属性为false的*( 这里也是用上面的findLevelNode方法来实现 )*。上面两个判断我都是用ES6的every()方法来实现

复制代码
let checkedArr = _this.$refs.tree.getCheckedNodes();
      let isChecked = checkedArr.every(item => {
        //选中的checkBox的层级不一致切换到自定义
        return item.level == _this.radioValue;
      });
      let checkArr = _this.findLevelNode( _this.suitableTreeData,_this.radioValue);
      let isChecked2 = checkArr.every(item => {
        //如果有一个同层级级节点checked为false切换到自定义
        return item.checked == true;
      });
      if (!isChecked || !isChecked2) {
        _this.radioValue = "0";
        _this.checkedNodesId = checkedArr.map(i => {//保存自动切换自定义时所勾选节点的id
          return i.id;
        });
      }

由于之前设定的点击自定义按钮会清空所选节点,而要实现"切换到自定义按钮,并保存之前的勾选的节点不清空"这个逻辑,跟之前有点相冲突,所以比前两个交互难一点。这里需要在data里创建一个数组用来保存自动切换到自定义时所勾选的节点id,实现就是上面代码中checkedNodesId数组。然后在之前的getTree方法中添加一个判断

复制代码
          if (_this.radioValue == "0") {
            //解决勾选不同层级的复选框后,切换自定义按钮时,已勾选复选框取消选中的问题
            _this.checkedNodesId.forEach(d => {
              obj.id == d ? (obj.checked = true) : obj.checked;
            });
          } else {
            if (obj.level == _this.radioValue) {
              obj.checked = true;
            }
          }

这样在勾选不同层级或取消同层级的一个节点自动切回自定义时,也会保存上一次勾选的值。不过这样也造成了一个bug,那就时从其他radio按钮直接点回自定义按钮时,也会保存上一次的所选择的按钮,并没有清空。要解决这个bug就需在RadioGroup 的事件函数中添加一个判断,当value为0时,将checkedNodesId这个数组清空,就能解决。

4.实现"在自定义中,若勾选的节点是同一个层级且此层级节点全部被选中,自动切换到对应层级按钮。"要实现这个交互,首先也要获取所有勾选的节点( 也是用this.$refs.tree.getCheckedNodes()来实现 ),然后再将这些节点分级,我们可以将使用getCheckedNodes方法获取的节点打印出来看看

log.png

这里我分级采用的方法使用reduce将数组分级,代码如下:

复制代码
groupByArray(objArr, porperty) {
      //根据属性值将对象数组分类
      return objArr.reduce((acc, obj) => {
        let key = obj[porperty];
        if (!acc[key]) {
          acc[key] = [];
        }
        acc[key].push(obj);
        return acc;
      }, {});
    }

如果对reduce不太熟悉可以去mdn上看看。然后在Tree组件的on-check-change事件方法中调用,代码如下:

复制代码
handleCheckedChange() {
      const _this = this;
      let checkedArr = _this.$refs.tree.getCheckedNodes();
      let isChecked = checkedArr.every(item => {
        //选中的checkBox的层级不一致切换到自定义
        return item.level == _this.radioValue;
      });
      let checkArr = _this.findLevelNode( _this.suitableTreeData,_this.radioValue);
      let isChecked2 = checkArr.every(item => {
        //如果有一个同层级级节点checked为false切换到自定义
        return item.checked == true;
      });
      if (!isChecked || !isChecked2) {
        _this.radioValue = "0";
        _this.checkedNodesId = checkedArr.map(i => {
          return i.id;
        });
      }
   //上面是之前的对第三个交互的判断
  //******************************************************************************************************************
 
      let finalCheckedArr = _this.groupByArray(checkedArr, "level"); //分级后的数组
      let currentLevel = null; //当前勾选的层级
      //如果只勾选了一个层级的节点,判断这个层级的节点是否全部勾选,如果是切换到对应的业务组,如果不是切换到自定义
      if (Object.keys(finalCheckedArr).length < 2) {
        currentLevel = Object.keys(finalCheckedArr)[0];
        let currentLevelArr = _this.findLevelNode(_this.suitableTreeData, currentLevel).map(i => i.id);
        let currentCheckedLevelArr =  Object.values(finalCheckedArr)[0].map(i => i.id);
        //isEqual()是lodash的方法,用来判断对象或数组是否相等
        isEqual(currentCheckedLevelArr, currentLevelArr)? (_this.radioValue = currentLevel.toString()): _this.radioValue;
        }
      // console.log("finalCheckedArr", this.$ps(finalCheckedArr));
     // console.log("checkedArr", this.$ps(checkedArr));
    },

以上就是实现此种下拉树交互的主要代码
© 著作权归作者所有,转载或内容合作请联系作者

喜欢的朋友记得点赞、收藏、关注哦!!!

相关推荐
guitarjoy1 小时前
Compose原理 - 整体架构与主流程
java·开发语言
babicu1232 小时前
CSS Day07
java·前端·css
小鸡脚来咯2 小时前
spring IOC控制反转
java·后端·spring
[email protected]3 小时前
ASP.NET Core SignalR的基本使用
后端·asp.net·.netcore
怡人蝶梦4 小时前
Java后端技术栈问题排查实战:Spring Boot启动慢、Redis缓存击穿与Kafka消费堆积
java·jvm·redis·kafka·springboot·prometheus
Lalolander4 小时前
设备制造行业项目管理难点解析,如何有效解决?
大数据·制造·工程项目管理·四算一控·epc·装备制造项目管理
项目管理打工人4 小时前
高端装备制造企业如何选择适配的项目管理系统提升项目执行效率?附选型案例
大数据·人工智能·驱动开发·科技·硬件工程·团队开发·制造
瓯雅爱分享4 小时前
MES管理系统:Java+Vue,含源码与文档,实现生产过程实时监控、调度与优化,提升制造企业效能
java·mysql·vue·软件工程·源代码管理
鬼多不菜4 小时前
一篇学习CSS的笔记
java·前端·css