EasyExcel动态合并单元格

简介: EasyExcel动态合并单元格

一、合并前后对比

合并前效果

合并后效果

二、如何合并

自定义一个内容相同合并的处理拦截器

 
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
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.util.CellRangeAddress;
 
import java.util.List;
 
 
@Data
public class ExcelFillCellMergeStrategy implements CellWriteHandler {
    /**
     * 合并字段的下标,如第一到五列new int[]{0,1,2,3,4}
     */
    private int[] mergeColumnIndex;
    /**
     * 从第几行开始合并,如果表头占两行,这个数字就是2
     */
    private int mergeRowIndex;
 
    public ExcelFillCellMergeStrategy() {
    }
 
    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }
 
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
                                 Head head, Integer integer, Integer integer1, Boolean aBoolean) {
 
    }
 
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
                                Head head, Integer integer, Boolean aBoolean) {
 
    }
 
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                       CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {
 
    }
 
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
//当前行
        int curRowIndex = cell.getRowIndex();
        //当前列
        int curColIndex = cell.getColumnIndex();
 
        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i]) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }
 
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() :
                cell.getNumericCellValue();
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() :
                preCell.getNumericCellValue();
        // 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行
        //
        if (curData.equals(preData)) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex,
                        curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

使用

.registerWriteHandler(new ExcelFillCellMergeStrategy(1,new int[]{0,1,2,3,4,5}))
  public static <T> void writeTwo(HttpServletResponse response, String filename, String sheetName,
                                 Class<T> head, List<T> data) throws IOException {
        // 输出 Excel
        EasyExcel.write(response.getOutputStream(), head)
                .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
                .registerWriteHandler(new ExcelFillCellMergeStrategy(1,new int[]{0,1,2,3,4,5})) // 基于 column 长度,自动适配。最大 255 宽度
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet(sheetName).doWrite(data);
        // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    }

三、其他代码

//获取数据     
 List<SystemPlanApply> list = applyService.export(reqVO);
        List<PlanExcelVO> excellist = list.stream().map(it -> {
            List<PlanExcelVO> res = new ArrayList<>();
           //业务处理
            return res;
        }).flatMap(it -> it.stream()).collect(Collectors.toList());
        ExcelUtils.writeTwo(response, "工作日志.xls", "工作日志表", PlanExcelVO.class, excellist);
 
 
/**
 * 用户 Excel 导出 VO
 */
@Data
@ApiModel("管理后台 - 日志导出")
public class PlanExcelVO {
    /**
     * ID
     */
    @ApiModelProperty(value = "ID")
    @ExcelProperty("ID")
    private Long id;
    /**
     * 计划年月(月计划、周计划)
     */
    @ApiModelProperty(value = "计划年月(月计划、周计划)")
    @ExcelProperty("计划月")
    private Integer planYearMonth;
    @ExcelProperty("申请人")
    private String systemUserName;
    /**
     * 计划类型:1、年计划;2、月计划;3、周计划;4、日计划;5、年报;6、月报;7、周报;8、日报;
     */
    @ApiModelProperty(value = "计划类型:1、年计划;2、月计划;3、周计划;4、日计划;5、年报;6、月报;7、周报;8、日报;")
    @ExcelProperty("报告类型")
    private String type;
    @ExcelProperty("提交时间")
    private Date createTime;
    /**
     * 编号
     */
    @ApiModelProperty(value = "编号")
    @ExcelProperty("编号")
    private Integer detailNo;
    /**
     * 优先级
     */
    @ApiModelProperty(value = "优先级")
    @ExcelProperty(value = "优先级", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.PLAN_PRIORITY)
    //plan_priority
    private String priority;
    /**
     * 标题
     */
    @ApiModelProperty(value = "标题")
    @ExcelProperty("标题")
    private String title;
   
}
目录
相关文章
|
9月前
|
BI
使用EasyPOI实现列数动态生成,多个sheet生成
使用EasyPOI实现列数动态生成,多个sheet生成
|
easyexcel 索引
easyexcel读取合并单元格
easyexcel读取合并单元格
4754 1
easyexcel读取合并单元格
|
Java API Android开发
【POI框架实战】——POI设置Excel单元格格式
 “这个excel中的数据怎么不能做加法啊?”、“标头这一行的字体怎么这么小啊,我都看不清”、“这一列能不能换个颜色,明显一些”、“你把这一列的数据给我留两个小数位。”、“这些数据能不能以货币的类型展示啊,就每个三位一个小逗号那种……”
【POI框架实战】——POI设置Excel单元格格式
|
1月前
|
API
Easypoi导出合并单元格并统计
Easypoi导出合并单元格并统计
|
1月前
|
Java Apache
Java代码使用POI导出的单元格的字体加粗设置
【5月更文挑战第3天】Java代码使用POI导出的单元格的字体加粗设置
40 1
|
1月前
|
Java Apache
Java代码使用POI导出的单元格加上边框和背景色
【5月更文挑战第3天】Java代码使用POI导出的单元格加上边框和背景色
79 0
|
1月前
POI(excel)中表格(单元格)样式应用实践总结
POI(excel)中表格(单元格)样式应用实践总结
54 0
|
7月前
FreeMarker合并单元格
FreeMarker合并单元格
121 0
|
10月前
|
前端开发 easyexcel Java
EasyExcel解析动态表头及导出
excel文件导入及导出,是日常开发中经常遇到的需求。本次笔者以EasyExcel为例,针对在项目中遇到的动态表头解析及导出的场景,详细介绍具体的代码实现过程。
2272 0
EasyExcel解析动态表头及导出
|
9月前
POI入门操作Excel单元格
POI入门操作Excel单元格
119 0