From 641c1c4387fcddd68ddf56fb621f514a9b1f6d89 Mon Sep 17 00:00:00 2001 From: Cody8722 <150524916+Cody8722@users.noreply.github.com> Date: Fri, 4 Sep 2026 23:35:44 +0800 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=E5=8C=AF=E5=87=BA=E6=AA=94?= =?UTF-8?q?=E5=90=8D=E6=94=B9=E7=94=A8=E3=80=8C=E7=8F=AD=E8=A1=A8=E5=90=8D?= =?UTF-8?q?=E7=A8=B1=5F=E6=97=A5=E6=9C=9F=E7=AF=84=E5=9C=8D=5F=E6=99=82?= =?UTF-8?q?=E9=96=93=E6=88=B3=E8=A8=98=E3=80=8D=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E6=AA=94=E5=90=8D=E4=BA=92=E7=9B=B8=E8=A6=86?= =?UTF-8?q?=E8=93=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PDF、Excel、人員 Excel 三個匯出過去都用固定檔名(班表.pdf、班表.xlsx、 人員班表.xlsx),每次匯出瀏覽器只能自動加 (1)(2)... 分辨,看檔名完全 不知道是哪次、哪份班表匯出的。 新增 exportFilename.js 的 buildExportFilename(),統一組出 「已儲存班表名稱(沒有就用預設)_ 週次日期範圍 _ 匯出時間戳記」的檔名, 三個匯出函式共用同一份邏輯,避免各自重複實作。 已用真實瀏覽器操作驗證三種情況:未儲存班表時的 Excel 匯出(班表_ 0907~0918_時間戳.xlsx)、已儲存班表「中午」時的 PDF 匯出(中午_ 0907~0918_時間戳.pdf)、人員 Excel 匯出(中午_人員_0907~0918_時間戳. xlsx),檔名皆正確。backend 229 個、frontend 68 個測試(新增 6 個) 與 production build 皆正常。 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0117v1Qxx7foQEH4cd6pfnLS --- CLAUDE.md | 1 + frontend/src/features/schedule/pdfExport.js | 3 +- .../src/features/schedule/personnelView.js | 3 +- frontend/src/main.js | 3 +- frontend/src/utils/exportFilename.js | 30 ++++++++++++ frontend/src/utils/exportFilename.test.js | 48 +++++++++++++++++++ 6 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 frontend/src/utils/exportFilename.js create mode 100644 frontend/src/utils/exportFilename.test.js diff --git a/CLAUDE.md b/CLAUDE.md index 6e6b675..2f62564 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,6 +117,7 @@ frontend/ capacityStatus.js # 容量/填補率狀態計算 connectionStatus.js # 後端連線狀態顯示 excelDownload.js # ExcelJS Workbook → Blob 觸發瀏覽器下載 + exportFilename.js # 組合匯出檔名(班表名稱_日期範圍_時間戳記),PDF/Excel/人員Excel 共用 features/schedule/ personnelView.js # 人員月曆視圖 + Excel 下載 diffSummary.js # 變更摘要 Modal(buildDiff、共用 renderDiffSection) diff --git a/frontend/src/features/schedule/pdfExport.js b/frontend/src/features/schedule/pdfExport.js index f45672b..bdadd1a 100644 --- a/frontend/src/features/schedule/pdfExport.js +++ b/frontend/src/features/schedule/pdfExport.js @@ -4,6 +4,7 @@ import { api } from '../../api/client.js'; import { showToast } from '../../ui/toast.js'; import { renderEditableSchedule } from './editableSchedule.js'; import { renderPersonTaskStatsHtml } from './personTaskStats.js'; +import { buildExportFilename } from '../../utils/exportFilename.js'; export async function printSchedule() { const exportData = getExportData(); @@ -196,7 +197,7 @@ export async function exportToPdf() { activeStyle = null; } - pdf.save('班表.pdf'); + pdf.save(buildExportFilename(exportData, 'pdf')); } catch (err) { console.error('html2canvas failed:', err); showToast('PDF 導出失敗,請稍後再試', 'error'); diff --git a/frontend/src/features/schedule/personnelView.js b/frontend/src/features/schedule/personnelView.js index c073a06..7398a40 100644 --- a/frontend/src/features/schedule/personnelView.js +++ b/frontend/src/features/schedule/personnelView.js @@ -2,6 +2,7 @@ import { escapeHtml } from '../../utils/escapeHtml.js'; import { getAppState } from '../../state/appState.js'; import { downloadWorkbook } from '../../utils/excelDownload.js'; import { getExportData } from './exportWeekFilter.js'; +import { buildExportFilename } from '../../utils/exportFilename.js'; export const TASK_COLORS = [ 'pv-task-0', @@ -145,5 +146,5 @@ export const exportPersonnelExcel = async () => { ]); }); - await downloadWorkbook(wb, '人員班表.xlsx'); + await downloadWorkbook(wb, buildExportFilename(data, 'xlsx', { suffix: '_人員' })); }; diff --git a/frontend/src/main.js b/frontend/src/main.js index d13bd40..f2befbd 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -59,6 +59,7 @@ import { computePersonTaskStats, initPersonTaskStats } from './features/schedule // ── Utils ── import { checkConnectionStatus } from './utils/connectionStatus.js'; import { downloadWorkbook } from './utils/excelDownload.js'; +import { buildExportFilename } from './utils/exportFilename.js'; // ───────────────────────────────────────────── // DOM 元素集合(在 DOMContentLoaded 後填入) @@ -590,7 +591,7 @@ document.addEventListener('DOMContentLoaded', async () => { } } - await downloadWorkbook(wb, '班表.xlsx'); + await downloadWorkbook(wb, buildExportFilename(exportData, 'xlsx')); }); // PDF diff --git a/frontend/src/utils/exportFilename.js b/frontend/src/utils/exportFilename.js new file mode 100644 index 0000000..c4ab630 --- /dev/null +++ b/frontend/src/utils/exportFilename.js @@ -0,0 +1,30 @@ +import { getCurrentScheduleName } from '../state/appState.js'; + +const pad = (n) => String(n).padStart(2, '0'); + +const formatTimestamp = (date) => + `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`; + +/** + * 組合匯出檔名:已儲存班表名稱(若有,否則用 fallbackBase)_ 週次日期範圍 _ 匯出時間戳記.副檔名 + * 避免每次匯出都用同一個固定檔名,導致瀏覽器自動加 (1)(2)... 分不清是哪次匯出的內容。 + * @param {Array} exportData 目前要匯出的週資料(用來取日期範圍),可為 null/空陣列 + * @param {string} ext 副檔名(不含點),例如 'pdf'、'xlsx' + * @param {{ suffix?: string, fallbackBase?: string, now?: Date }} [options] + */ +export const buildExportFilename = (exportData, ext, options = {}) => { + const { suffix = '', fallbackBase = '班表', now = new Date() } = options; + const scheduleName = getCurrentScheduleName(); + const parts = [(scheduleName || fallbackBase) + suffix]; + + if (exportData && exportData.length > 0) { + const firstRange = exportData[0].dateRange || ''; + const lastRange = exportData[exportData.length - 1].dateRange || ''; + const start = firstRange.split(' - ')[0]?.trim().replace(/\//g, ''); + const end = (lastRange.split(' - ')[1] || lastRange.split(' - ')[0])?.trim().replace(/\//g, ''); + if (start && end) parts.push(`${start}~${end}`); + } + + parts.push(formatTimestamp(now)); + return `${parts.join('_')}.${ext}`; +}; diff --git a/frontend/src/utils/exportFilename.test.js b/frontend/src/utils/exportFilename.test.js new file mode 100644 index 0000000..0548cf0 --- /dev/null +++ b/frontend/src/utils/exportFilename.test.js @@ -0,0 +1,48 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { buildExportFilename } from './exportFilename.js'; +import { setCurrentScheduleName } from '../state/appState.js'; + +const makeWeek = (dateRange) => ({ dateRange }); +const fixedNow = new Date(2026, 8, 4, 14, 30, 22); // 2026-09-04 14:30:22 + +describe('buildExportFilename', () => { + beforeEach(() => { + setCurrentScheduleName(null); + }); + + it('沒有已儲存班表名稱時用 fallbackBase 當開頭', () => { + const name = buildExportFilename([makeWeek('09/07 - 09/11')], 'pdf', { now: fixedNow }); + expect(name).toBe('班表_0907~0911_20260904-143022.pdf'); + }); + + it('有已儲存班表名稱時優先使用該名稱', () => { + setCurrentScheduleName('中午'); + const name = buildExportFilename([makeWeek('09/07 - 09/11')], 'pdf', { now: fixedNow }); + expect(name).toBe('中午_0907~0911_20260904-143022.pdf'); + }); + + it('多週時日期範圍取第一週開始到最後一週結束', () => { + const data = [makeWeek('09/07 - 09/11'), makeWeek('09/14 - 09/18'), makeWeek('09/21 - 09/25')]; + const name = buildExportFilename(data, 'xlsx', { now: fixedNow }); + expect(name).toBe('班表_0907~0925_20260904-143022.xlsx'); + }); + + it('suffix 會接在名稱後面(人員 Excel 用)', () => { + const name = buildExportFilename([makeWeek('09/07 - 09/11')], 'xlsx', { + suffix: '_人員', + now: fixedNow, + }); + expect(name).toBe('班表_人員_0907~0911_20260904-143022.xlsx'); + }); + + it('沒有匯出資料時仍回傳合理檔名(只含名稱與時間戳)', () => { + const name = buildExportFilename(null, 'pdf', { now: fixedNow }); + expect(name).toBe('班表_20260904-143022.pdf'); + }); + + it('沒有匯出資料但有已儲存班表名稱時使用該名稱', () => { + setCurrentScheduleName('中午'); + const name = buildExportFilename([], 'pdf', { now: fixedNow }); + expect(name).toBe('中午_20260904-143022.pdf'); + }); +}); From 0dbe2cf7708e872d519efdaec2b7c5492f52d1f9 Mon Sep 17 00:00:00 2001 From: Cody8722 <150524916+Cody8722@users.noreply.github.com> Date: Sat, 5 Sep 2026 10:29:20 +0800 Subject: [PATCH 02/19] =?UTF-8?q?fix:=20=E5=8C=AF=E5=87=BA=E6=8C=89?= =?UTF-8?q?=E9=88=95=E5=8A=A0=E4=B8=8A=E9=98=B2=E9=80=A3=E9=BB=9E=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=90=8C=E6=99=82=E8=A7=B8=E7=99=BC=E5=85=A9?= =?UTF-8?q?=E5=80=8B=E5=8C=AF=E5=87=BA=E6=B5=81=E7=A8=8B=E4=BA=92=E7=9B=B8?= =?UTF-8?q?=E5=B9=B2=E6=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 除錯 PDF 匯出問題時親自撞到:PDF/Excel/人員 Excel 三個匯出按鈕點擊後 沒有任何 disable 或 loading 提示,匯出過程(尤其 PDF 要跑好幾秒 html2canvas)使用者容易誤以為沒反應而多點幾下,導致兩個匯出流程同時 執行、共用同一份注入的