Checkbox 多选框实现勾选后,选项按勾选顺序进行排序

898 阅读3分钟

背景介绍

Checkbox 多选框勾选选项后,界面上的选项按照勾选的先后顺序进行排列,实时更新选项顺序

图片预览

多选框

需求分析

  1. 勾选时设置顺序并把所选项提前
  2. 对未勾选的选项根据选项本身再进行排序
  3. 取消勾选时去掉排序后再按选项本身进行排序
  4. 实时更新组件,避免选项改变位置时,补位的选项无法选择问题
  5. 编辑时按顺序对选项进行回显

组件代码(以Element ui 组件Checkbox 多选框为例)

Checkbox 多选框组件代码

参数解析(已封装好组件,传对应参数即可)

componentName:"elCheckboxGroup", //组件名称
multiple:true, //是否多选
vModel:'newSelectEffectList', //v-model
checkboxGroupKey:'checkboxGroupKey', //更新组件值
checkboxListName:'allNewEffect' //选择列表

import(建议在main.js中引入)

import _ from "lodash";

data

data(){
    return {
        sortSelectEffectList:[], //勾选顺序列表
        newSelectEffectList:[], //勾选值列表
        checkboxGroupKey:'', //组件key
        allNewEffect:[
            {
                label:'章鱼小丸子',
                url:require('@/static/image/zyxwz.svg'),
                value:'1'
            },
            {
                label:'奥尔良烤翅',
                url:require('@/static/image/aelkc.svg'),
                value:'2'
            },
            {
                label:'慕斯',
                url:require('@/static/image/ms.svg'),
                value:'3'
            },
            {
                label:'布朗尼',
                url:require('@/static/image/bln.svg'),
                value:'4'
            },

            {
                label:'提拉米苏',
                url:require('@/static/image/tlms.svg'),
                value:'5'
            },
            {
                label:'牛肉干',
                url:require('@/static/image/nrg.svg'),
                value:'6'
            },
            {
                label:'奶昔',
                url:require('@/static/image/nx.svg'),
                value:'7'
            },
            {
                label:'奶茶',
                url:require('@/static/image/nc.svg'),
                value:'8'
            },
            {
                label:'双皮奶',
                url:require('@/static/image/spn.svg'),
                value:'9'
            },
            {
                label:'手抓饼',
                url:require('@/static/image/szb.svg'),
                value:'10'
            },
            {
                label:'咖喱鱼丸',
                url:require('@/static/image/klyw.svg'),
                value:'11'
            },
            {
                label:'寿司',
                url:require('@/static/image/ss.svg'),
                value:'12'
            },
            {
                label:'北极贝',
                url:require('@/static/image/bjb.svg'),
                value:'13'
            },
            {
                label:'三文治',
                url:require('@/static/image/smz.svg'),
                value:'14'
            },
            {
                label:'冰激凌',
                url:require('@/static/image/bql.svg'),
                value:'15'
            },
            {
                label:'烧烤',
                url:require('@/static/image/sk.svg'),
                value:'16'
            },
            {
                label:'咚咚烧',
                url:require('@/static/image/dds.svg'),
                value:'17'
            }
        ]
    }
}

勾选时设置顺序并把所选项提前、对未勾选的选项根据选项本身再进行排序、取消勾选时去掉排序后再按选项本身进行排序

