diff --git a/fastexcel-test/src/test/java/cn/idev/excel/test/core/template/TemplateMergeTest.java b/fastexcel-test/src/test/java/cn/idev/excel/test/core/template/TemplateMergeTest.java new file mode 100644 index 000000000..934b1b976 --- /dev/null +++ b/fastexcel-test/src/test/java/cn/idev/excel/test/core/template/TemplateMergeTest.java @@ -0,0 +1,54 @@ +package cn.idev.excel.test.core.template; + +import cn.idev.excel.FastExcel; +import cn.idev.excel.test.util.TestFileUtil; +import cn.idev.excel.write.metadata.fill.FillConfig; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * @author wangmeng + */ +public class TemplateMergeTest { + + private static File file01; + + @BeforeAll + public static void init() { + file01 = TestFileUtil.createNewFile("template" + File.separator + "template_out01.xlsx"); + } + + @Test + public void testMerge() throws IOException { + write(file01); + } + + public static void write(File file) { + FastExcel.write(file) + .withTemplate(TestFileUtil.readFile("template" + File.separator + "template01.xlsx")) + .sheet() + .doFill( + data(), + FillConfig.builder().forceNewRow(true).autoStyle(true).build()); + } + + private static List> data() { + List> list = new LinkedList<>(); + for (int i = 0; i < 10; i++) { + Map map = new HashMap<>(); + map.put("name", "name- " + i); + map.put("number", String.valueOf(i)); + map.put("age", String.valueOf(i)); + map.put("orderNo", "order-" + i); + map.put("status", "1"); + list.add(map); + } + return list; + } +} diff --git a/fastexcel-test/src/test/resources/template/template01.xlsx b/fastexcel-test/src/test/resources/template/template01.xlsx new file mode 100644 index 000000000..ccfcf94b1 Binary files /dev/null and b/fastexcel-test/src/test/resources/template/template01.xlsx differ diff --git a/fastexcel/src/main/java/cn/idev/excel/write/executor/ExcelWriteFillExecutor.java b/fastexcel/src/main/java/cn/idev/excel/write/executor/ExcelWriteFillExecutor.java index 94c1ffd00..ec98192e5 100644 --- a/fastexcel/src/main/java/cn/idev/excel/write/executor/ExcelWriteFillExecutor.java +++ b/fastexcel/src/main/java/cn/idev/excel/write/executor/ExcelWriteFillExecutor.java @@ -7,45 +7,25 @@ import cn.idev.excel.exception.ExcelGenerateException; import cn.idev.excel.metadata.data.WriteCellData; import cn.idev.excel.metadata.property.ExcelContentProperty; -import cn.idev.excel.util.BeanMapUtils; -import cn.idev.excel.util.ClassUtils; -import cn.idev.excel.util.FieldUtils; -import cn.idev.excel.util.ListUtils; -import cn.idev.excel.util.MapUtils; -import cn.idev.excel.util.PoiUtils; -import cn.idev.excel.util.StringUtils; -import cn.idev.excel.util.WriteHandlerUtils; +import cn.idev.excel.util.*; import cn.idev.excel.write.handler.context.CellWriteHandlerContext; import cn.idev.excel.write.handler.context.RowWriteHandlerContext; import cn.idev.excel.write.metadata.fill.AnalysisCell; import cn.idev.excel.write.metadata.fill.FillConfig; import cn.idev.excel.write.metadata.fill.FillWrapper; import cn.idev.excel.write.metadata.holder.WriteSheetHolder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.apache.commons.collections4.CollectionUtils; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.CellType; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; /** * Fill the data into excel - * - * */ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { @@ -68,6 +48,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { */ private final Map> collectionFieldStyleCache = MapUtils.newHashMap(); + /** + * Record the merged region information of the initial row, to be used for setting the merged regions for newly added rows + */ + private final Map> originalMergeRegionMap = MapUtils.newHashMap(); /** * Row height cache for collection */ @@ -124,11 +108,28 @@ public void fill(Object data, FillConfig fillConfig) { while (iterator.hasNext()) { doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); } + // handle merge + if (!originalMergeRegionMap.isEmpty()) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + originalMergeRegionMap.forEach((analysisCell, regionList) -> { + for (int index = analysisCell.getRowIndex(); + index < analysisCell.getRowIndex() + collectionData.size(); + index++) { + for (CellRangeAddress cellAddresses : regionList) { + cachedSheet.addMergedRegionUnsafe(new CellRangeAddress( + index, index, cellAddresses.getFirstColumn(), cellAddresses.getLastColumn())); + } + } + }); + } } else { doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); } } + /** + * Leave space for the newly added data rows and shift the existing data down + */ private void shiftRows(int size, List analysisCellList) { if (CollectionUtils.isEmpty(analysisCellList)) { return; @@ -385,9 +386,26 @@ private void createCell( cellWriteHandlerContext.setCell(cell); if (isOriginalCell) { + // Record the style of the original row so that the subsequent rows can inherit its style Map collectionFieldStyleMap = collectionFieldStyleCache.computeIfAbsent(currentUniqueDataFlag, key -> MapUtils.newHashMap()); collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + // Find the column merges in the initial row + List mergedRegions = cachedSheet.getMergedRegions(); + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) + && fillConfig.getForceNewRow() + && fillConfig.getAutoStyle()) { + List oneRowRegionList = mergedRegions.stream() + // if the merge spans across rows, do not process it + .filter(region -> + region.getFirstRow() == region.getLastRow() && region.getFirstRow() == row.getRowNum()) + .filter(region -> region.getFirstColumn() <= cell.getColumnIndex() + && region.getLastColumn() >= cell.getColumnIndex()) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(oneRowRegionList)) { + originalMergeRegionMap.put(analysisCell, oneRowRegionList); + } + } } }