EasyExcel自定义下拉注解的三种实现方式总结
作者:冬天vs不冷
使用EasyExcel设置下拉数据时,每次都要创建一个SheetWriteHandler组件确实比较繁琐,为了优化这个过程,我们可以通过自定义注解来简化操作,下面就来看看具体实现方法吧
一、简介
在使用EasyExcel设置下拉数据时,每次都要创建一个SheetWriteHandler组件确实比较繁琐。为了优化这个过程,我们可以通过自定义注解来简化操作,使得只需要在需要添加下拉数据的字段上添加注解即可。
注解实现三种方式可供选择
- 方式一:固定值
- 方式二:动态获取复杂数据
- 方式三:通过码值获取码值表的数据列表
二、关键组件
1、ExcelSelected注解
用于在数据模型类中标注需要添加下拉列表的字段及其属性
三种方式都是通过此注解实现
/** * 定义Excel列下拉列表属性的注解。 */ @Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelSelected { /** * 方式一:固定的下拉选项 */ String[] source() default {}; /** * 方式二:提供动态下拉选项的类 */ Class<? extends ExcelDynamicSelect>[] sourceClass() default {}; /** * 方式三:基于码值从数据库查询数据 */ String codeField() default ""; /** * 下拉列表的起始行(默认从第二行开始)。 */ int firstRow() default 1; /** * 下拉列表的结束行(默认到第65536行)。 */ int lastRow() default 65536; }
2、ExcelDynamicSelect接口(仅用于方式二)
方式二定义动态获取下拉列表数据的规范
实现该接口的类可以从数据库、外部服务或其他动态来源获取数据
/** * 动态下拉列表数据提供者接口。 */ public interface ExcelDynamicSelect { /** * 获取动态生成的下拉列表选项。 * * @return 下拉选项数组。 */ String[] getSource(); }
3、ExcelSelectedResolve类
负责解析ExcelSelected注解,获取下拉列表的具体数据
/** * 根据 ExcelSelected 注解解析下拉列表数据源。 */ @Data @Slf4j public class ExcelSelectedResolve { /** * 下拉选项数组。 */ private String[] source; /** * 下拉列表的起始行。 */ private int firstRow; /** * 下拉列表的结束行。 */ private int lastRow; /** * 解析下拉列表数据来源 * * @param excelSelected 下拉框注解对象 * @return 下拉框选项数组 */ public String[] resolveSelectedSource(ExcelSelected excelSelected) { if (excelSelected == null) { return null; } // 方式一:获取固定下拉框的内容 String[] source = excelSelected.source(); if (source.length > 0) { return source; } // 方式二:获取动态下拉框的内容 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("解析动态下拉框数据异常", e); } } // 方式三:获取码值下拉数据(动态下拉) 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("解析动态下拉框(码值)数据异常", e); } } return null; } }
4、SelectedSheetWriteHandler类
- SheetWriteHandler实现类,在Sheet创建后设置下拉列表
- 在隐藏的sheet中存储下拉选项,然后设置数据验证以实现下拉功能
- 最后这里添加了阻止输入非下拉选项的值的校验
/** * 处理Excel下拉列表的SheetWriteHandler实现类。 */ @Slf4j @Data public class SelectedSheetWriteHandler implements SheetWriteHandler { // 存储列索引与对应下拉列表解析器的映射 private Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>(); /** * 构造方法,解析表头类中的下拉列表注解信息。 * * @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(); // 解析下拉列表数据源 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); } } } } } /** * 在创建Sheet之前调用的方法。 */ @Override public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { // 此处无需操作,保持空实现 } /** * 在Sheet创建后调用的方法,用于设置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.创建一个隐藏的sheet,名称为hidden,用于存储下拉列表选项 String hiddenName = "hidden"; XSSFSheet hiddenSheet = sw.getXSSFWorkbook().createSheet(hiddenName); // 将隐藏的sheet设置为不可见 workbook.setSheetHidden(workbook.getSheetIndex(hiddenName), true); // 创建数据验证辅助器 DataValidationHelper helper = sheet.getDataValidationHelper(); // 为每个需要下拉列表的列创建数据验证 selectedMap.forEach((index, selectedResolve) -> { // 设置下拉列表的范围:起始行,结束行,起始列,结束列 CellRangeAddressList rangeList = new CellRangeAddressList( selectedResolve.getFirstRow(), selectedResolve.getLastRow(), index, index ); // 在隐藏的sheet中生成下拉列表选项值 String[] values = selectedResolve.getSource(); generateSelectValue(hiddenSheet, index, values); // 获取Excel列标,例如A, B, AA String excelLine = getExcelLine(index); // 引用隐藏sheet中的单元格区域,例如hidden!$H$1:$H$50 String refers = hiddenName + "!$" + excelLine + "$1:$" + excelLine + "$" + values.length; // 使用引用的内容作为下拉列表的值 DataValidationConstraint constraint = helper.createFormulaListConstraint(refers); DataValidation validation = helper.createValidation(constraint, rangeList); // 设置验证属性,阻止输入非下拉选项的值 validation.setErrorStyle(DataValidation.ErrorStyle.STOP); validation.setShowErrorBox(true); validation.setSuppressDropDownArrow(true); validation.createErrorBox("提示", "请输入下拉选项中的内容"); // 将验证添加到当前的sheet中 sheet.addValidationData(validation); }); } /** * 获取Excel列标(例如:A-Z, AA-ZZ)。 * * @param num 列索引,从0开始。 * @return Excel列标字符串。 */ public static String getExcelLine(int num) { StringBuilder line = new StringBuilder(); // 计算列标,使用字母表示,例如 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 下拉列表选项值数组。 */ 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); } // 在指定列中创建单元格并设置下拉列表选项值 row.createCell(col).setCellValue(values[i]); } } }
三、实际应用
包含三种方式,固定值、动态获取、码值数据库获取
@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; }
方式二的动态获取数据
public class PositionDynamicSelect implements ExcelDynamicSelect { @Override public String[] getSource() { // 动态生成职位列表 return new String[]{"软件工程师", "项目经理", "人事专员", "财务分析师"}; } }
测试类
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); } }
下拉效果
输入非下拉框数据效果
总结
方式一只需要添加注解@ExcelSelected(source = {"x1", "x2"})即可
方式二在查询复杂的情况下使用,每个下拉都需要创建一个ExcelDynamicSelect的实现类,并添加注解@ExcelSelected(sourceClass = {xxx.class})
方式三只需要添加注解@ExcelSelected(codeField = "xxx_code"),所有系统应该都有码值表,在ExcelSelectedResolve类中已写好通过码值查询数据的方法
同样也支持@ExcelSelected注解的扩展,添加属性,然后在ExcelSelectedResolve类中去添加获取下拉数据的方法。
以上就是EasyExcel自定义下拉注解的三种实现方式总结的详细内容,更多关于EasyExcel自定义下拉注解的资料请关注脚本之家其它相关文章!