watch:{
    newSelectEffectList:function (val,oldVal) {
        if(val.length == oldVal.length){ //相等时默认按选项的排序进行勾选
            this.allNewEffect.filter(v=>{
              this.$set(v,'orderNum',v.value);
            })
        }else if(val.length > oldVal.length){

        var differenceVal = _.difference(val,oldVal); //找出勾选的值
        if(differenceVal.length > 1){ //编辑
          this.sortSelectEffectList = differenceVal; //勾选的顺序
        }else { //新增
          this.sortSelectEffectList.push(differenceVal[0]);
        }

        this.sortSelectEffectList.filter((v,index)=>{ //按勾选顺序,重新排列选项
          var effectIndex = _.findIndex(this.allNewEffect,['value',v]);
          this.$set(this.allNewEffect[effectIndex],'orderNum',(index+1));
        })

        var orderNumList = [];
        var orderList = [];

        this.allNewEffect.filter((v,index)=>{
          if(v.orderNum){
            orderNumList.push(v);
          }else {
            orderList.push({label:v.label,url:v.url,value:Number(v.value)});
          }
        })
        orderNumList = _.sortBy(orderNumList, 'orderNum'); //按orderNum排序
        orderList = _.sortBy(orderList, 'value');//按orderList排序
        this.allNewEffect = [];
        this.allNewEffect = [...orderNumList,...orderList]; //合并数组

        this.checkboxGroupKey = Math.round(); //更新组件
      }else {
          if(val.length == 0){
            this.sortSelectEffectList = [];
          }
          var differenceVal = _.difference(oldVal,val)[0]; //找出取消勾选的值
     
          if(differenceVal){

            //将删除取消勾选的值对应的orderNumer
            var clearTargetIndex = _.findIndex(this.allNewEffect,['value',differenceVal]);
            delete this.allNewEffect[clearTargetIndex].orderNum; //删除orderNum参数

            //按顺序设置(除已删除)以外的值
            this.sortSelectEffectList = _.filter(this.sortSelectEffectList, function(o){return o !== differenceVal});

            this.sortSelectEffectList.filter((v,index)=>{
              var effectIndex = _.findIndex(this.allNewEffect,['value',v]);
              this.$set(this.allNewEffect[effectIndex],'orderNum',(index+1));
            })

            var orderNumList = [];
            var orderList = [];
            this.allNewEffect.filter(v=>{
              if(v.orderNum){
                orderNumList.push(v);
              }else {
                orderList.push({label:v.label,url:v.url,value:Number(v.value)});
              }
            })
            orderNumList = _.sortBy(orderNumList, 'orderNum'); //按orderNum排序
            orderList = _.sortBy(orderList, 'value'); //按orderList排序
            this.allNewEffect = [...orderNumList,...orderList]; //合并数组

            this.checkboxGroupKey = Math.round(); //更新组件
          }
      }
    }
}

编辑时按顺序对选项进行回显(触发监听方法)

methods:{
    var that = this;
    //请求数据进行回显
    apiRequestParams(productManage,'getProduct',data,function (res) {
        if(that.form.effectType.length > 0){ //存在选项时再进行操作
            var selectList = [];
            res.newSelectEffectList.filter(v=>{
                selectList.push(v.value);
           })
           that.newSelectEffectList = selectList; //对newSelectEffectList进行赋值,赋值后会触发监听方法进行排序
       }else{
          //选项列表为空时,默认选择全部
          that.allNewEffect.filter(v=>{
            that.newSelectEffectList.push(v.value);
          })
       }
    })
}

首次进界面时进行初始化

created(){
      this.newSelectEffectList = [];
      //默认选择全部
      this.allNewEffect.filter(v=>{
        this.newSelectEffectList.push(v.value);
      })
}

效果

勾选后选项提前,取消勾选后会按照选项本身顺序进行排序,提交后会已勾选的选项参数会添加orderNum(按勾选顺序),回显时会按照orderNum顺序进行勾选,未勾选的按照选项本身进行排序

checkboxGroupKey

组件的Key用于更新组件 由于选项位置发生变化,勾选选项后,选项被提前,选项默认位置进行补位,会出现无法勾选的情况 所以需要添加checkboxGroupKey属性对组件进行更新

watch

在watch中添加合适的逻辑进行处理,在回显数据时直接触发这些逻辑达到实时更新效果

代码逻辑优化(三级菜单添加同级)

效果

image.png

