报表行号生成:从全局连续到组内重排

2 阅读3分钟

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

规则一:进入一个分组节点

如果 节点层级 == resetLevelrowCounts[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] 清零?globalRowCountrowCounts[0](部门层总行数)rowCounts[1]行号
进入销售一组✅→0000
生成 100 行1001001001…100
进入销售二组✅→01001000
生成王五10110111
进入技术部→前端组✅→01011010
生成赵六、钱七10310321, 2
  • rowCounts[0] 从 0 涨到 103,它是整个部门层的总行数,跨所有部门累加。
  • 小组层计数每次进新组都归零,所以组内行号重新开始。

6. 总结

  • 清零 是为了让新组第一行从 1 开始,本质是把该层计数器拉回 0。
  • 树有几层就设几个计数器,每行所有计数器都 +1;哪一层负责清零,由 resetLevel 决定。
  • resetLevel = -1 时,直接用 globalRowCount 做基数,退化为全局连续编号。