2024年10月27日 星期日

運用 Google Apps Script 合併列印學習扶助通知單

原本是請學校教學組利用 AutoCrat 的外掛程式來合併列印學習扶助通知單的,
但教學組發現步驟好像有點多,詢問是否可以再簡化合併列印步驟!
想不到有了之前設計合併列印代課老師簽到單的經驗後,
想說把開學前教學組要發的學習扶助調查通知單也一併請 Gemini 設計
Gemini 果然沒讓人失望,趕快來看看如何使用吧!



🎯 程式使用方法:
需準備一個 Google試算表Google試算表ID


👉👉 Google 試算表範本下載 👈👈

🎯比較要注意的地方就是這邊要跟試算表的欄位順序一致!
// --- 定義 placeholder 陣列 --- 
var placeholders = ['{{姓名}}','{{年}}', '{{班}}', '{{座號}}', '{{班別}}', '{{時間}}']; // 依照試算表欄位順序設定

二個 Google文件 檔案:Google文件範本ID套印目標Google文件ID
(🎯 通知單範本檔案、🎯 請自行設定好一個設定好邊界的空白套印目標檔案)




// --- 設定全域變數 ---
var spreadsheetId = '試算表ID';
var templateDocumentId = '範本文件ID';
var targetDocumentId = '目標文件ID';

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('🚀自訂選單')
    .addItem('📄開啟範本文件', 'openTemplateDocumentLink')
    .addSeparator()
    .addItem('🎯合併所有資料', 'mergeDataToPages')
    .addItem('🗑️刪除文件內容', 'clearTargetDocument')
    .addToUi();
}


