EasyExcel 动态列,横向扩展,横向循环

Excel 模板版

1 模板 指令介绍

模板是处理复杂Excel的简单方法,复杂的Excel样式,可以用Excel直接编辑,完美的避开了代码编写样式的雷区,同时指令的支持,也提了模板的有效性 下面列举下EasyPoi支持的指令以及作用,最主要的就是各种fe的用法

  • 空格分割
  • 三目运算 {{test ? obj:obj2}}
  • n: 表示 这个cell是数值类型 {{n:}}
  • le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 : obj2}}
  • fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}}
  • fn: 格式化数字 {{fn:(obj;###.00)}}
  • fe: 遍历数据,创建row
  • !fe: 遍历数据不创建row
  • $fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入
  • #fe: 横向遍历
  • v_fe: 横向遍历值
  • !if: 删除当前列 {{!if:(test)}}
  • 单引号表示常量值 '' 比如'1' 那么输出的就是 1
  • &NULL& 空格
  • ]] 换行符 多行遍历导出
  • sum: 统计数据

2.下载模板

关键字段1、【fe: 】代表向下循环 

关键字段2、【v_fe:】代表向右循环 

 

实现效果

 3.实现代码:

 3.1此处用的layui

form.on('submit(dowAll)', function (data) {
        layer.load(2, {time: 2000});
        var param = data.field;
        param.langCode = langCode;

        //拼接查询参数
        var params = '';
        $.each(param, function (k, v) {
            params += '&' + k + '=' + v;
        });

        location.href=layui.setter.reqHost + 'afterCtrl/dowAll?langCode='+langCode+params;
        return false;
    });

3.2 后端 Controller

@GetMapping("/dowAll")
    public void dowAll(
            String client, String contractNo, String plateNo, String purchaseDate, Integer status, String prodCode, String saleman, Integer branch, Integer area, String depositDate, String dueDate, String paymentDate, String langCode, Integer currentPage, Integer pageSize,
            HttpServletResponse response
    ) {
        try{
            AcctPageParam param = new AcctPageParam(client, contractNo, plateNo, purchaseDate, status, prodCode, saleman, branch, area, depositDate, dueDate, paymentDate, currentPage, pageSize, langCode);
            log.info("下载还款计划【afterCtrl/dowAll】  start = {}", JSONObject.toJSONString(param));

            Map<String,Object> list = accountService.dowAll(param);
            //TODO Easypoi 根据模板下载
            TemplateExportParams params = new TemplateExportParams("template/Sale_temp.xlsx");
            params.setColForEach(true);
            Workbook workbook = ExcelExportUtil.exportExcel(params, list);
            OutputStream output = null;
            response.setContentType("application/force-download");// 应用程序强制下载
            //导出文件名:Claim+客诉种类+ERP CODE+导出日期,sheet名用客诉编号
            String fileName = "ACCT_" + "_" +DateUtils.formatDate2String(new Date(), DateUtils.SUP_SHORT_PATTERN2);
            try {
                response.setHeader("Content-Disposition", "attachment;filename="+fileName+".xls");
                output = response.getOutputStream();
                workbook.write(output);
                output.flush();
            } catch (Exception e) {
                e.printStackTrace();
                log.error("文件流关闭失败", e);
            } finally {
                try {
                    if (output != null) {
                        output.close();
                    }
                } catch (Exception e) {
                    log.error("文件流关闭失败", e);
                }
            }
            log.info("下载还款计划 end");
        }catch (Exception e){
            e.printStackTrace();
            log.error("loan_acct_details  err----",e);
        }
    }

3.3 ServiceImpl实现 【关键代码】

/**
     * 集合对象转为 temp 可识别的 Map
     * @param biList
     * @return
     * @throws Exception
     */
    public Map<String,Object> acctToExcelMap(List<AcctExcel> biList) throws Exception {
        Map<String,Object> data = new HashMap<>();
        List<Map<String, Object>> titles = new ArrayList<Map<String, Object>>();
        List<Map<String, Object>> rowList = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < biList.size(); i++) {
            AcctExcel excel = biList.get(i);
            Map<String, Object> row = EmptyUtil.objectToMapExclude(excel,"collList");
            Map<String, Object> title;

            int idx = 0;
            for (ScheduleExcel item : excel.getCollList()) {
                if(i == 0){
                    title = new HashMap<String, Object>();
                    title.put("title_1", "PAYMENT");
                    title.put("title_2", item.getRepayDueDate() + "DATE");
                    title.put("title_3", "BALANCE");
                    title.put(REPAY_DUE_DATE, "t.col_" + (idx + 1));
                    title.put("paidTotalAmt", "t.col_" + (idx + 2));
                    title.put("totalBal", "t.col_" + (idx + 3));
                    titles.add(title);
                }
                row.put("col_" + (idx + 1), item.getTermNo());
                row.put("col_" + (idx + 2), item.getLoanAccountNo());
                row.put("col_" + (idx + 3), item.getRepayDueDate());
                idx += 3;
            }
            rowList.add(row);
        }
        data.put("titles", titles);
        data.put("rowList", rowList);
        System.out.println(JSON.toJSON(data));
        return data;
    }

