EasyExcel动态头导出

news2025/2/27 17:28:42

前言

这段时间的项目中需要导出动态表格。
根据所选的参数导出对应的字段内容
在这里插入图片描述

下图所示选择下面几个tab页就需要导出对应的表头字段
在这里插入图片描述

下面为具体实现的效果。表头样式可以通过EasyExcel 提供的方法自定义。
在这里插入图片描述


具体实现

主要是通过 传入 exportItem 这个条件来决定导出的事项。

下附实现代码


public boolean export(QueryBO queryBo) throws CustomException {
    List<Integer> exportItem = queryBo.getExportItem();
    List<BankAccountOverviewVO> bankAccountOverviewList =
        accountOverviewList(queryBo, new PageInfoVo());
    // 动态创建表头
    List<List<String>> headList = new ArrayList<>();
    // 组装数据
    List<List<Object>> dataList = new ArrayList<>();
    // 表头合并的逻辑
    List<CellRangeAddress> cellRangeAddressList = new ArrayList<>();
    // 组装账户信息总览 导出数据
    builidAccountOverviewExportData(
        exportItem, bankAccountOverviewList, headList, dataList, cellRangeAddressList);

    String fileName = "信息总览.xlsx";
		// 调用EasyExcel 方法输出文件流
    try {
      HttpServletResponse response = ServletUtils.getResponse();
      response.setContentType("application/msexcel;charset=utf-8");
      response.setHeader(
          "content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "utf-8"));
      OutputStream out = response.getOutputStream();
      EasyExcel.write(out)
          .head(headList) // 设置表头数据
          .registerWriteHandler(new SimpleColumnWidthStyleStrategy(25)) // excel 样式设置
          .autoCloseStream(Boolean.TRUE) // 自动关闭文件流
          .sheet("信息总览")
          .doWrite(dataList); // 数据list
      // 开始导出
    } catch (Exception e) {
      LoggerUtils.error(e.getMessage());
      return false;
    }
    return true;
  }

数据组装方法

builidAccountOverviewExportData

主要实现的原理是通过List<Integer> exportItem 来判断需要导出的tab块。

其中使用了 getApiModelName(OverviewBankBookOcceVO.class); 方法映射对象的字段名进行快捷组装。

private void builidAccountOverviewExportData(
      List<Integer> exportItem,
      List<BankAccountOverviewVO> bankAccountOverviewList,
      List<List<String>> headList,
      List<List<Object>> dataList,
      List<CellRangeAddress> cellRangeAddressList) {
    // --------------表头拼接-------------
    // 账户信息
    List<String> bankBookHead = new ArrayList<>();
    bankBookHead.add("企业主体编码");
    bankBookHead.add("企业主体名称");
    for (String head : bankBookHead) {
      List<String> bankBookHeadTitle = new ArrayList<>();
      bankBookHeadTitle.add(head);
      headList.add(bankBookHeadTitle);
    }
	// 字段下标!!!!!!!!!!
    int index = 13;
    if (CollectionUtils.isNotEmpty(exportItem)) {
      // 账面发生额
      if (exportItem.contains(BankAccountOverviewExportTypeEnum.BANK_BOOK_OCCE.getValue())) {
        List<String> apiModelNameList = getApiModelName(OverviewBankBookOcceVO.class);
        for (String apiModelName : apiModelNameList) {
          List<String> bankBookOccHeadTitle = new ArrayList<>();
          bankBookOccHeadTitle.add("账面发生额");
          bankBookOccHeadTitle.add(apiModelName);
          headList.add(bankBookOccHeadTitle);
        }
        //      // 需要合并的表头位置  (起始行,结束行,起始列,结束列)
        //      int size = apiModelNameList.size();
        //      CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, index, index + size -
        // 1);
        //      cellRangeAddressList.add(cellRangeAddress);
        //      index += size;
      }

      // 发生额调节表
      if (exportItem.contains(BankAccountOverviewExportTypeEnum.OCC_ADJUSTMENT.getValue())) {
        List<String> apiModelNameList = getApiModelName(OverviewOccAdjustmentVO.class);
        for (String apiModelName : apiModelNameList) {
          List<String> occAdjustmentInfoHeadTitle = new ArrayList<>();
          occAdjustmentInfoHeadTitle.add("发生额调节表");
          occAdjustmentInfoHeadTitle.add(apiModelName);
          headList.add(occAdjustmentInfoHeadTitle);
        }
      }

      // 网银流水
      if (exportItem.contains(BankAccountOverviewExportTypeEnum.EBANK_FLOW.getValue())) {
        List<String> apiModelNameList = getApiModelName(OverviewEbankFlowVO.class);
        for (String apiModelName : apiModelNameList) {
          List<String> ebankFlowHeadTitle = new ArrayList<>();
          ebankFlowHeadTitle.add("网银流水");
          ebankFlowHeadTitle.add(apiModelName);
          headList.add(ebankFlowHeadTitle);
        }
      }

      // 核对余额
      if (exportItem.contains(BankAccountOverviewExportTypeEnum.RECONCILE_BALANCE.getValue())) {
        List<String> apiModelNameList = getApiModelName(OverviewReconcileBalanceVO.class);
        for (String apiModelName : apiModelNameList) {
          List<String> reconcileBalancesHeadTitle = new ArrayList<>();
          reconcileBalancesHeadTitle.add("核对余额");
          reconcileBalancesHeadTitle.add(apiModelName);
          headList.add(reconcileBalancesHeadTitle);
        }
        // 需要合并的表头位置  (起始行,结束行,起始列,结束列)
        int size = apiModelNameList.size();
        CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, index, index + size - 1);
        cellRangeAddressList.add(cellRangeAddress);
        index += size;
      }

      // 双向核对结果
      if (exportItem.contains(BankAccountOverviewExportTypeEnum.CHECK_RESULT.getValue())) {
        List<String> apiModelNameList = getApiModelName(OverviewCheckResultVO.class);
        for (String apiModelName : apiModelNameList) {
          List<String> checkResultHeadTitle = new ArrayList<>();
          checkResultHeadTitle.add("双向核对结果");
          checkResultHeadTitle.add(apiModelName);
          headList.add(checkResultHeadTitle);
        }
      }
    }

    // ----------------组装数据-------------
    for (BankAccountOverviewVO bankAccountOverview : bankAccountOverviewList) {
      List<Object> data = new ArrayList<>();
      // 账户信息总览
      data.add(bankAccountOverview.getCompanyId());
      data.add(bankAccountOverview.getCompanyName());
      String checkPlanFlag = bankAccountOverview.getCheckPlanFlag();
      if (StringUtils.isNotEmpty(checkPlanFlag)) {
        checkPlanFlag = checkPlanFlag.replaceAll("0", "是");
        checkPlanFlag = checkPlanFlag.replaceAll("1", "否");
      }
      data.add(checkPlanFlag);
      data.add(bankAccountOverview.getNoCheckReason());
      // -------动态表格数据组装-----
      if (CollectionUtils.isNotEmpty(exportItem)) {
        // 账面发生额
        if (exportItem.contains(BankAccountOverviewExportTypeEnum.BANK_BOOK_OCCE.getValue())) {
          data.add(bankAccountOverview.getBankBookOccInfo().getOpenBalance());
          data.add(bankAccountOverview.getBankBookOccInfo().getCurrentIssueAdd());
          data.add(bankAccountOverview.getBankBookOccInfo().getCurrentIssueReduce());
          data.add(bankAccountOverview.getBankBookOccInfo().getBalance());
          data.add(bankAccountOverview.getBankBookOccInfo().getOpenBalanceF());
          data.add(bankAccountOverview.getBankBookOccInfo().getCurrentIssueAddF());
          data.add(bankAccountOverview.getBankBookOccInfo().getCurrentIssueReduceF());
          data.add(bankAccountOverview.getBankBookOccInfo().getBalanceF());
        }

        // 发生额调节表
        if (exportItem.contains(BankAccountOverviewExportTypeEnum.OCC_ADJUSTMENT.getValue())) {
          data.add(bankAccountOverview.getOccAdjustmentInfo().getCurrentIssueAdd());
          data.add(bankAccountOverview.getOccAdjustmentInfo().getCurrentIssueReduce());
        }

        // 网银流水
        if (exportItem.contains(BankAccountOverviewExportTypeEnum.EBANK_FLOW.getValue())) {
          data.add(bankAccountOverview.getEbankFlowInfo().getOpenBalance());
          data.add(bankAccountOverview.getEbankFlowInfo().getCurrentIssueAdd());
          data.add(bankAccountOverview.getEbankFlowInfo().getCurrentIssueReduce());
          data.add(bankAccountOverview.getEbankFlowInfo().getBalance());
        }

        // 核对余额
        if (exportItem.contains(BankAccountOverviewExportTypeEnum.RECONCILE_BALANCE.getValue())) {
          data.add(bankAccountOverview.getReconcileBalances().getBalanceVerify());
          data.add(bankAccountOverview.getReconcileBalances().getDebitOccFVerify());
          data.add(bankAccountOverview.getReconcileBalances().getCreditOccFVerify());
          data.add(bankAccountOverview.getReconcileBalances().getNetAmountVerify());
        }

        // 双向核对结果
        if (exportItem.contains(BankAccountOverviewExportTypeEnum.CHECK_RESULT.getValue())) {
          data.add(bankAccountOverview.getCheckResult().getDebitOccF());
          data.add(bankAccountOverview.getCheckResult().getCreditOccF());
          data.add(bankAccountOverview.getCheckResult().getDebitRatio());
          data.add(bankAccountOverview.getCheckResult().getCreditRatio());
          data.add(bankAccountOverview.getCheckResult().getUntreatedProject());
          data.add(bankAccountOverview.getCheckResult().getExceptionProject());
        }
      }
      dataList.add(data);
    }
  }

