当前位置: 首页 > article >正文

如何使用EasyExcel生成多列表组合填充的复杂Excel示例

作者:Funky_oaNiu

    • 一、(需求)生成的表格效果:
    • 二、搞一个模板文件
    • 三、建立对应的表格实体类
    • 四、开始填充
    • 五、Vue3前端发起请求下载
    • 六、官方文档及AI问答

一、(需求)生成的表格效果:

其中只有顶部日期、中间表格数据、底部总计数据,是通过模板填充的
表头、绿色区域、列名、背景颜色、四个表格的结构是模板中定义好的
本文主要可学习如何填充数据
在这里插入图片描述

二、搞一个模板文件

这模板文件是自己编写的,占位符使用{xx}的形式,注意观察一下表格和外部的站位符区别,表格是{data1.xxx}这种形式,用来标识不同表格
在这里插入图片描述

三、建立对应的表格实体类

@Data
public class FillData {
    private String name;
    private double number;
    private Date date;
}

四、开始填充

这里是SpringBoot项目,写在Controller里的,仔细看代码注释,这里就不过多描述了

@Operation(summary = "导出excel")
@GetMapping("/export-excel")
public void exportExcel(PsbLoc psbLoc, HttpServletResponse response) throws IOException {
    // 读取资源文件(这样打成jar模板文件也在里面,防止模板文件被修改导致功能不可用,文件是在src/main/resources/excelTemplates下的)
    ClassPathResource resource = new ClassPathResource("excelTemplates"+File.separator+"productReceiveSendTemplate.xlsx");
    // 组织并填充模板数据
    ByteArrayOutputStream byteArrayOutputStream = compositeFill(resource.getInputStream());

    // 这里文件名不起作用是因为前端是写死的
    String fileName = "多列表" + System.currentTimeMillis() + ".xlsx";
    // 设置响应头,告诉浏览器这是一个下载的文件,这里文件名
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

    // 将文件内容写入响应输出流,浏览器可以直接触发下载
    response.getOutputStream().write(byteArrayOutputStream.toByteArray());
    response.getOutputStream().flush();
}

private ByteArrayOutputStream compositeFill(InputStream templateInputStream) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    // 使用EasyExcel的模板填充功能,在这里指定合并单元格,这里应该是easyExcel的bug,第一列无法合并,其他列都可以,所以第一列单独用原生poi进行合并
    try (ExcelWriter excelWriter = EasyExcel.write(byteArrayOutputStream).withTemplate(templateInputStream)
            // 这里的参数就是圈了个合并区域,在这个正方形内的单元格都会合并,要注意索引是从0开始的,并且区域内不能存在已经合并的单元格否则报错
            .registerWriteHandler(new OnceAbsoluteMergeStrategy(2, 14, 9, 9))
            .registerWriteHandler(new OnceAbsoluteMergeStrategy(15, 27, 9, 9))
            .build()) {
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        // 防止上面两个表格覆盖下面两个表格,每一行都采用新增一行的方式
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        // 使用模板填充,必须使用FillWrapper,这是官方要求,并且每行两个表格只能有一个表格设置增行,否则会存在一个表格有空行,这里是造的测试数据
        excelWriter.fill(new FillWrapper("data1", makeTestData(1)), fillConfig, writeSheet);
        excelWriter.fill(new FillWrapper("data2", makeTestData(2)), writeSheet);
        excelWriter.fill(new FillWrapper("data3", makeTestData(3)), fillConfig, writeSheet);
        excelWriter.fill(new FillWrapper("data4", makeTestData(4)), writeSheet);

        // 设置表格外的填充数据,例如总计、日期等数据
        HashMap<String, Object> map = new HashMap<>();
        map.put("date", "2024年11月15日");
        map.put("allInStaockQty", 1);
        map.put("allOutStockQty", 2);
        map.put("allConvertStockQty", 3);
        map.put("allStockQty", 4);
        map.put("convertCenterStockQty", 5);
        map.put("mixOutStockQty", 6);
        map.put("endItem1", 7);
        map.put("endItem2", 8);
        map.put("endItem3", 9);
        map.put("endItem4", 10);
        map.put("endItem5", 11);
        excelWriter.fill(map, writeSheet);
    }
    // 合并单元格,由于easyExcel自带的OnceAbsoluteMergeStrategy合并策略bug,这里需要用poi合并一下
    return mergeCells(byteArrayOutputStream);
}

