EasyExcel自定義下拉注解的三種實現(xiàn)方式總結(jié)
一、簡介
在使用EasyExcel設(shè)置下拉數(shù)據(jù)時,每次都要創(chuàng)建一個SheetWriteHandler組件確實比較繁瑣。為了優(yōu)化這個過程,我們可以通過自定義注解來簡化操作,使得只需要在需要添加下拉數(shù)據(jù)的字段上添加注解即可。
注解實現(xiàn)三種方式可供選擇
- 方式一:固定值
- 方式二:動態(tài)獲取復(fù)雜數(shù)據(jù)
- 方式三:通過碼值獲取碼值表的數(shù)據(jù)列表
二、關(guān)鍵組件
1、ExcelSelected注解
用于在數(shù)據(jù)模型類中標(biāo)注需要添加下拉列表的字段及其屬性
三種方式都是通過此注解實現(xiàn)
/** * 定義Excel列下拉列表屬性的注解。 */ @Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelSelected { /** * 方式一:固定的下拉選項 */ String[] source() default {}; /** * 方式二:提供動態(tài)下拉選項的類 */ Class<? extends ExcelDynamicSelect>[] sourceClass() default {}; /** * 方式三:基于碼值從數(shù)據(jù)庫查詢數(shù)據(jù) */ String codeField() default ""; /** * 下拉列表的起始行(默認(rèn)從第二行開始)。 */ int firstRow() default 1; /** * 下拉列表的結(jié)束行(默認(rèn)到第65536行)。 */ int lastRow() default 65536; }
2、ExcelDynamicSelect接口(僅用于方式二)
方式二定義動態(tài)獲取下拉列表數(shù)據(jù)的規(guī)范
實現(xiàn)該接口的類可以從數(shù)據(jù)庫、外部服務(wù)或其他動態(tài)來源獲取數(shù)據(jù)
/** * 動態(tài)下拉列表數(shù)據(jù)提供者接口。 */ public interface ExcelDynamicSelect { /** * 獲取動態(tài)生成的下拉列表選項。 * * @return 下拉選項數(shù)組。 */ String[] getSource(); }
3、ExcelSelectedResolve類
負(fù)責(zé)解析ExcelSelected注解,獲取下拉列表的具體數(shù)據(jù)
/** * 根據(jù) ExcelSelected 注解解析下拉列表數(shù)據(jù)源。 */ @Data @Slf4j public class ExcelSelectedResolve { /** * 下拉選項數(shù)組。 */ private String[] source; /** * 下拉列表的起始行。 */ private int firstRow; /** * 下拉列表的結(jié)束行。 */ private int lastRow; /** * 解析下拉列表數(shù)據(jù)來源 * * @param excelSelected 下拉框注解對象 * @return 下拉框選項數(shù)組 */ public String[] resolveSelectedSource(ExcelSelected excelSelected) { if (excelSelected == null) { return null; } // 方式一:獲取固定下拉框的內(nèi)容 String[] source = excelSelected.source(); if (source.length > 0) { return source; } // 方式二:獲取動態(tài)下拉框的內(nèi)容 Class<? extends ExcelDynamicSelect>[] classes = excelSelected.sourceClass(); if (classes.length > 0) { try { ExcelDynamicSelect excelDynamicSelect = classes[0].newInstance(); String[] dynamicSelectSource = excelDynamicSelect.getSource(); if (dynamicSelectSource != null && dynamicSelectSource.length > 0) { return dynamicSelectSource; } } catch (InstantiationException | IllegalAccessException e) { log.error("解析動態(tài)下拉框數(shù)據(jù)異常", e); } } // 方式三:獲取碼值下拉數(shù)據(jù)(動態(tài)下拉) String codeField = excelSelected.codeField(); if (ObjectUtils.isNotEmpty(codeField)) { try { // 這里就是通過碼值查詢碼值表,寫死了,每次傳碼值查詢即可 String[] codeFieldSource = SpringUtil.getBean(xxxService.class) .selectByCode(codeField); if (ObjectUtils.isNotEmpty(codeFieldSource)) { return codeFieldSource; } } catch (Exception e) { log.error("解析動態(tài)下拉框(碼值)數(shù)據(jù)異常", e); } } return null; } }
4、SelectedSheetWriteHandler類
- SheetWriteHandler實現(xiàn)類,在Sheet創(chuàng)建后設(shè)置下拉列表
- 在隱藏的sheet中存儲下拉選項,然后設(shè)置數(shù)據(jù)驗證以實現(xiàn)下拉功能
- 最后這里添加了阻止輸入非下拉選項的值的校驗
/** * 處理Excel下拉列表的SheetWriteHandler實現(xiàn)類。 */ @Slf4j @Data public class SelectedSheetWriteHandler implements SheetWriteHandler { // 存儲列索引與對應(yīng)下拉列表解析器的映射 private Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>(); /** * 構(gòu)造方法,解析表頭類中的下拉列表注解信息。 * * @param head 表頭類。 */ public SelectedSheetWriteHandler(Class<?> head) { // 獲取所有聲明的字段 Field[] fields = head.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; // 獲取 ExcelSelected 注解 ExcelSelected selected = field.getAnnotation(ExcelSelected.class); ExcelProperty property = field.getAnnotation(ExcelProperty.class); if (selected != null) { ExcelSelectedResolve resolve = new ExcelSelectedResolve(); // 解析下拉列表數(shù)據(jù)源 String[] source = resolve.resolveSelectedSource(selected); if (source != null && source.length > 0) { resolve.setSource(source); resolve.setFirstRow(selected.firstRow()); resolve.setLastRow(selected.lastRow()); // 使用注解中的索引或字段順序作為列索引 if (property != null && property.index() >= 0) { selectedMap.put(property.index(), resolve); } else { selectedMap.put(i, resolve); } } } } } /** * 在創(chuàng)建Sheet之前調(diào)用的方法。 */ @Override public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { // 此處無需操作,保持空實現(xiàn) } /** * 在Sheet創(chuàng)建后調(diào)用的方法,用于設(shè)置Excel下拉列表。 * * @param writeWorkbookHolder 寫入的工作簿持有者。 * @param writeSheetHolder 寫入的Sheet持有者。 */ @Override public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { Sheet sheet = writeSheetHolder.getSheet(); Workbook workbook = sheet.getWorkbook(); // SXSSFWorkbook 是 Apache POI 庫中用于處理大文件的一種特殊工作簿類型 SXSSFWorkbook sw = (SXSSFWorkbook) workbook; // 1.創(chuàng)建一個隱藏的sheet,名稱為hidden,用于存儲下拉列表選項 String hiddenName = "hidden"; XSSFSheet hiddenSheet = sw.getXSSFWorkbook().createSheet(hiddenName); // 將隱藏的sheet設(shè)置為不可見 workbook.setSheetHidden(workbook.getSheetIndex(hiddenName), true); // 創(chuàng)建數(shù)據(jù)驗證輔助器 DataValidationHelper helper = sheet.getDataValidationHelper(); // 為每個需要下拉列表的列創(chuàng)建數(shù)據(jù)驗證 selectedMap.forEach((index, selectedResolve) -> { // 設(shè)置下拉列表的范圍:起始行,結(jié)束行,起始列,結(jié)束列 CellRangeAddressList rangeList = new CellRangeAddressList( selectedResolve.getFirstRow(), selectedResolve.getLastRow(), index, index ); // 在隱藏的sheet中生成下拉列表選項值 String[] values = selectedResolve.getSource(); generateSelectValue(hiddenSheet, index, values); // 獲取Excel列標(biāo),例如A, B, AA String excelLine = getExcelLine(index); // 引用隱藏sheet中的單元格區(qū)域,例如hidden!$H$1:$H$50 String refers = hiddenName + "!$" + excelLine + "$1:$" + excelLine + "$" + values.length; // 使用引用的內(nèi)容作為下拉列表的值 DataValidationConstraint constraint = helper.createFormulaListConstraint(refers); DataValidation validation = helper.createValidation(constraint, rangeList); // 設(shè)置驗證屬性,阻止輸入非下拉選項的值 validation.setErrorStyle(DataValidation.ErrorStyle.STOP); validation.setShowErrorBox(true); validation.setSuppressDropDownArrow(true); validation.createErrorBox("提示", "請輸入下拉選項中的內(nèi)容"); // 將驗證添加到當(dāng)前的sheet中 sheet.addValidationData(validation); }); } /** * 獲取Excel列標(biāo)(例如:A-Z, AA-ZZ)。 * * @param num 列索引,從0開始。 * @return Excel列標(biāo)字符串。 */ public static String getExcelLine(int num) { StringBuilder line = new StringBuilder(); // 計算列標(biāo),使用字母表示,例如 A, B, ..., Z, AA, AB, ... int first = num / 26; int second = num % 26; if (first > 0) { line.append((char) ('A' + first - 1)); } line.append((char) ('A' + second)); return line.toString(); } /** * 在隱藏的sheet中生成下拉列表選項值。 * * @param sheet 隱藏的sheet對象。 * @param col 列索引。 * @param values 下拉列表選項值數(shù)組。 */ private void generateSelectValue(Sheet sheet, int col, String[] values) { // 將下拉列表選項值寫入隱藏的sheet中,每個選項值占用一行 for (int i = 0, length = values.length; i < length; i++) { Row row = sheet.getRow(i); if (row == null) { row = sheet.createRow(i); } // 在指定列中創(chuàng)建單元格并設(shè)置下拉列表選項值 row.createCell(col).setCellValue(values[i]); } } }
三、實際應(yīng)用
包含三種方式,固定值、動態(tài)獲取、碼值數(shù)據(jù)庫獲取
@Data public class Employee { @ExcelProperty(value = "用戶編號") private Integer id; @ExcelProperty(value = "姓名") private String name; @ExcelProperty(value = "性別") @ExcelSelected(source = {"男", "女"}) private String gender; @ExcelProperty(value = "職位") @ExcelSelected(sourceClass = {PositionDynamicSelect.class}) private String position; @ExcelProperty(value = "國家") @ExcelSelected(codeField = "country_code") private String country; }
方式二的動態(tài)獲取數(shù)據(jù)
public class PositionDynamicSelect implements ExcelDynamicSelect { @Override public String[] getSource() { // 動態(tài)生成職位列表 return new String[]{"軟件工程師", "項目經(jīng)理", "人事專員", "財務(wù)分析師"}; } }
測試類
public class EmployeeExcelTest { public static void main(String[] args) { String fileName = "/Users/xuchang/Documents/employee.xlsx"; EasyExcel.write(fileName, Employee.class) .registerWriteHandler(new SelectedSheetWriteHandler(Employee.class)) .sheet().doWrite((Collection<?>) null); } }
下拉效果
輸入非下拉框數(shù)據(jù)效果
總結(jié)
方式一只需要添加注解@ExcelSelected(source = {"x1", "x2"})即可
方式二在查詢復(fù)雜的情況下使用,每個下拉都需要創(chuàng)建一個ExcelDynamicSelect的實現(xiàn)類,并添加注解@ExcelSelected(sourceClass = {xxx.class})
方式三只需要添加注解@ExcelSelected(codeField = "xxx_code"),所有系統(tǒng)應(yīng)該都有碼值表,在ExcelSelectedResolve類中已寫好通過碼值查詢數(shù)據(jù)的方法
同樣也支持@ExcelSelected注解的擴(kuò)展,添加屬性,然后在ExcelSelectedResolve類中去添加獲取下拉數(shù)據(jù)的方法。
以上就是EasyExcel自定義下拉注解的三種實現(xiàn)方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于EasyExcel自定義下拉注解的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java工具類SendEmailUtil實現(xiàn)發(fā)送郵件
這篇文章主要為大家詳細(xì)介紹了java工具類SendEmailUtil實現(xiàn)發(fā)送郵件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02java 線程中start方法與run方法的區(qū)別詳細(xì)介紹
這篇文章主要介紹了java 線程中start方法與run方法的區(qū)別詳細(xì)介紹的相關(guān)資料,在java線程中調(diào)用start方法與run方法的區(qū)別在哪里? 這兩個問題是兩個非常流行的初學(xué)者級別的多線程面試問題,這里進(jìn)行詳細(xì)說明,需要的朋友可以參考下2016-11-11利用Java實現(xiàn)解析網(wǎng)頁中的內(nèi)容
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言做一個解析指定網(wǎng)址的網(wǎng)頁內(nèi)容小應(yīng)用,文中的實現(xiàn)步驟講解詳細(xì),感興趣的可以嘗試下2022-10-10JAVA中實現(xiàn)原生的 socket 通信機(jī)制原理
本篇文章主要介紹了JAVA中實現(xiàn)原生的 socket 通信機(jī)制原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08