在這篇文章中,我將分享一個利用 Google Apps Script 實現的簡單且強大的更新管理功能。這個功能可以讓使用者在 Google Sheets 中輕鬆管理記錄,並透過直觀的界面進行操作,無需任何額外的插件或工具。

以下為系統的界面:

功能概述

此程式提供了三大主要功能:

  1. 更新記錄:使用者可以將新的更新日期與內容保存至 Google Sheets 中。透過日期選擇器選擇日期,並輸入相應的內容,點擊「保存」即可輕鬆新增更新記錄。
  2. 瀏覽更新記錄:使用者可以瀏覽所有更新過的記錄。每條記錄顯示更新日期和內容,
  3. 購買書的讀者獨家優惠:支援編輯和刪除功能。當點擊「編輯」按鈕時,使用者可以修改日期和內容,並且在修改後點擊「確認」按鈕保存變更。如果需要刪除某條記錄,則可以點擊「刪除」按鈕。

應用場景

這個程式非常適合用於需要定期更新和管理記錄的工作場景,比如:

  • 項目進度更新:記錄項目進度、會議記錄等。
  • 資料管理:管理資料更新、產品版本更新等。
  • 日常工作日誌:對工作日誌進行管理,方便追蹤。

以下為程式分享

code.gs

function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('ℹ️更新管理')
    .addItem('🆙更新', 'showUpdateModal')
    .addItem('📁瀏覽', 'showBrowseModal')
    .addToUi();
}

function showUpdateModal() {
  const html = HtmlService.createHtmlOutputFromFile('UpdateModal')
    .setWidth(800)
    .setHeight(550);
  SpreadsheetApp.getUi().showModalDialog(html, '更新');
}

function showBrowseModal() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('更新');
  const data = sheet.getDataRange().getValues();
  
  // Format and sort data in descending order by date
  const formattedData = data
    .slice(1) // Exclude header row
    .map(row => [formatDate(row[0]), row[1] || '']) // Ensure text format
    .sort((a, b) => new Date(b[0]) - new Date(a[0])); // Sort by date (newest first)

  const template = HtmlService.createTemplateFromFile('BrowseModal');
  template.data = formattedData;
  
  const html = template.evaluate().setWidth(800).setHeight(650);
  SpreadsheetApp.getUi().showModalDialog(html, '瀏覽');
}


function formatDate(date) {
  if (date instanceof Date) {
    return Utilities.formatDate(date, Session.getScriptTimeZone(), "yyyy/MM/dd");
  }
  return date; // Return as is if not a valid date
}

function saveData(date, content) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('更新');

  // Get the last row with data
  const lastRow = sheet.getLastRow();

  // Fix timezone issue by adding time offset
  let selectedDate = new Date(date);
  selectedDate.setHours(selectedDate.getHours() + 8); // Adjust for time zone differences (UTC+8 for Taiwan/HK)

  // Convert to yyyy/MM/dd format
  let formattedDate = Utilities.formatDate(selectedDate, Session.getTimeZone(), "yyyy/MM/dd");

  // Insert the formatted date and content in the next empty row
  sheet.getRange(lastRow + 1, 1).setValue(formattedDate);
  sheet.getRange(lastRow + 1, 1).setNumberFormat("yyyy/MM/dd"); // Ensure format
  sheet.getRange(lastRow + 1, 2).setValue(content);
}

UpdateModal.html

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* General Styling */
      body {
        font-family: 'Poppins', sans-serif;
        background-color: #e3e6ed;
        color: #2a3f5f;
        padding: 20px;
        margin: 0;
      }

      .container {
        max-width: 100%;
        margin: auto;
        background: linear-gradient(135deg, #d1d6e0, #b0b8c7);
        padding: 45px;
        border-radius: 12px;
        box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2);
        border: 1px solid #9aa3b5;
      }

  
    </style>
    <script>
      // Function to set today's date as the default value in the date picker
      function setDefaultDate() {
        const today = new Date();
        const yyyy = today.getFullYear();
        const mm = String(today.getMonth() + 1).padStart(2, '0'); // Months start from 0
        const dd = String(today.getDate()).padStart(2, '0');
        document.getElementById("date").value = `${yyyy}-${mm}-${dd}`;
      }

      function saveData() {
        const date = document.getElementById("date").value;
        const content = document.getElementById("content").value;

        if (!date || !content) {
          alert("請輸入日期與內容!");
          return;
        }

        google.script.run.saveData(date, content);
        google.script.host.close();
      }

      // Set default date when the modal loads
      window.onload = setDefaultDate;
    </script>
  </head>
  <body>
    <div class="container">
      <h3>新增更新</h3>
      <div class="form-group">
        <label for="date">日期:</label>
        <input type="date" id="date" class="form-control" required>
      </div>

      <div class="form-group">
        <label for="content">內容:</label>
        <textarea id="content" class="form-control" rows="4" required></textarea>
      </div>

      <div class="button-container">
        <button class="save-button" onclick="saveData()">儲存</button>
        <button class="cancel-button" onclick="google.script.host.close()">取消</button>
      </div>
    </div>
  </body>
</html>

BrowseModal.html

<!DOCTYPE html>
<html>
  <head>
    <!-- Include Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Include DataTables CSS and JS -->
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.css">
    <script type="text/javascript" charset="utf8" src="https://code.jquery.com/jquery-3.5.1.js"></script>
    <script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.js"></script>
    
   <script>
  $(document).ready(function() {
    $('#dataTable').DataTable({
      paging: true,
      searching: true,
      info: false,
      order: [[0, "desc"]],
      language: {
        "processing": "處理中...",
        "search": "搜尋:",
        "lengthMenu": "顯示 _MENU_ 筆資料",
        "info": "顯示第 _START_ 至 _END_ 筆,共 _TOTAL_ 筆",
        "infoEmpty": "沒有資料可顯示",
        "infoFiltered": "(從 _MAX_ 筆資料篩選)",
        "loadingRecords": "載入中...",
        "zeroRecords": "沒有符合的資料",
        "paginate": {
          "first": "首頁",
          "last": "末頁",
          "next": "下一頁",
          "previous": "上一頁"
        }
      }
    });

 
  });
</script>


    <style>
      .icon {
        cursor: pointer;
        font-size: 16px;
        transition: 0.3s;
        margin-right: 8px;
      }

    </style>
  </head>
  <body>
    <div class="container mt-3">
      <h3>更新記錄</h3>
      <table id="dataTable" class="table table-striped">
        <thead>
          <tr>
            <th>日期</th>
            <th>內容</th>
          
          </tr>
        </thead>
        <tbody>
          <? for (var i = data.length - 1; i >= 0; i--) { ?>
            <tr>
              <td><?= data[i][0] ?></td>
              <td><?= data[i][1] ?></td>
           
            </tr>
          <? } ?>
        </tbody>
      </table>
      <button class="btn btn-secondary" onclick="google.script.host.close()">關閉</button>
    </div>
  </body>
</html>

發表迴響