vue递归处理树形数据

先看实现的功能缩略图

IMG_2259.JPG

业务需求:

(1) 省行政区域层级页增加“全国”字段,支持全选和全不选设备权限
(2) 若子层级的设备权限全部选中,则上级组织展示选中状态,如图1
(3) 若子层级的设备权限有部分选中,则上级组织展示为半选中状态,如图3,4
(4) 若子层级的设备权限全部未选中,则上级组织展示为非选中状态,如图2
( 5 ) 可直观定位和追溯当前组织所拥有的摄像机权限信息
( 6 ) 各层级展示已选数量/全部数量(例:已选2/10),用于展示在各地区已选的设备权限情况,展示格式:已选X/Y,其中X表示当前行政区域内已选择的设备数量,Y表示当前行政区域内全部设备数量

实现思路

每一个单元格item都有3种状态标识,selecteTag分为0,1,2。0是未选中,1是半选中,2是选中状态。点击复选框选中和非选中的方法里,同时向上和向下递归处理,向上拿着该item的上级组织的id递归遍历所有上级组织,同时根据012的逻辑给上级组织selecteTag赋值;向下如果该item的存在子级,就把所有子级递归赋值selecteTag为0或者2。同时只对设备进行处理,在选中的数组newCheckList中进行添加或删除操作。

实现代码

核心代码
/** 选中当前的值
             * @param {Object} e
             * @param {Object} item 当前项
             * @param {Object} index 索引
             */
            checkboxChange(e, item, index) {
                uni.showLoading({
                    title: '正在加载,请稍候'
                });
                console.log('点击chechckBox', e.detail.value, item);
                let that = this;
                var data = that.newCheckList;
                let findIdex = -1; //that.newCheckList.indexOf(item)
                for (let i in data) {
                    if (data[i].deviceCode == item.deviceCode) {
                        findIdex = i;
                        break;
                    }

                }
                console.log('选中的数组中的第几个', findIdex);
                if (e.detail.value && e.detail.value > 0) {
                    // 点击选中
                    if (!item.children && !item.deviceCode) {
                        console.log('选中第一级', item);
                        // 遍历找到item的上级,赋值选中
                        this.partRadioEach(this.parent_data, item.id, 1);
                        //第一级的选中
                        this.parentSelected(item.id, 1);

                    } else {
                        that.tree[index].selecteTag = 2
                        if (item.deviceCode && findIdex < 0) {
                            that.newCheckList.push(that.tree[index]);
                        }
                        console.log('选中de', that.newCheckList);
                        var childrendata = that.tree[index].children;

                        if (childrendata && childrendata.length > 0) {
                            that.selectedDown(childrendata);
                        }
                    }

                } else {
                    // 点击不选
                    if (!item.children && !item.deviceCode) {
                        console.log('取消选中第一级', item);
                        // 遍历找到item的上级,赋值不选中
                        this.partRadioEach(this.parent_data, item.id, 0);
                        //第一级的不选中
                        this.parentSelected(item.id, 0);

                    } else {
                        if (item.deviceCode) {
                            that.newCheckList.splice(findIdex, 1);
                        }
                        that.tree[index].selecteTag = 0
                        var cancledata = that.tree[index].children;
                        console.log('取消删除', cancledata);
                        if (Array.isArray(cancledata) && cancledata.length > 0) {
                            that.deleteDown(cancledata);
                        }
                    }
                }

                that.parentforEach(that.parent_data, item.pid);

                console.log('最后的选中数组', this.newCheckList, item.pid);
                that.checckNewList();
                this.topSelString = this.getTopChooseItem();
                that.$forceUpdate(); //强制更新数据
                uni.hideLoading();
            },
第一个item的选中和非选中事件,因为第一个item在上级也存在
    //遍历找到选中的item,赋值选中或不选中
            partRadioEach(arr, itemid, tag) {
                for (let i = 0; i < arr.length; i++) {
                    if (arr[i].id == itemid) {
                        if (tag == 1) {
                            arr[i].selecteTag = 2;

                        } else {
                            arr[i].selecteTag = 0;
                        }
                    }
                    if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
                        this.partRadioEach(arr[i].children, itemid, tag);
                    }
                }
            },

只对设备进行处理,在选中的数组newCheckList中进行添加或删除操作