//查找归属于某个类的菜单列表
searchCurrentMenu(id,list){ //目前菜单只有两个三层
    //children,type,parentId 下级永远拥有一个父级(parentId)指向父级
    var menuList = [];
    if(list){
        list.filter(v=>{
            this.deepPermTreeData.filter(p=>{
                if(v.id == p.parentId && !p.permissionsId){
                    menuList.push(p);
                }
            })
        })
    }else {
        this.deepPermTreeData.filter(v=>{
            if(v.parentId == id){
                menuList.push(v);
            }
        })
    }
    return menuList;
},
//查找归属于某个类的权限菜单列表
searchPermissionsMenu(id,list){
    var menuList = [];
    if(list.length > 0){
        this.deepPermTreeData.filter(p=>{
            if(id == p.permissionsId){
                menuList.push(p);
            }
        })
    }
    return menuList;
},
//递归菜单列表
deepMenuList(data){
    data.filter(v=>{

        this.$set(v,'checked',false);

        var menuPermissionsList = _.filter(this.permissionsList, ['menuId', v.id]);

        if(menuPermissionsList.length > 0){
            _.forEach(menuPermissionsList,item=>{
                item.rootId = item.id;
                item.id = v.id + Math.random(5); // 避免权限菜单id与菜单id重复
                item.type = 2; // 设置为三级菜单类型(层级)
                item.parentId = v.id; // 归属于某个菜单下面
                item.permissionsId = v.parentId; // 二级菜单父Id

                // item.checked = false; 这种赋值形式会让动态列表无法勾选
                this.$set(item,'checked',false); 
                this.$set(v.children,v.children.length,item); //往当前二级菜单子数组里添加权限菜单
            });
           
        }

        this.deepPermTreeData.push(v);

        if(v.children){ // 不能添加v.children.length > 0否则会跳过后面的执行
            this.deepMenuList(v.children);
        }
    })
},
//勾选菜单(主要逻辑)
checkMenu(row){

    // 查找当前节点父级
    const parentList = this.findparent(row.parentId, [])

    // 查找当前节点子级
    const childList =  this.findChild(row, [])
    
    _.forEach(childList, item=>{
        item.checked = row.checked
    })

    //取消勾选并且满足取消勾选条件
    if(!row.checked && parentList.length>0) {
        this.deepUncheck(row,parentList)
      return ;
    }
 
    _.forEach(parentList, item=>{
        item.checked = row.checked
    })

},
//递归勾选条件(主要逻辑)
deepUncheck(row, parentList){
    const allParentBrother = _.filter(this.deepPermTreeData, item=>{ // 找出所有比当前类型(级别)高的父级并且已勾选
        return (item.type < row.type) && item.checked == true; 
    })

    const sameple = _.filter(this.deepPermTreeData, item=>{ // 找出相同父级的兄弟元素并且已勾选
        return (item.parentId == row.parentId) && item.checked == true;
    })

    // 规则:取消勾选时每一个父级都为一个时,代表当前列表的层级
    if(allParentBrother.length >= row.type && sameple.length == 0){ // 父级元素大于等于当前层级并且相同父级的兄弟元素并且已勾选
        const currentParent =  _.find(parentList, ['id', row.parentId]);  // 在parentList 找出与当前父级id匹配的父级并取消勾选
        if(!!currentParent) {
            currentParent.checked = false;
            const currentParentList = this.findparent(currentParent.parentId,[]);
            return this.deepUncheck(currentParent, currentParentList);
        } 
    }

},
//找到对应父节点列表
findparent(id, parentList){
    const parent = _.find(this.deepPermTreeData, ['id', id])
    
    if(parent){
        parentList.push(parent)
    }
    if(!parent || parent.parentId ==0) {
     return parentList;
    }
    return this.findparent(parent.parentId, parentList)
},

//找到对应子节点列表
findChild(row, childList){
    if(row.children){ 
        _.forEach(row.children, item=>{
            childList.push(item);
            this.findChild(item, childList)
        }) 
    }
    return childList;
}