薪资模块 Univer 表格开发问题记录
字段到列号的映射、公式按行转换、主职兼职合计只算一次、range protection 控制编辑——接入 Univer 的六个细节。
本文是「bylcloud-web 开发问题记录」系列之一。bylcloud-web 是基于 Vue 3 + Vite + Element Plus 的企业后台系统,笔记按「现象、排查、处理、下次注意」记录开发中真实遇到的问题。
记录 1:字段配置到单元格坐标的映射最关键
薪资模块不是固定列。字段来自薪资配置,每个字段有 proCode、proName、colCode、editableFlag、formula、dataRule 等信息。Univer 需要的是 workbook 结构和 cellData。
一开始我容易把重点放在 Univer API 上,后来发现真正容易出错的是字段到列号的映射。colCode 是桥梁,比如业务字段对应 A、B、AA 这样的列。代码里写了两个方法:
columnToNumber:把A、AA转成 0-based 列号。numberToColumn:把数字列号转回字母。
这里如果差一位,整张表都会错位,而且不一定立刻报错。薪资这种表格一错位,用户看到的是字段和值对不上,影响很大。
记录 2:公式不能直接复制到每一行
薪资字段支持公式。表头配置的公式可能写的是 A1+B1 这种形式,但真正落到第 5 行时,应该变成 A5+B5。如果直接复制公式,每一行都会引用第一行。
项目里用 useExcel.js 的 formulaToCellFormula 做转换,用正则找出公式里的单元格引用,再替换行号。
这个地方要注意两个问题:
- 只替换单元格行号,不要改列字母。
- 公式为空时直接返回空字符串,不要生成无意义公式。
这类 bug 不好查,因为公式本身看起来是合法的,只是算出来的数据不对。
记录 3:合计应发工资不是每行都算
totalSalary 有一个业务细节:同一个人可能有主职和兼职多条薪资数据。合计应发工资只应该在主职那条上计算;如果没有主职,就选择第一次出现的兼职。
这个逻辑在代码里用了 totalSalarySeen 记录某个 userId 是否已经计算过。处理时先判断这个人是否存在主职,再决定当前行是不是应该保留公式。
这个问题很像“业务规则藏在表格里”。如果只从技术角度看,会以为每行都套公式就行,但实际会导致同一个人的工资重复计算。
记录 4:不可编辑列不是禁用 input
普通表格里禁用编辑可能就是 input disabled,但 Univer 里要通过 range protection 控制。项目里根据 editableFlag 收集不可编辑列,再调用 setRangeEditable。
当时还遇到一个体验问题:设置保护后,Univer 自己的权限弹窗不应该让业务用户看到,所以需要 permission.setPermissionDialogVisible(false)。
这个细节不处理,用户可能会看到底层表格库的权限提示,和系统整体体验不一致。
记录 5:隐藏列和空列要一起处理
薪资字段的 colCode 不一定连续,比如用了 A、B、D,中间 C 是空列。如果只隐藏禁用字段,不隐藏空列,表格里会出现莫名其妙的空白列。
代码里先根据最大列号循环,找出字段配置里不存在的列,放进 colBlank。再把不可用字段列和空列合并,一起调用 setColumnVisible({ visible: false })。
这个处理很细,但对用户体验影响明显。薪资表列本来就多,多几个空列会让用户误以为数据缺失。
记录 6:单击和双击冲突只能先做延迟判断
薪资表里单击单元格需要展示公式解释,双击表头需要打开公式配置。Univer 事件里处理时,单击和双击会互相影响。
当时用了一个 250ms 的窗口:先记录上次点击的行列和时间,如果短时间内点的是同一个单元格,就当双击;否则延迟执行单击逻辑。
这个方案不算优雅,但解决了实际问题。以后如果换事件方案,需要重点回归这个交互。
