EasyExcel导出Excel文件——合并单元格多层级数据导出

合并单元格多层数据导出

思维脑图

在这里插入图片描述

代码实现

/**
 * 导出所有信息
 *
 * @param request 请求体
 */
@Override
public void getWilliamExportList(WilliamReqVo request, HttpServletResponse response) throws Exception {
    List<SysDictData> dataByType = dictDataService.getDictDataByType("status");
    Map<String, String> calendarStatus = dataByType.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
    List<WilliamAllExportVo> list = baseMapper.getWilliamExportData(request);
    if (CollectionUtils.isNotEmpty(list)) {
        list.forEach(s -> s.setStatus(calendarStatus.get(s.getStatus())));
    }
    // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setCharacterEncoding("utf-8");
    // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
    String fileName = URLEncoder.encode("某某表" + DateUtil.format(new Date(), SysConstants.DATE_FORMAT), "UTF-8").replaceAll("\\+", "%20");
    response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
    ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream(), WilliamAllExportVo.class);
    //合并策略
    addMergeStrategy(list, writerBuilder);
    writerBuilder.sheet("信息表").doWrite(list);
}


/**
 * 合并策略
 *
 * @param list          数据集
 * @param writerBuilder excel写对象
 */
private static void addMergeStrategy(List<WilliamAllExportVo> list, ExcelWriterBuilder writerBuilder) {
    // 第一级,选定键值进行分层准备
    Map<String, List<WilliamAllExportVo>> collect = list.stream().collect(Collectors.groupingBy(s -> s.getProjectSn() + s.getMonth()));
    Map<String, List<WilliamAllExportVo>> collect2 = list.stream().collect(Collectors.groupingBy(s -> s.getProjectSn() + s.getMonth() + s.getTitle()));
    //第一层合并单元格处理
    for (int i = 1; i < list.size(); ) {
        WilliamAllExportVo exportVo = list.get(i);
        //计算集合长度,确定有多少行
        int size = collect.get(exportVo.getProjectSn() + exportVo.getMonth()).size();
        //按照分层合并范围的属性,设置j的数值
        for (int j = 0; j < 10; j++) {
            OnceAbsoluteMergeStrategy strategy = new OnceAbsoluteMergeStrategy(i, i + size - 1, j, j);
            writerBuilder.registerWriteHandler(strategy);
        }
        i = i + size;
    }
    //第二层合并单元格处理,
    for (int i = 1; i < list.size(); ) {
        WilliamAllExportVo exportVo = list.get(i);
        int size = collect2.get(exportVo.getProjectSn() + exportVo.getMonth() + exportVo.getTitle()).size();
        for (int j = 10; j < 14; j++) {
            OnceAbsoluteMergeStrategy strategy = new OnceAbsoluteMergeStrategy(i, i + size - 1, j, j);
            writerBuilder.registerWriteHandler(strategy);
        }
        i = i + size;
    }
}

逻辑分析

1. 数据准备与状态映射

首先,通过dictDataService.getDictDataByType(“status”)获取字典数据,这通常用于将数据库中的状态码(如数字或简短字符串)转换为更加友好的展示标签。之后,使用Java 8的Stream API将这些数据转换成一个Map,键为状态值,值为状态标签,便于后续替换列表中项目的状态字段。

List<SysDictData> dataByType = dictDataService.getDictDataByType("project_calendar_status");
Map<String, String> calendarStatus = dataByType.stream()
    .collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));

接着,调用baseMapper.getWilliamExportData(request)获取项目日历导出数据列表,然后遍历列表,使用前面构建的状态映射替换项目状态字段的代码值为描述文本。

2. 设置响应头及内容类型

让HTTP响应能够以Excel文件的形式下载。首先,设置了响应的内容类型为Excel的OpenXML格式,然后通过URLEncoder.encode方法对文件名进行编码,以防止中文乱码,并使用特定的Content-Disposition头部来指示浏览器以附件形式下载文件,并给出文件名。

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("某某表" + DateUtil.format(new Date(), SysConstants.DATE_FORMAT), "UTF-8")
   .replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

