🌐Vue 3 表格數值靠右對齊與 is not defined 錯誤完整解法(含 Excel 匯出)
摘要
在 Vue 3 專案中,我們想讓表格中的「數值欄位」在 UI 上靠右對齊,並在匯出 Excel
時維持數字格式(含千分位)。過程中遇到兩個常見錯誤:rightAlignColumns is not defined
與
numericFields is not defined
。本文用工程師角度、淺顯語言,帶你一步步修正「變數作用域(scope)」問題,並提供可直接貼上的程式碼範例與檢查清單。
什麼情境會遇到?
-
你使用 Vue 3 + Composition API 建一個可篩選、可合併列的寬表格
-
表格資料 keys 都被轉成大寫(例如
Material2
會變成MATERIAL2
) -
想讓數值欄位在 UI 靠右,同時 Excel 匯出也保持數字型別與千分位格式
-
在 template 使用
isNumericColumn(hdr)
判斷時,主控台卻噴:-
ReferenceError: rightAlignColumns is not defined
-
或
ReferenceError: numericFields is not defined
-
問題根因:變數作用域(Scope)
-
你在
exportToExcel()
函式裡面宣告numericFields
,所以只有那個函式看得到它。 -
但 template 在 render 時會呼叫
isNumericColumn()
,如果isNumericColumn()
去用到numericFields
(或rightAlignColumns
)而 它不在同一個作用域,就會噴is not defined
。
**重點:**把「數值欄位集合」提升到 檔案最頂層(module top-level),讓 UI 與 Excel 共用同一組設定,錯誤自然消失。
解法總覽
-
在
<script setup>
前半段建立一個大寫的欄位集合NUMERIC_COLS
(Set)。 -
寫
isNumericColumn(colKey)
:判斷欄位是否需靠右。 -
在 template 的
<td>
綁:class
,數值欄位套用.text-right
。 -
在 CSS 新增
.text-right { text-align: right !important; }
。 -
在
exportToExcel()
中用const numericFields = [...NUMERIC_COLS]
共用同一組欄位,再設定 Excel 數字格式與千分位。
可直接貼用的程式碼片段
只列出與本主題相關的關鍵部分;其餘原程式維持不變即可。
1)
<script setup>
:統一的數值欄位集合與判斷函式
<script setup lang="js"> // === 數值欄位(統一大寫,UI靠右 + Excel數字格式共用) === const NUMERIC_COLS = new Set([ 'DEMAND_QTY', 'DEAL_QTY', 'TFT2', 'TFT5', 'LCM2', 'LCM5', 'MATERIAL2', 'MATERIAL5' // 注意:用大寫 MATERIAL* ]) // 給 template 用:判斷欄位是否靠右 function isNumericColumn(colKey) { return NUMERIC_COLS.has(String(colKey).toUpperCase()) } </script>
2) <template>
:在
<td>
綁定靠右 class
<td v-if="rowSpans[rowIndex][hdr] > 0" :rowspan="rowSpans[rowIndex][hdr]" :class="{ 'material-multiline': hdr === 'MATERIAL1', 'text-right': isNumericColumn(hdr) }" > {{ row[hdr] }} </td>
3)
<style scoped>
:定義靠右樣式
.text-right { text-align: right !important; }
4)
exportToExcel()
:共用同一組欄位並套用數字格式
function exportToExcel() { // 與 UI 共用的欄位集合,轉成陣列以便 .includes() const numericFields = [...NUMERIC_COLS]; // 將字串千分位轉回數字,避免 Excel 變文字 const bodyRows = dataToExport.map((row) => tableHeaders.value.map((key) => { let v = row[key] ?? '' if (numericFields.includes(key) && typeof v === 'string') { v = parseFloat(v.replace(/,/g, '')) || 0 } return v }) ) // 生成工作表 const ws = XLSX.utils.aoa_to_sheet([groupRow, subHeaderRow, ...bodyRows]) ws['!merges'] = merges // 套用 Excel 數字格式(含千分位) tableHeaders.value.forEach((k, colIdx) => { if (!numericFields.includes(k)) return const colLetter = XLSX.utils.encode_col(colIdx) for (let row = 2; row <= bodyRows.length + 1; row++) { const addr = `${colLetter}${row}` const cell = ws[addr] if (cell && typeof cell.v === 'number') { cell.t = 'n' cell.z = '#,##0' } } }) }
為什麼要「全部大寫」?
你的流程中有這行:
function normalizeKey(key) { return key.toUpperCase() }
也就是說,所有欄位鍵(例如
Material2
)最後在表格資料與表頭都會變成
MATERIAL2
。
因此:
-
在
NUMERIC_COLS
請使用 大寫 欄位名。 -
在 Excel 的
numericFields.includes(key)
判斷也要用 大寫的 key 才會命中。
常見錯誤與排除
1)
ReferenceError: rightAlignColumns is not defined
原因:rightAlignColumns
沒在檔案頂層宣告,或變數名稱更改但 template 未更新。
解法:用本文的
NUMERIC_COLS
寫法,並確認
template 使用
isNumericColumn(hdr)
。
2)
ReferenceError: numericFields is not defined
原因:numericFields
宣告在
exportToExcel()
函式裡,但
isNumericColumn()
(template
用)也拿它來判斷。
解法:把欄位集合拉到檔案頂層(NUMERIC_COLS
),UI 與 Excel 共同使用;在
exportToExcel()
內以
const numericFields = [...NUMERIC_COLS]
取得陣列版本即可。
3) 為什麼我有些欄位沒靠右?
原因:欄位名大小寫不一致,或
NUMERIC_COLS
未包含該欄位。
解法:確定你的
tableHeaders
與資料鍵都是
大寫,並把需要靠右的欄位加入
NUMERIC_COLS
。
檢查清單(快速自查)
-
NUMERIC_COLS
是否在 檔案最頂層 宣告? -
NUMERIC_COLS
的欄位名是否為 大寫?(例如MATERIAL2
而非Material2
) -
template 是否使用
isNumericColumn(hdr)
並在 CSS 有.text-right
? -
exportToExcel()
是否用const numericFields = [...NUMERIC_COLS]
取得同一組欄位? -
Excel 內有設定
cell.t = 'n'
與cell.z = '#,##0'
嗎?
延伸:想自動判斷「像數字的字串」也靠右?
可以把
isNumericColumn()
改成帶值參考的版本,讓「數字字串」也靠右:
function cellShouldRightAlign(colKey, value) { if (NUMERIC_COLS.has(String(colKey).toUpperCase())) return true return typeof value === 'string' && /^[\d,]+(\.\d+)?$/.test(value) }
template 改為:
:text-right="cellShouldRightAlign(hdr, row[hdr])"
(這是可選加強版,基本需求用
isNumericColumn
已足夠。)
結語
這個案例的關鍵在於統一管理「數值欄位集合」並放在正確的作用域:
-
UI 的靠右對齊與 Excel 的數字格式處理都應該共用同一份設定。
-
一次定義,雙處使用,避免「某處改了、另一處忘了」的錯誤。
留言
張貼留言