您现在的位置是:首页 > 技术教程 正文

EasyExcel导出带下拉选数据的Excel数据导入模板(以及下拉选数据量多不展示的问题)

admin 阅读: 2024-03-24
后台-插件-广告管理-内容页头部广告(手机)

#因为项目中需要导入一些信息,但是这些信息比较不常见,且在项目字典数据中维护有这些数据,所以在导出模板的时候,把这些数据一并导出,可以减少用户的编写,避免在导入的时候因为数据错误,发生一些业务问题

直接开始

一、首先引入依赖   easyexcel依赖

  1. <dependency>
  2. <groupId>com.alibabagroupId>
  3. <artifactId>easyexcelartifactId>
  4. <version>2.2.10version>
  5. dependency>

二、创建自定义注解

  1. import java.lang.annotation.*;
  2. /**
  3. * 标注导出的列为下拉框类型,并为下拉框设置内容
  4. */
  5. @Documented
  6. @Retention(RetentionPolicy.RUNTIME)
  7. @Target(ElementType.FIELD)
  8. public @interface ExcelSelected {
  9. /**
  10. * 固定下拉内容
  11. */
  12. String[] source() default {};
  13. /**
  14. * 动态下拉内容
  15. */
  16. Class<? extends ExcelDynamicSelect>[] sourceClass() default {};
  17. /**
  18. * 设置下拉框的起始行,默认为第二行
  19. */
  20. int firstRow() default 1;
  21. /**
  22. * 设置下拉框的结束行,默认为最后一行
  23. */
  24. int lastRow() default 0x10000;
  25. }

三、解析自定义注解接口

  1. public interface ExcelDynamicSelect {
  2. /**
  3. * 获取动态生成的下拉框可选数据
  4. * @return 动态生成的下拉框可选数据
  5. */
  6. String[] getSource();
  7. }

四、创建一个实体类,(这边以我项目内的演示)

  1. import com.alibaba.excel.annotation.ExcelProperty;
  2. import com.alibaba.excel.annotation.write.style.ColumnWidth;
  3. import com.alibaba.excel.annotation.write.style.ContentRowHeight;
  4. import com.alibaba.excel.annotation.write.style.HeadRowHeight;
  5. import lombok.Data;
  6. import org.springblade.basicdata.service.impl.DeptTemplateServiceImpl;
  7. import org.springblade.basicdata.service.impl.SchoolTemplateServiceImpl;
  8. import org.springblade.basicdata.service.impl.StationlateServiceImpl;
  9. import org.springblade.basicdata.service.impl.ZgTypeslateServiceImpl;
  10. import java.io.Serializable;
  11. @Data
  12. @HeadRowHeight(20)
  13. @ContentRowHeight(18)
  14. public class TeacherExcel implements Serializable {
  15. private static final long serialVersionUID = 1L;
  16. /**
  17. * 职工编号
  18. */
  19. @ExcelProperty(index = 0, value = "职工编号")
  20. @ColumnWidth(20)
  21. private String no;
  22. /**
  23. * 姓名
  24. */
  25. @ExcelProperty(index = 1, value = "姓名")
  26. @ColumnWidth(30)
  27. private String name;
  28. /**
  29. * 身份证
  30. */
  31. @ExcelProperty(index = 2, value = "身份证号")
  32. @ColumnWidth(25)
  33. private String idCard;
  34. /**
  35. * 电话
  36. */
  37. @ExcelProperty(index = 3, value = "联系电话")
  38. @ColumnWidth(15)
  39. private String phone;
  40. /**
  41. * 学校id
  42. */
  43. //需要自定义实现 SchoolTemplateServiceImpl,下面有具体的实现方式
  44. @ExcelSelected(sourceClass = SchoolTemplateServiceImpl.class)
  45. @ExcelProperty(index = 4, value = "校区")
  46. @ColumnWidth(45)
  47. private String schoolName;
  48. /**
  49. * 部门
  50. */
  51. //需要自定义实现 DeptTemplateServiceImpl,下面有具体的实现方式
  52. @ExcelSelected(sourceClass = DeptTemplateServiceImpl.class)
  53. @ExcelProperty(index = 5, value = "部门")
  54. @ColumnWidth(45)
  55. private String bumenName;
  56. /**
  57. * 岗位类别
  58. */
  59. //需要自定义实现 StationlateServiceImpl,下面有具体的实现方式
  60. @ExcelSelected(sourceClass = StationlateServiceImpl.class)
  61. @ExcelProperty(index = 6, value = "岗位类别")
  62. @ColumnWidth(15)
  63. private String station;
  64. /**
  65. * 职工类型
  66. */
  67. //需要自定义实现 ZgTypeslateServiceImpl,下面有具体的实现方式
  68. @ExcelSelected(sourceClass = ZgTypeslateServiceImpl.class)
  69. @ExcelProperty(index = 7, value = "职工类型")
  70. @ColumnWidth(15)
  71. private String zhigongleibie;
  72. /**
  73. * 备注
  74. */
  75. @ExcelProperty(index = 8, value = "备注")
  76. @ColumnWidth(15)
  77. private String remarks;
  78. }

