多级部门的实现

1,647

效果图

123.gif

Demo

gitee.com/crazybadam/…

背景

最近实现了一个部门选择器,包含多级部门的显示,全选以及反选功能,运用到了树状结构的概念

准备工作

  • NRDesignateGroupModel 用来表示部门的Model,包含父节点和子groups数据,负责父checked的指向和子groups的checked的遍历
  • NRBreadcrumView 面包屑View,负责层级的展示,可进行查看不同层级数据的操作
  • NRDesignateDeptCell 部门的Cell,包含部门名称、选择和取消选择的按钮功能
  • NRDesignateChoiceTableHeaderView 全选按钮的View
  • 一个tableView用来展示当前节点的部门列表

Controller解析

面包屑要注意与下方tableView的联动,tableView点击的时候触发面包屑的标题添加,面包屑点击的时候触发节点的变更,从而变更tableView的数据

面包屑的点击
///修改面包屑的节点数组长度
-(void)breadcrumViewDidSelectedGroupModel:(NRDesignateGroupModel *)groupModel{
///当前节点点击不做处理
    if(![self.currentGroupNodeModel.departmentId isEqualToString:groupModel.departmentId]){
        NSInteger index = [self.breadcrumArray indexOfObject:groupModel];
        NSArray *array = [self.breadcrumArray subarrayWithRange:NSMakeRange(0, index+1)];
        self.breadcrumArray = [NSMutableArray arrayWithArray:array];
        self.breadcrumView.breadcrumArray = array;
        [self updateCurrentGroupModel:groupModel];
    }
}
///触发节点改变,改变tableView的数据
- (void)updateCurrentGroupModel:(NRDesignateGroupModel *)groupModel{
    self.currentGroupNodeModel = groupModel;
    self.allSelectView.checked = groupModel.checked;
    [self getDepartmentGroupData:groupModel];
}
tableView的点击
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    NRDesignateGroupModel *model = [self.currentGroupNodeModel.groups objectAtIndex:indexPath.row];
    [self updateBreadcrumWithGroupData:model];
}
///触发面包屑的改变。从而触发tableView的改变,与上面面包屑的流程就一致了
- (void)updateBreadcrumWithGroupData:(NRDesignateGroupModel *)groupModel{
    [self.breadcrumArray addObject:groupModel];
    self.breadcrumView.breadcrumArray = self.breadcrumArray;
    [self updateCurrentGroupModel:groupModel];
}
单选
///修改model的checked,updateChecked在model内部实现,遍历子groups的checked和检查父节点的checked(当父节点的groups全选中的时候要checked=YES),从而显示allSelectView的checked
- (void)selectedOnceBtn:(NSIndexPath *)indexPath{
    NRDesignateGroupModel *groupModel = [self.currentGroupNodeModel.groups objectAtIndex:indexPath.row];
    [groupModel updateChecked:!groupModel.checked];
    self.allSelectView.checked = self.currentGroupNodeModel.checked;
}
全选
///当前节点调用updateChecked,作用于上面单选一致,遍历子和父
- (void)selectedAllBtn{
    [self.currentGroupNodeModel updateChecked:!self.currentGroupNodeModel.checked];
    [self.tableView reloadData];
}
提交遍历
///allFlag是只表示第一级部门的全选和非全选
///遍历到当前节点checked为YES时,停止遍历,因为下面的所有子部门一定全选
///当上面遍历完之后,我们遍历之前选中但是后来因为代码的操作影响了checked的,也要传给服务器,服务器要做减法处理,不做的可以不考虑。
///然后一级一级遍历下去
- (void)traversalGroupNode:(NRDesignateGroupModel *)groupNode{
    ///第一个循环找出被选中的
    if(groupNode.checked){
        if(groupNode.departmentId){
            NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
            dict[@"departmentId"] = groupNode.departmentId;
            dict[@"checked"] = @"1";
            dict[@"name"] = groupNode.name;
            [self.groupChangedArray addObject:dict];
        }else{
            self.allFlag = 1;
        }
        return;
    }
    ///第一个循环找出之前被选中然后被其他代码取消选中的
    if(groupNode.codeChanged){
        if(groupNode.departmentId){
            NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
            dict[@"departmentId"] = groupNode.departmentId;
            dict[@"checked"] = @"0";
            dict[@"name"] = groupNode.name;
            [self.groupChangedArray addObject:dict];
        }else{
            self.allFlag = 2;
        }
    }
    for(NRDesignateGroupModel *model in groupNode.groups){
        [self dfsGroupNode:model];
    }
}

Model的解析

updateChecked
///codeChanged 是在Controller里用来获取到部门数据之后,根据父节点的checked来显示当前节点的checked
- (void)updateChecked:(BOOL)checked{
    self.checked = checked;
    self.codeChanged = YES;
    [self setAllChildChecked:checked];
    if(self.parentGroupModel){
        [self.parentGroupModel isAllChecked];
    }
}
///遍历子groups的checked
- (void)setAllChildChecked:(BOOL)checked{
    for(NRDesignateGroupModel *groupModel in self.groups){
        if(groupModel.checked != checked){
            groupModel.checked = checked;
            groupModel.codeChanged = YES;
            [groupModel setAllChildChecked:checked];
        }
    }
}
///遍历父节点的checked
- (void)isAllChecked{
    BOOL flag = YES;
    for(NRDesignateGroupModel *groupModel in self.groups){
        if(groupModel.checked == NO){
            flag = NO;
        }
    }
    if(self.checked != flag){
        self.checked = flag;
        self.codeChanged = YES;
        if(self.parentGroupModel){
            [self.parentGroupModel isAllChecked];
        }
    }
}

总结

用到了递归的知识,总的结构可以看成一个树状结构,每个节点下都保留了父节点和子groups这是关键,这样我们不需要知道当前到第几级,只要拿到当前节点,就可以显示和操作数据了。