diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java index 5aa1cc3a..e3a1a657 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java @@ -66,6 +66,17 @@ public class Head { */ private FontProperty headFontProperty; + public Head(Integer columnIndex, Field field, String fieldName, String headName) { + this.columnIndex = columnIndex; + this.field = field; + this.fieldName = fieldName; + List headNameList = new ArrayList<>(); + headNameList.add(headName); + this.headNameList = headNameList; + this.forceIndex = true; + this.forceName = true; + } + public Head(Integer columnIndex, Field field, String fieldName, List headNameList, Boolean forceIndex, Boolean forceName) { this.columnIndex = columnIndex; diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/entity/ExcelExportEntity.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/entity/ExcelExportEntity.java new file mode 100644 index 00000000..633c4800 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/entity/ExcelExportEntity.java @@ -0,0 +1,32 @@ +package com.alibaba.excel.metadata.entity; + +import lombok.Getter; +import lombok.Setter; + +/** + * Excel export dynamic column entity + * + * @author wangfarui + * @since 2022/6/27 + */ +@Getter +@Setter +public class ExcelExportEntity { + + /** + * data field name + */ + private String key; + + /** + * Table column name + */ + private String name; + + public ExcelExportEntity() {} + + public ExcelExportEntity(String key, String name) { + this.key = key; + this.name = name; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 61240d31..9675eb65 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -1,16 +1,14 @@ package com.alibaba.excel.metadata.property; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; +import java.util.stream.Collectors; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Holder; +import com.alibaba.excel.metadata.entity.ExcelExportEntity; import com.alibaba.excel.util.ClassUtils; import com.alibaba.excel.util.FieldUtils; import com.alibaba.excel.util.MapUtils; @@ -62,21 +60,41 @@ public class ExcelHeadProperty { ignoreMap = MapUtils.newHashMap(); headKind = HeadKindEnum.NONE; headRowNumber = 0; - if (head != null && !head.isEmpty()) { - int headIndex = 0; - for (int i = 0; i < head.size(); i++) { - if (holder instanceof AbstractWriteHolder) { - if (((AbstractWriteHolder)holder).ignore(null, i)) { - continue; + + if (headClazz == null) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("The current sheet/table is not specified with headClazz"); + } + return; + } + + // dynamic column + if (holder instanceof AbstractWriteHolder && + (!CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getDynamicColumnFieldNames()) + || !CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getDynamicColumnEntities()))) { + if (!CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getDynamicColumnFieldNames())) { + initColumnNameProperties(((AbstractWriteHolder) holder).getDynamicColumnFieldNames()); + } + if (!CollectionUtils.isEmpty(((AbstractWriteHolder) holder).getDynamicColumnEntities())) { + initColumnEntityProperties(((AbstractWriteHolder) holder).getDynamicColumnEntities()); + } + } else { + if (head != null && !head.isEmpty()) { + int headIndex = 0; + for (int i = 0; i < head.size(); i++) { + if (holder instanceof AbstractWriteHolder) { + if (((AbstractWriteHolder)holder).ignore(null, i)) { + continue; + } } + headMap.put(headIndex, new Head(headIndex, null, null, head.get(i), Boolean.FALSE, Boolean.TRUE)); + headIndex++; } - headMap.put(headIndex, new Head(headIndex, null, null, head.get(i), Boolean.FALSE, Boolean.TRUE)); - headIndex++; + headKind = HeadKindEnum.STRING; } - headKind = HeadKindEnum.STRING; + // convert headClazz to head + initColumnProperties(holder); } - // convert headClazz to head - initColumnProperties(holder); initHeadRowNumber(); if (LOGGER.isDebugEnabled()) { @@ -104,6 +122,57 @@ public class ExcelHeadProperty { } } + private void initColumnNameProperties(Collection dynamicColumnFieldNames) { + headKind = HeadKindEnum.CLASS; + int i = 0; + List fields = FieldUtils.getAllFields(headClazz); + + for (String filedName : dynamicColumnFieldNames) { + for (Field field : fields) { + if (filedName.equals(field.getName())) { + initOneColumnProperty(i++, field, true); + break; + } + } + } + + if (headMap.size() == fields.size()) { + return; + } + + for (Field field : fields) { + if (!dynamicColumnFieldNames.contains(field.getName())) { + ignoreMap.put(field.getName(), field); + } + } + } + + private void initColumnEntityProperties(Collection dynamicColumnEntities) { + headKind = HeadKindEnum.CLASS; + int i = 0; + List fields = FieldUtils.getAllFields(headClazz); + + for (ExcelExportEntity entity : dynamicColumnEntities) { + for (Field field : fields) { + if (field.getName().equals(entity.getKey())) { + initOneColumnProperty(i++, field, entity); + break; + } + } + } + + if (headMap.size() == fields.size()) { + return; + } + + Set keyList = dynamicColumnEntities.stream().map(ExcelExportEntity::getKey).collect(Collectors.toSet()); + for (Field field : fields) { + if (!keyList.contains(field.getName())) { + ignoreMap.put(field.getName(), field); + } + } + } + private void initColumnProperties(Holder holder) { if (headClazz == null) { return; @@ -153,6 +222,18 @@ public class ExcelHeadProperty { headMap.put(index, head); } + /** + * Initialization column property + * + * @param index map index + * @param field the current field + * @param entity ExcelExportEntity + */ + private void initOneColumnProperty(int index, Field field, ExcelExportEntity entity) { + Head head = new Head(index, field, entity.getKey(), entity.getName()); + headMap.put(index, head); + } + public boolean hasHead() { return headKind != HeadKindEnum.NONE; } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java index 42cc3e4a..f113e7e4 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java @@ -2,6 +2,9 @@ package com.alibaba.excel.util; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import com.alibaba.excel.metadata.NullObject; @@ -166,4 +169,18 @@ public class FieldUtils { return match; } + /** + * Gets the fields of the current class and its parent class + * @param clazz + * @return + */ + public static List getAllFields(Class clazz) { + List fields = new ArrayList<>(); + while (clazz != null) { + Collections.addAll(fields, clazz.getDeclaredFields()); + clazz = clazz.getSuperclass(); + } + return fields; + } + } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java index f8844a52..57eaff8d 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import com.alibaba.excel.metadata.AbstractParameterBuilder; +import com.alibaba.excel.metadata.entity.ExcelExportEntity; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteBasicParameter; @@ -120,4 +121,26 @@ public abstract class AbstractExcelWriterParameterBuilder dynamicColumnFieldNames) { + return setDynamicColumnFieldNames(true, dynamicColumnFieldNames); + } + + public T setDynamicColumnFieldNames(Boolean bool, Collection dynamicColumnFieldNames) { + if (bool) { + parameter().setDynamicColumnFieldNames(dynamicColumnFieldNames); + } + return self(); + } + + public T setDynamicColumnEntities(Collection dynamicColumnEntities) { + return setDynamicColumnEntities(true, dynamicColumnEntities); + } + + public T setDynamicColumnEntities(Boolean bool, Collection dynamicColumnEntities) { + if (bool) { + parameter().setDynamicColumnEntities(dynamicColumnEntities); + } + return self(); + } } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java index b0745d38..87fb24e2 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.List; import com.alibaba.excel.metadata.BasicParameter; +import com.alibaba.excel.metadata.entity.ExcelExportEntity; import com.alibaba.excel.write.handler.WriteHandler; import lombok.EqualsAndHashCode; @@ -56,4 +57,13 @@ public class WriteBasicParameter extends BasicParameter { * Only output the custom columns. */ private Collection includeColumnFieldNames; + /** + * Dynamic column field name + */ + private Collection dynamicColumnFieldNames; + + /** + * Dynamic column entity + */ + private Collection dynamicColumnEntities; } diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index b6907e42..58aab807 100644 --- a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -17,6 +17,7 @@ import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.event.NotRepeatExecutor; import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.entity.ExcelExportEntity; import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.LoopMergeProperty; import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; @@ -94,6 +95,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ * Only output the custom columns. */ private Collection includeColumnFieldNames; + /** + * Dynamic column field name + */ + private Collection dynamicColumnFieldNames; + /** + * Dynamic column entity + */ + private Collection dynamicColumnEntities; /** * Write handler @@ -199,6 +208,16 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ } else { this.includeColumnIndexes = writeBasicParameter.getIncludeColumnIndexes(); } + if (writeBasicParameter.getDynamicColumnFieldNames() == null && parentAbstractWriteHolder != null) { + this.dynamicColumnFieldNames = parentAbstractWriteHolder.getDynamicColumnFieldNames(); + } else { + this.dynamicColumnFieldNames = writeBasicParameter.getDynamicColumnFieldNames(); + } + if (writeBasicParameter.getDynamicColumnEntities() == null && parentAbstractWriteHolder != null) { + this.dynamicColumnEntities = parentAbstractWriteHolder.getDynamicColumnEntities(); + } else { + this.dynamicColumnEntities = writeBasicParameter.getDynamicColumnEntities(); + } // Initialization property this.excelWriteHeadProperty = new ExcelWriteHeadProperty(this, getClazz(), getHead()); diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 607a91be..38bebd4d 100644 --- a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -27,6 +27,7 @@ import com.alibaba.excel.metadata.data.ImageData; import com.alibaba.excel.metadata.data.ImageData.ImageType; import com.alibaba.excel.metadata.data.RichTextStringData; import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.entity.ExcelExportEntity; import com.alibaba.excel.util.BooleanUtils; import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.ListUtils; @@ -452,7 +453,7 @@ public class WriteTest { // 背景设置为红色 headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); WriteFont headWriteFont = new WriteFont(); - headWriteFont.setFontHeightInPoints((short)20); + headWriteFont.setFontHeightInPoints((short) 20); headWriteCellStyle.setWriteFont(headWriteFont); // 内容的策略 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); @@ -462,7 +463,7 @@ public class WriteTest { contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); WriteFont contentWriteFont = new WriteFont(); // 字体大小 - contentWriteFont.setFontHeightInPoints((short)20); + contentWriteFont.setFontHeightInPoints((short) 20); contentWriteCellStyle.setWriteFont(contentWriteFont); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 HorizontalCellStyleStrategy horizontalCellStyleStrategy = @@ -697,6 +698,48 @@ public class WriteTest { EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); } + /** + * 实时生成列写入, 简化 {@link this#dynamicHeadWrite()} 的数据结构 + *

相较于 dynamicHeadWrite() 方法, 此方法无需双层集合, 适用于简单表格的导出. + *

表格样式仍然基于数据类的注解属性, 在此基础上可以指定只导出指定列, 并且导出列的顺序由集合的有序性决定. + */ + @Test + public void dynamicColumnFieldWrite() { + String fileName = TestFileUtil.getPath() + "dynamicColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; + + List fieldNames = new ArrayList<>(); + fieldNames.add("date"); + fieldNames.add("string"); + + EasyExcel.write(fileName, DemoData.class) + .setDynamicColumnFieldNames(fieldNames) + .sheet("测试实时生成列") + .doWrite(data()); + + System.out.println(fileName); + } + + /** + * 动态列的写入, 基于{@link ExcelExportEntity}实体集合实现 + *

表格样式仍然基于数据类的注解属性, 在此基础上可以指定导出列以及列名称, 并且导出列的顺序由集合的有序性决定. + *

一般用于导出列名可以由用户编辑的业务场景. + */ + @Test + public void dynamicColumnEntityWrite() { + String fileName = TestFileUtil.getPath() + "dynamicColumnEntityWrite" + System.currentTimeMillis() + ".xlsx"; + + List fieldEntities = new ArrayList<>(); + fieldEntities.add(new ExcelExportEntity("string", "字符串")); + fieldEntities.add(new ExcelExportEntity("date", "日期")); + + EasyExcel.write(fileName, DemoData.class) + .setDynamicColumnEntities(fieldEntities) + .sheet("测试动态实体列") + .doWrite(data()); + + System.out.println(fileName); + } + private List dataLong() { List list = ListUtils.newArrayList(); for (int i = 0; i < 10; i++) {