五、重点是以下的代码

1、以岗位类型为例,展示数据的实现方式

创建StationlateServiceImpl实现类,实现方法 为我自己的业务需求,你们可以根据你们的情况去修改,这边返回给注解的是一个字符串的数组,也就是需要导出的下拉选数据

  1. import java.util.List;
  2. import java.util.stream.Collectors;
  3. /**
  4. * 岗位类别下拉选数据实现类
  5. */
  6. public class StationlateServiceImpl implements ExcelDynamicSelect {
  7. private static IDictClient dictClient;
  8. static {
  9. dictClient = SpringUtil.getBean(IDictClient.class);
  10. }
  11. @Override
  12. public String[] getSource() {
  13. R> dictClientList =
  14. dictClient.getList(DictEnum.POST_TYPE.getName());
  15. if(Func.isNotEmpty(dictClientList) && dictClientList.getCode() == 200){
  16. if(Func.isNotEmpty(dictClientList.getData())){
  17. return Func.toStrArray(dictClientList.getData().stream().map(Dict::getDictValue).collect(Collectors.joining(",")));
  18. }
  19. }
  20. return new String[0];
  21. }
  22. }

Func.toStrArray方法就是讲给定的逗号拼接的字符串转为字符串数组,这个百度一下,一大堆

六、EasyExcelUtil工具类

  1. import com.alibaba.excel.EasyExcel;
  2. import com.alibaba.excel.annotation.ExcelProperty;
  3. import com.alibaba.excel.write.metadata.WriteSheet;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang3.StringUtils;
  6. import java.lang.reflect.Field;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. import java.util.regex.Matcher;
  10. import java.util.regex.Pattern;
  11. @Slf4j
  12. public class EasyExcelUtil {
  13. /**
  14. * 创建即将导出的sheet页(sheet页中含有带下拉框的列)
  15. * @param head 导出的表头信息和配置
  16. * @param sheetNo sheet索引
  17. * @param sheetName sheet名称
  18. * @param 泛型
  19. * @return sheet页
  20. */
  21. public static WriteSheet writeSelectedSheet(Class head, Integer sheetNo, String sheetName) {
  22. Map selectedMap = resolveSelectedAnnotation(head);
  23. return EasyExcel.writerSheet(sheetNo, sheetName)
  24. .head(head)
  25. .registerWriteHandler(new SelectedSheetWriteHandler(selectedMap))
  26. .build();
  27. }
  28. /**
  29. * 解析表头类中的下拉注解
  30. * @param head 表头类
  31. * @param 泛型
  32. * @return Map<下拉框列索引, 下拉框内容> map
  33. */
  34. private static Map resolveSelectedAnnotation(Class head) {
  35. Map selectedMap = new HashMap<>();
  36. // getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性
  37. Field[] fields = head.getDeclaredFields();
  38. for (int i = 0; i < fields.length; i++){
  39. Field field = fields[i];
  40. // 解析注解信息
  41. ExcelSelected selected = field.getAnnotation(ExcelSelected.class);
  42. ExcelProperty property = field.getAnnotation(ExcelProperty.class);
  43. if (selected != null) {
  44. ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();
  45. String[] source = excelSelectedResolve.resolveSelectedSource(selected);
  46. if (source != null && source.length > 0){
  47. excelSelectedResolve.setSource(source);
  48. excelSelectedResolve.setFirstRow(selected.firstRow());
  49. excelSelectedResolve.setLastRow(selected.lastRow());
  50. if (property != null && property.index() >= 0){
  51. selectedMap.put(property.index(), excelSelectedResolve);
  52. } else {
  53. selectedMap.put(i, excelSelectedResolve);
  54. }
  55. }
  56. }
  57. }
  58. return selectedMap;
  59. }
  60. public static boolean isIDNumber(String IDNumber) {
  61. if (IDNumber == null || "".equals(IDNumber)) {
  62. return false;
  63. }
  64. // 定义判别用户身份证号的正则表达式(15位或者18位,最后一位可以为字母)
  65. String regularExpression = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" +
  66. "(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";
  67. //假设18位身份证号码:41000119910101123X 410001 19910101 123X
  68. //^开头
  69. //[1-9] 第一位1-9中的一个 4
  70. //\\d{5} 五位数字 10001(前六位省市县地区)
  71. //(18|19|20) 19(现阶段可能取值范围18xx-20xx年)
  72. //\\d{2} 91(年份)
  73. //((0[1-9])|(10|11|12)) 01(月份)
  74. //(([0-2][1-9])|10|20|30|31)01(日期)
  75. //\\d{3} 三位数字 123(第十七位奇数代表男,偶数代表女)
  76. //[0-9Xx] 0123456789Xx其中的一个 X(第十八位为校验值)
  77. //$结尾
  78. //假设15位身份证号码:410001910101123 410001 910101 123
  79. //^开头
  80. //[1-9] 第一位1-9中的一个 4
  81. //\\d{5} 五位数字 10001(前六位省市县地区)
  82. //\\d{2} 91(年份)
  83. //((0[1-9])|(10|11|12)) 01(月份)
  84. //(([0-2][1-9])|10|20|30|31)01(日期)
  85. //\\d{3} 三位数字 123(第十五位奇数代表男,偶数代表女),15位身份证不含X
  86. //$结尾
  87. boolean matches = IDNumber.matches(regularExpression);
  88. //判断第18位校验值
  89. if (matches) {
  90. if (IDNumber.length() == 18) {
  91. try {
  92. char[] charArray = IDNumber.toCharArray();
  93. //前十七位加权因子
  94. int[] idCardWi = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
  95. //这是除以11后,可能产生的11位余数对应的验证码
  96. String[] idCardY = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
  97. int sum = 0;
  98. for (int i = 0; i < idCardWi.length; i++) {
  99. int current = Integer.parseInt(String.valueOf(charArray[i]));
  100. int count = current * idCardWi[i];
  101. sum += count;
  102. }
  103. char idCardLast = charArray[17];
  104. int idCardMod = sum % 11;
  105. if (idCardY[idCardMod].toUpperCase().equals(String.valueOf(idCardLast).toUpperCase())) {
  106. return true;
  107. } else {
  108. return false;
  109. }
  110. } catch (Exception e) {
  111. e.printStackTrace();
  112. return false;
  113. }
  114. }
  115. return false;
  116. }
  117. return matches;
  118. }
  119. public static boolean isMobile(String phone){
  120. Pattern p = null;
  121. Matcher m = null;
  122. boolean b = false;
  123. // 验证手机号
  124. String s2="^[1](([3|5|6|7|8|9][\\d])|([4][4,5,6,7,8,9])|([6][2,5,6,7])|([7][^9])|([9][1,8,9]))[\\d]{8}$";
  125. if(StringUtils.isNotBlank(phone)){
  126. p = Pattern.compile(s2);
  127. m = p.matcher(phone);
  128. b = m.matches();
  129. }
  130. return b;
  131. }
  132. }

