EasyExcel 合并單元格最佳實踐:基于注解的自動合并與樣式控制

EasyExcel 合并單元格最佳實踐:基于注解的自動合并與樣式控制

前言

在日常開發中,我們經常需要導出 Excel 報表,而合并單元格是提升報表可讀性的常見需求。本文將介紹如何基于 EasyExcel 實現智能的單元格合并功能,通過自定義注解 @ExcelMerge 標記需要合并的字段,并確保合并后的內容完美居中對齊。

核心功能

  1. 注解驅動:通過 @ExcelMerge 注解標記需要合并的字段
  2. 自動合并:相鄰行相同值的單元格自動合并
  3. 樣式控制:合并后的單元格內容水平和垂直居中
  4. 兼容性:支持 EasyExcel 原生功能(自動列寬、下拉框等)

實現代碼

easyexcel 版本

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.4</version></dependency>

1. 定義合并注解

import java.lang.annotation.*;/*** 標記需要合并的 Excel 列*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelMerge {/*** 是否啟用合并(默認 true)*/boolean enable() default true;
}

2. Excel 合并工具類

package cn.iocoder.yudao.framework.excel.core.util;import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;/*** Excel 合并單元格工具類(支持注解驅動)*/
public class ExcelMergeUtils {/*** 導出 Excel 并自動合并標記字段** @param outputStream  響應* @param filename  文件名* @param sheetName Sheet 名稱* @param head      表頭類* @param data      數據列表* @param <T>       數據類型* @throws IOException 寫入異常*/public static <T> void write(OutputStream outputStream, String filename, String sheetName,Class<T> head, List<T> data) throws IOException {// 內容樣式:水平 + 垂直居中WriteCellStyle contentStyle = new WriteCellStyle();contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 注冊樣式策略HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(null, contentStyle);// 自動合并策略(基于注解)AbstractMergeStrategy mergeStrategy = new AnnotationBasedMergeStrategy<>(data, head);// 輸出 ExcelEasyExcel.write(outputStream, head).autoCloseStream(false).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自動列寬.registerWriteHandler(new SelectSheetWriteHandler(head))         // 下拉框支持.registerWriteHandler(mergeStrategy)                           // 自動合并.registerWriteHandler(styleStrategy)                           // 居中對齊.registerConverter(new LongStringConverter())                  // Long 轉 String.sheet(sheetName).doWrite(data);}public static <T> void write(HttpServletResponse response, String filename, String sheetName,Class<T> head, List<T> data) throws IOException {write(response.getOutputStream(), filename, sheetName, head, data);// 設置響應頭response.addHeader("Content-Disposition", "attachment;filename=" +URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));response.setContentType("application/vnd.ms-excel;charset=UTF-8");}/*** 基于注解的合并策略*/private static class AnnotationBasedMergeStrategy<T> extends AbstractMergeStrategy {private final List<T> dataList;private final Class<T> clazz;public AnnotationBasedMergeStrategy(List<T> dataList, Class<T> clazz) {this.dataList = dataList != null ? dataList : new ArrayList<>();this.clazz = clazz;}@Overrideprotected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {if (relativeRowIndex != 0) return; // 只在第一行處理int columnIndex = cell.getColumnIndex();Field field = clazz.getDeclaredFields()[columnIndex];if (field.isAnnotationPresent(ExcelMerge.class)) {mergeColumn(sheet, columnIndex);}}private void mergeColumn(Sheet sheet, int columnIndex) {List<CellRangeAddress> ranges = new ArrayList<>();if (dataList.isEmpty()) return;try {Field field = clazz.getDeclaredFields()[columnIndex];field.setAccessible(true);Object currentValue = field.get(dataList.get(0));int startRow = 1; // 從第2行開始(第1行是標題)for (int i = 1; i < dataList.size(); i++) {Object value = field.get(dataList.get(i));if (!value.equals(currentValue)) {if (startRow < i) {ranges.add(new CellRangeAddress(startRow, i, columnIndex, columnIndex));}currentValue = value;startRow = i + 1;}}// 處理最后一段if (startRow < dataList.size()) {ranges.add(new CellRangeAddress(startRow, dataList.size(), columnIndex, columnIndex));}// 應用合并for (CellRangeAddress range : ranges) {sheet.addMergedRegion(range);}} catch (IllegalAccessException e) {throw new RuntimeException("反射獲取字段值失敗", e);}}}
}

使用示例

1. 定義實體類

public class UserVO {@ExcelMerge // 此字段相同值會自動合并private String username;@ExcelMerge(enable = false) // 不合并private Integer age;@ExcelMerge // 此字段相同值會自動合并private String department;// 省略構造方法、getter/setter
}

2. 導出 Excel

List<UserVO> users = Arrays.asList(new UserVO("張三", 25, "研發部"),new UserVO("張三", 30, "研發部"), // username 和 department 相同,會自動合并new UserVO("李四", 28, "市場部")
);// HTTP 響應方式
ExcelMergeUtils.write(response, "users.xlsx", "用戶列表", UserVO.class, users);// 或者輸出流方式
try (OutputStream out = new FileOutputStream("users.xlsx")) {ExcelMergeUtils.write(out, "users.xlsx", "用戶列表", UserVO.class, users);
}

技術要點解析

