合并单元格多层数据导出

思维脑图

java 导出表头合并单元格 java导出excel带合并单元格_java

代码实现

/**
 * 导出所有信息
 *
 * @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中数据的分组和单元格合并,提升报表的可读性。