虽然最之前是在其他地方看到的,但最终因缘巧合下找到了原文,还是尊重一下原作者。
参考引用了这位佬的博客,确实方便使用。
https://blog.csdn.net/qq_45914616/article/details/137200688?spm=1001.2014.3001.5502
如果想通过枚举类来做替换,而不是直接写替换内容的,请看另一篇:EasyExcel 自定义转换器、自定义导出枚举类替换,完整代码+详细注释说明
这是一个基于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
*/
@Override
public Class<?> supportJavaTypeKey() {
return Object.class;
}
/**
* excel中的数据类型,统一设置字符串
*
* @return
*/
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 导入转换
*
* @param cellData 当前单元格对象
* @param contentProperty 当前单元格属性
* @param globalConfiguration
* @return
* @throws Exception
*/
@Override
public 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属性为integer
if (single) {
String res = enumMap.get(cellMsg);
return StringUtils.hasText(res) ? Integer.valueOf(res) : null;
} else {
// 多选分隔符
String spiteChar = enumFiledConvert.spiteChar();
// 多选枚举,默认Java属性为字符串,格式为 key1,key2,key3
List<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
*/
@Override
public 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属性为integer
if (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,key3
List<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,转换成key
enumMap.put(data[1], data[0]);
} else {
// 写excel Java中的数据都是key,转换成value
enumMap.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,转换成value
fontColorMap.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页数据写出,这个就和原生一致了。