  1. 合并策略實現

    • 繼承 AbstractMergeStrategy 實現自定義合并邏輯
    • 通過反射獲取標記了 @ExcelMerge 的字段值
    • 計算需要合并的單元格區域(CellRangeAddress
  2. 樣式控制

    • 使用 HorizontalCellStyleStrategy 設置內容居中對齊
    • 表頭使用默認樣式,內容使用自定義樣式
  3. 性能優化

    • 只在第一行數據時執行合并操作(relativeRowIndex == 0
    • 按列處理,避免重復計算

常見問題解決

1. 合并區域重疊問題

錯誤信息:

Cannot add merged region A2:A6 to sheet because it overlaps with an existing merged region

解決方案:

  • 確保每個合并操作只執行一次
  • 可以使用 Set 記錄已處理的列,避免重復合并

2. 字段順序問題

確保實體類字段順序與 Excel 列順序一致:

  1. 保持字段聲明順序
  2. 或使用 @ExcelProperty 注解指定順序

3. 大數據量性能優化

當數據量較大時:

  1. 考慮分批處理
  2. 緩存字段信息,減少反射調用

總結

本文實現的 Excel 合并工具具有以下優勢:

  1. 簡單易用:通過注解標記即可實現自動合并
  2. 靈活可控:可以單獨控制每個字段是否合并
  3. 樣式美觀:合并后的單元格自動居中對齊
  4. 功能完善:兼容 EasyExcel 的各項特性

通過這種方式,我們可以輕松實現專業級的 Excel 導出功能,提升報表的可讀性和美觀度。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94124.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94124.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94124.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity設置UI顯示區域

系列文章目錄 untiy工具 文章目錄 系列文章目錄 ??前言 ??一、效果圖 ??二、制作過程(檢測中心點位置) ??2-1、代碼實現 ??三、優化為檢測整個UI四個角點 ??四、性能優化建議 ??壁紙分享 ??總結 ??前言 思路: 獲取屏幕的寬度和高度,定義中間區域的范圍…

Qt中用于圖像縮放的核??法QPixmap::scaled

QPixmap::scaled是Qt中用于圖像縮放的核??法&#xff0c;其作?和?法如下&#xff1a;?一、核心作用??圖像尺寸調整?根據指定尺寸對圖像進?等?例或?等?例縮放&#xff0c;?持放?和縮?操作。?保持寬高比?通過AspectRatioMode參數控制是否保持原始圖像的寬??。…

SQL Workbench/J:一款免費開源、跨平臺的通用SQL查詢工具

SQL Workbench/J 是一款基于 Java 開發的免費開源、跨平臺的通用 SQL 查詢工具。 SQL Workbench/J 主要專注于 SQL 腳本開發和數據導入導出功能&#xff0c;不提供各種數據庫管理功能。 功能特性 跨平臺&#xff1a;可以在任何安裝了 Java 運行時環境的操作系統上運行&#xf…

DOLO 上漲:Berachain 生態爆發的前奏?

在 Berachain 生態逐漸進入公眾視野之際&#xff0c;Dolomite&#xff08;簡稱 Dolomite&#xff0c;代幣 DOLO&#xff09;成為鏈上表現最為突出的明星協議。其代幣價格在短短兩個月內&#xff0c;從 $0.03 飆升至 $0.3&#xff0c;漲幅接近 10 倍。市場不僅將其視作 Berachai…

吉利汽車與芯鼎微成立聯合創新實驗室共譜車規級LCoS顯示新篇章

2025年8月20日&#xff0c;吉利汽車研究院技術規劃中心副主任李莉、光學實驗室負責人李金樺博士等一行四人蒞臨芯鼎微&#xff0c;雙方共同為"吉利汽車-芯鼎微聯合創新實驗室"揭牌&#xff0c;標志著兩家企業在車載先進顯示技術領域邁入深度協同創新的新階段。 在這汽…

NPM組件 @angular_devkit/core 等竊取主機敏感信息

【高危】NPM組件 angular_devkit/core 等竊取主機敏感信息 漏洞描述 當用戶安裝受影響版本的 angular_devkit/core 等NPM組件包時會竊取用戶的主機名、用戶名、IP地址信息并發送到攻擊者可控的服務器地址。 MPS編號MPS-1jf5-s6ix處置建議強烈建議修復發現時間2025-08-14投毒…

docker cuda版安裝 dockercuda版安裝

目錄 1.一鍵安裝docker 測試ok 2.安裝cuda支持 通用的應該沒問題 安裝工具包 配置 runtime&#xff1a; 3.檢查 Docker 是否支持 NVIDIA 運行時 1.一鍵安裝docker 測試ok curl -fsSL https://get.docker.com | sh 2.安裝cuda支持 通用的應該沒問題 也可以搜索安裝 cuda版d…

Spring發布訂閱模式詳解

Spring 的發布訂閱模式&#xff08;Publish-Subscribe Pattern&#xff09;是一種基于事件驅動的設計模式&#xff0c;通過 "事件" 作為中間載體實現組件間的解耦。在這種模式中&#xff0c;"發布者"&#xff08;Publisher&#xff09;負責產生事件并發布&…

服務器硬件中的磁盤SSD與HDD性能區別,以及分別適用于什么業務?

SSD&#xff08;固態硬盤&#xff09;和 HDD&#xff08;機械硬盤&#xff09;是服務器中常見的存儲設備類型&#xff0c;兩者在性能、可靠性、成本等方面存在顯著差異。根據這些特性&#xff0c;它們適用于不同的業務需求。以下是詳細的對比與應用場景分析&#xff1a;1. SSD …

AI驅動的SEO關鍵詞優化秘籍

內容概要人工智能技術的飛速發展正重塑SEO關鍵詞優化領域&#xff0c;為從業者帶來全新機遇與挑戰。本文將系統解析AI如何革新關鍵詞策略&#xff0c;覆蓋從語義搜索深度解析到長尾詞智能挖掘的核心環節。通過工具驅動的內容優化路徑&#xff0c;讀者將掌握提升流量轉化率的關鍵…

自然語言處理(NLP)技術的發展歷史

自然語言處理&#xff08;NLP&#xff09;作為人工智能的重要分支&#xff0c;其發展歷程跨越了大半個世紀&#xff0c;從早期的規則式嘗試到如今的大模型時代&#xff0c;技術路徑不斷迭代&#xff0c;核心目標始終是實現人機間的自然語言交互。以下從關鍵階段、技術突破和標志…

Swift 解法詳解 LeetCode 361:轟炸敵人,用動態規劃輕松拿下

文章目錄摘要描述題解答案題解代碼分析代碼解析示例測試及結果時間復雜度空間復雜度總結摘要 “轟炸敵人”這道題名字聽起來就很帶感&#xff0c;它其實是一個二維網格搜索問題。我們要找到一個能放置炸彈的位置&#xff0c;讓炸掉的敵人最多。雖然題目看起來復雜&#xff0c;…

如何高效推進將科技創新成果轉化為標準?

2024年10月26日&#xff0c;全國標準信息公共服務平臺正式發布了國家標準《科技成果評估規范》&#xff08;GB/T 44731-2024 &#xff09;&#xff0c;并從發布之日起正式實施。這一標準的正式推出&#xff0c;標志著政府在推進科技成果轉化、提升科技服務能力方面邁出了重要一…

CMake 快速開始

CMake 快速開始 CMake 安裝 編輯環境&#xff1a;VS Code 編譯環境&#xff1a;VS Code Remote SSH模式 Ubuntu 24.04 CMake 官?源代碼下載地址&#xff1a;https://cmake.org/download/ CMake 官?英? 檔地址&#xff1a;https://cmake.org/cmake/help/latest/index.html S…

STM32F1 EXTI介紹及應用

第三章 EXTI介紹及應用 1. EXTI介紹 EXTI&#xff08;External interrupt/event controller&#xff09;—外部中斷/事件控制器&#xff0c;管理了控制器的 20 個中斷/事件線。每個中斷/事件線都對應有一個邊沿檢測器&#xff0c;可以實現輸入信號的上升沿檢測和下降沿的檢測。…

Oracle SYS用戶無法登錄數據庫-ORA-12162

錯誤詳情 [Oracleorcl bin]$ ./sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on Mon Aug 18 08:12:04 2025 Copyright (c) 1982, 2013, Oracle. All rights reserved. ERROR: ORA-12162: TNS:net service name is incorrectly specifiedOS登錄解析 注意&…

【計算機視覺與深度學習實戰】06基于光流算法的實時運動檢測系統設計與實現——以蚊子軌跡追蹤為例(有完整代碼)

第一章 引言 計算機視覺作為人工智能領域的重要分支,近年來在目標檢測、運動分析、行為識別等方面取得了顯著進展。其中,運動檢測技術作為視頻分析的基礎技術之一,在安防監控、交通管理、體感交互、生物行為研究等領域發揮著越來越重要的作用。光流算法作為運動檢測的經典方…

國產CANFD芯片技術特性與應用前景綜述:以ASM1042系列為例

摘要本文綜述了國科安芯推出的國產CANFD芯片ASM1042系列的技術特性與應用前景。ASM1042系列作為一款高性能的CANFD收發器&#xff0c;支持5Mbps的高速通信和高達70V的總線耐壓&#xff0c;廣泛應用于汽車電子、工業控制和航空航天等領域。文中詳細分析了其高速率設計、高耐壓設…

偶現型Bug處理方法---用系統方法對抗隨機性

在軟件開發中&#xff0c;Bug是影響產品質量的核心問題&#xff0c;而偶現型Bug&#xff08;Intermittent Bug&#xff09;因其“時隱時現、難以復現”的特性&#xff0c;成為最頭疼的挑戰之一。這類Bug不像必現Bug那樣有穩定的觸發路徑&#xff0c;可能在特定環境、特定操作序…

一分鐘docker部署onlyoffice 在線預覽word pdf excel...

目錄 效果 1.執行命令 2.訪問 3.測試 3.1執行下面的命令 3.2測試效果 3.3預覽效果 3.4轉換 效果 1.執行命令 sudo docker run -i -t -d -p 80:80 onlyoffice/documentserver 稍等片刻 2.訪問 瀏覽器打開ip:80即可訪問 3.測試 3.1執行下面的命令 sudo docker exec 7…