EasyExcel设置下拉值,兼容java不同版本

工具类在底部,先上代码。

静态下拉值设置

复制代码
public class ActiveGifts {
复制代码
private String lpbarcode;
复制代码
@ExcelProperty(value = "礼品名称")
private String lpname;
复制代码
@ExcelProperty(value = "是否可用")
@ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true)
private String isvalid;

}

动态下拉值设置

java8版本

实体

复制代码
public class ActiveGifts {
复制代码
private String lpbarcode;
复制代码
@ExcelProperty(value = "礼品名称")
private String lpname;
复制代码
@ExcelProperty(value = "是否可用")
@ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true)
private String isvalid;

}

示例方法:动态修改为:是,否

复制代码
public void downloadTemplate(HttpServletResponse response) {
    try {
        ExcelImportUtils<ActiveGifts> activeGiftsExcelImportUtils = new ExcelImportUtils<>(ActiveGifts.class);
        String[] strings = new String[1];
        strings[0] = "isvalid";
        Object[] values = new Object[strings.length];
        values[0] = new String[]{"是", "否"};
        //动态修改ExcelDropdown注解的值
        activeGiftsExcelImportUtils.setExcelDropdownValue(strings, values);
        activeGiftsExcelImportUtils.downLoad(response,new ArrayList<>(),"文件名", "sheet名");
    } catch (Exception e) {
        e.printStackTrace();
        throw new ServiceException("下载失败");
    }

}

java9及以上版本

实体

去掉这行:@ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true)

复制代码
public class ActiveGifts {
复制代码
private String lpbarcode;
复制代码
@ExcelProperty(value = "礼品名称")
private String lpname;
复制代码
@ExcelProperty(value = "是否可用")
private String isvalid;

}

示例方法

复制代码
public void downloadTemplate(HttpServletResponse response) {
    try {
        ExcelImportUtils<ActiveGifts> activeGiftsExcelImportUtils = new ExcelImportUtils<>(ActiveGifts.class);
        //动态设置下拉的值
        activeGiftsExcelImportUtils.setDropDownValue("isvalid", new String[]{"是", "否"});
        activeGiftsExcelImportUtils.downLoad(response, new ArrayList<>(), "文件名", "sheet名");
    } catch (Exception e) {
        e.printStackTrace();
        throw new ServiceException("下载失败");
    }
}

工具类

AnnotationUtil

复制代码
@Data
@Accessors(chain = true)
@ToString
public class AnnotationUtil<T> {

    public Class<T> clazz;

    public AnnotationUtil(Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * 动态修改对象属性上某个注解的属性值,通过getClazz()方法可以得到修改后的class
     *
     * @param fieldName       对象属性名称
     * @param annotationClass 注解class
     * @param attrName        注解属性名称
     * @param attrValue       注解属性值
     * @return 本工具类实例
     * @throws Exception 异常
     */
    public AnnotationUtil updateAnnoAttrValue(String fieldName, Class<? extends Annotation> annotationClass, String attrName, Object attrValue) throws Exception {
        Field[] declaredFields = this.clazz.getDeclaredFields();
        if (null != declaredFields && declaredFields.length != 0) {
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                if (fieldName.equals(declaredField.getName())) {
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(declaredField.getAnnotation(annotationClass));


                    Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
                    hField.setAccessible(true);
                    Map memberValues = (Map) hField.get(invocationHandler);
                    memberValues.put(attrName, attrValue);
                    break;
                }
            }
        }
        return this;
    }


    /**
     * !!!!!!!!!! java版本过高用不了,须在启动类中添加配置 参考:https://blog.csdn.net/wenxuankeji/article/details/140672099
     * 动态修改对象属性上某个注解的属性值,通过getClazz()方法可以得到修改后的class
     * @author ljd
     * @date 2024/12/4
     * @param fieldNames      字段数组
     * @param annotationClass 注解
     * @param attrNames       属性名数组-和字段依次匹配
     * @param attrValues      属性值数组-和字段依次匹配
     * @return
     * @throws Exception
     */
    public AnnotationUtil updateAnnoAttrValue(String[] fieldNames, Class<? extends Annotation> annotationClass, String[] attrNames, Object[] attrValues) throws Exception {
        if (fieldNames == null || fieldNames.length == 0 || attrNames == null || attrValues == null) {
            throw new Exception("参数错误!");
        }

        Field[] declaredFields = this.clazz.getDeclaredFields();
        if (null != declaredFields && declaredFields.length != 0) {
            HashMap<String, Field> map = new HashMap<>();
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                map.put(declaredField.getName(), declaredField);
            }

            for (int j = 0; j < fieldNames.length; j++) {
                if (!map.containsKey(fieldNames[j])) {
                    throw new Exception("字段名错误");
                }
                if(map.get(fieldNames[j]).getAnnotation(annotationClass) == null){
                    throw new Exception("该属性上无此注解");
                }
                InvocationHandler invocationHandler = Proxy.getInvocationHandler(map.get(fieldNames[j]).getAnnotation(annotationClass));
                Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
                hField.setAccessible(true);
                Map memberValues = (Map) hField.get(invocationHandler);
                memberValues.put(attrNames[j], attrValues[j]);
            }
        }
        return this;
    }

}

