Vue + D3.js -專案發想實作

Jacy Chu
6 min readAug 17, 2021

--

Vue + D3.js + Firebase 打造收入追蹤器

📋 目錄

  1. 實作介紹
  2. 實作步驟
  3. 實作學習筆記
  4. 成果展示
  5. 小結

實作介紹

現代社會,自由接案工作者數量越來越多。與一般上班族不同之處,在於有多種收入,此實作將打造一個 Income Tracker 收入追蹤器。

將分為四個會計季度(Q1, Q2, Q3, Q4),季度之間是切分開的。Source 欄位輸入收入來源,Amount 欄位輸入金額。按下 Add 按鍵,將項目按該季度所佔比例在右方繪製環圈圖,並會算出該季度家總金額。下方依四個季度,繪製整年的收入直方圖,並會有整年收入的加總。

直接看成果,請點這裡 👀

實作步驟

實作分為 3 個步驟(此實作不包含打包部署程式)

step1 發想專案

基於 D3.js 結合 Firebase 服務,提供資料存儲的功能,發想實作的主題。希望資料是由使用者提供,而非單方面呈現 API 撈取的資料。
想到作帳類型很適合,每筆開支是由使用者手動輸入,經發想收斂後,決定依四個季度做為區隔,並簡化只記錄收入的部分。(開銷追蹤器所有功能可完全複製此專案)

step2 Figma 繪製 prototype

前一個步驟是簡單紙筆隨意繪製,為了讓想法更具象化。使用 Figma 協作工具製作 prototpy。由於此實作是筆者自己繪製,自己開發,所以沒有花太多時間配置色系與設定操作動畫。圖如下:

figma 繪製 prototype

step3 選擇開發工具

選擇使用 Vue cli 方式進行開發,樣式部分選擇 Tailwind UI library 輔助。考量 Vue plugin 時,由於此實作沒有多個分頁,功能也多以 D3.js 為主,故沒有加入 Vue Router & Vuex。

實作學習筆記

在實作過程中,遇到大大小小的困難。有的稍微上網查一下便迎刃而解,有的嘗試了好幾個小時只有一點進展。不論問題點是大是小,都從中獲得許多,以下將依開發的順序,記錄這些學習。

(1) 引用 Tailwind CSS 至專案

最初參考這支 Youtube 教學影片 Set up a New Project with Vue.js and tailwind.css。後來發現只要在創建好專案時,加上: vue add tailwind 指令,即自動配置好原本需手動修改的部分。

(2) 季度切換之樣式改變

若在一般 js 檔案開發,會使用querySelectAll 將所有按鈕做選取,先將所有 button 的 active 取消,再把選中的加上 active class,類似下方程式碼的寫法:

然而在 Vue 中寫法不同,在 Q1->Q4 的 buttons 上,各自綁定 {active:quarter === 'QX'} ,下方 methods 設定被選中的 e.target.dataset.quarter

(3) Send Form 檢查

表單檢查,先用 isNaN() 檢查 amount 是不是數字,還有檢查是否有空值情況就按送出,若有以上情況會出現錯誤訊息。檢查的 methods 綁定在 button 上的 click 事件,若這邊用 <form> 去包的話,不論檢查有沒有過都會送出!要特別小心。

(4) 資料庫連動

Firebase 提供 onSnapshot 監聽功能,如果資料有改變會自動觸發。由於繪製表格部分與季度 & 填寫表單是分開的。本來想說要用變數從 props 父傳子去觸發改動,後來發現不需要做這件事。Send Form 動作改動到 Firebase 資料,設定好的情況下,會自動觸發。

(5) Q1->Q4 四張圖表切割

一開始寫的時候,只有 source 與 amount 兩個欄位。因為要切換季度所以多加上 quarter 欄位。update function 會接收到所有的資料,先過濾只留下選中的季度:data = data.filter(item => item.quarter == this.quarter)。後續操作即切分開來!

(6) Hover 環圈圖

游標移動到環圈圖上面,出現客製化的 tooltip,顯示項目詳細資訊,點按一下即會刪除該項目。實作過程中,發現 mouseover 與 click 事件,操作上有點問題。處於 hover 狀態,被切換成 click,還沒有完成刪除動作又 hover 在新的區塊上。為了解決這樣的問題,在 mouseover 觸發加上 setTimeout 0.2秒的時間。

(7) 計算 Total & Annual Income

此專案沒有使用 Vuex,所以採用 $emit 將子元件傳送計算好的數字,傳給父元件呈現。使用 new.Intl.NumberFormat().format 將數字採用千進位。

(8) 自製重組 Q1->Q4 資料

Firebase 只有每一筆資料,在畫面下方,四個季度整理的長條圖資料需要自行後製。每個季度整理形式如下:

將季度的金額加總,重新組合繪製在畫布上。

(9) Source 相同 數字加上去

一開始寫法,使用 db.collection("incometracker").add(),即便輸入的 source 已經存在仍會產生新的區塊(不會覆蓋掉原本的,若要覆蓋使用 update )。以上都不是此實作想採用的作法,會希望使用者輸入同項目時,直接加到存在的項目上,由於此操作在寫進 Firebase 之前,故在加到 Firebase 前做下列檢查:

先過濾 quarter,再過濾 source。接著檢查 querySnapshot是否為空,若已經有值,使用 update 不過需要搭配使用 firebase.firestore.increment()。這邊設定在 Firebase.js 已經將 firebase.firestore 設定給 increment 變數。

成果展示

功能齊全 ✨ 介面走現代低調路線!

小結

此實作結合 Vue 以及 D3.js,串接 Firebase 取得實時資料。過程中卡在不少小問題上,一個個找尋解決方法或是構想更好的設計。特別感謝 Zhenmao Wan 💙 解答有關 Firebase update 資料相關問題!附上 GitHub 檔案連結 🔗 ,專案資料夾名稱:vue-d3-firebase-incometracker。有任何指點或建議都可以在下方留言~🙏

🎩 Thanks for reading!

--

--