getApiModelName 方法代码如下,因为项目引入了 swagger 所以数据接口对象都会使用到 @ApiModelProperty 注解 如下图所示。通过反射获取实体类的对应属性的注解名称list。

@Data
@ApiModel(value = "vo对象", description = "vo对象")
public class OverviewBankBookOcceVO {
  @ApiModelProperty(value = "银行账号")
  private String bankAccount;

  @ApiModelProperty("期初余额(原币)")
  private BigDecimal openBalance;
}
/**
   * @description 获取对象中 api 注解的名称
   * @params [object]
   * @return
   */
  private List<String> getApiModelName(Object object) {
    Class<T> aClass = (Class<T>) object;
    Field[] fields = aClass.getDeclaredFields();
    List<String> apiModelName = new ArrayList<>();
    for (Field field : fields) {
      ApiModelProperty annotation = field.getAnnotation(ApiModelProperty.class);
      String value = annotation.value();
      if (field.getName().equals("bankAccount")) {
        continue;
      }
      apiModelName.add(value);
    }
    return apiModelName;
  }

本文由 SoGeek_Studio 发布,有任何问题请留言评论,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/468515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GCM与CCM的的规格和加解密过程

CCM CCM&#xff08;Counter with CBC-MAC&#xff09;是一种基于对称加密算法的认证加密&#xff08;Authenticated Encryption&#xff09;模式&#xff0c;结合了CBC-MAC&#xff08;Cipher Block Chaining Message Authentication Code&#xff09;用于消息认证和CTR&…

