🧾Vue + SheetJS 匯出 Excel 錯誤:「wsFour['!merges'] is undefined」完整解析與一行修好

 

摘要

在用 SheetJS (xlsx) 產生 Excel 工作表時,如果你呼叫:

wsFour["!merges"].push({...})

從未初始化 wsFour["!merges"],就會噴:

TypeError: can't access property "push", wsFour['!merges'] is undefined

解法:在使用前先初始化:

const wsFour = XLSX.utils.aoa_to_sheet([headerRow41, headerRow42]); wsFour["!merges"] = []; // ✅ 先建立 merges 陣列 // 或:wsFour["!merges"] ??= [];

背景:我們在做什麼?

這個頁面(PageFour.vue)的任務是把多個資料表(2、3、4、6、7、8、9、10)匯出成 多工作表 Excel
技術棧:

  • Vue 3(<script setup>

  • Pinia(共用版本 globalApsVersion

  • Axios(封裝為 apiClient 呼叫 .NET 後端)

  • SheetJS(xlsx,動態建立工作表、欄寬、合併儲存格與數字格式)

在實作 第 4 張工作表(4.DemandPegLog) 時,主控台報錯:

TypeError: can't access property "push", wsFour['!merges'] is undefined

症狀:為什麼只有第 4 張表壞掉?

其他工作表(例如 2、3)都有先寫:

wsTwo["!merges"] = [ ... ] wsThree["!merges"] = [ ... ]

但在 第 4 張表(變數 wsFour)我們只做了:

const wsFour = XLSX.utils.aoa_to_sheet([headerRow41, headerRow42]); // (這裡少了初始化 !merges)

當資料筆數為 0、我們想在 A3 合併成一行提示文字時:

wsFour["!merges"].push({ s:{r:2,c:0}, e:{r:2,c:headerRow42.length - 1} })

因為 !merges 仍是 undefinedpush 當然會炸掉。


快速修復:加一行初始化

在建立 wsFour立即初始化 !merges

// 1) 建立空白表,帶入兩排表頭 const wsFour = XLSX.utils.aoa_to_sheet([headerRow41, headerRow42]); // 2) ✅ 必須先初始化 merges,之後才能 push wsFour["!merges"] = []; // 或 wsFour["!merges"] ??= []; // 3) 欄寬設定 wsFour["!cols"] = headerRow42.map(h => ({ wch: Math.ceil(h.length * 2) })); if (dataFour.length === 0) { // 4) 無資料 → 放入一行訊息並合併整列 XLSX.utils.sheet_add_aoa(wsFour, [["此版本無使用DemandPegLog"]], { origin: "A3" }); wsFour["!merges"].push({ s: { r: 2, c: 0 }, e: { r: 2, c: headerRow42.length - 1 } }); } else { // 5) 有資料 → 直接寫入內容並做數字格式化 XLSX.utils.sheet_add_json(wsFour, dataFour, { skipHeader: true, origin: "A3" }); // ...依需求設定格式... }

這就是整個錯誤的核心:先建立陣列再 push


為什麼 SheetJS 需要手動初始化 !merges

  • XLSX.utils.aoa_to_sheet 會回傳一個工作表物件,但不會預設建立 !merges

  • 合併儲存格本質上就是一個陣列:每個元素是一個範圍 { s: {r,c}, e: {r,c} }

  • 你要使用 push() 之前,一定要先把它變成空陣列或放入初始合併範圍。


擴充與最佳實務

1) 始終在建立工作表後初始化 !merges

const ws = XLSX.utils.aoa_to_sheet([row1, row2]); ws["!merges"] = []; // 習慣性先來這行

2) 使用保險寫法避免重複初始化

ws["!merges"] ??= [];

3) 註解與表頭數量保持一致

你在註解中寫了「合併 A3:H3(8 欄)」但實際用 headerRow42.length 計算,更新註解避免之後的人被誤導。

4) 第一、二列表頭欄數一致(選配)

headerRow41(第一列)現在只有 7 欄,headerRow42 有 33 欄。雖然不會報錯,但若第一列要做橫向合併或對齊,建議讓兩列欄數一致,或明確在第一列做合併範圍設定,視覺會更整齊。

5) 抽共用工具函式(更乾淨)

function ensureMerges(ws) { ws["!merges"] ??= []; return ws["!merges"]; } function mergeRow(ws, rowIndex, fromCol, toCol) { ensureMerges(ws).push({ s:{ r: rowIndex, c: fromCol }, e:{ r: rowIndex, c: toCol } }); }

之後就能寫:

mergeRow(wsFour, 2, 0, headerRow42.length - 1);

完整脈絡(發生在何處)

  • Vue 3 頁面 PageFour.vue,匯出多工作表 Excel。

  • 2、3、6、7、8、9、10 張工作表都有初始化 !merges 或沒有在空資料時呼叫 push()

  • 只有 4.DemandPegLog 在「空資料分支」需要合併第三列做提示,卻忘了初始化 → 觸發 TypeError。


結語

這類錯誤不是 SheetJS 的怪問題,而是少了一行初始化導致的正常行為。
只要把 wsFour["!merges"] = [](或 ??=)加上,就能讓整個匯出流程穩定運作。也建議把這樣的初始化動作納入你專案的共用範本或工具函式,之後新增任何工作表都不會再踩雷。

留言

這個網誌中的熱門文章

🛠【ASP.NET Core + Oracle】解決 ORA-00904 "FALSE": 無效的 ID 錯誤與資料欄位動態插入顯示問題

🛠【實戰排除教學】從 VS Code 的 _logger 錯誤,到 PowerShell 找不到 npm/serve,再到 Oracle ORA-03135 連線中斷——一次搞懂!

🔎如何在 Oracle PL/SQL 儲存過程中為文字欄位加入換行符號(CHR(10))——以 Updlcmremark 為例