七、SelectedSheetWriteHandler

  1. import com.alibaba.excel.write.handler.SheetWriteHandler;
  2. import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
  3. import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
  4. import lombok.AllArgsConstructor;
  5. import lombok.Data;
  6. import org.apache.poi.ss.usermodel.DataValidation;
  7. import org.apache.poi.ss.usermodel.DataValidationConstraint;
  8. import org.apache.poi.ss.usermodel.DataValidationHelper;
  9. import org.apache.poi.ss.usermodel.Sheet;
  10. import org.apache.poi.ss.util.CellRangeAddressList;
  11. import java.util.Map;
  12. @Data
  13. @AllArgsConstructor
  14. public class SelectedSheetWriteHandler implements SheetWriteHandler {
  15. private final Map selectedMap;
  16. /**
  17. * 设置阈值,避免生成的导入模板下拉值获取不到,可自行设置数量大小
  18. */
  19. private static final Integer LIMIT_NUMBER = 25;
  20. /**
  21. * Called before create the sheet
  22. */
  23. @Override
  24. public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
  25. }
  26. /**
  27. * Called after the sheet is created
  28. */
  29. @Override
  30. public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
  31. // 这里可以对cell进行任何操作
  32. Sheet sheet = writeSheetHolder.getSheet();
  33. DataValidationHelper helper = sheet.getDataValidationHelper();
  34. selectedMap.forEach((k, v) -> {
  35. // 设置下拉列表的行: 首行,末行,首列,末列
  36. CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), v.getLastRow(), k, k);
  37. // 如果下拉值总数大于25,则使用一个新sheet存储,避免生成的导入模板下拉值获取不到
  38. if (v.getSource().length > LIMIT_NUMBER) {
  39. //定义sheet的名称
  40. //1.创建一个隐藏的sheet 名称为 hidden + k
  41. String sheetName = "hidden" + k;
  42. Workbook workbook = writeWorkbookHolder.getWorkbook();
  43. Sheet hiddenSheet = workbook.createSheet(sheetName);
  44. for (int i = 0, length = v.getSource().length; i < length; i++) {
  45. // 开始的行数i,列数k
  46. hiddenSheet.createRow(i).createCell(k).setCellValue(v.getSource()[i]);
  47. }
  48. Name category1Name = workbook.createName();
  49. category1Name.setNameName(sheetName);
  50. String excelLine = getExcelLine(k);
  51. // =hidden!$H:$1:$H$50 sheet为hidden的 H1列开始H50行数据获取下拉数组
  52. String refers = "=" + sheetName + "!$" + excelLine + "$1:$" + excelLine + "$" + (v.getSource().length + 1);
  53. // 将刚才设置的sheet引用到你的下拉列表中
  54. DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);
  55. DataValidation dataValidation = helper.createValidation(constraint, rangeList);
  56. writeSheetHolder.getSheet().addValidationData(dataValidation);
  57. // 设置存储下拉列值得sheet为隐藏
  58. int hiddenIndex = workbook.getSheetIndex(sheetName);
  59. if (!workbook.isSheetHidden(hiddenIndex)) {
  60. workbook.setSheetHidden(hiddenIndex, true);
  61. }
  62. }
  63. // 设置下拉列表的值
  64. DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSource());
  65. // 设置约束
  66. DataValidation validation = helper.createValidation(constraint, rangeList);
  67. // 阻止输入非下拉选项的值
  68. validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
  69. validation.setShowErrorBox(true);
  70. // validation.setSuppressDropDownArrow(true);
  71. validation.createErrorBox("提示", "请输入下拉选项中的内容");
  72. sheet.addValidationData(validation);
  73. });
  74. }
  75. /**
  76. * 返回excel列标A-Z-AA-ZZ
  77. *
  78. * @param num 列数
  79. * @return java.lang.String
  80. */
  81. private String getExcelLine(int num) {
  82. String line = "";
  83. int first = num / 26;
  84. int second = num % 26;
  85. if (first > 0) {
  86. line = (char) ('A' + first - 1) + "";
  87. }
  88. line += (char) ('A' + second) + "";
  89. return line;
  90. }
  91. }

