Bläddra i källkod

Fix 修复excel导出多sheet合并单元格失效问题

Yue 1 vecka sedan
förälder
incheckning
616db2d32f

+ 55 - 41
SERVER/VberAdminPlusV3/vber-common/vber-common-excel/src/main/java/com/vber/common/excel/core/CellMergeHandler.java

@@ -30,23 +30,9 @@ public class CellMergeHandler {
         this.rowIndex = hasTitle ? 1 : 0;
     }
 
-    /**
-     * 创建一个单元格合并处理器实例
-     *
-     * @param hasTitle 是否合并标题
-     * @return 单元格合并处理器
-     */
-    public static CellMergeHandler of(final boolean hasTitle) {
-        return new CellMergeHandler(hasTitle);
-    }
-
-    /**
-     * 创建一个单元格合并处理器实例(默认不合并标题)
-     *
-     * @return 单元格合并处理器
-     */
-    public static CellMergeHandler of() {
-        return new CellMergeHandler(false);
+    private CellMergeHandler(final boolean hasTitle, final int rowIndex) {
+        this.hasTitle = hasTitle;
+        this.rowIndex = hasTitle ? rowIndex : 0;
     }
 
     @SneakyThrows
@@ -80,8 +66,10 @@ public class CellMergeHandler {
                 // 当前行数据字段值
                 Object currentRowObjFieldVal = ReflectUtils.invokeGetter(currentRowObj, field.getName());
 
-                // 空值跳过不处理
-                if (currentRowObjFieldVal == null || "".equals(currentRowObjFieldVal)) {
+                // 空值视为合并中断,需要先收口上一段合并区间
+                if (isBlankCell(currentRowObjFieldVal)) {
+                    appendMergeResult(result, rowRepeatCellMap.get(field), i - 1, colNum);
+                    rowRepeatCellMap.remove(field);
                     continue;
                 }
 
@@ -94,37 +82,22 @@ public class CellMergeHandler {
                 // 获取 单元格合并Map 中字段值
                 RepeatCell repeatCell = rowRepeatCellMap.get(field);
                 Object cellValue = repeatCell.value();
-                int current = repeatCell.current();
 
                 // 检查是否满足合并条件
                 // currentRowObj 当前行数据
-                // rows.get(i - 1) 上一行数据 注:由于 if (!rowRepeatCellMap.containsKey(field)) 条件的存在,所以该 i 必不可能小于1
+                // rows.get(i - 1) 上一行数据 注:由于 if (!rowRepeatCellMap.containsKey(field))
+                // 条件的存在,所以该 i 必不可能小于1
                 // cellMerge 当前行字段合并注解
                 boolean merge = isMerge(currentRowObj, rows.get(i - 1), cellMerge);
 
-                // 是否添加到结果集
-                boolean isAddResult = false;
-                // 最新行
-                int lastRow = i + rowIndex - 1;
-
                 // 如果当前行字段值和缓存中的字段值不相等,或不满足合并条件,则替换
                 if (!currentRowObjFieldVal.equals(cellValue) || !merge) {
+                    appendMergeResult(result, repeatCell, i - 1, colNum);
                     rowRepeatCellMap.put(field, RepeatCell.of(currentRowObjFieldVal, i));
-                    isAddResult = true;
-                }
-
-                // 如果最后一行不能合并,检查之前的数据是否需要合并;如果最后一行可以合并,则直接合并到最后
-                if (i == rows.size() - 1) {
-                    isAddResult = true;
-                    if (i > current) {
-                        lastRow = i + rowIndex;
-                    }
-                }
-
-                if (isAddResult && i > current) {
-                    result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
                 }
             }
+            appendMergeResult(result, rowRepeatCellMap.get(field), rows.size() - 1, colNum);
+            rowRepeatCellMap.remove(field);
         }
         return result;
     }
@@ -166,12 +139,12 @@ public class CellMergeHandler {
     private boolean isMerge(Object currentRow, Object preRow, CellMerge cellMerge) {
         final String[] mergeBy = cellMerge.mergeBy();
         if (StrUtil.isAllNotBlank(mergeBy)) {
-            //比对当前行和上一行的各个属性值一一比对 如果全为真 则为真
+            // 比对当前行和上一行的各个属性值一一比对 如果全为真 则为真
             for (String fieldName : mergeBy) {
                 final Object valCurrent = ReflectUtil.getFieldValue(currentRow, fieldName);
                 final Object valPre = ReflectUtil.getFieldValue(preRow, fieldName);
                 if (!Objects.equals(valPre, valCurrent)) {
-                    //依赖字段如有任一不等值,则标记为不可合并
+                    // 依赖字段如有任一不等值,则标记为不可合并
                     return false;
                 }
             }
@@ -179,6 +152,17 @@ public class CellMergeHandler {
         return true;
     }
 
+    private boolean isBlankCell(Object value) {
+        return value == null || StrUtil.isBlankIfStr(value);
+    }
+
+    private void appendMergeResult(List<CellRangeAddress> result, RepeatCell repeatCell, int endIndex, int colNum) {
+        if (repeatCell == null || endIndex <= repeatCell.current()) {
+            return;
+        }
+        result.add(new CellRangeAddress(repeatCell.current() + rowIndex, endIndex + rowIndex, colNum, colNum));
+    }
+
     /**
      * 单元格合并
      */
@@ -197,4 +181,34 @@ public class CellMergeHandler {
         }
     }
 
+    /**
+     * 创建一个单元格合并处理器实例
+     *
+     * @param hasTitle 是否合并标题
+     * @param rowIndex 行索引
+     * @return 单元格合并处理器
+     */
+    public static CellMergeHandler of(final boolean hasTitle, final int rowIndex) {
+        return new CellMergeHandler(hasTitle, rowIndex);
+    }
+
+    /**
+     * 创建一个单元格合并处理器实例
+     *
+     * @param hasTitle 是否合并标题
+     * @return 单元格合并处理器
+     */
+    public static CellMergeHandler of(final boolean hasTitle) {
+        return new CellMergeHandler(hasTitle);
+    }
+
+    /**
+     * 创建一个单元格合并处理器实例(默认不合并标题)
+     *
+     * @return 单元格合并处理器
+     */
+    public static CellMergeHandler of() {
+        return new CellMergeHandler(false);
+    }
+
 }

+ 15 - 8
SERVER/VberAdminPlusV3/vber-common/vber-common-excel/src/main/java/com/vber/common/excel/core/CellMergeStrategy.java

@@ -2,9 +2,10 @@ package com.vber.common.excel.core;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.idev.excel.metadata.Head;
-import cn.idev.excel.write.handler.WorkbookWriteHandler;
-import cn.idev.excel.write.handler.context.WorkbookWriteHandlerContext;
+import cn.idev.excel.write.handler.SheetWriteHandler;
 import cn.idev.excel.write.merge.AbstractMergeStrategy;
+import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
+import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -15,10 +16,10 @@ import java.util.List;
 /**
  * 列值重复合并策略
  *
- * @author Lion Li
+ * @author Iwb
  */
 @Slf4j
-public class CellMergeStrategy extends AbstractMergeStrategy implements WorkbookWriteHandler {
+public class CellMergeStrategy extends AbstractMergeStrategy implements SheetWriteHandler {
 
     private final List<CellRangeAddress> cellList;
 
@@ -30,12 +31,16 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook
         this.cellList = CellMergeHandler.of(hasTitle).handle(list);
     }
 
+    public CellMergeStrategy(List<?> list, boolean hasTitle, int rowIndex) {
+        this.cellList = CellMergeHandler.of(hasTitle, rowIndex).handle(list);
+    }
+
     @Override
     protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
         if (CollUtil.isEmpty(cellList)) {
             return;
         }
-        //单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
+        // 单元格写入了,遍历合并区域,如果该Cell在区域内,但非首行,则清空
         final int rowIndex = cell.getRowIndex();
         for (CellRangeAddress cellAddresses : cellList) {
             final int firstRow = cellAddresses.getFirstRow();
@@ -46,13 +51,15 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements Workbook
     }
 
     @Override
-    public void afterWorkbookDispose(final WorkbookWriteHandlerContext context) {
+    public void afterSheetCreate(final WriteWorkbookHolder writeWorkbookHolder,
+            final WriteSheetHolder writeSheetHolder) {
         if (CollUtil.isEmpty(cellList)) {
             return;
         }
-        //当前表格写完后,统一写入
+        // 在 Sheet 创建时提前写入合并区域;后续写入只会影响首格,不会移除合并
+        final Sheet sheet = writeSheetHolder.getSheet();
         for (CellRangeAddress item : cellList) {
-            context.getWriteContext().writeSheetHolder().getSheet().addMergedRegion(item);
+            sheet.addMergedRegion(item);
         }
     }