位置: IT常识 - 正文

EasyExcel复杂表头导出(一对多)升级版(excel表格复杂表头)

编辑:rootadmin
EasyExcel复杂表头导出(一对多)升级版 一、前言

推荐整理分享EasyExcel复杂表头导出(一对多)升级版(excel表格复杂表头),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:easyui复杂表头,easyexcel复杂表头合并单元格,easyexcel复杂表头导出,easyexcel复杂表头合并单元格,easyexcel复杂表头导入校验,easyexcel复杂表头导入数据库,easyexcel复杂表头导出,easyexcel复杂表头导出,内容如对您有帮助,希望把文章链接给更多的朋友!

        在之前写的 EasyExcel复杂表头导出(一对多)的博客的结尾,受限于当时的能力和精力,留下一些问题及展望。现在写下此博客,目的就是解决之前遗留的问题。

        背景介绍,见上述链接指向的博客,这里主要通过自定义拦截器的形式来完美解决。

二、导出功能的实现2.1 Entity 对象import com.alibaba.excel.annotation.ExcelProperty;import com.alibaba.excel.annotation.write.style.ColumnWidth;import com.alibaba.excel.annotation.write.style.ContentRowHeight;import com.alibaba.excel.annotation.write.style.HeadRowHeight;import com.alibaba.excel.annotation.write.style.HeadStyle;import com.alibaba.excel.converters.string.StringImageConverter;import lombok.AllArgsConstructor;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.NoArgsConstructor;import java.net.URL;@Data@EqualsAndHashCode@HeadRowHeight(30)@ContentRowHeight(80)@ColumnWidth(15)@HeadStyle(fillForegroundColor = 44)@NoArgsConstructor@AllArgsConstructorclass Customer { @ExcelProperty({"客户编号"}) private String userCode; @ExcelProperty({"客户名称"}) private String userName; @ColumnWidth(25) @ExcelProperty({"客户所在地址"}) private String address; @ExcelProperty({"联系人信息", "联系人姓名"}) private String personName; @ExcelProperty({"联系人信息", "联系电话"}) private String telephone; @ExcelProperty({"图片"}) private URL picture; /** * 你也可以通过字符串的形式来保存图片,具体说明见注意事项3.1 */ //@ExcelProperty(converter = StringImageConverter.class, value = {"本地图片"}) //private String localPic;}2.2 Controller 层@PostMapping("/exportExcel")@ApiOperation("导出Excel")public void exportExcel(HttpServletResponse response) throws Exception {// 查询需要导出的数据List result = getData();// 1设置表头样式WriteCellStyle headStyle = new WriteCellStyle();// 1.1设置表头数据居中headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 2设置表格内容样式WriteCellStyle bodyStyle = new WriteCellStyle();// 2.1设置表格内容水平居中bodyStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 2.2设置表格内容垂直居中bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 3设置表格sheet样式WriteSheet sheet = EasyExcel.writerSheet("客户信息").head(Customer.class).sheetNo(1).build();// 4拿到表格处理对象ExcelWriter writer = EasyExcel.write(response.getOutputStream()).needHead(true).excelType(ExcelTypeEnum.XLSX) // 设置需要待合并的行和列。参数1:数值数组,指定需要合并的列;参数2:数值,指定从第几行开始合并.registerWriteHandler(new ExcelMergeCellHandler(new int[]{0, 1, 2, 5}, 0)) // 设置单元格的风格样式.registerWriteHandler(new HorizontalCellStyleStrategy(headStyle, bodyStyle)).build();// 5写入excel数据writer.write(result, sheet);// 6通知浏览器以附件的形式下载处理,设置返回头要注意文件名有中文response.setHeader("Content-disposition", "attachment;filename=" + new String("客户信息表".getBytes("gb2312"), "ISO8859-1") + ".xlsx");response.setContentType("multipart/form-data");response.setCharacterEncoding("utf-8");writer.finish();}2.3 自定义拦截器(ExcelMergeCellHandler)import com.alibaba.excel.metadata.Head;import com.alibaba.excel.metadata.data.WriteCellData;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.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.apache.poi.ss.usermodel.Cell;import org.apache.poi.ss.usermodel.CellType;import org.apache.poi.ss.usermodel.Sheet;import org.apache.poi.ss.util.CellRangeAddress;import java.util.List;/** * @author DaHuaJia * @Description 自定义单元格合并处理Handler类 * @Date 2022-08-18 19:25:58 */@Data@NoArgsConstructor@AllArgsConstructorpublic class ExcelMergeCellHandler implements CellWriteHandler { // 需要合并的列,从0开始算 private int[] mergeColIndex; // 从指定的行开始合并,从0开始算 private int mergeRowIndex; /** * 在单元格上的所有操作完成后调用,遍历每一个单元格,判断是否需要向上合并 */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 获取当前单元格行下标 int currRowIndex = cell.getRowIndex(); // 获取当前单元格列下标 int currColIndex = cell.getColumnIndex(); // 判断是否大于指定行下标,如果大于则判断列是否也在指定的需要的合并单元列集合中 if (currRowIndex > mergeRowIndex) { for (int i = 0; i < mergeColIndex.length; i++) { if (currColIndex == mergeColIndex[i]) { /** * 获取列表数据的唯一标识。不同集合的数据即使数值相同也不合并 * 注意:我这里的唯一标识为客户编号(Customer.userCode),在第一列,即下标为0。大家需要结合业务逻辑来做修改 */ // 获取当前单元格所在的行数据的唯一标识 Object currCode = cell.getRow().getCell(0).getStringCellValue(); // 获取当前单元格的正上方的单元格所在的行数据的唯一标识 Object preCode = cell.getSheet().getRow(currRowIndex - 1).getCell(0).getStringCellValue(); // 判断两条数据的是否是同一集合,只有同一集合的数据才能合并单元格 if(preCode.equals(currCode)){ // 如果都符合条件,则向上合并单元格 mergeWithPrevRow(writeSheetHolder, cell, currRowIndex, currColIndex); break; } } } } } /** * 当前单元格向上合并 * * @param writeSheetHolder 表格处理句柄 * @param cell 当前单元格 * @param currRowIndex 当前行 * @param currColIndex 当前列 */ private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int currRowIndex, int currColIndex) { // 获取当前单元格数值 Object currData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); // 获取当前单元格正上方的单元格对象 Cell preCell = cell.getSheet().getRow(currRowIndex - 1).getCell(currColIndex); // 获取当前单元格正上方的单元格的数值 Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); // 将当前单元格数值与其正上方单元格的数值比较 if (preData.equals(currData)) { Sheet sheet = writeSheetHolder.getSheet(); List<CellRangeAddress> mergeRegions = sheet.getMergedRegions(); // 当前单元格的正上方单元格是否是已合并单元格 boolean isMerged = false; for (int i = 0; i < mergeRegions.size() && !isMerged; i++) { CellRangeAddress address = mergeRegions.get(i); // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 if (address.isInRange(currRowIndex - 1, currColIndex)) { sheet.removeMergedRegion(i); address.setLastRow(currRowIndex); sheet.addMergedRegion(address); isMerged = true; } } // 若上一个单元格未被合并,则新增合并单元 if (!isMerged) { CellRangeAddress cellRangeAddress = new CellRangeAddress(currRowIndex - 1, currRowIndex, currColIndex, currColIndex); sheet.addMergedRegion(cellRangeAddress); } } }}2.4 getDate方法(用于模拟service层拿到的数据)public static List<Customer> getData() throws Exception {List<Customer> data = new ArrayList<>();Customer customer = new Customer("JiangXi", "江西电信公司", "江西省南昌市东湖区", "张三", "12345678910", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184128-4e6652a71714f6e.jpg"));data.add(customer);Customer customer2 = new Customer("JiangXi", "江西电信公司", "江西省南昌市东湖区", "李四", "15848563521", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184128-4e6652a71714f6e.jpg"));data.add(customer2);Customer customer3 = new Customer("GuangDong", "广东电信公司", "广东省广州市花都区", "小明", "15847953624", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184134-4e6652a71714f6e.jpg"));data.add(customer3);Customer customer4 = new Customer("GuangDong", "广东电信公司", "广东省广州市天河区", "小红", "16849531548", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184140-4e6652a71714f6e.jpg"));data.add(customer4);Customer customer5 = new Customer("GuangDong", "广东电信公司", "广东省广州市天河区", "小华", "16985632481", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184140-4e6652a71714f6e.jpg"));data.add(customer5);Customer customer6 = new Customer("BeiJing", "北京电信公司", "北京市东城区", "姜维", "16598645874", new URL("https://www.yuucn.com/wp-content/uploads/2023/05/1684184147-4e6652a71714f6e.jpg"));data.add(customer6);return data;}2.5 效果EasyExcel复杂表头导出(一对多)升级版(excel表格复杂表头)

 三、注意事项3.1 图片导出问题

对于图片的导出,其字段可以有多种数据类型,官网就介绍了5种(File、InputStream、String、byte[]、URL、WriteCellData<Void>)。这里简要介绍一下String 和 URL。

1、String 类型/***  如果图片地址通过String类型保存,则需要加一个自带的类型转换器(StringImageConverter)*/@ExcelProperty(converter = StringImageConverter.class, value = {"本地图片"})private String localPic;2、URL类型 @ExcelProperty({"网络图片"})private URL picture;

        经过测试发现,String类型只能保存本地图片地址,如果保存网络图片地址,则会导致图片无法下载。原因则是EasyExcel会把“//” 转换成 “\”,导致地址错误。

        因此,可以约定String类型用于保存本地图片地址,URL类型用于保存网络图片地址。

3.2 图片单元格合并问题

        图片类型单元格无法做到相同的图片合并单元格,主要是因为无法通过单元格对象拿到图片的序列化值。

3.3 表格样式

        表格的样式既可以表格样式类(例如:WriteCellStyle)来设置,也可以通过注解(例如:@HeadStyle)来设置,两者互补,不冲突。

本文链接地址:https://www.jiuchutong.com/zhishi/313145.html 转载请保留说明!

上一篇:使用curl抓取网页遇到HTTP跳转时得到多个HTTP头部的问题(curl抓包)

下一篇:DEDECMS不规则的列表实现方法 每隔5行加一虚线(div不规则排版)

  • 快手V10.2.30版本(快手v9.2.30)

    快手V10.2.30版本(快手v9.2.30)

  • play4tpro有陀螺仪功能吗(荣耀play4tpro陀螺仪延迟怎么办)

    play4tpro有陀螺仪功能吗(荣耀play4tpro陀螺仪延迟怎么办)

  • 红米k30怎么拍月亮(红米K30怎么拍月亮专业模式)

    红米k30怎么拍月亮(红米K30怎么拍月亮专业模式)

  • 手机越删东西内存越小(手机越删东西内存越小是怎么回事)

    手机越删东西内存越小(手机越删东西内存越小是怎么回事)

  • 支付宝合种爱情树多少能量能长大(支付宝合种爱情树名称大全)

    支付宝合种爱情树多少能量能长大(支付宝合种爱情树名称大全)

  • 无线网需要认证登录怎么回事(无线网需要认证登录怎么操作)

    无线网需要认证登录怎么回事(无线网需要认证登录怎么操作)

  • s^msung是什么牌子手机(s^msung是什么牌子电视怎么开)

    s^msung是什么牌子手机(s^msung是什么牌子电视怎么开)

  • 苹果11打电话对方断断续续(苹果11打电话对方听到的声音很小怎么解决)

    苹果11打电话对方断断续续(苹果11打电话对方听到的声音很小怎么解决)

  • 网线接路由器哪个口(网线接路由器哪个口上网)

    网线接路由器哪个口(网线接路由器哪个口上网)

  • 手机号被恶意举报停机(手机号被恶意举报为骚扰电话怎么办)

    手机号被恶意举报停机(手机号被恶意举报为骚扰电话怎么办)

  • 启用语音信箱是什么意思(启用语音信箱是啥意思)

    启用语音信箱是什么意思(启用语音信箱是啥意思)

  • app闪退是什么原因(app闪退是什么原因 怎么解决华为)

    app闪退是什么原因(app闪退是什么原因 怎么解决华为)

  • cpu后面带f是什么意思(cpu后面的f的英文全称)

    cpu后面带f是什么意思(cpu后面的f的英文全称)

  • ipad型号a1983是几代(ipad型号a1983是2018嘛)

    ipad型号a1983是几代(ipad型号a1983是2018嘛)

  • 快手可以查到手机号码(快手能查)

    快手可以查到手机号码(快手能查)

  • qq特别关注对方知道吗(qq特别关注对方怎么取消)

    qq特别关注对方知道吗(qq特别关注对方怎么取消)

  • 华为荣耀9x有人脸识别吗(华为荣耀9x手机)

    华为荣耀9x有人脸识别吗(华为荣耀9x手机)

  • 抖音粉丝按什么排序(抖音粉丝什么时候更新)

    抖音粉丝按什么排序(抖音粉丝什么时候更新)

  • 唯品会怎么切换省份(唯品会怎么切换男女)

    唯品会怎么切换省份(唯品会怎么切换男女)

  • 浏览器收藏夹丢失怎么办(浏览器收藏夹不小心删除了怎么找回?)

    浏览器收藏夹丢失怎么办(浏览器收藏夹不小心删除了怎么找回?)

  • 小米应用商店无法联网(小米应用商店无法升级软件)

    小米应用商店无法联网(小米应用商店无法升级软件)

  • 花呗额度怎么提升(花呗额度怎么提现到银行卡里)

    花呗额度怎么提升(花呗额度怎么提现到银行卡里)

  • word怎么把图片转为文字(word怎么把图片设置成一样大小)

    word怎么把图片转为文字(word怎么把图片设置成一样大小)

  • 苹果x漏液怎么办(iphonex漏液了)

    苹果x漏液怎么办(iphonex漏液了)

  • 微信小程序实现发送短信的功能(发送短信)(微信小程序实现发红包)

    微信小程序实现发送短信的功能(发送短信)(微信小程序实现发红包)

  • HTML <span>标签(html span标签作用)

    HTML <span>标签(html span标签作用)

  • python生成器中的send()方法和next()方法(python中生成器的作用)

    python生成器中的send()方法和next()方法(python中生成器的作用)

  • 预收账款的税费怎么处理
  • 以前年度损益调整属于哪类科目
  • 上月开票这月退票要红冲吗?
  • 餐饮业增值税是多少
  • 进项税额转出有什么影响
  • 公司用的摄像头
  • 生产经营所得的税率表
  • 大病医疗可以抵扣吗
  • 为什么租赁只能20年
  • 账本一般保存几年就可以销毁2004年的规定
  • 投标函中的其他是指什么
  • 小规模纳税申报时间
  • 申请增值税专用发票需要什么资料
  • 本季度盈利可以当季弥补以前亏损吗
  • 出口退税代理费用大概多少
  • 分公司所得税怎么交
  • 增值税未实际收到款项可以抵扣吗?
  • 公司用现金发放工资在税法上合规吗
  • 工会经费用于什么
  • 增值税不征税收入的三个条件
  • 低价销售代替非正常损失避免转出进项税?
  • 什么是应交税费应交增值税科目设置的贷方明细科目
  • 人工费开专票最多能开几个点的
  • 公司为员工负担的工资
  • 附加税增值税免抵税额的数据从哪里提取的
  • 长期零申报有什么影响
  • 固定资产折旧转入什么科目
  • 合同资产和工程存货的区别
  • 发票丢了还可以重开吗
  • 房租费进项税能不能抵扣
  • 销售费用营业费用的区别
  • 金税盘减免税款申报的时候怎么填写
  • win10系统日志在哪个文件夹
  • php时间戳相减
  • phpstorm运行php
  • php1 zybdjx
  • 会计利润类科目是什么
  • 阿圭罗来自哪里
  • 免税货物增值税计算公式
  • 广告费和业务宣传费调增还是调减
  • 现在用yii框架的人还多么
  • 金税三期系统的对比有哪几个
  • ai训练流程
  • 在vue3项目中使用vue2
  • 库存现金盘亏盘盈
  • 息税前利润为什么不减利息
  • 政府补贴什么时候开始的
  • 材料发票和工程发票的区别
  • 属于长期险种的是
  • 跨年发票一般分为哪几类
  • 开具房租发票的分录如何做?
  • 暂估入库的商品能出库吗
  • 安装调试费属于劳务还是服务
  • 技术服务费是否可以开具专票
  • 消费税也是流转税吗
  • 城建税如何计提税金
  • 纳税期限与申报期限的区别
  • 新准则公允价值变动科目余额为负数
  • 预付下个月租金分录
  • 做假账本怎么判
  • mysql获取当前时间
  • Mac OS10.11下mysql5.7.12 安装配置方法图文教程
  • freebsd怎么安装软件
  • Linux中通过Socket文件描述符寻找连接状态介绍
  • js立即执行函数几种写法
  • Node.js中的construct构造函数
  • Node.js中的construct构造函数
  • python笛卡尔积
  • 模仿文明
  • node转go
  • nodejs 性能优化
  • js操作对象的方法
  • python约瑟夫问题最高效算法
  • python不同进制的整数之间可以直接运算
  • jquery中点击事件点击没动静
  • 国家税务总局河北地税局
  • 湖南地税电话号码
  • 诺诺发票怎样上报汇总
  • 物业监控不完善怎么提意见
  • 电子发票怎么作废
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设