八、ExcelDynamicSelect

  1. public interface ExcelDynamicSelect {
  2. /**
  3. * 获取动态生成的下拉框可选数据
  4. * @return 动态生成的下拉框可选数据
  5. */
  6. String[] getSource();
  7. }

九、ExcelSelectedResolve

  1. @Data
  2. @Slf4j
  3. public class ExcelSelectedResolve {
  4. /**
  5. * 下拉内容
  6. */
  7. private String[] source;
  8. /**
  9. * 设置下拉框的起始行,默认为第二行
  10. */
  11. private int firstRow;
  12. /**
  13. * 设置下拉框的结束行,默认为最后一行
  14. */
  15. private int lastRow;
  16. public String[] resolveSelectedSource(ExcelSelected excelSelected) {
  17. if (excelSelected == null) {
  18. return null;
  19. }
  20. // 获取固定下拉框的内容
  21. String[] source = excelSelected.source();
  22. if (source.length > 0) {
  23. return source;
  24. }
  25. // 获取动态下拉框的内容
  26. Class<? extends ExcelDynamicSelect>[] classes = excelSelected.sourceClass();
  27. if (classes.length > 0) {
  28. try {
  29. ExcelDynamicSelect excelDynamicSelect = classes[0].newInstance();
  30. String[] dynamicSelectSource = excelDynamicSelect.getSource();
  31. if (dynamicSelectSource != null && dynamicSelectSource.length > 0) {
  32. return dynamicSelectSource;
  33. }
  34. } catch (InstantiationException | IllegalAccessException e) {
  35. log.error("解析动态下拉框数据异常", e);
  36. }
  37. }
  38. return null;
  39. }
  40. }