ExcelDropdown

复制代码
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDropdown {
    String[] value() default {};  //下拉列表
    boolean isAllowOtherValue() default false;   //是否允许设置其他的值。false:只能是下拉列表的值;true:允许列表之外的值
}

ExcelImportUtils

复制代码
@Slf4j
public class ExcelImportUtils<T extends Object> {

    private Class<T> clazz;
    // 对应列的下拉列表
    Map<Integer, Map<String, Object>> mapDropDown = new HashMap<>();

    //对应日期位置列表
    List<Integer> dateList = new ArrayList<>();


    public ExcelImportUtils(Class<T> clazz) {
        this.clazz = clazz;
    }

    //导入
    public List<T> excelImport(InputStream stream) throws Exception {

        List<T> list = new ArrayList<>();

        EasyExcel.read(stream, Object.class, new AnalysisEventListener<T>() {
            @SneakyThrows
            @Override
            public void invoke(T o, AnalysisContext analysisContext) {
                list.add(o);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                log.debug("导入完成!");
            }
        }).sheet().doRead();

        return list;
    }

    /**
     * 下载
     *
     * @param res       响应
     * @param data      下载的数据
     * @param fileName  文件名
     * @param sheetName 表名
     * @throws Exception
     */
    public void downLoad(HttpServletResponse res, List<T> data, String fileName, String sheetName) throws Exception {
        setMapDropDown(this.clazz);
        DropdownWriteHandler dropdownWriteHandler = new DropdownWriteHandler();
        EasyExcelFactory.write(getOutputStream(fileName, res), this.clazz).sheet(sheetName).registerWriteHandler(dropdownWriteHandler).doWrite(data);
    }