function mergeDataToPages() {
  // --- 讀取試算表所有資料 ---
  var ss = SpreadsheetApp.openById(spreadsheetId);
  var sheet = ss.getSheetByName('工作表1');
  var data = sheet.getRange('A2:Z' + sheet.getLastRow()).getValues(); // 讀取 A 到 Z 欄位
  var placeholders = sheet.getRange('A1:Z1').getValues()[0]; // 讀取第一列作為 placeholders

  // --- 錯誤處理:檢查檔案是否存在 ---
  try {
    SpreadsheetApp.openById(spreadsheetId);
    DriveApp.getFileById(templateDocumentId);
    DocumentApp.openById(targetDocumentId);
  } catch (e) {
    SpreadsheetApp.getActiveSpreadsheet().toast('檔案不存在,請確認 ID 是否正確', '錯誤');
    return;
  }

  // --- 取得目標文件 ---
  var targetDoc = DocumentApp.openById(targetDocumentId);
  var targetBody = targetDoc.getBody();

  // --- 取得 Google 文件範本檔案 ---
  var templateFile = DriveApp.getFileById(templateDocumentId);
  var templateDoc = DocumentApp.openById(templateFile.getId());
  var templateBody = templateDoc.getBody();

  // --- 移除範本文件最上方的空白段落 ---
  var firstElement = templateBody.getChild(0);
  if (firstElement.getType() === DocumentApp.ElementType.PARAGRAPH && firstElement.getText().trim() === "") {
    templateBody.removeChild(firstElement);
  }

  // --- 顯示合併中視窗 ---
  var htmlOutput = HtmlService.createHtmlOutput('<p>資料合併中,請稍候...</p>');
  var ui = SpreadsheetApp.getUi();
  var dialog = ui.showModelessDialog(htmlOutput, '合併中');

  // --- 顯示合併進度 ---
  SpreadsheetApp.getActiveSpreadsheet().toast('開始合併...', '合併進度');

  // --- 定義 placeholder 陣列 ---
  var placeholders = ['{{姓名}}','{{年}}', '{{班}}', '{{座號}}', '{{班別}}', '{{時間}}']; // 依照試算表欄位順序設定

  // --- 批次處理資料 ---
  var batchSize = 10; // 設定每個批次處理的資料筆數
  for (var i = 0; i < data.length; i += batchSize) {
    var dataBatch = data.slice(i, i + batchSize); // 取得目前的資料批次

    // --- 處理目前的資料批次 ---
    for (var j = 0; j < dataBatch.length; j++) {

      // --- 複製範本內容到目標文件 ---
      var templateElements = templateBody.getNumChildren();
      for (var k = 0; k < templateElements; k++) {
        var element = templateBody.getChild(k).copy();
        // --- 立即將資料複製到目標文件 ---
        if (element.getType() === DocumentApp.ElementType.TABLE) {
          var table = element;
          for (var r = 0; r < table.getNumRows(); r++) {
            for (var c = 0; c < table.getRow(r).getNumCells(); c++) {
              var cell = table.getRow(r).getCell(c);
              for (var l = 0; l < placeholders.length; l++) {
                cell.replaceText(placeholders[l], dataBatch[j][l]);
              }
            }
          }
          targetBody.appendTable(table);
        } else if (element.getType() === DocumentApp.ElementType.PARAGRAPH) {
          var paragraph = element;
          for (var l = 0; l < placeholders.length; l++) {
            paragraph.replaceText(placeholders[l], dataBatch[j][l]);
          }
          targetBody.appendParagraph(paragraph);
        } else if (element.getType() === DocumentApp.ElementType.LIST_ITEM) {
          var listItem = element;
          for (var l = 0; l < placeholders.length; l++) {
            listItem.replaceText(placeholders[l], dataBatch[j][l]);
          }
          targetBody.appendListItem(listItem);
        } else {
          Logger.log('未處理的元素類型:' + element.getType());
        }
      }

      // --- 在每個資料列的最後一筆資料處理完畢後插入分頁符號 ---
      if (j < dataBatch.length - 1) {
        targetBody.appendPageBreak();
      }
    }
    // --- 更新合併進度 ---
    SpreadsheetApp.getActiveSpreadsheet().toast('已合併 ' + (i + dataBatch.length) + '/' + data.length, '合併進度');
  }

  // --- 移除所有空白頁 ---
  var numChildren = targetBody.getNumChildren();
  for (var i = numChildren - 1; i >= 0; i--) {
    var child = targetBody.getChild(i);
    if (child.getType() === DocumentApp.ElementType.PAGE_BREAK) {
      var nextChild = targetBody.getChild(i - 1);
      if (nextChild && nextChild.getType() === DocumentApp.ElementType.PAGE_BREAK) {
        targetBody.removeChild(child);
      }
    }
  }

  // --- 合併完成訊息 ---
  SpreadsheetApp.getActiveSpreadsheet().toast('合併完成!', '合併進度');

  // --- 關閉「合併中」視窗 ---
  ui.alert('合併完成');

  // --- 開啟目標檔案的連結 ---
  var targetFileLink = `https://docs.google.com/document/d/${targetDocumentId}/edit`;

  // --- 建立 HTML 輸出並開啟目標檔案視窗 ---
  var htmlOutput = HtmlService.createHtmlOutput(`
    <script>
      window.open('${targetFileLink}', '_blank');
      google.script.host.close();
    </script>
  `);

  // --- 顯示開啟目標檔案的對話方塊 ---
  ui.showModalDialog(htmlOutput, '開啟目標檔案');
}

function openTemplateDocumentLink() {
  var templateFileLink = `https://docs.google.com/document/d/${templateDocumentId}/edit`;

  var htmlOutput = HtmlService.createHtmlOutput(`
    <script>
      window.open('${templateFileLink}', '_blank');
      google.script.host.close();
    </script>
  `);
  SpreadsheetApp.getUi().showModalDialog(htmlOutput, '開啟範本檔案');
}

function clearTargetDocument() {
  // --- 錯誤處理:檢查檔案是否存在 ---
  try {
    DocumentApp.openById(targetDocumentId); // 修正此行
  } catch (e) {
    SpreadsheetApp.getActiveSpreadsheet().toast('檔案不存在,請確認 ID 是否正確', '錯誤');
    return;
  }
  var targetDoc = DocumentApp.openById(targetDocumentId);
  var targetBody = targetDoc.getBody();
  targetBody.clear();
  SpreadsheetApp.getActiveSpreadsheet().toast('目標文件已清空!', '完成');
  Browser.msgBox('目標文件已清空!');
}




快來試試吧~~






沒有留言:

張貼留言

歡迎大家一起留言討論!