下载对象

/**
 * 不变行对象
 *
 */
@Data
public class AcctExcel {

    @Excel(name = "SN")         //行号
    private Integer index;
    @Excel(name = "DATE")       //日期
    private String purchaseDate;
    @Excel(name = "BRANCH")     //品牌
    private String branchText;
    @Excel(name = "AREA")       //区域
    private String areaText;
    @Excel(name = "CLIENT")     //客户端
    private String client;
    @Excel(name = "COLOUR")     //颜色
    private String colorText;
    @Excel(name = "PLATE N0")   //板N0
    private String plateNo;
    @Excel(name = "QTY")        //数量
    private String qty;
    @Excel(name = "UNIT PRICE") //单价
    private String unitPrice;

    List<ScheduleExcel> collList = new ArrayList<>();
}

/**
 * 重复向右扩展对象
 *
 */
@Data
public class ScheduleExcel {
    @Excel(name = "DATE")           //还款日期
    private String repayDueDate;
    @Excel(name = "PAYMENT")        //付款金额
    private String paidTotalAmt;
    @Excel(name = "BALANCE")        //余额
    private String totalBal;
    private BigDecimal remainPrinAmt;

    private String loanAccountNo;
    private Integer termNo;
}

<think>好的,用户想用EasyExcel导出不规则表格的数据。首先,我需要回忆一下EasyExcel的基本用法,以及如何处理复杂布局。根据提供的引用,EasyExcel使用POI的sax模式解析,但导出方面可能需要更多的定制。 用户提到的“不规则表格”可能涉及合并单元格、多级表头、动态列或者嵌套结构。比如,可能有多个行表头,或者某些列需要合并。这时候,需要用到EasyExcel的注解和自定义样式功能。 参考引用[1]和[2],EasyExcel的常规导入导出比较简单,但复杂情况需要更多配置。引用[4]提到write方法的几种写法,可能需要使用自定义的WriteHandler来处理合并单元格等操作。 接下来,我应该考虑如何实现多级表头。EasyExcel的@ExcelProperty注解支持数组,可以定义多级表头。例如,使用@ExcelProperty({"主标题", "子标题"})来生成两级表头。 然后,合并单元格的问题。这可能需要自定义单元格合并策略,通过实现CellWriteHandler接口,在afterCellDispose方法中判断是否需要合并。例如,根据数据中的相同值来合并行或列。 另外,动态列的情况,比如根据数据库查询结果动态生成列,可以参考引用[5]中的动态展示停车场名字的方法,可能需要使用模板写入或者动态构建表头。 数据填充方面,不规则布局可能需要使用@ExcelIgnore注解忽略某些字段,或者在自定义转换器中处理数据格式。例如,将对象中的嵌套属性展平到不同的单元格。 最后,需要给出一个代码示例,展示如何定义实体类、设置自定义合并策略,并调用EasyExcel的write方法。同时,提醒用户注意性能问题,尤其是在处理大量数据时的内存管理。 总结步骤:定义多级表头注解 → 创建合并策略 → 构建数据列表 → 导出时注册策略。同时,可能需要处理样式和格式,如字体、颜色等,通过HeadStyle注解或自定义样式处理器。 需要验证这些步骤是否符合EasyExcel的最新文档,并确保代码示例的正确性。可能还需要提到使用模板导出,如果用户有固定模板的话,引用[3]提到获取导出数据,但这里可能需要更复杂的处理。 用户的问题可能还涉及横向或纵向合并,或者斜线表头,但EasyExcel对斜线的支持可能有限,可能需要使用其他工具或POI底层操作。不过,通常建议尽量使用EasyExcel的现有功能,避免过于复杂的实现。 最后,生成相关问题,帮助用户进一步了解优化方法、动态表头实现以及性能调优,这些都在引用中有部分涉及,可以引导用户查阅更多资料或官方示例。</think>### 使用EasyExcel导出复杂布局Excel的实现步骤 #### 1. 定义复杂表头实体类 通过`@ExcelProperty`注解实现多级表头和合并单元格声明: ```java public class IrregularData { // 合并主标题(跨两列) @ExcelProperty(value = {"基本信息", "停车场名称"}, index = 0) private String parkingName; @ExcelProperty(value = {"基本信息", "停车场名称"}, index = 1) private String parkingAlias; // 二级合并标题 @ExcelProperty({"运营数据", "月收入(万元)"}) private BigDecimal monthlyIncome; @ExcelProperty({"运营数据", "车位使用率"}) private String utilizationRate; // 三级嵌套表头 @ExcelProperty({"设备信息", "入口设备", "型号"}) private String entranceDevice; @ExcelProperty({"设备信息", "出口设备", "型号"}) private String exitDevice; } ``` #### 2. 自定义合并策略(关键) 实现`CellWriteHandler`处理跨行/列合并: ```java public class IrregularMergeStrategy implements CellWriteHandler { @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 合并停车场名称列(第0列) if (cell.getColumnIndex() == 0 && !isHead) { Sheet sheet = writeSheetHolder.getSheet(); int rowIndex = cell.getRowIndex(); Object cellValue = cell.getStringCellValue(); // 查找需要合并的连续相同值 int mergeStart = rowIndex; while (rowIndex + 1 < sheet.getLastRowNum() && sheet.getRow(rowIndex + 1).getCell(0).getStringCellValue().equals(cellValue)) { rowIndex++; } if (mergeStart < rowIndex) { sheet.addMergedRegion(new CellRangeAddress(mergeStart, rowIndex, 0, 0)); } } } } ``` #### 3. 动态数据填充示例 ```java List<IrregularData> dataList = new ArrayList<>(); // 构造嵌套数据 dataList.add(new IrregularData("上海中心停车场", "魔都停车场", new BigDecimal("120.5"), "78%", "LPR-3000", "LPR-3000")); dataList.add(new IrregularData("上海中心停车场", "魔都停车场", new BigDecimal("115.2"), "75%", "LPR-3000", "LPR-3000")); // 注意:第二行停车场名称相同将触发合并 ``` #### 4. 执行导出操作 ```java String fileName = "irregular_report.xlsx"; EasyExcel.write(fileName, IrregularData.class) .registerWriteHandler(new IrregularMergeStrategy()) // 注册合并策略 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动列宽 .sheet("运营报表") .doWrite(dataList); ``` #### 5. 扩展不规则布局支持 * **斜线表头**:使用`@ContentStyle`注解配合自定义样式(需要POI底层支持) * **动态列生成**:通过`WriteSheet`的`table()`方法动态构建列头[^5] * **多sheet组合**:循环调用`write`方法生成多个sheet ```java // 动态列示例 List<List<String>> dynamicHead = Arrays.asList( Arrays.asList("动态列", "第一季度"), Arrays.asList("动态列", "第二季度") ); EasyExcel.write("dynamic.xlsx") .head(dynamicHead) .sheet() .doWrite(dataWithDynamicColumns); ``` ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值