iT邦幫忙

2025 iThome 鐵人賽

DAY 14
0
佛心分享-IT 人自學之術

我只是不想加班:一名客服人員的GAS自救之路系列 第 14

Day14|實作回顧:用GAS腳本模仿Google Sheets既有功能

  • 分享至 

  • xImage
  •  

週末的點閱率都很差,那就讓我來回顧一下黑歷史吧!今天沒有任何有價值的技術分享,就只是一個輕鬆的客服日常小故事:五彩斑斕的表格、東拼西湊的codes,和一點點我當時真的以為自己沒那麼愚蠢的錯覺。

前言

可能週五半夜(Day12)的題目真的太無聊了,幾乎沒有人點閱。都已經來到第二週了,我也差不多該寫一些對得起《我只是不想加班:一名客服人員的GAS自救之路》這個標題的內容😅

今天就讓我隨便挑一個簡單的實作故事來分享吧!

正文

※ 以下案例經過大幅改編與簡化

需求

主管要求客服人員於值班後,將共用的待辦清單做整理,依截止日期由近至遠排序。

該待辦清單表格請參考此示例:《to-do-legacy》。[^1]

其中,括號內的日期即為截止日期。

既有實作流程

主管身體力行,親自示範了用手動拖曳的方式,一列一列地將各別事項排列整齊。

提案

我們都知道,不論是Excel或是Google Sheets,都有內建排序功能,只要我們將欄位拆分清楚,就能依日期做排序、或依類型做篩選。我向主管提案可以將既有表格拆分並重構為:《重要公告》《to-do-refactor》

可是提案被否決了。

經多次溝通與其他相關表格優化的feedback後,能夠揣摩並同理主管有意保留部分information asymmetry,以便在upward stakeholder management中保有更高的strategic adaptability與tactical bandwidth。進一步考察,主管與主管的主管雙方皆有相同之需求,並且在任務執行產生偏差或額外成本時,能夠保留clear lines of accountability,以利責任釐清與組織內部追蹤。

用GAS自救

已知《to-do-legacy》乍看五彩斑斕,其實有許多看似bugs,實則不可撼動之features。

身為第一線的基層客服人員,在維持既有features的前提下,我決定使用GAS腳本來模仿Google Sheets內建的排序功能。

首先,建立一個新的按鈕:

function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu("自定義")
    .addItem("依時限排序", "sortRowsByDeadline")
    .addToUi();
}

接著,建立一個空白的欄位並隱藏。將日期extract出來,放進隱藏的欄位:[^2]

function processDatesInRange() {
  const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const startRow = 3; 
  const endRow = activeSheet.getLastRow();
  const columnC = activeSheet.getRange(startRow, 3, endRow - startRow + 1, 1);

  columnC.getValues().forEach((row, index) => {
    const cellValue = row[0];
    const cell = columnC.offset(index, 0);

    const contentInBrackets = extractContentInBrackets(cellValue);
    const cleanedContent = cleanNonNumericCharacters(contentInBrackets);
    const formattedDate = formatDate(cleanedContent);

    if (formattedDate) {
      cell.offset(0, -1).setValue(formattedDate);
    } else {
      cell.offset(0, -1).setValue("");
    }
  });
}

最後,寫一個排序的邏輯:[^3]

/**
 * Sorts rows by Column B in ascending order.
 *
 * @param {number} startRow - The row index to start sorting from (1-based index).
 * @param {number} numRows - The number of rows to include in the sorting range.
 */
function sortRows(startRow, numRows) {
  const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const range = activeSheet.getRange(
    startRow,
    1,
    numRows,
    activeSheet.getLastColumn()
  );
  range.sort({ column: 2, ascending: true });
}
function sortRowsByDeadline() {
  const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  let startRow;
  let numRows;

  processDatesInRange();

  const data = activeSheet.getRange("C:C").getValues();

  for (let i = 2; i < data.length; i++) {
    if (data[i][0] === "待辦清單") {
      startRow = i + 2;
      break;
    }
  }

  const regex = /^[\u4e00-\u9fa5]{2}部門$/; 
  for (let i = startRow - 1; i < data.length; i++) {
    if (regex.test(data[i][0])) {
      numRows = i - (startRow - 1);
      break;
    }
  }

  sortRows(startRow, numRows);
}

如此一來,就可以把每日處理這個大表的瑣碎步驟自動化:

function generateDailyTodoSheet() {
  copySheet();
  deleteGrayRows();
  sortRowsByDeadline();
}

後話

當然,在這個場景,GAS腳本只能讓瑣碎步驟更簡便一點、稍微減少重複作業的機械疲倦感。它還沒辦法如宏大的AI Agents敘事那般,自主判斷並代為與stakeholders溝通,也無法處理cross-functional accountability reallocation或陪我們一起走predefined failure trajectory。

希望今天分享的小故事,可以給讀者一點靈感:很多時候,在實務上存在各種限制導致我們無法使用modern solution,但相信聰明的你一定能找到其他pragmatic workaround。

Annotations

[^1]: 所基原版經大幅簡化後,僅保留一小部分多彩設計之神韻。

[^2]: 現在重看,根本黑歷史,寫得很醜,例如過度拆分出在其他function不會用到的extractContentInBrackets&cleanNonNumericCharacters、沒有考慮到使用者typo的情況、offset無法應對表格欄位變動等等。

[^3]: 是的,程式碼沒辦法完全對應我給的示例表格,請原諒我不想重構這些爛扣。


上一篇
Day13|GAS註解:指引GenAI遵循JSDoc格式
系列文
我只是不想加班:一名客服人員的GAS自救之路14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言