public ByteArrayOutputStream mergeCells(ByteArrayOutputStream inputStream) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputStream.toByteArray());
         Workbook workbook = new XSSFWorkbook(byteArrayInputStream)) {
        // 获取第一个工作表
        Sheet sheet = workbook.getSheetAt(0);

        // 合并第1列第3行到第15行,注意行索引从0开始
        CellRangeAddress range1 = new CellRangeAddress(2, 14, 0,0);
        sheet.addMergedRegion(range1);

        // 合并第1列第16行到第28行,注意行索引从0开始
        CellRangeAddress range2 = new CellRangeAddress(15, 27, 0, 0);
        sheet.addMergedRegion(range2);

        // 设置合并单元格的样式
        CellStyle style = workbook.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 将修改后的内容写入 ByteArrayOutputStream
        workbook.write(outputStream);
        // 刷新一下,确保数据完全写入
        outputStream.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return outputStream;
}

private List<FillData> makeTestData(Integer listIndx) {
        List<FillData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            list.add(fillData);
            fillData.setName("张三"+listIndx);
            fillData.setNumber(5.2);
            fillData.setDate(new Date());
        }
        return list;
    }

五、Vue3前端发起请求下载

const handleExport = async () => {
  try {
  	// 这里的确认框、loading框代码就省略了
    // 发起导出
    const data = await ReportTestApi.testReport(queryParams)
    download.excel(data, '测试模板填充.xlsx')
  } catch {
  } finally {
    exportLoading.value = false
  }
}

const download = {
  // 下载 Excel 方法
  excel: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/vnd.ms-excel')
  },
  // 下载 Word 方法
  word: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/msword')
  },
  // 下载 Zip 方法
  zip: (data: Blob, fileName: string) => {
    download0(data, fileName, 'application/zip')
  },
  // 下载 Html 方法
  html: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/html')
  },
  // 下载 Markdown 方法
  markdown: (data: Blob, fileName: string) => {
    download0(data, fileName, 'text/markdown')
  }
}

const download0 = (data: Blob, fileName: string, mineType: string) => {
  // 创建 blob
  const blob = new Blob([data], { type: mineType })
  // 创建 href 超链接,点击进行下载
  window.URL = window.URL || window.webkitURL
  const href = URL.createObjectURL(blob)
  const downA = document.createElement('a')
  downA.href = href
  downA.download = fileName
  downA.click()
  // 销毁超连接
  window.URL.revokeObjectURL(href)
}

六、官方文档及AI问答

直接拉到下面,看“多列表组合填充”
https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill#%E6%A8%A1%E6%9D%BF-2
easyExcel官方问答AI,对比了一下,其他AI都没有它答的好
无疑: https://answer.opensource.alibaba.com/docs/intro

客官点个赞再走呗


http://www.kler.cn/a/396588.html

相关文章:

  • 【全面系统性介绍】虚拟机VM中CentOS 7 安装和网络配置指南
  • 直接映射缓存配置
  • 求A/B高精度值
  • 鸿蒙 管理应用拥有的状态有Localstorage、Appstorage、PersistentStorage、Environment、用户首选项、持久化方案。
  • 如何在 Ubuntu 上 部署 OceanBase
  • 【大模型实战篇】vLLM的由来以及大模型部署、推理加速实践
  • Dockerfile构建镜像(练习一Apache镜像)(5-1)
  • 蓝桥杯每日真题 - 第10天
  • [Android]相关属性功能的裁剪
  • Linux---常用shell脚本
  • 力扣654:最大二叉树
  • 【鸿蒙开发】第二十二章 IPC与RPC进程间通讯服务
  • 【LeetCode】【算法】53. 最大子数组和
  • 【日常记录-Git】撤销工作区中所有已跟踪文件的修改
  • Java集合(Collection+Map)
  • 回调函数的概念、意义和应用场景
  • SQL 审核在 CloudQuery 的四大场景应用
  • leetcode hot100【 LeetCode 121.买卖股票的最佳时机】java实现
  • uniapp ios app以framwork形式接入sentry
  • 使用--log-file保存pytest的运行日志
  • WP网站如何增加文章/页面的自定义模板
  • Node.Js+Knex+MySQL增删改查的简单示例(Typescript)
  • 猫狗识别之BUG汇总
  • C++编程技巧与规范-类和对象
  • conda 和 pip 的比较
  • 嵌入式面试题练习 - 2024/11/15