🌐在 Vue 3 + ASP.NET Core 中剔除「全 0 列」並在 Excel 匯出動態合併儲存格:一步到位的實作教學

 

摘要(Meta Description)

本教學示範如何在 APS「計劃排程結果-考題結論」頁面,後端先過濾掉 TFT/LCM/Material 三大區塊 整列皆為 0 的資料,再於 前端 Excel 匯出時,針對 連續且相同 的欄位值做 動態合併儲存格(rowspan 效果),讓報表更乾淨好讀。




背景與目標(看不懂系統的人也能秒懂)

在工廠/供應鏈報表裡,常會有「評估明細」區塊(例如 TFT、LCM、Material)。有些列這三個區塊全部都是 0,其實沒有分析價值;另外,報表裡像 SEQ_NO、APS_PLAN_NO、SITE、APPL、CUSTOMER_NAME 這類識別欄位,若連續多列相同,匯出成 Excel 時希望能自動合併儲存格,更好閱讀。

因此,我們要完成兩件事:

  1. 後端剃除雜訊:把三大區塊都為 0 的列先濾掉。

  2. 前端匯出美化:Excel 匯出時,連續相同的識別欄位自動合併儲存格。



系統架構小圖(概念)

  1. ASP.NET Core 透過 EF Core 查表,組成 APSZRESULTC 清單

  2. 在回傳前,做一次「全 0 列剃除

  3. Vue 3 取得資料 → 顯示 → 使用 SheetJS 匯出 Excel

  4. 匯出時:先合併群組表頭 → 再動態合併正文連續欄位




一、後端(ASP.NET Core):剃除三大區塊「整列為 0」的資料

先把資料清乾淨,前端就不必再負擔昂貴的篩選。

關鍵點:本系統把數值欄位轉成字串(千分位格式),因此判斷「0」要用字串比較"0"),若未轉格式則以數值判斷(0m)。

在你的 GenerateAPSZRESULTC(string apsVersion) 最後、return result; 之前加入這段:

// 4) 剃除「TFT、LCM、Material」三大群組的數值全為 "0" 的列 result = result .Where(item => // 只要任一群組有「非 0」就保留 item.TFT2 != "0" || item.TFT5 != "0" || item.LCM2 != "0" || item.DISPLACE_QTY != "0" || item.Material2 != "0" || item.Material5 != "0" ) .ToList(); return result;

為什麼不是放在前端?

  • 效能:一次查上萬筆時,先在後端過濾可省下網路傳輸與前端運算。

  • 一致性:所有消費端(Web、批次)拿到的資料都已乾淨。

小提醒:若未來把 TFT2/5、LCM2/DISPLACE_QTY、Material2/5 改成數值型別,請把 "0" 改為 0 判斷。




二、前端(Vue 3 + SheetJS):Excel 匯出時動態合併儲存格

你原本已經完成群組表頭的合併。接著在 exportToExcel()header merges 之後ws['!merges'] = merges 之前,加入「正文動態合併」的邏輯:

// 5. 先建立 header 的 merges(你原本的程式)... const merges = []; // 依你的程式:合併前 9 欄向下、考題結論 2 欄、TFT 5 欄、LCM 5 欄、Material 5 欄 // merges.push(...) // 5.1 正文「連續且相同」欄位的動態合併 const mergeColumns = ['SEQ_NO','APS_PLAN_NO','SITE','APPL','CUSTOMER_NAME']; mergeColumns.forEach(key => { const c = tableHeaders.value.indexOf(key); if (c < 0) return; let start = 0; while (start < dataToExport.length) { let end = start + 1; while (end < dataToExport.length && dataToExport[end][key] === dataToExport[start][key]) { end++; } if (end - start > 1) { merges.push({ // 注意:工作表第 1 列是 groupRow,第 2 列是 subHeaderRow, // 真正資料從第 3 列開始,所以 row index 要 +2 s: { r: start + 2, c }, e: { r: end - 1 + 2, c } }); } start = end; } }); // 5.2 寫回所有合併資訊 ws['!merges'] = merges;

這段在做什麼?

  • 逐欄位掃描(例如 SEQ_NO),把「連續且相同」的區段找出來。

  • 找到區段就新增一筆合併範圍(s=起點、e=終點)。

  • 因為上面有兩層表頭,所以正文資料列的起始 index 要 +2




三、完整匯出流程(你已具備的關鍵)

  1. 先組三段陣列:groupRow(群組大標)、subHeaderRow(欄名)、bodyRows(資料)

  2. XLSX.utils.aoa_to_sheet([...]) 建工作表

  3. header merges

  4. 對數值欄位(如 DEMAND_QTYTFT2LCM2 等)去逗號還原數值並套 #,##0 格式

  5. 動態 merges(本文新增)

  6. 建立活頁簿、寫出檔案




驗證清單

  • 切換不同版本,是否仍能穩定過濾掉「三大區塊全 0」的列?

  • Excel 匯出後,連續相同SEQ_NO/APS_PLAN_NO/SITE/APPL/CUSTOMER_NAME 是否合併?

  • 千分位格式是否正確(Excel 內可計算)?

  • 如有排序,務必在合併前完成,避免錯把非相鄰相同值合併。




常見坑位&最佳實務

  • 字串 vs 數字:若欄位先轉成帶逗號的字串,判斷與計算要特別處理。

  • 合併僅限連續:Excel merge 不會跨不相鄰的列;若要「同值聚合」,請先排序。

  • 效能:大量資料時,合併邏輯是 O(n)。若欄位多、資料多,請限制要合併的欄位清單。

  • 維護性:把「要合併的欄位」與「數值欄位」做成常數陣列,日後改規格只動一處。




範例程式片段(可直接套用)

1) 後端過濾(C#,放在 return result; 前)

result = result.Where(item => item.TFT2 != "0" || item.TFT5 != "0" || item.LCM2 != "0" || item.DISPLACE_QTY != "0" || item.Material2 != "0" || item.Material5 != "0" ).ToList(); return result;

2) 前端動態合併(Vue 3 / SheetJS,放在 exportToExcel() 的 merges 設定之後)

const mergeColumns = ['SEQ_NO','APS_PLAN_NO','SITE','APPL','CUSTOMER_NAME']; mergeColumns.forEach(key => { const c = tableHeaders.value.indexOf(key); if (c < 0) return; let start = 0; while (start < dataToExport.length) { let end = start + 1; while (end < dataToExport.length && dataToExport[end][key] === dataToExport[start][key]) end++; if (end - start > 1) merges.push({ s: { r: start + 2, c }, e: { r: end - 1 + 2, c } }); start = end; } }); ws['!merges'] = merges;



結語

把「全 0 列」先在後端剃除,再於前端匯出時做動態合併儲存格,就能讓 APS 報表更精簡、專注與易讀。這種「先清洗、後美化」的分工,在企業系統很實用:效能好、維護易、跨端一致。

留言

這個網誌中的熱門文章

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

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

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