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



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

應用場景
這個程式非常適合用於需要定期更新和管理記錄的工作場景,比如:
- 項目進度更新:記錄項目進度、會議記錄等。
- 資料管理:管理資料更新、產品版本更新等。
- 日常工作日誌:對工作日誌進行管理,方便追蹤。
以下為程式分享
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>