沃通TSA可信时间戳服务,保障电子数据法律效力

在全球信息化的大趋势下&#xff0c;以计算机及其网络为依托的电子数据&#xff0c;在证明案件事实的过程中起着越来越重要的作用&#xff0c;而可信时间戳已成为确立电子数据法律效力的重要技术之一。沃通TSA可信时间戳服务&#xff0c;提供具有法律效力的第三方可信时间戳认证…

android10 关闭默认输入法的“更正建议”

1. 场景 使用系统默认的输入法&#xff0c;在进行输入时&#xff0c;在输入法上方&#xff0c;会显示更正建议列表&#xff0c;同时会干扰我们的输入内容&#xff1a;会自动补全到输入框&#xff0c;而且删除不掉&#xff0c;甚至越删越多&#xff0c;非常讨厌。 如下&#x…

Spring IOC之对象的创建方式、策略及销毁时机和生命周期且获取方式

目录 一、对象的创建方式 1. 使用构造方法 2. 使用工厂类方法 3. 使用工厂类的静态方法 二、对象的创建策略 1. 单例策略 2. 多例策略 三、对象的销毁时机 四、生命周期方法 1. 定义生命周期方法 2. 配置生命周期方法 3. 测试 五、获取Bean对象的方式 1. 通过id/…

学习ZLmediaKit流媒体服务器时候遇到的问题

照zlmediakit的源码 自己复制了一份 然后有的地方编译不过修改了部分 测试的时候发现有两个问题 第一是 ffmpeg的ffplay 能播放 vlc不能播放 第二个问题是directProxy设置为0的时候 推流的时候 然后用ffplay播放 只有音频没有视频 查了好久终于解决这个问题 第一个…

pytest 自动化框架搭建-生成一个报告(alluer)

简单的case跑通了&#xff0c;尝试生成报告 1、使用allure-pytest 安装pip3 install allure-pytest 2、使用--alluredir../allure-results 如下我是直接在pytest.ini文件中使用命令 如下生成文件夹和json格式的文件 二、生成allure报告 1、安装allure 我是使用的命令直接…

字节超全学习流程图流出,100天涨薪10k,从功能测试到自动化测试...

今年年初&#xff0c;由于经济压力让我下定决心进阶自动化测试&#xff0c;已经24的我做了3年功能测试&#xff0c;坐标广州薪资定格在8k&#xff0c;可能是生活过的太安逸&#xff0c;觉得8000的工资也够了。 但是生活总是多变的&#xff0c;女朋友的突然怀孕&#xff0c;让我…

thinkphp6(tp6)初步小尝试(记录一下)

thinkphp6&#xff08;tp6&#xff09;初步小尝试&#xff08;记录一下&#xff09; 一、服务器环境二、创建tp6项目三、配置apache四、创建应用五、各应用入口六、简单模板 一、服务器环境 操作系统&#xff1a;ubuntu-22.04.2-live-server-amd64集成xampp(Apache MariaDB …

