开发者

EasyExcel自定义下拉注解的三种实现方式总结

目录
  • 一、简介
  • 二、关键组件
    • 1、ExcelSelected注解
    • 2、ExcelDynamicSelect接口(仅用于方式二)
    • 3、ExcelSelectedResolve类
    • 4、SelectedSheetWriteHandler类
  • 三、实际应用
    • 总结

      一、简介

      在使用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 {
          /**
           * 获取动态生成的下拉列表选项。
           * 
      php     * @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("解析动态下拉框(码值)数据http://www.devze.com异常", 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 = getExjavascriptcelLine(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 val编程客栈ues 下拉列表选项值数组。
           */
          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);
          }
      }
      

      下拉效果

      EasyExcel自定义下拉注解的三种实现方式总结

      输入非下拉框数据效果

      EasyExcel自定义下拉注解的三种实现方式总结

      总结

      方式一只需要添加注解@ExcelSelected(source = {"x1", "x2"})即可

      方式二在查询复杂的情况下使用,每个下拉都需要创建一个ExcelDynamicSelect的实现类,并添加注解@ExcelSelected(sourceClass = {xxx.class})

      方式三只需要添加注解@ExcelSelected(codeField = "xxx_code"),所有系统应该都有码值表,在ExcelSelectedResolve类中已写好通过码值查询数据的方法

      同样也支持@ExcelSelected注解的扩展,添加属性,然后在ExcelSelectedResolve类中去添加获取下拉数据的方法。

      以上就是EasyExcel自定义下拉注解的三种实现方式总结的详细内容,更多关于EasyExcel自定义下拉注解的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