十、导出接口

  1. /** 教师入职Excel模板导出 */
  2. @ApiLog("教师入职Excel模板导出")
  3. @GetMapping("export-template")
  4. @ApiOperationSupport(order = 7)
  5. @ApiOperation(value = "教师入职Excel模板导出")
  6. public void exportTeacher(HttpServletResponse response, HttpServletRequest request) {
  7. String filename = "教师入职信息导入模板";
  8. try {
  9. String userAgent = request.getHeader("User-Agent");
  10. if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
  11. // 针对IE或者以IE为内核的浏览器:
  12. filename = java.net.URLEncoder.encode(filename, "UTF-8");
  13. } else {
  14. // 非IE浏览器的处理:
  15. filename = new String(filename.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
  16. }
  17. response.setContentType("application/vnd.ms-excel");
  18. response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", filename + Func.formatDateTime(new Date()) + ".xlsx"));
  19. response.setHeader("Cache-Control", "no-cache");
  20. response.setHeader("Pragma", "no-cache");
  21. response.setDateHeader("Expires", -1);
  22. response.setCharacterEncoding("UTF-8");
  23. ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
  24. WriteSheet writeSheet = EasyExcelUtil.writeSelectedSheet(TeacherExcel.class, 0, "教师入职信息数据表");
  25. excelWriter.write(new ArrayList(), writeSheet);
  26. excelWriter.finish();
  27. } catch (UnsupportedEncodingException e) {
  28. e.printStackTrace();
  29. } catch (IOException e) {
  30. e.printStackTrace();
  31. }
  32. }

十一、导出效果图

完结,有问题欢迎指正,QQ 781999475

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索
排行榜