🧾Vue + SheetJS 匯出 Excel 錯誤:「wsFour['!merges'] is undefined」完整解析與一行修好
- 取得連結
- X
- 以電子郵件傳送
- 其他應用程式
摘要
在用 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
仍是
undefined
,push
當然會炸掉。
快速修復:加一行初始化
在建立
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"] = []
(或
??=
)加上,就能讓整個匯出流程穩定運作。也建議把這樣的初始化動作納入你專案的共用範本或工具函式,之後新增任何工作表都不會再踩雷。
留言
張貼留言