數(shù)據(jù)可視化探索之 SpreadJS
本文首發(fā)于政采云前端團(tuán)隊博客:數(shù)據(jù)可視化探索之 SpreadJS
https://www.zoo.team/article/explore-spreadjs

一、前言
數(shù)據(jù)可視化包含三個分支:科學(xué)可視化、信息可視化、可視分析。
1、科學(xué)可視化主要關(guān)注的是三維現(xiàn)象的可視化,如建筑學(xué)、氣象學(xué)、醫(yī)學(xué)或生物學(xué)方面的各種系統(tǒng)。重點在于對體、面以及光源等等的逼真渲染,或許甚至還包括某種動態(tài)成分。
2、信息可視化是一種將數(shù)據(jù)與設(shè)計結(jié)合起來的圖片,有利于個人或組織簡短有效地向受眾傳播信息的數(shù)據(jù)表現(xiàn)形式。
3、可視分析學(xué)被定義為由可視交互界面為基礎(chǔ)的分析推理科學(xué),將圖形學(xué)、數(shù)據(jù)挖掘、人機(jī)交互等技術(shù)融合在一起,形成人腦智能和機(jī)器智能優(yōu)勢互補和相互提升。
可視化分析中可視化報表是重中之重,把大量的數(shù)據(jù)快速的展示出來,并且靈活的進(jìn)行數(shù)據(jù)操作,其中操作包括數(shù)據(jù)的篩選、關(guān)聯(lián)、聯(lián)動、鉆取,文案的查詢,替換、樣式設(shè)置,條件格式的注入實現(xiàn)多色階、圖標(biāo)集、數(shù)據(jù)條、重復(fù)值,以及公式的插入,跨表聯(lián)動等。SpreadJS 在解決可視化分析報表中最為突出,下面我們只針對可視化分析中 SpreadJS 所扮演色做探討。
二、報表可視化難點
互聯(lián)網(wǎng)電商服務(wù)業(yè)行業(yè),平時會處理大量商業(yè)信息和用戶信息,客服和數(shù)據(jù)分析師,是報表主要用戶人員。
客服平時每天都會處理大量的工單填報、客訴登記、第三方平臺原始數(shù)據(jù)的導(dǎo)入、統(tǒng)計匯總、審核審批、電簽、分發(fā)等工作。平時大部分工作信息的載體都是 Excel,每天服務(wù)器需要處理海量的文檔,由于 Excel 文檔本身數(shù)據(jù)難以提取入庫,模板更新時也不方便第一時間分發(fā)到操作員處,難以整合到 Web 頁面里等問題。
數(shù)據(jù)分析師需要拿到數(shù)據(jù)進(jìn)行匯總,算出各個商品品牌的銷售額,最大值、最小值、平均值等,標(biāo)識出有價值的數(shù)據(jù)。抓取有效數(shù)據(jù),制作成報表給 boss。
針對以上的場景,報表可視化可以總結(jié)出以下幾個難點:
1. 并發(fā)
公司客服人數(shù)眾多,幾千人同時在線重度操作,業(yè)務(wù)流轉(zhuǎn)周期短、數(shù)據(jù)量大,所以對服務(wù)端并發(fā)性能消耗是很大的。可以在后臺用 Apache POI 來提取和修改 Excel 數(shù)據(jù)、并執(zhí)行其中的公式計算等。這樣會遇到兩個性能瓶頸:
1)需要頻繁地上傳、下載文檔,服務(wù)器帶寬承受了很大的壓力;
2)所有 Excel 解析、提取的操作都在服務(wù)器端,頻繁的 IO 操作讓服務(wù)器不堪重負(fù)。
以上兩個性能點,在目前的架構(gòu)下很難突破,這也是重構(gòu)項目時最具挑戰(zhàn)性的需求點之一。當(dāng)然硬堆服務(wù)器配置也是一個解決方案,但無法解決其它的一些問題,并且也會帶來運維的壓力。
2. 對 Excel 操作和兼容性要求較高
新系統(tǒng)如果不能讓大家快速上手使用,以這個項目用戶的體量,培訓(xùn)成本將無法承受。而且要能夠直接導(dǎo)入已有的 Excel 報表模板,否則再次開發(fā)或設(shè)計所有 Excel 報表也是難以接受的。
3. 報表格式靈活多變
針對不同的業(yè)務(wù)場景,報表的模版也是千變?nèi)f化。因此不需要研發(fā)的介入,操作員的設(shè)計和填報都可以在頁面上完成顯得尤為重要。
4. 支持公式計算
由于涉及到商品、訂單、成本核算、財務(wù)統(tǒng)計等模塊,對計算公式的種類和性能要求較高。
5. 工作流中的數(shù)據(jù)文檔
以前系統(tǒng)的工作流,涉及到 Excel 報表時,要么數(shù)據(jù)會先在服務(wù)端和 Excel 模板進(jìn)行拼裝,要么系統(tǒng)根據(jù)路徑找到文件服務(wù)器的 Excel 文件,然后流轉(zhuǎn)到對應(yīng)環(huán)節(jié)。一些新的業(yè)務(wù)模塊,甚至還只能用郵件進(jìn)行文件傳輸。
這個過程會產(chǎn)生大量的文件,對文件服務(wù)器的帶來了很大壓力,后臺也不得不定期做批量的數(shù)據(jù)拆分和維護(hù)。這次升級系統(tǒng)需要解決這個問題。
三、思考如何選型
首先,選型的第一步就是搞清楚市面上具體有哪些產(chǎn)品供我們選擇,對于目前市面上能集成到系統(tǒng)中,支持這種在線表格文檔編輯的產(chǎn)品有不少,大體我把他們分了兩類。
1. 云文檔類型產(chǎn)品
這種產(chǎn)品有很多,類似 WPS、石墨文檔、Office Online。它們本身具備較高的完成度,已經(jīng)幫用戶實現(xiàn)了包括在線協(xié)同內(nèi)的幾乎所有功能,甚至也支持一定程度的二次開發(fā)而且可以私有化部署。但問題在于通常這類產(chǎn)品封閉性比較強,二次定制開發(fā)還是相對比較困難,且不夠輕量。授權(quán)方式也多以按時間、按并發(fā)量、用戶數(shù)量等方式授權(quán),價格昂貴,不是很適合我們的需要。
2. 控件類型產(chǎn)品
像 LuckySheet、Handsontable、SpreadJS 這種就是標(biāo)準(zhǔn)的控件了,它們都是純前端表格控件,都支持 Excel 的功能特性和 json 數(shù)據(jù)綁定。
LuckySheet (https://v2ex.com/t/713716?p=1&fileGuid=QKgTJRrrCD96PXwh) 是國內(nèi)的MIT開源軟件,可以拿來商用。但在我調(diào)研時它才剛上線 1、2 個月,并且不像 React 這種有某個大廠來背書,所以不可能拿來用到我們的正式項目里。截止目前已經(jīng)過去了 1 年,陸續(xù)推出了 QQ 群、論壇等交流平臺,但仍顯薄弱。
Handsontable (https://handsontable.com/?fileGuid=QKgTJRrrCD96PXwh) 是國外的一個商業(yè)表格控件,據(jù)說二次開發(fā)坑較多,但對我們來說最大的問題是它沒有中文支持團(tuán)隊。
SpreadJS (https://demo.grapecity.com.cn/SpreadJS/WebDesigner/?fileGuid=QKgTJRrrCD96PXwh) 是葡萄城公司的商業(yè)Excel表格控件,有趣的是我發(fā)現(xiàn)在 V2EX 的 LuckySheet 下方評論區(qū)中,LuckySheet 的作者也說 SpreadJS 是行業(yè)標(biāo)桿。它支持導(dǎo)入包括公式、圖表、樣式、條件格式在內(nèi)的絕大部分 Excel 特性(不支持宏)。并且最驚喜的是,它的操作界面是一個完整的 Excel 界面,完全純 JS 開發(fā)的,用 json 進(jìn)行模板和數(shù)據(jù)交互。同時 SpreadJS 也有對應(yīng)的售后支持團(tuán)隊,技術(shù)問題可以工作日期間隨時電話、論壇交流,相關(guān)的資料包括視頻、文檔、示例、API 手冊也都非常豐富,甚至還可以請他們的技術(shù)顧問來公司培訓(xùn)。對于像我們這種工期短、開發(fā)任務(wù)比較繁重的項目組,確實能節(jié)約大量的精力,降低了風(fēng)險。

圖片來源:SpreadJS 在線 Excel 編輯器 (https://demo.grapecity.com.cn/SpreadJS/WebDesigner/?fileGuid=QKgTJRrrCD96PXwh)
那么什么是控件?為什么要用控件?
“引用維基百科: (https://zh.wikipedia.org/wiki/%E6%8E%A7%E4%BB%B6?fileGuid=QKgTJRrrCD96PXwh) 在計算機(jī)編程當(dāng)中,控件(或部件,widget或control)是一種 圖形用戶界面 元素,其顯示的信息排列可由用戶改變,例如 視窗 或 文本框。控件定義的特點是為給定數(shù)據(jù)的直接操作(direct manipulation)提供單獨的互動點。控件是一種基本的可視構(gòu)件塊,包含在應(yīng)用程序中,控制著該程序處理的所有數(shù)據(jù)以及關(guān)于這些數(shù)據(jù)的交互操作。
按照我自己理解,控件就是只提供了基本功能,支持二次開發(fā)的功能模塊。控件相對依賴更輕,可塑性更好,并且也有對應(yīng)的開發(fā)文檔和 API,是面向開發(fā)者的基礎(chǔ)功能包,便于按需求來定制功能。
四、SpreadJS 需求解決方案和優(yōu)勢
1. 并發(fā)
由于 SpreadJS 是數(shù)據(jù)和模板分離的設(shè)計,填報人員只需要在頁面上完成填報。提交時可以只提交填報好的數(shù)據(jù) json 即可,服務(wù)器再也不用集中解析所有Excel 文件了。帶寬消耗也直接節(jié)約了一半。
2. 對 Excel 操作和兼容性要求較高
在內(nèi)部試用時,財務(wù)和客服的小姐姐們反饋,使用體驗跟 Excel 幾乎完全一樣,不需要再特意培訓(xùn)。而且我們自己的大量 Excel 報表可以直接導(dǎo)入進(jìn)去(二次開發(fā)后也可以實現(xiàn)批量和遠(yuǎn)程導(dǎo)入),包括圖表、公式、表格樣式等等一系列元素都可以直接導(dǎo)入線上操作。
3. 報表格式靈活多變
設(shè)計人員可以直接在線設(shè)計,或者把 Excel 設(shè)計好的報表,拿到 Web 端,做好數(shù)據(jù)綁定,提交保存成 json 格式即可(Spread JS 的 ssjson 格式包括 Excel 文檔的所有信息)
4. 支持公式計算
支持了 450 多種( Excel 一共 480 多種)公式,還可以自己開發(fā)擴(kuò)展自定義公式,對財務(wù)也夠用了。同時還支持所有 Excel 的引用操作,比如跨 sheet 引用、絕對引用、函數(shù)命名信息之類。
5. 工作流中的數(shù)據(jù)文檔
基本脫離了對文件的依賴,所有流程狀態(tài)和依賴的數(shù)據(jù)都可以在數(shù)據(jù)庫中記錄,文件服務(wù)器只需要保存少量的模板文檔即可(其實模板數(shù)量不大時可以直接放到數(shù)據(jù)庫里,不過我們有現(xiàn)成的文件服務(wù)器)。這里節(jié)約了我們 90% 文件服務(wù)器的空間開銷,運維的小伙伴半夜都要笑醒。
五、深入SpreadJS
重點來了,其實最讓我這個前端開發(fā)者感興趣的就是 SpreadJS 的一些底層設(shè)計、以及對內(nèi)存、性能平衡性的優(yōu)化。對此我做了很多調(diào)研和學(xué)習(xí),好在這方面資料不難找,常??梢栽?strong style="color: rgb(71, 193, 168);">葡萄城官方論壇的公開課版塊(https://gcdn.grapecity.com.cn/forum.php?mod=forumdisplay&fid=225&filter=typeid&typeid=274&fileGuid=QKgTJRrrCD96PXwh)遇到一些相關(guān)的技術(shù)分享。下邊是自己了解學(xué)習(xí)到的內(nèi)容,做個簡單總結(jié):
1. 渲染性能
性能肯定是每個深度表格控件用戶最擔(dān)心的問題。我們的數(shù)據(jù)量常常達(dá)到好幾千條,而且 Excel 不方便分頁(涉及前端的公式計算匯總),所以選型期間很擔(dān)心。后來發(fā)現(xiàn)想多了,SpreadJS 可以輕松加載 50 萬條數(shù)據(jù)加載耗時 200 ms左右(官網(wǎng)性能演示示例(https://demo.grapecity.com.cn/spreadjs/BenchmarkSample/?fileGuid=QKgTJRrrCD96PXwh)只能加載 5 萬,我們自己扒下來測的 50 萬)。后來深入了解才知道,解決這個問題,他們的思路是這樣的:
實時渲染 + Double buffering (翻譯成雙層緩存?):
用 Canvas 渲染表格部分,并且只渲染用戶看到的部分內(nèi)容,這就實現(xiàn)了加載 1000 行和加載 100000 行數(shù)據(jù)速度都很快,性能相差不大的現(xiàn)象。
而 Double buffering 是為了解決連續(xù)渲染的連續(xù)性體驗問題,也可以進(jìn)一步提升渲染速度。這個名詞估計聽過的人少,但應(yīng)該人人都體驗過,Double buffering 在圖形學(xué)里,一般稱作雙緩沖,實際上的繪圖指令是在一個緩沖區(qū)完成,這里的繪圖非常的快,在繪圖指令完成之后,再通過交換指令把完成的圖形立即顯示在屏幕上,這就避免了出現(xiàn)繪圖的不完整,同時效率很高。在游戲里其實很常見,當(dāng)我們主控的人物在地圖上奔跑時,游戲引擎會按照人物移動方向?qū)崟r加載和渲染地圖,這就避免了一次性加載超大地圖時那漫長的等待。

圖片來源:葡萄城公開課【SpreadJS性能優(yōu)化】(https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=86035&extra=page=1&filter=typeid&typeid=274&fileGuid=QKgTJRrrCD96PXwh)
稀疏數(shù)組:
SpreadJS 對表格數(shù)據(jù)的存儲優(yōu)化采用了稀疏數(shù)組的數(shù)據(jù)結(jié)構(gòu)。稀疏數(shù)組常用來優(yōu)化二維數(shù)組(比如棋盤、地圖等場景)的內(nèi)存占用,但它有個天生的缺陷,就是訪問性能慢。

所以當(dāng)時針對這個疑問,我給它做了壓力測試,百萬級別的遍歷耗時 200 多ms。性能可以滿足我們的需求。

2. 計算引擎
據(jù)官方介紹來看,公式引擎其實是包含了兩大實現(xiàn)的部分,一個是計算邏輯系統(tǒng)、一個是引用系統(tǒng)。
引用系統(tǒng)
Excel 中公式的計算都是依賴于某些原始數(shù)據(jù)的,比如 C1 引用 B1、B1 又引用 A1等等, SpreadJS 把這部分功能封裝的已經(jīng)非常原生化了,根本不需要開發(fā)者操心(除非有引用回溯等特殊需求)。
Excel 中有直接引用、跨 Sheet 表單引用、相對/絕對引用、命名信息的引用、 table 行列公式的引用、跨工作簿引用等等(沒列舉完,感興趣的同學(xué)可以自行搜索學(xué)習(xí))。SpreadJS 的 runtime 是在網(wǎng)頁端,因此跨 Workbook 引用就別想了,至少目前肯定沒支持。
計算邏輯
SUM、IF、MATCH、VLOOKUP 這種能輸入到單元格里的計算公式,用起來就像是一個個的小“邏輯包”,目前 SpreadJS 有 460+ 種原生的公式函數(shù),而 Excel 只有 490+ 種,并且 SpreadJS 能自定制公式,使用體驗與原生公式一樣。
對于底層實現(xiàn),實際上經(jīng)過多個版本的迭代,這些公式早已不是一個個獨立的“邏輯孤島”了。公式的實現(xiàn)在底層有大量的抽象和復(fù)用,據(jù)說新版本在性能提升的同時,代碼量比老版本有明顯精簡,這對前端工程打包也是比較友好的。
對于嵌套公式計算的實現(xiàn),SpreadJS 在底層建立起了 AST 樹來解析用戶設(shè)置公式的計算邏輯,從官方示例的代碼來看,公式底層建立了一套 Expression,并且有對應(yīng)的 public 接口可供調(diào)用,如圖:

圖片來源:【SpreadJS公式結(jié)構(gòu)樹形展示】(https://gcdn.grapecity.com.cn/showtopic-79188-1-1.html?fileGuid=QKgTJRrrCD96PXwh)
性能
首先,作為一個前端技術(shù),咱們可以先從公式計算的技術(shù)要求上來分析性能可能出現(xiàn)的瓶頸以及造成的影響。我們在開發(fā)時用到了大量的用戶事件、臟數(shù)據(jù)、聯(lián)動等功能,所有這些功能確保正確運行的一個重要前提,就是必須能確保隨時可以拿到正確的計算結(jié)果,那么最直接的實現(xiàn)思路就是讓公式以高優(yōu)先級、同步的方式來執(zhí)行完計算。
大家都知道,多線程可以幫助分擔(dān)計算壓力,但是先拋開設(shè)計和實現(xiàn)難度不說,即便支持了 Web Worker,JavaScript 嚴(yán)格來說也只能算是一個單線程語言,因為它的 Web Worker 子線程完全受主線程控制,并且主線程無法被阻塞掛起。所以即使引入了 Web Worker,也無法確保上邊提到的同步執(zhí)行。
經(jīng)過以上分析,可以看出公式計算性能的局限性,取決于 JavaScript 的計算能力。我找了一張相關(guān)的圖片,可以直觀反映 Node.js 的計算能力(Node.js 是 V8 引擎,公認(rèn)最快的 JS 引擎)

圖片引用自《深入淺出 Node.js》
而 SpreadJS 官方給出了一組公式的計算數(shù)據(jù),參考如下:

據(jù)我們測試,以上計算性能接近原生JS的計算性能,SpreadJS 在這方面的優(yōu)化已經(jīng)十分接近物理極限了。目前在我們的應(yīng)用場景中,這個計算性能已經(jīng)足夠使用,但不排除以后會出現(xiàn)海量的數(shù)據(jù)和公式的計算需求,而在這方面官方也給出了相關(guān)解決方案,參考這里 (https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=74083&fileGuid=QKgTJRrrCD96PXwh)。
據(jù)說,官方還在進(jìn)一步開發(fā)緩存技術(shù),來實現(xiàn)公式計算的分塊緩存:即使引用鏈上有值發(fā)生變化,也不需要計算整個引用鏈的公式。聽起來很強大,思路也靠譜,但愿早點推出。
3. 樣式系統(tǒng)
Excel 的樣式系統(tǒng)非常復(fù)雜,邊框、字體、對齊、數(shù)據(jù)格式、條件格式等等每一個功能點都有非常靈活龐大的實現(xiàn),剛開始了解 SpreadJS 時,我也被它的 Style 類驚呆了,除了我能想象到的邊框、背景、字體、對齊等這些能“看得到”的,竟然也有單元格類型、數(shù)據(jù)格式、表格按鈕、下拉、水印這類東西。不由得感嘆 Style 太重了,如果定制大量的單元格樣式,內(nèi)存和性能肯定都不好。不過實際應(yīng)用中倒也沒發(fā)現(xiàn)瓶頸,原來這里采用了分層結(jié)構(gòu)來設(shè)計,如圖:

圖片來源:葡萄城公開課【SpreadJS性能優(yōu)化】
六、SpreadJS怎么用?
1. 渲染表格

圖 6.1-1 綁定數(shù)據(jù)和公式
首先獲取全局 spread 對象,spread 是整個表格的主體,spread 又分成多個 sheet。SpreadJS 初始化結(jié)束都會返回一個 spread 對象。
vue 版本 spread 對象
<gc-spread-sheets @workbookInitialized='spreadInitHandle($event)' />
methods:{
spreadInitHandle: function (spread) {
this.spread = sprea
},
}
綁定數(shù)據(jù),綁定公式
tableDataBind() {
// 數(shù)據(jù)源,可以從后臺請求拿到
var dataSource = {
// 注意這里加了一層bindPath,用于映射表格的綁定路徑
bindPath_table: [{
c1: 100,
c2: 90,
c3: 30,
c4: 40
}, {
c1: 88,
c2: 66,
c3: 55,
c4:100
}, {
c1: 30,
c2: 89,
c3: 100,
c4: 40
},{
c1: 40,
c2: 66,
c3: 88,
c4: 40
}]
};
// 表格綁定和單元格綁定數(shù)據(jù)源,需要用SpreadJS的CellBindingSource包裝一下
var spreadNS = GC.Spread.Sheets;
var dataSource1 = new spreadNS.Bindings.CellBindingSource(dataSource);
var table2 = this.activeSheet.tables.add("tableName", 0, 0, 1, 5, spreadNS.Tables.TableThemes.light6);
table2.showFooter(true);
table2.autoGenerateColumns(false);
var c1 = new spreadNS.Tables.TableColumn(1);
c1.name("語文");
c1.dataField("c1");
var c2 = new spreadNS.Tables.TableColumn(2);
c2.name("數(shù)學(xué)");
c2.dataField("c2");
var c3 = new spreadNS.Tables.TableColumn(3);
c3.name("英語");
c3.dataField("c3");
var c4 = new spreadNS.Tables.TableColumn(4);
c4.name("化學(xué)");
c4.dataField("c4");
var c5 = new spreadNS.Tables.TableColumn(5);
c5.name("合計");
table2.bindColumns([c1, c2, c3, c4, c5]);
table2.bindingPath("bindPath_table");
// 設(shè)置公式
table2.setColumnDataFormula(4, "=[@語文]+[@數(shù)學(xué)]+[@英語]+[@化學(xué)]");
table2.setColumnFormula(4, "=SUBTOTAL(109,[合計])");
// 設(shè)置允許單元格的內(nèi)容超出單元格,與綁定無關(guān)
this.activeSheet.options.allowCellOverflow = true;
// 綁定dataSource
this.activeSheet.setDataSource(dataSource1);
this.spread.resumePaint();
},
圖 6.1-2函數(shù)名和函數(shù)碼映射表2. 渲染條件格式
渲染條件格式:數(shù)據(jù)渲染完成只能保證數(shù)據(jù)能正常顯示出來,但是這還不能滿足數(shù)據(jù)分析師的需求,還要明顯展示有效數(shù)據(jù)譬如:最大值,最小值標(biāo)紅,進(jìn)度條展示一個變化狀態(tài),圖標(biāo)展示上升還是下降,雙色階,三色階,等,具體怎么實現(xiàn)?
圖標(biāo)集:效果如圖

實現(xiàn)代碼
iconset() {
var activeSheet = this.activeSheet;
var iconSetRule = new GC.Spread.Sheets.ConditionalFormatting.IconSetRule();
// 演示demo先寫死區(qū)域
iconSetRule.ranges([new GC.Spread.Sheets.Range(0,0, 5, 5)]);
// IconSetType圖標(biāo)志的類型:箭頭,圓圈和execl 打通的,excel有哪些這這邊就支持哪些
iconSetRule.iconSetType(GC.Spread.Sheets.ConditionalFormatting.IconSetType.threeArrowsColored);
var iconCriteria = iconSetRule.iconCriteria();
iconCriteria[0] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion(
true,
GC.Spread.Sheets.ConditionalFormatting.IconValueType.number,
60
);//(<60)
iconCriteria[1] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion(
true,
GC.Spread.Sheets.ConditionalFormatting.IconValueType.number,
90
);//(60<= <90)
iconCriteria[2] = new GC.Spread.Sheets.ConditionalFormatting.IconCriterion(
true,
GC.Spread.Sheets.ConditionalFormatting.IconValueType.number,
90
);//(>=90)
iconSetRule.reverseIconOrder(false);
iconSetRule.showIconOnly(false);
activeSheet.conditionalFormats.addRule(iconSetRule);
},
進(jìn)度條:效果如圖

實現(xiàn)代碼
dataBar(){
var activeSheet = this.activeSheet;
activeSheet.conditionalFormats.addDataBarRule(
GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number,0,//最小數(shù)
GC.Spread.Sheets.ConditionalFormatting.ScaleValueType.number, 100,//最大值
"orange",//顏色
[new GC.Spread.Sheets.Range(0,0, 5, 4)]
);
},
重復(fù)值:效果如圖

實現(xiàn)代碼
duplicateValue() {
var activeSheet = this.activeSheet;
var style = new GC.Spread.Sheets.Style();
style.backColor = "yellow";
style.foreColor = "red";
var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 4)];
activeSheet.conditionalFormats.addDuplicateRule(style, ranges);
},
包含文本 6 的單元格:效果如圖

實現(xiàn)代碼
includeText() {
var activeSheet = this.activeSheet;
var style = new GC.Spread.Sheets.Style();
style.backColor = "red";
var ranges = [new GC.Spread.Sheets.Range(0,0, 5, 5)];
activeSheet.conditionalFormats.addSpecificTextRule(
GC.Spread.Sheets.ConditionalFormatting.TextComparisonOperators.contains, "6", style, ranges
);
},
綜合以上實現(xiàn)結(jié)果如圖

七、寫在最后
本文主要介紹了自己在數(shù)據(jù)可視化方向的一些探索,針對一些準(zhǔn)備做市場大盤以及郵件訂閱報表,線上協(xié)同協(xié)作,可視化分析等方向的同學(xué)有一定的幫助。
看完兩件事
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我兩件小事
1.點個「在看」,讓更多人也能看到這篇內(nèi)容(點了「在看」,bug -1 ??)
招賢納士
政采云前端團(tuán)隊(ZooTeam),一個年輕富有激情和創(chuàng)造力的前端團(tuán)隊,隸屬于政采云產(chǎn)品研發(fā)部,Base 在風(fēng)景如畫的杭州。團(tuán)隊現(xiàn)有 40 余個前端小伙伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風(fēng)暴團(tuán)。成員構(gòu)成既有來自于阿里、網(wǎng)易的“老”兵,也有浙大、中科大、杭電等校的應(yīng)屆新人。團(tuán)隊在日常的業(yè)務(wù)對接之外,還在物料體系、工程平臺、搭建平臺、性能體驗、云端應(yīng)用、數(shù)據(jù)分析及可視化等方向進(jìn)行技術(shù)探索和實戰(zhàn),推動并落地了一系列的內(nèi)部技術(shù)產(chǎn)品,持續(xù)探索前端技術(shù)體系的新邊界。
如果你想改變一直被事折騰,希望開始能折騰事;如果你想改變一直被告誡需要多些想法,卻無從破局;如果你想改變你有能力去做成那個結(jié)果,卻不需要你;如果你想改變你想做成的事需要一個團(tuán)隊去支撐,但沒你帶人的位置;如果你想改變既定的節(jié)奏,將會是“5 年工作時間 3 年工作經(jīng)驗”;如果你想改變本來悟性不錯,但總是有那一層窗戶紙的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望參與到隨著業(yè)務(wù)騰飛的過程,親手推動一個有著深入的業(yè)務(wù)理解、完善的技術(shù)體系、技術(shù)創(chuàng)造價值、影響力外溢的前端團(tuán)隊的成長歷程,我覺得我們該聊聊。任何時間,等著你寫點什么,發(fā)給 [email protected]