//第一个item的选中和非选中,只对设备进行处理,在选中的数组newCheckList中进行添加或删除操作
            parentSelected(pid, tag) {
                for (let i in this.tree) {
                    var treechildata = this.tree[i].children;
                    if (tag == 1) {
                        let item = this.tree[i];
                        item.selecteTag = 2;
                        //判断是否是设备
                        if (item.deviceCode) {
                            let findIdex = -1;
                            for (let i in this.newCheckList) {
                                if (this.newCheckList[i].deviceCode == item.deviceCode) {
                                    findIdex = i;
                                    break;
                                }
                            }
                            if (findIdex < 0) {
                                this.newCheckList.push(item);
                            }
                        }
                        if (treechildata && treechildata.length > 0) {
                            this.selectedDown(treechildata);
                        }
                    } else {
                        this.tree[i].selecteTag = 0;
                        var List = this.newCheckList;
                        console.log('删除第一、选中的数组', this.newCheckList);
                        let Idex = -1;
                        for (let j in List) {
                            if (List[j].deviceCode) {
                                if (List[j].deviceCode == this.tree[i].deviceCode) {
                                    Idex = j;
                                    this.newCheckList.splice(Idex, 1);
                                    break;
                                }
                            }
                        }
                        if (treechildata && treechildata.length > 0) {
                            this.deleteDown(treechildata);
                        }
                    }
                };

            },

向下递归选中

    //向下递归选中最底层设备
        selectedDown(arr) {
            for (let i = 0; i < arr.length; i++) {
                let item = arr[i];
                item.selecteTag = 2;
                //如果是设备的话,才加入选中的数组
                if (item.deviceCode) {
                    let findIdex = -1;
                    for (let i in this.newCheckList) {
                        if (this.newCheckList[i].deviceCode == item.deviceCode) {
                            findIdex = i;
                            break;
                        }
                    }
                    if (findIdex < 0) {
                        this.newCheckList.push(item);
                    }
                }
                if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
                    this.selectedDown(arr[i].children);
                }
            }
        },

向下递归删除

    //向下递归删除
        deleteDown(data) {
            var List = this.newCheckList;
            console.log('删除、选中的数组', this.newCheckList);
            for (let i in data) {
                if (Array.isArray(data) && data.length > 0) {
                    let Idex = -1;
                    for (let j in List) {
                        if (List[j].deviceCode) {
                            if (List[j].deviceCode == data[i].deviceCode) {
                                Idex = j;
                                this.newCheckList.splice(Idex, 1);
                                break;
                            }
                        }

                    }
                    data[i].selecteTag = 0;
                    if (Array.isArray(data[i].children) && data[i].children.length > 0) {
                        this.deleteDown(data[i].children);
                    }
                }
            };

            console.log('向下删除循环后的选中数组', this.newCheckList);
        },

根据上级组织id,递归遍历所有上级,根据子层级的设备权限是否选中,给上级组织状态赋值

        //遍历所有上级赋值
            parentforEach(arr, itempid) {
                for (let i = 0; i < arr.length; i++) {
                    //是上级的处理
                    if (arr[i].id == itempid) {

                        var sum = 0;
                        var subdata = arr[i].children //.splice(0, 1);
                        for (let j in subdata) {
                            sum += subdata[j].selecteTag ? subdata[j].selecteTag : 0;
                        }
                        sum = sum - (subdata[0].selecteTag ? subdata[0].selecteTag : 0);
                        console.log('遍历父级', sum, arr[i].children.length);
                        if (sum >= (arr[i].children.length - 1) * 2) {
                            // 子级全部选中时上级组织也选中
                            arr[i].selecteTag = 2;
                            if (arr[i].children[0]) {
                                arr[i].children[0].selecteTag = 2;
                                console.log('选中上级', arr[i]);
                            }
                        } else if (sum == 0) {
                            // 子级全部不选中时上级组织也不选中
                            console.log('不选中', arr[i]);
                            arr[i].selecteTag = 0
                            if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
                                arr[i].children[0].selecteTag = 0;
                            }
                        } else {
                            // 子级有选中且非全部选中的时候,上级组织时半选中
                            console.log('半选中', arr[i]);
                            arr[i].selecteTag = 1;
                            if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
                                arr[i].children[0].selecteTag = 1;
                            }
                        }
                        this.$forceUpdate(); //强制更新数据
                        if (arr[i].pid && Number(arr[i].pid) != 0) {
                            this.parentforEach(this.parent_data, arr[i].pid);
                        }
                        return;
                    }

                    if (Array.isArray(arr[i].children) && arr[i].children.length > 0) {
                        this.parentforEach(arr[i].children, itempid);
                    }
                }
            },
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容