在税务系统等企业级应用中,表格(Grid)的单元格间计算、跨表格联动计算是常见的开发痛点。传统做法是手写大量前台JS和后台代码,每次需求变更都要修改代码,维护成本极高。笔者曾参与这类系统开发,发现团队80%的时间都在处理表格计算逻辑。受Excel公式启发,我们设计了一个轻量级自动计算引擎 AutoCalculate,让开发者只需定义公式字符串,引擎自动解析并计算结果,大幅减少硬编码。
一、核心思路:公式即代码
AutoCalculate 的核心由两部分组成:公式字符串和计算引擎(AutoCalculate.js)。公式采用类似 Excel 的语法,例如 [Month1,1] = [Month1,2] + [Month1,3] 表示第1行第1列等于第1行第2列加第3列的值。其中 [y,x] 表示单元格,y 是列名(纵坐标),x 是行号(横坐标,对应参考字段的值)。
二、基础用法:实例化与计算
首先引入引擎:
- import AutoCalculate from '../components/AutoCalculate';
复制代码
定义公式数组,创建实例:
- let formulas = ['[Month1,1] = [Month1,2] + [Month1,3]'];
复制代码- let autoCal = new AutoCalculate(formulas);
复制代码
调用 cal 方法传入表格数据和参考字段名:
- autoCal.cal(gridDatas, 'RowNo');
复制代码
其中 gridDatas 是表格数据数组,refField 是用于定位横坐标的字段(如 RowNo 或唯一键)。
三、高级公式:区域公式与占位符
对于多列重复公式(如1到10月),可用大括号 { } 定义列名集合,结合占位符 @ 简化书写:
- {Month1,Month2,...,Month10}[@,1] = [@,2] + [@,3]
复制代码
占位符 @ 可代替纵坐标或横坐标。例如计算年度合计,需要将多个月份数据累加到指定行,可用区域公式:
- {2,3,4,5,6,7,9,12,13}[YearTotal,@] = [Month1,@] + [Month2,@] + ... + [Month10,@]
复制代码
这样一条公式替代了9条硬编码。
四、动态行数:无需显式横坐标的 [y] 公式
当表格行数不确定(如数据库结果行数可变),可用 [y] 形式表示整列所有行:
- [column3] = [column2] - [column1]
复制代码
引擎自动将公式应用于 column3 列下的每一行。
五、合计列 & 小数位数
如需引用某列合计值,用 <列名> 表示。例如计算占比:
- [GroupApprovedTotalPercent] = <GroupApprovedTotal> === 0 ? 0 : [GroupApprovedTotal] / <GroupApprovedTotal>
复制代码
默认保留2位小数;如需4位小数,在等号左侧单元格后加 #4:
- [GroupApprovedTotalPercent]#4 = ...
复制代码
注意 # 和小数位数之间不能有空格。
六、支持JS语法与注意事项
公式右侧支持任意JS表达式(包括三元运算符、Math函数等),但禁止使用数组元素(因方括号与单元格表示法冲突)。
空格规则:单元格内不能有空格,如 [Month12,1] 正确,[Month12,1 ] 错误;# 与数字之间无空格。
七、跨数据源计算(三元单元格)
从其他数据表取值时,用 [数据源名, y, x] 格式,例如:
- [Month1,4] = [OutputTax,Month1,7]
复制代码
实例化时通过 options.externalDatas 传入外部数据源:
- let options = {
- externalDatas: [
- { name: 'OutputTax', refField: 'RowNo', datas: outputTaxDatas },
- { name: 'TaxRate', refField: 'RowNo', datas: taxRateDatas }
- ]
- };
复制代码- let autoCal = new AutoCalculate(formulas, options);
复制代码
调用 calculate 方法得到最新数据:
- let newDatas = autoCal.calculate(payableTaxDatas, 'RowNo');
复制代码
这个过程同样适用于后台(需借助V8引擎执行JS公式)。
八、后台调用封装
实际项目中将公式提取为独立JS文件,后台封装方法示例:
- // 伪代码
- var engine = new V8Engine();
- var formulas = engine.evaluate(jsFileContent);
- var autoCal = new AutoCalculate(formulas, options);
- var newDatas = autoCal.calculate(currentDatas, refField);
复制代码
这样前后台共享同一套公式定义,避免逻辑不一致。
九、适用性与总结
AutoCalculate 不依赖具体UI框架,适用于ElementUI、EasyUI、ParamQuery Grid等所有支持数据提取的表格控件。核心优势:将计算逻辑从代码中剥离,写入可维护的公式文本,响应业务变更只需调整公式,无需重新编译。经项目实践,可减少80%表格计算代码量,大幅提升开发效率。
注意:公式调试时建议先在前台验证,确认结果正确后再后台部署。对于复杂嵌套或循环依赖,需提前设计好计算方法避免死循环。 |