1. 我们的树
报表
├── 销售部
│ ├── 销售一组 → 张三、李四 …(假设有100人)
│ └── 销售二组 → 王五
├── 技术部
│ ├── 前端组 → 赵六、钱七
│ └── 后端组 → 孙七
只有最末端的数据行需要编号。上层节点统称分组节点。
2. 需求一:全局连续
一个计数器 globalRowCount,初始 0。每碰到一个数据行:
globalRowCount += 1
行号 = globalRowCount
从头到尾不清零。
3. 需求二:小组内行号重排
规则:销售一组内 1 到 100,换到销售二组时重新从 1 开始,前端组也重新从 1 开始。
3.1 为什么需要清零
globalRowCount 到了销售一组末尾已经是 100。
如果不清零,王五就会变成 101,而不是 1。
要让王五是 1,必须在进入销售二组时,把 负责小组编号的那个数 强行拉回 0。
清零的目的:让新小组的第一行拿到基数 0,行号自然从 1 开始。
3.2 为小组层专门设一个计数器
我们给小组这一层单独准备一个计数器。
- 进入新小组 → 计数器归零
- 组内每行 → 以它为基数,然后 +1
它和 globalRowCount 互不干扰:一个管组内编号,一个管全局统计。
因为树有两层,我们把计数器做成数组:
rowCounts[0] 给部门层,rowCounts[1] 给小组层。
3.3 为什么还要维护部门层的计数器
每行数据不只属于小组,也属于部门(层级0)。
虽然当前需求下部门层不参与清零,但生成每一行时,两个计数器都 +1,是因为这一行同时属于这两层。
这样,rowCounts[0] 记录的就是整个部门层(所有部门)一共生成了多少行,而不是某个特定部门的行数。
如果将来需求变成“每个部门内重排”,只需要让部门层负责清零(
resetLevel = 0),那么rowCounts[0]就会在每个新部门开始时归零,记录当前部门内的行数——计数器本身没变,变的是哪一层负责清零。
4. 统一规则
变量:
globalRowCount:全局总行数,初始 0,只增不减rowCounts[]:长度 = 树层数,初始全 0。索引 0 对应部门层,1 对应小组层resetLevel:负责清零的层级索引(本例中 = 1)。没有则为 -1
规则一:进入一个分组节点
如果 节点层级 == resetLevel:
rowCounts[resetLevel] = 0 // 为新组的第一行准备基数 0
只有 resetLevel 这一层会清零,其他层保持原值。
规则二:生成一个数据行
基数 = (resetLevel == -1) ? globalRowCount : rowCounts[resetLevel]
行号 = 基数 + 1
globalRowCount += 1
rowCounts[0] += 1 // 每层都 +1,不管它是不是清零层
rowCounts[1] += 1
5. 跑一遍(resetLevel = 1,小组内重排)
初始:globalRowCount = 0, rowCounts = [0, 0]
| 当前动作 | rowCounts[1] 清零? | globalRowCount | rowCounts[0](部门层总行数) | rowCounts[1] | 行号 |
|---|---|---|---|---|---|
| 进入销售一组 | ✅→0 | 0 | 0 | 0 | – |
| 生成 100 行 | 否 | 100 | 100 | 100 | 1…100 |
| 进入销售二组 | ✅→0 | 100 | 100 | 0 | – |
| 生成王五 | 否 | 101 | 101 | 1 | 1 |
| 进入技术部→前端组 | ✅→0 | 101 | 101 | 0 | – |
| 生成赵六、钱七 | 否 | 103 | 103 | 2 | 1, 2 |
rowCounts[0]从 0 涨到 103,它是整个部门层的总行数,跨所有部门累加。- 小组层计数每次进新组都归零,所以组内行号重新开始。
6. 总结
- 清零 是为了让新组第一行从 1 开始,本质是把该层计数器拉回 0。
- 树有几层就设几个计数器,每行所有计数器都 +1;哪一层负责清零,由
resetLevel决定。 resetLevel = -1时,直接用globalRowCount做基数,退化为全局连续编号。