EasyExcel 自定义转换器、自定义导出字典映射替换、满足条件内容增加样式,完整代码+详细注释说明
虽然最之前是在其他地方看到的,但最终因缘巧合下找到了原文,还是尊重一下原作者。
参考引用了这位佬的博客,确实方便使用。
https://blog.csdn.net/qq_45914616/article/details/137200688?spm=1001.2014.3001.5502
这是一个基于Easyexcel通过注解的方式,
在巨人的肩膀上,对相关方法进行了一定的调整和说明注释,方便使用与定制调整为自己所需要的内容格式。
能做到的事情有:
- 通过注解实现自定义字典值映射,满足数字、英文等符合格式的导出字典映射,导入也支持。
- 支持字典值映射的单选或多选模式
- 支持对满足某些条件的内容,在输出时添加一些样式,比如字体颜色。
以下正文:
1.定义了一个枚举类
/*** @Description: 符号枚举类* @author: CloverAn* @email: * @created: 2023/02/27 21:01*/
public class SymbolConstant {private SymbolConstant() {}public static final String SPE1 = ",";/*** 竖线*/public static final String SPE9 = "\\|";}
多余出来的已经做了删除,这两个使用了所以保留,各位可以自行替换修改。
2.添加自定义注解
import com.data.utils.constant.SymbolConstant;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Description: 枚举数值的自定义注解* @author: CloverAn* @email:* @created: 2024/06/25 15:57*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumFiledConvert {/*** 枚举映射map key-value,key-value,key-value,key-value* <p>* key|value,key|value* <p>* 注意,这里的key和value都是英文字符串,所以实际上他可以是数字,也可以是英文,也可以是否个字符串* 例如:0|满勤,0-1|特殊,Y|是,n|error** @return*/String enumMap() default "未匹配到映射字典";/*** 枚举类导入、导出在excel中的分隔符号** @return*/String spiteChar() default SymbolConstant.SPE1;/*** 枚举类导入导出字典分隔符*/String dictChar() default SymbolConstant.SPE9;/*** 单选 or 多选* <p>* 即多个枚举映射,都会比对输出,例如** @return* @EnumFiledConvert(enumMap = "1-篮球,2-足球,3-乒乓球,4-羽毛球",single = false)* <p>* 字段值是"1,2,3,4"* 则最终输出的是"篮球,足球,乒乓球,羽毛球"* <p>* 字典值是"1,2"* 则最终输出的是"篮球,足球"*/boolean single() default true;/*** 条件样式,对满足条件的字符串,设置红色字体*/String fontColorMap() default "";
}
1.这里将原作者的直接使用符号,以及符合使用的 逗号和横杠 调整了以下,因为个人有这一块的需求,最终使用的是 逗号和竖线。
2.修改了默认值,添加了部分注释,更通俗易懂。
3.在作者原有的基础上,增加了字体颜色的注解属性,用于接收指定的判断条件和颜色要求
3.重写EasyExcel转换器接口,对excel读和写方法都进行自定义
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.data.keepriskexport.utils.constant.SymbolConstant;
import org.springframework.util.StringUtils;import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 导入导出针对枚举类型的转换器** @author CloverAn*/
public class EasyExcelConvert implements Converter<Object> {/*** 枚举列表*/private Map<String, String> enumMap = new HashMap<>();/*** 条件列表*/private Map<String, String> fontColorMap = new HashMap<>();/*** excel转化后的类型** @return*/@Overridepublic Class<?> supportJavaTypeKey() {return Object.class;}/*** excel中的数据类型,统一设置字符串** @return*/@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 导入转换** @param cellData 当前单元格对象* @param contentProperty 当前单元格属性* @param globalConfiguration* @return* @throws Exception*/@Overridepublic Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {String cellMsg = cellData.getStringValue();Field field = contentProperty.getField();EnumFiledConvert enumFiledConvert = field.getAnnotation(EnumFiledConvert.class);if (enumFiledConvert == null) {return null;}String enumStr = enumFiledConvert.enumMap();// 解析枚举映射关系getEnumMap(enumStr, true);// 是否为单选boolean single = enumFiledConvert.single();// 如果是单选,默认Java属性为integerif (single) {String res = enumMap.get(cellMsg);return StringUtils.hasText(res) ? Integer.valueOf(res) : null;} else {// 多选分隔符String spiteChar = enumFiledConvert.spiteChar();// 多选枚举,默认Java属性为字符串,格式为 key1,key2,key3List<String> strStr = Arrays.asList(cellMsg.split(spiteChar)).stream().map(s -> String.valueOf(enumMap.get(s))).collect(Collectors.toList());String str = String.join(spiteChar, strStr);return str;}}/*** 导出转化** @param value 当前值* @param contentProperty 当前单元格属性* @param globalConfiguration* @return* @throws Exception*/@Overridepublic WriteCellData<?> convertToExcelData(Object value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {Field field = contentProperty.getField();EnumFiledConvert enumFiledConvert = field.getAnnotation(EnumFiledConvert.class);if (enumFiledConvert == null) {return new WriteCellData();}// 解析枚举字符串String enumStr = enumFiledConvert.enumMap();getEnumMap(enumStr, false);// 是否为单选boolean single = enumFiledConvert.single();//条件样式String conditions = enumFiledConvert.fontColorMap();//解析条件样式getConditionsMap(conditions);// 如果是单选,默认Java属性为integerif (single) {//输出内容原始值String valueOf = String.valueOf(value);//输出内容转换,字典值String orDefault = enumMap.getOrDefault(valueOf, "");//创建输出内容WriteCellData writeCellData = new WriteCellData(orDefault);setWriteCellStyle(writeCellData, orDefault);return writeCellData;} else {// 多选分隔符String spiteChar = enumFiledConvert.spiteChar();// 多选枚举,默认Java属性为字符串,格式为 key1,key2,key3List<String> strStr = Arrays.asList(String.valueOf(value).split(spiteChar)).stream().map(s -> String.valueOf(enumMap.get(s))).collect(Collectors.toList());String str = String.join(spiteChar, strStr);WriteCellData writeCellData = new WriteCellData(str);return writeCellData;}}/*** 根据注解配置的枚举映射字符串进行解析到map中** @param mapStr* @param readOrWrite 读excel 、 写excel*/private void getEnumMap(String mapStr, boolean readOrWrite) {String[] enumS = mapStr.split(SymbolConstant.SPE1);for (String anEnum : enumS) {String[] data = anEnum.split(SymbolConstant.SPE9);if (readOrWrite) {// 读excel excel中的数据都是value,转换成keyenumMap.put(data[1], data[0]);} else {// 写excel Java中的数据都是key,转换成valueenumMap.put(data[0], data[1]);}}}/*** 根据注解配置的枚举映射字符串进行解析到map中* 只有写出的时候生效*/private void getConditionsMap(String conditions) {if (conditions != null && !conditions.isEmpty()) {String[] conditionsMaps = conditions.split(SymbolConstant.SPE1);for (String condition : conditionsMaps) {String[] data = condition.split(SymbolConstant.SPE9);// 写excel Java中的数据都是key,转换成valuefontColorMap.put(data[0], data[1]);}}}/**** 取到什么颜色,填充什么颜色,没有则不填充。** @param writeCellData*/private void setWriteCellStyle(WriteCellData writeCellData, String orDefault) {//输出样式转换,颜色值,默认黑色short orDefaultStyle = 0;//条件,满足条件则修改内容,可以叠加更多的条件,只要符合k-v规则,即比较k,填充v//因为这里是中文k 和 颜色索引,这里比较的就是中文,如果能保障k不重复,则k可以是数字、字符等,对应的v也就可以是数字、字符等,//这样就可以按照需求对单元格式进行一定的操作编辑//需要注意的是,这里用的转换之后的输出值进行的比较,有需要使用原始值的可以自行微调一下if (fontColorMap != null && fontColorMap.containsKey(orDefault)) {WriteFont writeFont = new WriteFont();WriteCellStyle writeCellStyle = new WriteCellStyle();orDefaultStyle = Short.parseShort(fontColorMap.getOrDefault(orDefault, ""));writeFont.setColor(orDefaultStyle);writeCellStyle.setWriteFont(writeFont);writeCellData.setWriteCellStyle(writeCellStyle);}}
}
1.这里面只是修改了部分直接引用字符的地方,将其使用字典值代替。
2.在作者原有的基础上,增加和调整了满足条件后,对字体颜色标记的功能。相关样式可以自己做调整,比如添加背景色等。
逻辑也跟简单,就是拿到转换之后的值,通过这个值,与注解传递的值进行比较,满足条件则添加样式。
因为添加样式只存在于导出的时候,所以只需要关注导出的方法体即可。多选的我暂时没有场景,没有添加,实际逻辑是一样的。
4.测试实体类(因为我懒。不是,要尊重作者! )
import com.alibaba.excel.annotation.ExcelProperty;
import com.example.test.excel.EasyExcelConvert;
import com.example.test.excel.EnumFiledConvert;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @Author 996* @Date 2024/3/20*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExcelDto {@ExcelProperty(value = "姓名")private String name;@ExcelProperty(value = "性别",converter = EasyExcelConvert.class)@EnumFiledConvert(enumMap = "0|保密,1|男,2|女")private Integer sex;@ExcelProperty(value = "爱好",converter = EasyExcelConvert.class)@EnumFiledConvert(enumMap = "1|篮球,2|足球,3|乒乓球,4|羽毛球",, fontColorMap = "篮球|2",single = false)private String hobbies;
}
使用时,只需要 2个步骤 ,(1)在实体类上添加自定义注解,(2)在原生的 @ExcelProperty 注解中,指定自定义解析Convert。
这时导出,对应字段列的值,就会被替换成对应的数据项。
需要注意:
1.跟原作者不一样,这里多个字典值之间使用的还是逗号,但是k-v的映射关系使用的是竖线。
2.这里的k-v不一定要符合现在的这种数字和中文的组合,只需要保证k-v分别为字符串,且内容不含逗号竖线即可。比如其他字典映射:Y|是 Y-1|特例1 N|bushi 是|正确的答案 只要符合对应的格式即可,更加灵活。
3.可以修改自定义注解中的符号,以调整对应的格式,但是需要注意,不是所有的都可以直接兼容,需要进行一定的转义,这一点肯定难不倒聪明的你。
4.注意第三个属性的注解,多加了fontColorMap,前面的是输出的值,可以对应enumMap里面的字典值转换,后面的是颜色索引,这里的2标识为红色,可以看org.apache.poi.ss.usermodel.IndexedColors; 中的IndexedColors.RED.getIndex()枚举,颜色索引很全。
基于以上字体颜色修改的逻辑,我们能够拿到所有输出的值的内容,包括原始值,那么我们就可以通过一系列的条件,来实现我们对某些特定内容的数据进行样式调整,包括但不限于字体调整、字体颜色调整、字体颜色调整、数据值调整、单元格背景颜色调整、单元格边框调整等等等等。
5.输出
这个大家都会,这里摘一部分代码套用
//数据集合,这里是用的实体类
List<ClockCount> countList = new ArrayList<>();ExcelWriter excelWriterCount = EasyExcel.write("文件完整路径,含后缀").build();
WriteSheet writeSheetCount = EasyExcel.writerSheet("表格中sheet名称").build();//我这是用实体类进行的文件输出
writeSheetCount.setClazz(ClockCount.class);
//写出数据
excelWriterCount.write(countList , writeSheetCount);
excelWriterCount.finish();
执行导出即可完成。通过循环,或者针对同一文件名,相同sheet名称或者不相同名称,可以实现对同一表格相同sheet页或多个sheet页数据写出,这个就和原生一致了。