3. 使用EasyExcel导出数据

EasyExcel是一个用于简化Excel操作的Java库,它允许开发者以更简洁的方式读写Excel文件。在这里,通过EasyExcel.write(response.getOutputStream(), WilliamAllExportVo.class)初始化一个Excel写入器,然后调用addMergeStrategy方法注册合并单元格的策略,最后在指定的工作表中写入数据。

4. 单元格合并策略

addMergeStrategy方法实现了Excel表格中单元格的合并策略。这里的逻辑是首先按照编号(projectSn)和月份(month)进行第一级分组,然后在同一组内再按照标题(title)进行第二级分组,分别对不同列范围内的连续行进行合并。

对于第一级分组(第1至第9列),合并所有属于同一项目、同一月份的行。
对于第二级分组(第10至第13列),在第一级的基础上,进一步根据标题合并。
使用了OnceAbsoluteMergeStrategy类,该类为EasyExcel提供的单元格合并策略之一,参数分别为起始行号、结束行号、起始列号、结束列号,指定了合并的具体范围。

知识点总结

  • 数据映射:利用Map和Stream API进行数据转换,提高代码的可读性和效率。
  • HTTP响应处理:设置正确的响应头以实现文件下载,以及解决中文乱码问题。
  • EasyExcel库:简化Excel文件的生成和读取过程,提升开发效率。
  • 单元格合并:通过自定义策略实现Excel中数据的分组和单元格合并,提升报表的可读性。
  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过实现EasyExcel的WriteHandler接口来实现相同数据合并单元格的功能。具体步骤如下: 1. 新建一个类实现WriteHandler接口,并重写对应的方法。 2. 在实现的方法中,通过Excel的API获取到要合并的单元格的起始行、起始列、结束行、结束列。可以使用Map来记录每一种相同数据的位置信息,然后再遍历Map合并单元格。 3. 使用EasyExcel的write方法时,通过指定Handler参数,将编写好的WriteHandler实例传递进去即可。 下面是示例代码: ```java public class MergeCellWriteHandler implements WriteHandler { private Map<String, List<CellRangeAddress>> mergeMap = new HashMap<>(); @Override public void row(int i, List<Object> list) { //处理行数据,将相同的数据合并单元格 String key = list.get(0).toString(); //以第一列为key if (mergeMap.containsKey(key)) { List<CellRangeAddress> cellRangeList = mergeMap.get(key); CellRangeAddress lastCellRange = cellRangeList.get(cellRangeList.size() - 1); if (i - lastCellRange.getLastRow() == 1) { //如果上一个单元格的结尾行是当前行的上一行,则可以合并单元格 lastCellRange.setLastRow(i); } else { cellRangeList.add(new CellRangeAddress(i, i, 0, list.size() - 1)); //如果不连续,则新建一个单元格范围 } } else { List<CellRangeAddress> cellRangeList = new ArrayList<>(); cellRangeList.add(new CellRangeAddress(i, i, 0, list.size() - 1)); mergeMap.put(key, cellRangeList); } } @Override public void sheet(int i) { } @Override public void start() { } @Override public void end() { //处理完整个sheet后,将记录的单元格范围进行合并 Sheet sheet = EasyExcel.writerSheet().build().getSheet(); for (Map.Entry<String, List<CellRangeAddress>> entry : mergeMap.entrySet()) { String key = entry.getKey(); List<CellRangeAddress> cellRangeList = entry.getValue(); for (CellRangeAddress cellRange : cellRangeList) { sheet.addMergedRegion(cellRange); } } } } ``` 使用方法: ```java EasyExcel.write("test.xlsx") .sheet() .registerWriteHandler(new MergeCellWriteHandler()) .head(head) .doWrite(data); ``` 其中head和data分别是表头和数据,可以从数据库或其他数据源中获取。要实现合并单元格,需要将第一列相同的数据合并。示例中以第一列为key,记录每一种相同数据所对应的单元格范围,最后进行合并。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值