【功能增强】状态转换成中文
需求:审批有很多状态是数字类型的,转成对应的中文名称导出
类似:
看了一下easyexcel在实体注解中存在注解且可以自定义 例如:
public class ConverterData {
public @interface ExcelProperty {
/**
* The name of the sheet header.
*
* <p>
* write: It automatically merges when you have more than one head
* 工作表标题的名称。
* write:当有多个头时,自动合并
* read:当有多个头时,取最后一个
* 返回:sheet header 的名称
* <p>
* read: When you have multiple heads, take the last one
*
* @return The name of the sheet header
*/
String[] value() default {""};
/**
* Index of column
*
* Read or write it on the index of column, If it's equal to -1, it's sorted by Java class.
*
* priority: index > order > default sort
*
* @return Index of column
*/
int index() default -1;
/**
* Defines the sort order for an column.
*
* priority: index > order > default sort
*
* @return Order of column
*/
int order() default Integer.MAX_VALUE;
/**
* Force the current field to use this converter.
*
* @return Converter
*/
Class<? extends Converter<?>> converter() default AutoConverter.class;
/**
*
* default @see com.alibaba.excel.util.TypeUtil if default is not meet you can set format
*
* @return Format string
* @deprecated please use {@link com.alibaba.excel.annotation.format.DateTimeFormat}
*/
@Deprecated
String format() default "";
}
}
目前eec版本不支持
public @interface ExcelColumn {
String value() default "";
boolean share() default false;
HeaderComment comment() default @HeaderComment;
String format() default "";
boolean wrapText() default false;
int colIndex() default -1;
double maxWidth() default -1.0;
boolean hide() default false;
}
EEC有两种方式实现此类需求,一时使用ConversionProcessor,另一种是实现自定义ICellValueAndStyle,可以对任意类型进行转换。
- 指定表头并设置Conversion
String[] typeName = { "待提交", "审核中", "一审", "二审", "终审" };
new Workbook()
.addSheet(new ListSheet<>(new Column("申请单", "id")
, new Column("抬头", "title")
, new Column("审核状态", "status", int.class, n -> typeName[(int) n])) )// 将状态0,1,2,3,4转为"待提交", "审核中", "一审", "二审", "终审"
.writeTo(Paths.get("./"));
- 自定义ICellValueAndStyle
new Workbook()
.addSheet(new ListSheet<>("请款审核").setCellValueAndStyle(new XMLCellValueAndStyle() {
@Override
public void setCellValue(int row, Cell cell, Object e, Column hc, Class<?> clazz, boolean hasProcessor) {
// 对审核状态特殊处理
if ("status".equals(hc.key)) {
cell.setSv(typeName[(int) e]);
} else super.setCellValue(row, cell, e, hc, clazz, hasProcessor);
}
})).writeTo(Paths.get("./"));
第三种是在Java Bean中增加一个获取文本的方法并在方法上添加@ExcelColumn注解,如果不希望被JSON序列化还需要加上@JsonIgnore
···
@ExcelColumn
public String getStatusTxt() {
return "审核中"; // 根据status的值返回文件
}
···
EEC有两种方式实现此类需求,一时使用ConversionProcessor,另一种是实现自定义ICellValueAndStyle,可以对任意类型进行转换。
- 指定表头并设置Conversion
String[] typeName = { "待提交", "审核中", "一审", "二审", "终审" }; new Workbook() .addSheet(new ListSheet<>(new Column("申请单", "id") , new Column("抬头", "title") , new Column("审核状态", "status", int.class, n -> typeName[(int) n])) )// 将状态0,1,2,3,4转为"待提交", "审核中", "一审", "二审", "终审" .writeTo(Paths.get("./"));
- 自定义ICellValueAndStyle
new Workbook() .addSheet(new ListSheet<>("请款审核").setCellValueAndStyle(new XMLCellValueAndStyle() { @Override public void setCellValue(int row, Cell cell, Object e, Column hc, Class<?> clazz, boolean hasProcessor) { // 对审核状态特殊处理 if ("status".equals(hc.key)) { cell.setSv(typeName[(int) e]); } else super.setCellValue(row, cell, e, hc, clazz, hasProcessor); } })).writeTo(Paths.get("./"));
首先,感谢您有时间回复提出的问题 ,我经测试第一、第二种测试 确实可以实现,但是可能不是很友好,如果有同样业务,代码会冗余,就是假设有5个类似业务,就需要写5份类似的代码
如果习惯使用注解可以参考WIKI自定义注解,只需要在createColumn方里下添加Converter转换即可,具体的Converter实现类需要实现ConversionProcessor接口
示例代码如下
// 复制wiki中的代码然后在createColumn中添加如下代码,放在`5. 列宽`下面即可
// 6. Converter
Class<? extends Converter<?>> clazz = ec.converter();
if (!AutoConverter.class.isAssignableFrom(clazz) && ConversionProcessor.class.isAssignableFrom(clazz)) {
try {
column.setProcessor((ConversionProcessor) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
这是Converter代码,需要实现ConversionProcessor接口并实现转换
public class StatusConverter implements com.alibaba.excel.converters.Converter<Integer>, ConversionProcessor {
static final String[] typeName = { "待提交", "审核中", "一审", "二审", "终审" };
@Override
public Object conversion(Object v) {
if (v == null) return null;
int n = Integer.parseInt(v.toString());
return n >= 0 && n < typeName.length ? typeName[n] : null;
}
}
使用以上代码就可以实现自定义Converter注解
测试代码如下
// 测试对象
public static class Entry {
@com.alibaba.excel.annotation.ExcelProperty("审核编号")
private String no;
@com.alibaba.excel.annotation.ExcelProperty(value = "审核状态", converter = StatusConverter.class) // <- 这里指定StatusConverter
private Integer status;
public static List<Entry > randomTestData() {
List<Entry > list = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
Entry e = new Entry ();
e.no = getRandomString();
e.status = random.nextInt(4);
list.add(e);
}
return list;
}
}
// 导出代码不变
new Workbook().addSheet(new EasyExcelSupportListSheet<>(Entry.randomTestData())).writeTo(Paths.get("d:/自定义转换器.xlsx"));
你可以使用自定义注解完全替换掉EEC的注解,这样就不需要切换现有实体中的注解了
如果习惯使用注解可以参考WIKI自定义注解,只需要在createColumn方里下添加Converter转换即可,具体的Converter实现类需要实现ConversionProcessor接口
示例代码如下
// 复制wiki中的代码然后在createColumn中添加如下代码,放在`5. 列宽`下面即可 // 6. Converter Class<? extends Converter<?>> clazz = ec.converter(); if (!AutoConverter.class.isAssignableFrom(clazz) && ConversionProcessor.class.isAssignableFrom(clazz)) { try { column.setProcessor((ConversionProcessor) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }这是Converter代码,需要实现ConversionProcessor接口并实现转换
public class StatusConverter implements com.alibaba.excel.converters.Converter<Integer>, ConversionProcessor { static final String[] typeName = { "待提交", "审核中", "一审", "二审", "终审" }; @Override public Object conversion(Object v) { if (v == null) return null; int n = Integer.parseInt(v.toString()); return n >= 0 && n < typeName.length ? typeName[n] : null; } }使用以上代码就可以实现自定义Converter注解
测试代码如下
// 测试对象 public static class Entry { @com.alibaba.excel.annotation.ExcelProperty("审核编号") private String no; @com.alibaba.excel.annotation.ExcelProperty(value = "审核状态", converter = StatusConverter.class) // <- 这里指定StatusConverter private Integer status; public static List<Entry > randomTestData() { List<Entry > list = new ArrayList<>(10); for (int i = 0; i < 10; i++) { Entry e = new Entry (); e.no = getRandomString(); e.status = random.nextInt(4); list.add(e); } return list; } } // 导出代码不变 new Workbook().addSheet(new EasyExcelSupportListSheet<>(Entry.randomTestData())).writeTo(Paths.get("d:/自定义转换器.xlsx"));你可以使用自定义注解完全替换掉EEC的注解,这样就不需要切换现有实体中的注解了
EasyExcelSupportListSheet这个类 , 不是eec的内部类吗, 我这边没有这个类,是版本的问题吗
是一个自定义类并不是eec的代码,复制wiki中的代码新建一个任意名的类即可
在 2023年10月19日,09:34,xiaoxpai @.***> 写道:
EEC有两种方式实现此类需求,一时使用ConversionProcessor,另一种是实现自定义ICellValueAndStyle,可以对任意类型进行转换。
指定表头并设置Conversion String[] typeName = { "待提交", "审核中", "一审", "二审", "终审" }; new Workbook() .addSheet(new ListSheet<>(new Column("申请单", "id") , new Column("抬头", "title") , new Column("审核状态", "status", int.class, n -> typeName[(int) n])) )// 将状态0,1,2,3,4转为"待提交", "审核中", "一审", "二审", "终审" .writeTo(Paths.get("./")); 自定义ICellValueAndStyle new Workbook() .addSheet(new ListSheet<>("请款审核").setCellValueAndStyle(new XMLCellValueAndStyle() { @Override public void setCellValue(int row, Cell cell, Object e, Column hc, Class<?> clazz, boolean hasProcessor) { // 对审核状态特殊处理 if ("status".equals(hc.key)) { cell.setSv(typeName[(int) e]); } else super.setCellValue(row, cell, e, hc, clazz, hasProcessor); } })).writeTo(Paths.get("./")); 首先,感谢您有时间回复提出的问题 ,我经测试第一、第二种测试 确实可以实现,但是可能不是很友好,如果有同样业务,代码会冗余,就是假设有5个类似业务,就需要写5份类似的代码
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.
很抱歉,由于工作原因,没有及时回复,还是非常感谢您的回复
我根据您的步骤,复制wiki中的代码新建一个 类 但实际上有一个构造器的问题,
step1: 新建了一个类,
public class SupportEasyExcelSheet {
public static class EasyExcelSupportListSheet<T> extends ListSheet<T> {
/**
* 过滤不需要导出的字段
*
* @param ao {@link T}对象定义的所有{@link java.lang.reflect.Field}和 {@link java.lang.reflect.Method}
* @return true: 忽略该字段(该字段不导出)
*/
@Override
protected boolean ignoreColumn(AccessibleObject ao) {
// Easyexcel使用注解com.alibaba.excel.annotation.ExcelIgnore来标记忽略字段
return ao.getAnnotation(ExcelIgnore.class) != null;
}
/**
* 创建列
*
* @param ao {@link T}对象定义的所有{@link java.lang.reflect.Field}和 {@link java.lang.reflect.Method}
* @return 列定义,可以返回null,表示忽略该字段
*/
@Override
protected ListSheet.EntryColumn createColumn(AccessibleObject ao) {
// 1. 过滤掉需要忽略的字段
if (ignoreColumn(ao)) return null;
ao.setAccessible(true);
// 2. Easyexcel使用com.alibaba.excel.annotation.ExcelProperty标记,这里可以替换为任意的定义的注解,当然需要包含一些列头的信息
ExcelProperty ec = ao.getAnnotation(ExcelProperty.class);
if (ec != null) {
ListSheet.EntryColumn column = new ListSheet.EntryColumn(ec.value()[0], EMPTY);
/*
Easyexcel格式化有3种方式
一是放在{@code ExcelProperty#format} 注解
二是使用DateTimeFormat和NumberFormat注解
所以这里要兼容这3种方式
EEC统一使用NumFmt
*/
DateTimeFormat dateTimeFormat = ao.getAnnotation(DateTimeFormat.class);
NumberFormat numberFormat = ao.getAnnotation(NumberFormat.class);
// 3. 格式化(支持日期和数字)
if (isNotEmpty(ec.format())) {
column.setNumFmt(ec.format());
} else if (dateTimeFormat != null) {
column.setNumFmt(dateTimeFormat.value());
} else if (numberFormat != null) {
column.setNumFmt(numberFormat.value());
}
// 4. 列位置
if (ec.index() > -1) {
column.setColIndex(ec.index());
}
// 5. 列宽
ColumnWidth columnWidth = ao.getAnnotation(ColumnWidth.class);
if (columnWidth != null && columnWidth.value() > 0) {
column.width = columnWidth.value();
}
// 6. Converter
Class<? extends Converter<?>> clazz = ec.converter();
if (!AutoConverter.class.isAssignableFrom(clazz) && ConversionProcessor.class.isAssignableFrom(clazz)) {
try {
column.setProcessor((ConversionProcessor) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
// TODO 其它属性
return column;
}
return null;
}
}
}
step2 测试类
只需要重载所有父类构造器即可,那部分代码属于基础代码无特殊意义所以wiki里没有写。比如SupportEasyExcelSheet(Column...headers) { super(headers); }
wiki已更新
新建java文件EasyExcelSupportListSheet.java,内容如下。支持多行表头,format和Convert转换
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.converters.AutoConverter;
import com.alibaba.excel.converters.Converter;
import org.ttzero.excel.processor.ConversionProcessor;
import java.lang.reflect.AccessibleObject;
import java.util.List;
import static org.ttzero.excel.util.StringUtil.isNotEmpty;
/**
* 支持EasyExcel注解
*
* @param <T>
*/
public class EasyExcelSupportListSheet<T> extends ListSheet<T> {
public EasyExcelSupportListSheet() {
}
public EasyExcelSupportListSheet(String name) {
super(name);
}
public EasyExcelSupportListSheet(Column... columns) {
super(columns);
}
public EasyExcelSupportListSheet(String name, Column... columns) {
super(name, columns);
}
public EasyExcelSupportListSheet(String name, WaterMark waterMark, Column... columns) {
super(name, waterMark, columns);
}
public EasyExcelSupportListSheet(List<T> data) {
super(data);
}
public EasyExcelSupportListSheet(String name, List<T> data) {
super(name, data);
}
public EasyExcelSupportListSheet(List<T> data, Column... columns) {
super(data, columns);
}
public EasyExcelSupportListSheet(String name, List<T> data, Column... columns) {
super(name, data, columns);
}
public EasyExcelSupportListSheet(List<T> data, WaterMark waterMark, Column... columns) {
super(data, waterMark, columns);
}
public EasyExcelSupportListSheet(String name, List<T> data, WaterMark waterMark, Column... columns) {
super(name, data, waterMark, columns);
}
/**
* 过滤不需要导出的字段
*
* @param ao {@link T}对象定义的所有{@link java.lang.reflect.Field}和 {@link java.lang.reflect.Method}
* @return true: 忽略该字段(该字段不导出)
*/
@Override
protected boolean ignoreColumn(AccessibleObject ao) {
// Easyexcel使用注解com.alibaba.excel.annotation.ExcelIgnore来标记忽略字段
// 可以是任意注解比如JsonIgnore也不能导出
return ao.getAnnotation(ExcelIgnore.class) != null;
}
/**
* 创建列
*
* @param ao {@link T}对象定义的所有{@link java.lang.reflect.Field}和 {@link java.lang.reflect.Method}
* @return 列定义,可以返回null,表示忽略该字段
*/
@Override
protected ListSheet.EntryColumn createColumn(AccessibleObject ao) {
// 1. 过滤掉需要忽略的字段
if (ignoreColumn(ao)) return null;
ao.setAccessible(true);
// 2. Easyexcel使用com.alibaba.excel.annotation.ExcelProperty标记,这里可以替换为任意的定义的注解,当然需要包含一些列头的信息
ExcelProperty ec = ao.getAnnotation(ExcelProperty.class);
if (ec != null) {
String[] columnNames = ec.value();
ListSheet.EntryColumn column = null;
// 多行表头
if (columnNames.length > 1) {
for (String cn : columnNames) {
ListSheet.EntryColumn col = new ListSheet.EntryColumn(cn);
if (column == null) {
column = col;
} else {
column.addSubColumn(col);
}
}
}
// 单行表头
else if (columnNames.length == 1) {
column = new ListSheet.EntryColumn(columnNames[0]);
} else {
column = new ListSheet.EntryColumn("");
}
/*
Easyexcel格式化有3种方式
一是放在{@code ExcelProperty#format} 注解
二是使用DateTimeFormat和NumberFormat注解
所以这里要兼容这3种方式
EEC统一使用NumFmt
*/
DateTimeFormat dateTimeFormat = ao.getAnnotation(DateTimeFormat.class);
NumberFormat numberFormat = ao.getAnnotation(NumberFormat.class);
// 3. 格式化(支持日期和数字)
if (isNotEmpty(ec.format())) {
column.setNumFmt(ec.format());
} else if (dateTimeFormat != null) {
column.setNumFmt(dateTimeFormat.value());
} else if (numberFormat != null) {
column.setNumFmt(numberFormat.value());
}
// 4. 列位置
if (ec.index() > -1) {
column.setColIndex(ec.index());
}
// 5. 列宽
ColumnWidth columnWidth = ao.getAnnotation(ColumnWidth.class);
if (columnWidth != null && columnWidth.value() > 0) {
column.width = columnWidth.value();
}
// 6. Convert
Class<? extends Converter<?>> clazz = ec.converter();
if (!AutoConverter.class.isAssignableFrom(clazz) && ConversionProcessor.class.isAssignableFrom(clazz)) {
try {
column.setProcessor((ConversionProcessor) clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
// TODO 其它属性
return column;
}
return null;
}
}
首先表示一下感谢,很幸运能够得到您的帮助,祝愿您在事业上持续取得成功
自己代码测试之后,可以实现,这里留个脚印,希望可以帮助到更多的人🎃😀
...
@com.alibaba.excel.annotation.ExcelProperty(value = "审核状态", converter = StatusConverter.class) // <- 这里指定StatusConverter
private Integer status;
export
...
new Workbook()
.addSheet(new EasyExcelSupportListSheet(mc)).writeTo(Paths.get("e:/excel"));
后续会在下一个release把这个功能加进去吗,如果您有业务时间的话
可以考虑,比起注解更推荐使用指定表头,后者没有侵入性并且与实体解耦。 这个只是建议,实际开的时候主要考虑业务现有框架和开发规范。
另外,像状态为类枚举值可以设置字符串共享和单元格居中显示。
在EasyExcelSupportListSheet.java文件的TODO 其它属性之后添加代码
// TODO 其它属性
// EEC的动态样式
StyleProcessor<?> styleProcessor = getDesignStyle(ao.getAnnotation(StyleDesign.class));
if (styleProcessor != null) {
for (Column col : column.toArray()) {
col.setStyleProcessor(styleProcessor);
}
}
这样就可以使用EEC的动态样式了
/**
* 单元格居中显示,无论值是多少都适用
*/
public class AlignCenter implements StyleProcessor<Object> {
@Override public int build(Object o, int style, Styles sst) {
return sst.modifyHorizontal(style, Horizontals.CENTER);
}
}
/**
* 驳回状态整行标红
*/
public class StatusRejectStyle implements StyleProcessor<Entry> {
Fill readFill = new Fill(PatternType.solid, Color.RED);
@Override public int build(Entry o, int style, Styles sst) {
// 审核状态为9:驳回
if (o.getStatus().equals(9)) {
return sst.modifyFill(style, readFill);
}
return style;
}
}
// 导出实体
@StyleDesign(using = StatusRejectStyle.class) // <- 作用于对象,影响整行样式
public static class Entry {
@com.alibaba.excel.annotation.ExcelProperty("名称")
private String no;
@StyleDesign(using = AlignCenter.class) // <- 作用于单个字段,影响单列样式
@com.alibaba.excel.annotation.ExcelProperty(value = "审核状态", converter = StatusConverter.class)
private Integer status;
}
EEC有较好的扩展性,熟悉之后你也可以根据自己的需求和开发习惯进行扩展
非常感激您所花费的时间和精力来理解我的问题并提供解决方案🤝🤝🤝
测试有效,感谢提供一个新特性,这里留个脚印,希望可以帮助到更多的人🎃😀
v0.5.12 已支持双向转换 动态转换
@ExcelColumn(converter = StatusConvert.class) // <- 指定转换类
private int status;
// 转换器实现
public static class StatusConvert implements Converter<Integer> {
final String[] statusDesc = { "未开始", "进行中", "完结", "中止" };
/**
* Excel读取的文本转为状态码
*
* @param v Excel原始值
* @return 状态码
*/
@Override
public Integer reversion(String v) {
for (int i = 0; i < statusDesc.length; i++) {
if (statusDesc[i].equals(v)) {
return i;
}
}
return null;
}
/**
* 状态码转为文本
*
* @param v 原始值
* @return 状态说明
*/
@Override
public Object conversion(Object v) {
return v != null ? statusDesc[(int) v] : null;
}
}