Google Sheets日志脚本如何自动按月份、日期分层分组新增行并折叠
实现方案
这个需求完全可以实现,直接与你现有RECORD_HISTORY()函数整合是最优方案,无需额外设置触发器,所有逻辑都可以在时间触发器调用记录函数时自动完成。
核心实现思路
- 每次追加新日志行后,先提取当前行的日期(按GMT+01:00时区处理),与上一行的日志日期对比
- 若检测到跨月,自动插入月份标题行(如
AUGUST 2024),作为顶层一级分组的头部 - 若检测到跨天,自动插入日期标题行(如
2024-08-15),作为二级子分组的头部 - 新生成的分组创建完成后自动折叠,用户打开表格时默认只看到月份分组,展开后可以看到日期分组,再展开就能看到对应时段的日志行
完整整合代码
// 时区配置,按你的需求设置为GMT+01:00 const TIME_ZONE = 'GMT+01:00'; const HISTORY_SHEET_NAME = 'HISTORY'; function RECORD_HISTORY() { let historySheet = getSheetWithName(HISTORY_SHEET_NAME); if (historySheet == null) { historySheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(HISTORY_SHEET_NAME, 1); // 新创建的表默认写入表头,可根据实际表头调整内容 historySheet.appendRow(['时间', '列2', '列3', '列4', '列5', '列6', '列7', '列8', '列9']); } // 读取要写入的原始数据 const range = historySheet.getRange(2, 1, 1, 9); const values = range.getValues(); const currentDate = new Date(); values[0][0] = currentDate; // 获取最后一行现有数据,判断是否需要插入标题行 const lastRow = historySheet.getLastRow(); let needInsertMonth = false; let needInsertDate = false; if (lastRow > 1) { // 读取上一行的时间值 const prevCellValue = historySheet.getRange(lastRow, 1).getValue(); if (prevCellValue instanceof Date) { // 格式化日期对比年月、日 const prevMonth = Utilities.formatDate(prevCellValue, TIME_ZONE, 'YYYY-MM'); const currentMonth = Utilities.formatDate(currentDate, TIME_ZONE, 'YYYY-MM'); const prevDay = Utilities.formatDate(prevCellValue, TIME_ZONE, 'YYYY-MM-dd'); const currentDay = Utilities.formatDate(currentDate, TIME_ZONE, 'YYYY-MM-dd'); needInsertMonth = prevMonth !== currentMonth; needInsertDate = prevDay !== currentDay; } else { // 上一行是标题行,默认需要插入新日期 needInsertDate = true; } } else { // 表为空首次写入,需要插入月份和日期标题 needInsertMonth = true; needInsertDate = true; } // 插入月份标题行(跨月时触发) if (needInsertMonth) { const monthTitle = Utilities.formatDate(currentDate, TIME_ZONE, 'MMMM').toUpperCase(); historySheet.appendRow([monthTitle, '', '', '', '', '', '', '', '']); const newMonthRow = historySheet.getLastRow(); // 月份标题行样式设置,可自行调整 historySheet.getRange(newMonthRow, 1).setFontWeight('bold').setFontSize(12); } // 插入日期标题行(跨天时触发) if (needInsertDate) { const dateTitle = Utilities.formatDate(currentDate, TIME_ZONE, 'YYYY-MM-dd').toUpperCase(); historySheet.appendRow([dateTitle, '', '', '', '', '', '', '', '']); const newDateRow = historySheet.getLastRow(); // 日期标题行样式设置,可自行调整 historySheet.getRange(newDateRow, 1).setFontWeight('bold'); } // 追加日志行 historySheet.appendRow(values[0]); const newLogRow = historySheet.getLastRow(); // 处理两级分组逻辑 if (needInsertDate) { // 新建日期分组 historySheet.getRange(newLogRow, 1).shiftRowGroupDepth(1); // 自动折叠日期分组 historySheet.getRowGroup(newLogRow, 1).collapse(); } else { // 加入已有日期分组 historySheet.getRange(newLogRow, 1).shiftRowGroupDepth(1); } if (needInsertMonth) { // 新建月份分组 const monthStartRow = needInsertDate ? newLogRow - 1 : newLogRow; historySheet.getRange(monthStartRow, 1).shiftRowGroupDepth(2); // 自动折叠月份分组 historySheet.getRowGroup(monthStartRow, 2).collapse(); } else { // 加入已有月份分组 historySheet.getRange(newLogRow, 1).shiftRowGroupDepth(2); } } function getSheetWithName(name) { const sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets(); for (let idx in sheets) { if (sheets[idx].getName() === name) { return sheets[idx]; } } return null; }
注意事项
- 若现有HISTORY表的表头结构与默认初始化表头不同,可自行调整代码中初始化表头的内容
- 月份、日期标题的展示格式可修改
Utilities.formatDate的格式化参数自定义 - 所有操作均在时间触发器调用函数时自动完成,无需用户做任何手动操作,打开表格即可看到已折叠好的两级分组结构
内容的提问来源于stack exchange,提问作者Verminous
相关产品推荐
相关产品推荐