    //响应头封装
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        return response.getOutputStream();
    }

    //自定义处理器:单元格下拉列表格式
    class DropdownWriteHandler implements SheetWriteHandler {

        @Override
        public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        }

        @Override
        public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

            Sheet sheet = writeSheetHolder.getSheet();
            //开始设置下拉框
            DataValidationHelper helper = sheet.getDataValidationHelper();//设置下拉框
            for (Map.Entry<Integer, Map<String, Object>> entry : mapDropDown.entrySet()) {
                /***起始行、终止行、起始列、终止列**/
                CellRangeAddressList addressList = new CellRangeAddressList(1, 100000, entry.getKey(), entry.getKey());  // 检查的区域
                /***设置下拉框数据**/
                DataValidationConstraint constraint = helper.createExplicitListConstraint((String[]) entry.getValue().get("val"));
                DataValidation dataValidation = helper.createValidation(constraint, addressList);
                /***处理Excel兼容性问题**/
                if (dataValidation instanceof XSSFDataValidation) {
                    dataValidation.setSuppressDropDownArrow(true);  // 验证输入数据是否真确
                    dataValidation.setShowErrorBox(true);  // 输入无效值时是否显示错误框
                    dataValidation.setShowPromptBox(true);  // 设置无效值时 是否弹出提示框
                    dataValidation.createPromptBox("温馨提示", "只能选择列表中的值!!!");   // 设置无效值时的提示框内容
                    sheet.addValidationData(dataValidation);
                } else {
                    dataValidation.setSuppressDropDownArrow(false);
                }
                sheet.addValidationData(dataValidation);
            }

            /***时间格式校验**/
            for (int i : dateList) {
                DataValidationConstraint constraint2 = helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN, "Date(1900, 1, 1)", "Date(2100, 12, 31)", "yyyy/MM/dd");
                DataValidation dataValidation2 = helper.createValidation(constraint2, new CellRangeAddressList(1, 100000, i, i));

                //校验时间
                dataValidation2.setSuppressDropDownArrow(true);  // 验证输入数据是否真确
                dataValidation2.setShowErrorBox(true);  // 输入无效值时是否显示错误框
                dataValidation2.setShowPromptBox(true);  // 设置无效值时 是否弹出提示框
                dataValidation2.createPromptBox("温馨提示", "请输入[yyyy-MM-dd]格式日期!!!");   // 设置无效值时的提示框内容
                sheet.addValidationData(dataValidation2);
            }

            //下面定时样式的
            Row row = sheet.getRow(0);
            if (row != null) {
                Workbook workbook = writeWorkbookHolder.getWorkbook();
                row.setHeight((short) 500);
                for (int i = 0; i < row.getLastCellNum(); i++) {
                    sheet.setColumnWidth(i, 5000);
                    Cell cell = row.getCell(i);
                    cell.setCellStyle(setStyle(workbook));
                }
                row.setHeight((short) (205 * 7));
            }
        }

        //设置单元格样式
        public CellStyle setStyle(Workbook wb) {
            Font dataFont = wb.createFont();
            dataFont.setColor(new HSSFColor().getIndex());
            dataFont.setFontName("宋体");
            dataFont.setFontHeight((short) 240);
            dataFont.setBold(true);
            dataFont.setFontHeightInPoints((short) 10);
            CellStyle dataStyle = wb.createCellStyle();
            dataStyle.setFont(dataFont);
            dataStyle.setWrapText(true);
            dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            dataStyle.setAlignment(HorizontalAlignment.CENTER);
            return dataStyle;
        }

    }

    /**
     * 设置下拉值
     * (提供该方法,主要解决java版本过高不能动态修改ExcelDropdown的value值的问题)
     *
     * @param field   字段名
     * @param strings 列表值
     * @author ljd
     * @date 2024/12/5
     */
    public void setDropDownValue(String field, String[] strings) {
        Map<Integer, Map<String, Object>> mapDropDown = new HashMap<>();
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("val", strings);
        Field[] fields = clazz.getDeclaredFields();
        //用于标志是excel的哪个列
        int flag = -1;
        for (int i = 0; i < fields.length; i++) {
            ExcelProperty annotation = fields[i].getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                flag = flag + 1;
            }
            if (fields[i].getName().equals(field)) {
                mapDropDown.put(flag, stringObjectHashMap);
            }
        }
        this.mapDropDown = mapDropDown;
    }

    //下拉列表
    private void setMapDropDown(Class clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();
        if (fields == null) {
            throw new Exception("属性为空");
        }
        //用于标记是第几个注解
        int annotationCount = -1;
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            //DateTimeFormat注解
            if (field.isAnnotationPresent(DateTimeFormat.class)) {
                int index = field.getAnnotation(ExcelProperty.class).index(); //位置
                dateList.add(index);
            }
            //判断这个字段上是否有相应的注解信息(ExcelDropdown.class)
            if (field.isAnnotationPresent(ExcelDropdown.class) && field.isAnnotationPresent(ExcelProperty.class)) {
//                int index = field.getAnnotation(ExcelProperty.class).index(); //位置
                String[] values = field.getAnnotation(ExcelDropdown.class).value(); //下拉列表的value内容
                boolean allowOtherValue = field.getAnnotation(ExcelDropdown.class).isAllowOtherValue();  //下拉列表的isAllowOtherValue值

                Map<String, Object> map = new HashMap<>();
                map.put("val", values);
                map.put("isAllow", allowOtherValue);
                mapDropDown.put(++annotationCount, map);
            }
        }
    }

    //设置对应属性的@ExcelDropdown注解的对应属性的对应值。
    // 如:fieldNames[0] 对应 attrNames[0] 对应 attrValues[0], 否则出错。
    public void setExcelDropdownValue(String[] fieldNames, Object[] attrValues) throws Exception {
        AnnotationUtil<T> s = new AnnotationUtil<T>(this.clazz);
        String[] attrNames = new String[fieldNames.length]; //ExcelDropdown注解只有value属性
        for (int i = 0; i < attrNames.length; i++) {
            attrNames[i] = "value";
        }
        //更新字段上ExcelDropdown注解的value属性的值
        s.updateAnnoAttrValue(fieldNames, ExcelDropdown.class, attrNames, attrValues);
    }
}

前端接收并下载

发送请求并处理res

复制代码
//下载模板
const downloadTemp = () => {
   activeGiftsApi.activeGiftsDownloadTemp().then((res) => {
      let blob = new Blob([res.data], { type: 'application/vnd.ms-excel;charset=utf-8' })
      let filename = '活动礼品导入模板'
      let url = window.URL.createObjectURL(blob)
      let aLink = document.createElement('a')
      aLink.style.display = 'none'
      aLink.href = url
      aLink.setAttribute('download', filename)
      document.body.appendChild(aLink)
      aLink.click()
      document.body.removeChild(aLink)
      window.URL.revokeObjectURL(url)
   })
}
相关推荐
oh,huoyuyan6 分钟前
火语言RPA--Excel删除内容
excel·rpa
南山十一少12 分钟前
Spring Security+JWT+Redis实现项目级前后端分离认证授权
java·spring·bootstrap
427724002 小时前
IDEA使用git不提示账号密码登录,而是输入token问题解决
java·git·intellij-idea
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
李长渊哦2 小时前
常用的 JVM 参数:配置与优化指南
java·jvm
计算机小白一个2 小时前
蓝桥杯 Java B 组之设计 LRU 缓存
java·算法·蓝桥杯
失败尽常态5234 小时前
用Python实现Excel数据同步到飞书文档
python·excel·飞书
南宫生5 小时前
力扣每日一题【算法学习day.132】
java·学习·算法·leetcode
计算机毕设定制辅导-无忧学长5 小时前
Maven 基础环境搭建与配置(一)
java·maven
风与沙的较量丶6 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言