Python学习10:计算三维空间某点距离原点的欧式距离(python123)

题目描述: 欧几里得度量&#xff08;euclidean metric&#xff09;&#xff08;也称欧氏距离&#xff09;是一个通常采用的距离定义。三维空间里点a和b的坐标如果分别为a(x1,y1,z1)、b(x2,y2,z2)&#xff0c;则ab的距离的计算机公式是dist(a,b) √( (x1-x2)^2(y1-y2)^2(z1-z2…

史上最全Maven教程(四)

文章目录 &#x1f525;Maven聚合开发_聚合关系&#x1f525;Maven聚合开发_继承关系&#x1f525;Maven聚合案例_搭建父工程 &#x1f525;Maven聚合开发_聚合关系 之前我们在Idea中开发时会将项目的所有包放在同一个工程当中。 ⭐ domain&#xff1a;定义实体类 ⭐ dao&…

learn_C_deep_7 (switch 语句的基本理解、case 的作用、break的作用switch、case 推荐规则)

目录 switch 语句的基本理解 case语句的深度理解 case 的作用 case语句的要求 break 的作用 switch case 推荐规则 规则一&#xff1a; 规则二&#xff1a; 小结&#xff1a; switch 语句的基本理解 switch是一种控制语句&#xff0c;用于控制程序流程的走向。在 swit…

Zookeeper系统模型介绍

目录 一、数据模型 二、 节点的类型 &#xff08;1&#xff09;持久节点 &#xff08;2&#xff09;持久顺序节点 &#xff08;3&#xff09;临时节点 &#xff08;4&#xff09;临时顺序节点 三、客户端命令行 &#xff08;1&#xff09;创建节点 &#xff08;2&…

k8s- kubernetes证书过期替换之kubeadm命令 certs renew all方式

k8s- kubernetes证书过期替换之kubeadm命令 certs renew all方式 大纲 基础概念证书替换测试使用kubeadm alpha certs renew all 更新证书重启所有组件和kubelet.service生成kubelet-client-current.pem证书测试替换后集群是否正常工作 基础概念 本次测试使用的k8s集群使用…

【代码随想录】刷题Day9 --- 我有自信讲清楚KMP的next原理

字符串 --- 找子串匹配算法_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130197908?spm1001.2014.3001.5501 首先具体KMP算法理论在上面的博客中&#xff0c;不过该博客我当初写的时候并没有了解next数组实现的过程是那样的&#xff0c;所以只…

【react全家桶学习】react中组件的props属性(详)

学过了vue的props属性&#xff0c;那react中的props属性如何使用呢&#xff1f; props的基本使用 我们先定义一个person组件 我们想要这三个参数动态传递进来&#xff0c;如何实现&#xff1f; 答&#xff1a;跟vue一样&#xff0c;直接在引用组件的地方进行传递即可。 在re…

Java——两个链表的第一个公共节点

题目链接 牛客网在线oj题——两个链表的第一个公共节点 题目描述 输入两个无环的单向链表&#xff0c;找出它们的第一个公共结点&#xff0c;如果没有公共节点则返回空。&#xff08;注意因为传入数据是链表&#xff0c;所以错误测试数据的提示是用其他方式显示的&#xff0…

润本“带伤”冲刺IPO,再闯“驱蚊第一股”

夏日来临&#xff0c;润本再度冲刺“驱蚊第一股”&#xff01; 今年3月&#xff0c;润本生物技术股份有限公司&#xff08;下称“润本”&#xff09;再次更新招股书&#xff0c;拟在上交所主板上市。 润本是一家多品牌家庭护理产品公司&#xff0c; 专注于驱蚊类、个人护理类…

米哈游新游正式公测!还没上线就已经“爆了”!

米哈游制作的3D冒险主题回合制策略游戏《崩坏&#xff1a;星穹铁道》&#xff0c;在2023年4月26日正式开启全平台公测。 该游戏在2021年10月27日曾开启过“始发测试”&#xff0c;后继续沉淀了两年才正式开启公测。 B站的ACG内容生态丰富&#xff0c;其中游戏相关内容当数米哈…

C++“this”指针的使用

1.this指针的引出 先看一段代码&#xff1a; class Date { public:Date(int year0,int month0,int day0){_year year;_month month;_day day;}void Print(){cout << _year << " "<< _month << " "<< _day << end…

无需公网IP 使用SSH远程连接Linux CentOS服务器【内网穿透】

文章目录 视频教程1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 本次教程我们来实现如何在外公网环境下&#xff0c;SSH远程连接家里/公司的Linux CentOS服务器&#xff0c;无需公网IP&#xff0c;也不需要设置…