想要达到的效果

easyExcel 模版导出 中间数据纵向延伸,并且对指定列进行合并-LMLPHP

引入maven引用

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
        </dependency>

按照要求创建模版

备注 :

模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list

easyExcel 模版导出 中间数据纵向延伸,并且对指定列进行合并-LMLPHP

代码部分 :

合并策略代码 :

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import io.jsonwebtoken.lang.Collections;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.ArrayList;
import java.util.List;

// 自定义合并策略 该类继承了AbstractMergeStrategy抽象合并策略,需要重写merge()方法
  public class CustomMergeStrategy extends AbstractMergeStrategy {

    /**
     * 分组,每几行合并一次
     */
    private List<Integer> exportFieldGroupCountList;

    /**
     * 目标合并列index
     */
    private Integer targetColumnIndex;

    // 需要开始合并单元格的首行index
    private Integer rowIndex;

    // exportDataList为待合并目标列的值 
    public CustomMergeStrategy(List<String> exportDataList, Integer targetColumnIndex) {
      this.exportFieldGroupCountList = getGroupCountList(exportDataList);
      this.targetColumnIndex = targetColumnIndex;
    }


    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {

      if (null == rowIndex) {
        rowIndex = cell.getRowIndex();
      }
      // 仅从首行以及目标列的单元格开始合并,忽略其他
      if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) {
        mergeGroupColumn(sheet);
      }
    }

    private void mergeGroupColumn(Sheet sheet) {
      int rowCount = rowIndex;
      for (Integer count : exportFieldGroupCountList) {
        if(count == 1) {
          rowCount += count;
          continue ;
        }
        // 合并单元格
        CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex);
        sheet.addMergedRegionUnsafe(cellRangeAddress);
        rowCount += count;
      }
    }

    // 该方法将目标列根据值是否相同连续可合并,存储可合并的行数 
    private List<Integer> getGroupCountList(List<String> exportDataList){
      if (Collections.isEmpty(exportDataList)) {
        return new ArrayList<>();
      }

      List<Integer> groupCountList = new ArrayList<>();
      int count = 1;

      for (int i = 1; i < exportDataList.size(); i++) {
        if (exportDataList.get(i).equals(exportDataList.get(i - 1))) {
          count++;
        } else {
          groupCountList.add(count);
          count = 1;
        }
      }
      // 处理完最后一条后
      groupCountList.add(count);
      return groupCountList;
    }
  }

导出部分 :

@Test
    public void complexFillTest() throws IOException {
        //正式代码时 改成 input或者 file 传入
        String templateFileName = "D:/模版.xls";
        //改成导出的output或者其他方式写出 也可
        String fileName = "D:/下载的文件.xls";

        // 方案1
        try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).excelType(ExcelTypeEnum.XLS).build()) {

            //数据准备 改成自己的即可
            InvoiceInfo invoiceInfo = invoiceInfoService.lambdaQuery()
                    .eq(InvoiceInfo::getId, 21)
                    .one();

            List<FreightOrderResponse> freightOrderResponseList = new ArrayList<>();

            ArrayList<Map<String, String>> dataList = new ArrayList<>();
            for (int i = 0; i < freightOrderResponseList.size(); i++) {
                FreightOrderResponse freightOrderResponse = freightOrderResponseList.get(0);
                Map<String, String> map = new HashMap<>();
                map.put("companyName",freightOrderResponse.getComName());
                if (freightOrderResponseList.size() / 2 < i){
                    map.put("companyName",freightOrderResponse.getComName() + "1");
                }
                map.put("sendAddr",freightOrderResponse.getSendProvince());
                map.put("arriveAddr",freightOrderResponse.getReceiveProvince());
                map.put("goodsName",freightOrderResponse.getGoodsName());
                map.put("practicalFee",freightOrderResponse.getOrderPrice().toString());
                dataList.add(map);
            }
            WriteSheet writeSheet = EasyExcel.writerSheet()
                    // 合并的单元格 并且将需要合并的那一列数据传入
                    .registerWriteHandler(new CustomMergeStrategy(dataList.stream().map(map -> map.get("companyName")).collect(Collectors.toList()), 0))
                    .build();
            // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
            // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
            // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
            // 如果数据量大 list不是最后一行 参照下一个

            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            excelWriter.fill(dataList, fillConfig, writeSheet);
            Map<String, Object> map = MapUtils.newHashMap();
            BigDecimal reduce = freightOrderResponseList.stream().map(obj -> obj.getOrderPrice() != null ? obj.getOrderPrice() : BigDecimal.ZERO).reduce(BigDecimal.ZERO, BigDecimal::add);
            map.put("practicalFee", reduce);
            map.put("customName", invoiceInfo.getCustomName());
            map.put("sellerComName", invoiceInfo.getSellerComName());
            excelWriter.fill(map, writeSheet);
            excelWriter.finish();
        }
    }

最终效果 : 

easyExcel 模版导出 中间数据纵向延伸,并且对指定列进行合并-LMLPHP

参考资料 : 

官方文档 : 

填充Excel | Easy Excel

合并代码参考 : 

https://www.cnblogs.com/monianxd/p/16359369.html

04-04 12:11