緣起:
我現在的工作,發薪日是 10 號,9/11
號時有問老闆,她說需要我回報工時,格式如下。我就趁著工作時的空檔寫一寫,有些時間忘了,還能翻翻日記來看那天到底是幾點做到幾點。
最近想到,每次做這個報表時都要這樣搞的話也太麻煩,直接開個
google spreadsheet 紀錄這些,感覺會方便許多,我印象中它還能寫 script
來處理資料,所以就花了點時間弄了一個小專案。
這篇文章記錄開發時的想法與遇到的問題。
日期與時間:
每列的資料很簡單,當天日期、當天的工作開始跟結束時間
再來設定日期那欄,全選欄,點擊右鍵,選 "資料驗證"。
雙擊那一欄的儲存格,就能直接選擇日期了。
=ARRAYFORMULA(SEQUENCE(48,1,0, 30)* 1/(60*24))
時間是每半小時一項,所以 SEQUENCE 公式代入的值是 48, 1, 0,
30
上方工具列,選擇擴充功能裡的 App
Script,它會為你建立一個對應此試算表的 App Script,一個 gs
檔。整個專案弄下來,就只會動到那個 gs 檔,還有執行 gs 檔裡頭定義的
function,它寫起來基本上跟 JavaScript 差不多。
又是 gpt 大神教我寫程式的時候了,先是寫一些測試用的
function,做基本的功能,像是讀某一格資料,確認沒有問題後,再把它們並入完整的
function 裡,編輯器上方有選單讓你選擇某一 function 來執行
首先是最重要的,產生報表
function showResult(){
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('main');
let timeZone='GMT+8';
let sResult = '';
let totalHours = 0;
for(let i=2;i<=sheet.getLastRow();i++){
let rowDate = sheet.getRange('A'+i.toString()).getValue();
let rowStartTime = sheet.getRange('B'+i.toString()).getValue();
let rowEndTime = sheet.getRange('C'+i.toString()).getValue();
sResult+=`${Utilities.formatDate(rowDate, timeZone, 'MM/dd')}\t${Utilities.formatDate(rowStartTime, timeZone, 'HH:mm')}~${Utilities.formatDate(rowEndTime, timeZone, 'HH:mm')}\n`;
totalHours+=(rowEndTime-rowStartTime)/(1000*60*60);
}
sResult+=`總共${totalHours}小時`;
SpreadsheetApp.getUi().alert(sResult);
}
第 2 行 :
用工作表的名稱來取得 sheet 實體
第 3~5 行 : timeZone
格式化時間會用到,sResult 用來組合輸出結果,totalHours
紀錄工時總和。
第 6 行 : getLastRow
可以取得最後一列有資料的。
第 7~9 行 : getRange
取得儲存格資料,參數是字串,ex :
"A1:C3",會回傳一個二維的陣列,但我們只需單一欄位的值,所以直接取 A{數字} B{數字} C{數字} 單一儲存格,然後再用 getValue
拿欄位裡的值。雖然開始時間跟結束時間的下拉選單只有呈現時間的值,但取值後其實還會有日期的資料,固定是
1899/12/30,不影響程式,我們只是需要求時間差而已。
第 10 行 :
我原本以為日期類別的資料可以像 C# 一樣直接呼叫 toString
來格式化,但正確做法應該是呼叫 Utilities.formatDate。
第 11 行 :
兩個時間資料相減,會回傳之間的毫秒,我要的單位是小時,所以除以
1000*60*60。
清空資料:
每次回報完後,舊資料就用不到了,所以我還有寫個清空資料的
function
function clearDatas() {
const ui =SpreadsheetApp.getUi();
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('main');
const cell = sheet.getRange('A2:C30');
let response = SpreadsheetApp.getUi().alert(
'提醒',
'確定清空?',
ui.ButtonSet.OK_CANCEL
)
if(response == ui.Button.OK){
cell.clearContent();
}
}
第 5~9 行 : alert
還能與使用者互動,用 response 來記錄使用者
第 11~13 行 :
使用者點擊"確定"後,用 clearContent 清空儲存格的資料
執行 function
後,會先跳出一個提示,用來避免不小心把整個月的資料都砍掉。
如果每次要操作,都還要跑去 app script 那邊執行
function,還是有些麻煩,所以我還有再問 gpt
有沒有辨法在工具列新增客製的選項,方法是有的
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('自訂工具')
.addItem('顯示報表', 'showResult')
.addItem('清空資料', 'clearDatas')
.addToUi();
}
呼叫 ui 的 createMenu,建立一個選單,再用 addItem 加入選項,第二個參數綁定呼叫的 function 名稱。執行一次 onOpen 的 function 後,試算表的工具列就能看到你自訂的選單
沒有留言:
張貼留言