🌐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 的數字格式處理都應該共用同一份設定。
-
一次定義,雙處使用,避免「某處改了、另一處忘了」的錯誤。
留言
張貼留言