告別硬編碼,讓你的前端表格自動計算
阿新 • • 發佈:2020-09-27
[GitHub](https://github.com/feng-haitao/auto-calculate) | [Demo](http://autocal.fenghaitao.net) | [部落格](http://www.fenghaitao.net) | [原文連結](https://www.jianshu.com/p/b0f350b1a4de)
## 序言
當我的團隊進行稅務系統模組開發的時候,我發現他們需要花費80%的時間去解決計算問題,尤其體現在表格(Grid)中的計算,這些時間花在:
1. 寫前臺js程式碼(因為使用者在表格中的輸入會影響其他單元格,所以需要即時將運算後的新值呈現給使用者看)
2. 寫後臺程式碼(因為使用者對錶格資料的更改會影響其他表格,所以要在使用者點選儲存時更新受影響表格的資料)
3. 實施修改計算方法,導致開發者需要修改程式碼
於是我調研了稅務其他模組的功能,發現稅務系統大量使用表格控制元件,而其中或多或少都會涉及到計算問題。而處理計算的方法,都是採用硬編碼。
計算,這個習以為常的編碼動作,其實很容易讓人聯想到Excel中的公式,更何況需求文件本身就是以Excel的形式提供的。當我們在使用Excel的時候,可以在單元格中設定公式,通過改變源頭單元格的值,Excel將自動計算單元格公式,將結果值賦予目標單元格。那麼,我們是否可以參考這種模式,開發者不再需要寫複雜難懂的計算邏輯,只需要根據實施提供的公式,將它們轉成某種格式的語句,再呼叫某種計算引擎產出結果,將結果呈現給使用者看或者持久化到資料庫?答案是肯定的,而這一切的核心就是自動計算引擎——**AutoCalculate**。
## 作用
AutoCalculate是表格複雜運算的解決方案,可以讓你省掉成百上千行的計算邏輯程式碼,從此寫程式碼就像寫Excel公式一般簡單。
## 適用範圍
前臺:
適用於ElementUI表格、EasyUI Grid控制元件、ParamQuery Grid等所有js表格控制元件中帶有公式的複雜運算
後臺:
適用,需要V8引擎
## 前臺用法
AutoCalculate由兩部分組成,分別是公式和計算引擎,公式是就是根據特定語法編寫的字串,如:[Month12,1]#3 = [Month11,1] * 10,計算引擎即是AutoCalculate.js,負責解析公式。以下開始介紹如何書寫公式。
### 單元格
假設有這樣的場景,單元格①=單元格②+單元格③,對應的公式是:
```
[Month1,1] = [Month1,2] + [Month1,3]
```
![img](http://www.fenghaitao.net/wp-content/uploads/2020/07/AutoCalculate_1.png)
先來看看`[Month1,1]`代表什麼,首先,中括號`[ ]`代表一個單元格,Month1即“1月”對應的列名,緊接著是一個逗號`,`,後面的1代表RowNo = 1,以此類推,
`[Month1,2]`代表列為“1月”且RowNo = 2的單元格
`[Month1,3]`代表列為“1月”且RowNo = 3的單元格
所以我們可以用`[y,x]`來代表一個單元格,y即列名,也稱作縱座標, x即RowNo的值,也稱作橫座標
> ***如果表格沒有RowNo列怎麼辦?如想尋找答案,請繼續往下閱讀***
### 讓公式生效
```js
//首先引入AutoCalculate.js
import AutoCalculate from '../components/AutoCalculate';
...
//定義一個AutoCalculate例項,formulas為公式陣列
let autoCal = new AutoCalculate(formulas);
/* 呼叫cal方法
* gridDatas(必填):表格資料
* refField(必填):參考欄位,即單元格[y,x]中x是哪個欄位的值
*/
autoCal.cal(gridDatas, refField);
```
### 區域公式
實際上,除了1月,2月,3月……10月也存在類似的公式,即:
```
[Month1,1] = [Month1,2] + [Month1,3]
[Month2,1] = [Month2,2] + [Month2,3]
[Month3,1] = [Month3,2] + [Month3,3]
……
……
……
[Month10,1] = [Month10,2] + [Month10,3]
```
也就是說我們需要寫10條這樣的公式,對於簡單的場景來說,這不成問題,但是對於某些包含大量公式的表格,這種寫法存在一些弊端,比如容易寫錯,還有,公式長的時候也需要花費較多時間才能寫完。所以,便有了區域公式。
觀察上面的公式可以發現,其實每條公式都可以用一條公式來代替,例如以下公式:
```
[@,1] = [@,2] + [@,3]
```
這裡沒有明確的列名,只是用了一個佔位符@,但它足以代表以上10條公式。這個時候,我們只需要在適當的位置補上列名就可以了,所以,最終的公式就是:
```
{Month1, Month2, Month3, Month4, Month5, Month6, Month7, Month8, Month9, Month10}[@,1] = [@,2] + [@,3]
```
你需要將列名用`,`隔開,並放置在大括號`{ }`內,如此,1條公式便相當於10條公式。
佔位符不僅僅可以用於縱座標,還可用於橫座標,如以下公式:
```js
//公式1:
[YearTotal,3] = [Month1,3] + [Month2,3] + [Month3,3] + [Month4,3] + [Month5,3] + [Month6,3] + [Month7,3] + [Month8,3] + [Month9,3] + [Month10,3]
//公式2:
[YearTotal,4] = [Month1,4] + [Month2,4] + [Month3,4] + [Month4,4] + [Month5,4] + [Month6,4] + [Month7,4] + [Month8,4] + [Month9,4] + [Month10,4]
//公式3:
[YearTotal,5] = [Month1,5] + [Month2,5] + [Month3,5] + [Month4,5] + [Month5,5] + [Month6,5] + [Month7,5] + [Month8,5] + [Month9,5] + [Month10,5]
//公式4:
[YearTotal,6] = [Month1,6] + [Month2,6] + [Month3,6] + [Month4,6] + [Month5,6] + [Month6,6] + [Month7,6] + [Month8,6] + [Month9,6] + [Month10,6]
//公式5:
[YearTotal,2] = [Month1,2] + [Month2,2] + [Month3,2] + [Month4,2] + [Month5,2] + [Month6,2] + [Month7,2] + [Month8,2] + [Month9,2] + [Month10,2]
//公式6:
[YearTotal,7] = [Month1,7] + [Month2,7] + [Month3,7] + [Month4,7] + [Month5,7] + [Month6,7] + [Month7,7] + [Month8,7] + [Month9,7] + [Month10,7]
//公式7:
[YearTotal,9] = [Month1,9] + [Month2,9] + [Month3,9] + [Month4,9] + [Month5,9] + [Month6,9] + [Month7,9] + [Month8,9] + [Month9,9] + [Month10,9]
//公式8:
[YearTotal,12] = [Month1,12] + [Month2,12] + [Month3,12] + [Month4,12] + [Month5,12] + [Month6,12] + [Month7,12] + [Month8,12] + [Month9,12] + [Month10,12]
//公式9:
[YearTotal,13] = [Month1,13] + [Month2,13] + [Month3,13] + [Month4,13] + [Month5,13] + [Month6,13] + [Month7,13] + [Month8,13] + [Month9,13] + [Month10,13]
```
使用區域公式,可以寫成:
```
{2, 3, 4, 5, 6, 7, 9, 12, 13}[YearTotal,@] = [Month1,@] + [Month2,@] + [Month3,@] + [Month4,@] + [Month5,@] + [Month6,@] + [Month7,@] + [Month8,@] + [Month9,@] + [Month10,@]
```
由此可見,區域公式為公式的書寫帶來了極大的便利。
### 支援js語法
在實際場景中,我們經常會碰到一些複雜的公式,如下圖,單元格公式使用了Excel自帶的Max函式,對於這樣的公式,我們可以這樣寫:
```
[Month1,9] = ([Month1,6] - [Month1,7] - [Month1,8] > 0 ? [Month1,6] - [Month1,7] - [Month1,8] : 0) + [Month1,5]
```
![img](http://www.fenghaitao.net/wp-content/uploads/2020/07/AutoCalculate_2.png)
如你所見,公式支援js語法,你可以在公式等號右邊放入一個js變數,甚至js函式,只要是js解析引擎認識的語法,都被支援。
> **這裡有個需要注意的地方,就是不可以將陣列元素放入公式中,因為js的陣列元素通常帶有“[ ]”符號,這與公式當中的單元格表示符”[ ]”產生衝突,所以陣列元素被禁止使用,請留意這一點。**
### [y]公式
接下來,帶大家看一看另外一種場景,如圖,存在這樣的關係:
單元格① = 單元格② - 單元格③
你可能很快就寫出了以下公式:
```
[column3,1] = [column2,1] - [column1,1]
[column3,2] = [column2,2] - [column1,2]
```
![img](http://www.fenghaitao.net/wp-content/uploads/2020/07/AutoCalculate_3.png)
這樣寫本身沒有錯,但是我得提醒你,這裡的行是不固定的,也就是說表格有多少行完全取決於當時的資料庫情況,有可能今天只有3行資料,明天會有5行,後天會有50行。我們不可能隨著行數增多而增加公式,所以對於這種行數不確定的表格,我們有一種新的寫法,我將它稱為[y]公式,因為跟普通公式相比,它沒有橫座標:
```
[column3] = [column2] - [column1]
```
只需要一行公式,AutoCalculate便會將公式應用於指定列名下的所有行。
### 合計列與小數位數
有時候,我們需要求某一列的和,雖然求某一列的和可能不是我們的最終目的,但卻是我們完成計算的必要步驟,如存在以下關係:
單元格③ = 單元格① / 單元格②
單元格②是`GroupApprovedTotal`列的合計值,我們用`<列名>`來表示