easypoi 模板导出兼容合并单元格功能

最新在使用easypoi,使用注解导出和模板导出的方式,现在主要就模板导出合并单元格一些坑和解决方法。

首先我建议刚接触的同学看这篇文章,很详细,功能也比较全面,比较感谢这篇文章的原创作者,网站1:easyPOI基本用法 - 钟小嘿 - 博客园

然后模板导出一些坑,可以看这篇文章,作者写的很详细,目前模板导出存在的一些问题,网站2:springboot集成easypoi并使用其模板导出功能和遇到的坑_巴中第一皇子的博客-CSDN博客_springboot的poi

这几天因为最后一个问题捣鼓了好久,最后写了一个临时方案来处理:

我目前需要处理的报表样式如下:

公司项目姓名有效工作日合计人数
寿险社保平台张三1212000.02
李四1312000.02
王五1412000.02
合计41200001200002400002
      
产险公积金平台赵六1312000.02
僧其1612000.02
合计81200001200002400002

上面是一个excel报表,上下分成两部分,分别是两个list(easypoi模板可以允许多list的)

这里的坑,可以看网站2中第四条中的说明,两种方法,即一个使用$fe , 一个使用fe (汗,第一次使用这个,我都没发现这两个不一样), 但是都存在缺陷,

 这种多层循环,使用$fe会产生空白行, 使用fe会覆盖循环的数据以下的内容, 如我需求里有 “合计” 行,必定会覆盖。

尝试了很多次后,我的想法是,先把 以人员为维度的所有数据全部查出来,比如上面这个excel表,我查询的数据总共就是5条(这里分成两个list,上面一个3条,下面是2条),这时可能有人疑惑,但是“公司”和“项目” 我是要相同的合并在一起呀,不错,我先以“人员”为粒度查出所有,然后导出前对需要合并的列进行合并。

首先这里两个list都必须使用$fe,因为这样才不会有覆盖的情况产生。

下面是我的模板,分成zkr和bat两个map,里面各自有对应的list:

公司项目姓名有效工作日合计人数
{{$fe:zkr.list t.companyNamet.projectNamet.namet.effectDayst.totalAmountt.emplNum}}
合计{{zkr.projectNum}}1200001200002400002
      
{{$fe:bat.list t.companyNamet.projectNamet.namet.effectDayst.totalAmountt.emplNum}}
合计{{bat.projectNum}}1200001200002400002

下面是我封装了下模板导出兼容合并单元格的方法,先贴网站1原始方法:

 /**
     * 根据模板生成excel后导出
     *
     * @param templatePath  模板路径
     * @param map 数据集合
     * @param fileName 文件名
     * @param response
     * @throws IOException
     */
    public static void exportExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);
        downLoadExcel(fileName, response, workbook);
    }

新兼容合并单元格方法:

/**
      * @Description: 合并指定列单元格并导出(合并行)
      * @Date: 2021/5/24 9:42
      * @Param templatePath: 模板路径
      * @Param map: 需要导出的数据map
      * @Param fileName: 导出文件名称
      * @Param response: response
      * @Param sheetMergeParamList: sheet参数集合
      * @return: void
      * @Version: 1.0
      **/
    public static void exportMergeExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response, List<SheetMergeParam> sheetMergeParamList) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);
        //合并单元格
        mergeExcel(workbook,sheetMergeParamList);
        downLoadExcel(fileName, response, workbook);
    }



/**
      * @Description: 合并单元格具体执行方法
      * @Date: 2021/5/24 14:58
      * @Param workbook: 工作薄
      * @Param sheetMergeParamList: sheet集合
      * @return: void
      * @Version: 1.0
      **/
    private static void mergeExcel(Workbook workbook, List<SheetMergeParam> sheetMergeParamList){
        for(SheetMergeParam sheetMergeParam : sheetMergeParamList){
            Sheet sheet = workbook.getSheetAt(sheetMergeParam.getSheetIndex());
            int lastRowNum = sheet.getLastRowNum();
            int i;
            for(i = 0;i < lastRowNum;i++){
                //获取每行第一个单元格
                if(null == sheet.getRow(i) || null == sheet.getRow(i).getCell(0)){
                    continue;
                }
                Cell cell = sheet.getRow(i).getCell(0);
                if(sheetMergeParam.getIgnoreCellValues().contains(cell.getStringCellValue()) || StringUtils.isEmpty(cell.getStringCellValue()) ){
                    continue;
                }
                //定义合并终止行数
                int endRowNum = 0;
                for(int j = i + 1 ;j <= lastRowNum;j++){
                    Cell desColumn = sheet.getRow(i).getCell(sheetMergeParam.getDesColumnIndex());
                    Cell nextDesColumn = sheet.getRow(j).getCell(sheetMergeParam.getDesColumnIndex());
                    if(!desColumn.getStringCellValue().equals(nextDesColumn.getStringCellValue())){
                        //值不同,终止此层循环
                        break;
                    }
                    endRowNum ++;
                }

                //判断是否有合并项
                if(endRowNum == 0){
                    continue;
                }
                //合并单元格操作
                for(int z = 0; z < sheetMergeParam.getMergeColumnIndexs().length; z++){
                    //合并起始行,终止行,起始列,终止列
                    int firstRow = i;
                    int lastRow = i + endRowNum;
                    int firstCol = sheetMergeParam.getMergeColumnIndexs()[z];
                    int lastCol = sheetMergeParam.getMergeColumnIndexs()[z];
                    PoiMergeCellUtil.addMergedRegion(sheet,firstRow,lastRow,firstCol,lastCol);
                }

                //合并后行号下移
                i = i + endRowNum;
            }
        }
    }

下面是我的测试类:

@GetMapping("/testExport")
    public void testExport(HttpServletResponse response) throws Exception{
        test(response);
    }

    private void test(HttpServletResponse response) throws Exception{
        //第一组map数据(内含list)
        Map zkr = Maps.newHashMap();
        zkr.put("projectNum",4);
        ContractTest contractTestZkr = new ContractTest("寿险","社保平台",12000,2,"张三",12
                );
        ContractTest contractTestZkr1 = new ContractTest("寿险","社保平台",12000,2,"李四",13);
        ContractTest contractTestZkr2 = new ContractTest("寿险","社保平台",12000,2,"王五",14);
        zkr.put("list",Lists.newArrayList(contractTestZkr,contractTestZkr1,contractTestZkr2));

        //第二组map数据(内含list)
        Map bat = Maps.newHashMap();
        bat.put("projectNum",8);
        ContractTest contractTestBat = new ContractTest("产险","公积金平台",12000,2,"赵六",13);
        ContractTest contractTestBat1 = new ContractTest("产险","公积金平台",12000,2,"僧其",16);
        bat.put("list",Lists.newArrayList(contractTestBat,contractTestBat1));

        Map map = Maps.newHashMap();
        map.put("zkr",zkr);
        map.put("bat",bat);

        TemplateExportParams templatePath = new TemplateExportParams("template/当月结算考勤汇总测试.xlsx",true);
        SheetMergeParam sheetMergeParam = new SheetMergeParam(0,0,new int[]{0,1},Lists.newArrayList("公司","合计"));
        ExcelUtils.exportMergeExcel(templatePath,map,"当月结算考勤汇总测试daochu",response, Lists.newArrayList(sheetMergeParam));
    }





@Data
@AllArgsConstructor
@NoArgsConstructor
public class SheetMergeParam implements Serializable{
    /***
     * sheet下标
     */
    private int sheetIndex;
    /***
     * 合并参考列列号
     */
    private int desColumnIndex;
    /***
     * 合并单元格列列号数组
     */
    private int[] mergeColumnIndexs;
    /***
     * 忽略行内容(如标题行,合计行等)
     */
    private List<String> ignoreCellValues;
}

这个是我的解决方案,如有不正确的地方,欢迎各位指正。

附加

关于导出的样式需要更改的,我贴一篇样式是实例:

/**
  * @Description: 导出表格样式类
  * @Date: 2021/4/22 18:02
  * @Param null:
  * @return: null
  * @Version: 1.0
  **/
public class ExcelStyleUtil implements IExcelExportStyler {
    private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
    private static final short FONT_SIZE_TEN = 9;
    private static final short FONT_SIZE_ELEVEN = 10;
    private static final short FONT_SIZE_TWELVE = 10;
    /**
     * 大标题样式
     */
    private CellStyle headerStyle;
    /**
     * 每列标题样式
     */
    private CellStyle titleStyle;
    /**
     * 数据行样式
     */
    private CellStyle styles;

    public ExcelStyleUtil(Workbook workbook) {
        this.init(workbook);
    }

    /**
     * 初始化样式
     *
     * @param workbook
     */
    private void init(Workbook workbook) {
        this.headerStyle = initHeaderStyle(workbook);
        this.titleStyle = initTitleStyle(workbook);
        this.styles = initStyles(workbook);
    }

    /**
     * 大标题样式
     *
     * @param color
     * @return
     */
    @Override
    public CellStyle getHeaderStyle(short color) {
        return headerStyle;
    }

    /**
     * 每列标题样式
     *
     * @param color
     * @return
     */
    @Override
    public CellStyle getTitleStyle(short color) {
        return titleStyle;
    }

    /**
     * 数据行样式
     *
     * @param parity 可以用来表示奇偶行
     * @param entity 数据内容
     * @return 样式
     */
    @Override
    public CellStyle getStyles(boolean parity, ExcelExportEntity entity) {
        return styles;
    }

    /**
     * 获取样式方法
     *
     * @param dataRow 数据行
     * @param obj     对象
     * @param data    数据
     */
    @Override
    public CellStyle getStyles(Cell cell, int dataRow, ExcelExportEntity entity, Object obj, Object data) {
        return getStyles(true, entity);
    }

    /**
     * 模板使用的样式设置
     */
    @Override
    public CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {
        return null;
    }

    /**
     * 初始化--大标题样式
     *
     * @param workbook
     * @return
     */
    private CellStyle initHeaderStyle(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
        return style;
    }

    /**
     * 初始化--每列标题样式
     *
     * @param workbook
     * @return
     */
    private CellStyle initTitleStyle(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_ELEVEN, false));
        //背景色
        style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return style;
    }

    /**
     * 初始化--数据行样式
     *
     * @param workbook
     * @return
     */
    private CellStyle initStyles(Workbook workbook) {
        CellStyle style = getBaseCellStyle(workbook);
        style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
        style.setDataFormat(STRING_FORMAT);
        return style;
    }

    /**
     * 基础样式
     *
     * @return
     */
    private CellStyle getBaseCellStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        //下边框
        style.setBorderBottom(BorderStyle.THIN);
        //左边框
        style.setBorderLeft(BorderStyle.THIN);
        //上边框
        style.setBorderTop(BorderStyle.THIN);
        //右边框
        style.setBorderRight(BorderStyle.THIN);
        //水平居中
        style.setAlignment(HorizontalAlignment.CENTER);
        //上下居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置自动换行
        style.setWrapText(true);
        return style;
    }

    /**
     * 字体样式
     *
     * @param size   字体大小
     * @param isBold 是否加粗
     * @return
     */
    private Font getFont(Workbook workbook, short size, boolean isBold) {
        Font font = workbook.createFont();
        //字体样式
        font.setFontName("宋体");
        //是否加粗
        font.setBold(isBold);
        //字体大小
        font.setFontHeightInPoints(size);
        return font;
    }
}

此处模板导出使用地方:

/**
     * excel 导出
     *
     * @param list         数据列表
     * @param pojoClass    pojo类型
     * @param fileName     导出时的excel名称
     * @param response
     * @param exportParams 导出参数(标题、sheet名称、是否创建表头,表格类型)
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
        //改变原始样式
        ExportParams exportParamsSelf = new ExportParams(exportParams.getTitle(), exportParams.getSheetName(), ExcelType.XSSF);
        exportParamsSelf.setStyle(ExcelStyleUtil.class);

        //把数据添加到excel表格中
        Workbook workbook = ExcelExportUtil.exportExcel(exportParamsSelf, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

多sheet表普通导出使用地方:

public ResultDomain testExportMutiSheet(HttpServletResponse response) throws Exception{

        Workbook workBook = null;


            List<TestEntityVo> exportList = Lists.newArrayList(new TestEntityVo("中二班","张三",12,"广东深圳"),
                    new TestEntityVo("中二班","李四",15,"湖北武汉"));

            // 创建参数对象(用来设定excel得sheet得内容等信息)
            ExportParams deptExportParams = new ExportParams();
            // 设置sheet得名称
            deptExportParams.setSheetName("员工表");
            deptExportParams.setStyle(ExcelStyleUtil.class);
            // 创建sheet1使用得map
            Map<String, Object> deptExportMap = new HashMap<>();
            // title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
            deptExportMap.put("title", deptExportParams);
            // 模版导出对应得实体类型
            deptExportMap.put("entity", TestEntityVo.class);
            // sheet中要填充得数据
            deptExportMap.put("data", exportList);

            List<TestGoodsVo> emExportList = Lists.newArrayList(new TestGoodsVo("001","辣条",1),
                    new TestGoodsVo("002","方便面",2));

            ExportParams empExportParams = new ExportParams();
            empExportParams.setSheetName("货物表");
            empExportParams.setStyle(ExcelStyleUtil.class);
            // 创建sheet2使用得map
            Map<String, Object> empExportMap = new HashMap<>();
            empExportMap.put("title", empExportParams);
            empExportMap.put("entity", TestGoodsVo.class);
            empExportMap.put("data", emExportList);

            // 将sheet1、sheet2、sheet3使用得map进行包装
            List<Map<String, Object>> sheetsList = new ArrayList<>();
            sheetsList.add(deptExportMap);
            sheetsList.add(empExportMap);

            ExcelUtils.exportMutiSheet(sheetsList,"测试",response);


        return ResultDomain.ok();
    }


public static void exportMutiSheet(List<Map<String,Object>> list,String fileName,HttpServletResponse response) throws IOException{
        Workbook  workbook = ExcelExportUtil.exportExcel(list,ExcelType.XSSF);
        downLoadExcel(fileName, response, workbook);
    }

下载:

/**
     * excel下载
     *
     * @param fileName 下载时的文件名称
     * @param response
     * @param workbook excel数据
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

  • 15
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
### 回答1: Easypoi是一个用于POI操作的Java库,可以方便地操作Excel文件。导出动态合并单元格Easypoi的一个功能特性,可以通过一定的操作来实现。 首先,需要使用Easypoi提供的注解@ExcelCollection来标注需要导出的数据集合,并设置合并的列和行。具体代码如下: ```java @ExcelCollection(name = "成绩表", orderNum = "1") private List<Student> studentList; @ExcelEntity(name = "学生信息") public class Student { @Excel(name = "学生姓名", mergeVertical = true) private String name; @Excel(name = "学生年龄") private Integer age; // 省略getter和setter方法 } ``` 其中,`@ExcelCollection`注解用于标注需要导出的数据集合,`name`属性设置导出的sheet名称,`orderNum`属性设置导出的sheet顺序,`@ExcelEntity`注解用于标注实体类。 在`Student`类中,使用`@Excel`注解来标注需要导出的字段,`name`属性设置导出的列名,`mergeVertical`属性设置是否垂直合并单元格。 接下来,使用Easypoi提供的`ExcelExportUtil.exportExcel`方法来导出Excel文件,具体代码如下: ```java ExcelExportUtil.exportExcel(new ExportParams("成绩单", "学生信息"), Student.class, studentList); ``` 其中,`ExportParams`类用于设置导出的参数,包括文件名和sheet名称。 通过以上的步骤,即可使用Easypoi导出动态合并单元格的Excel文件。 ### 回答2: Easypoi 是一个方便快捷的 Java Excel 操作工具类库,可以用来进行Excel 文件的导入和导出操作。它提供了丰富的功能,其中之一就是动态合并单元格。 使用 Easypoi 进行动态合并单元格导出操作,需要以下步骤: 1. 导入 Easypoi 的相关依赖包,并在项目中引入相应的类或方法。 2. 创建一个导出的 Excel 模板,定义需要合并单元格的位置。 3. 在代码中通过创建一个 Sheet 对象来表示 Excel 表格,并通过定义一系列的 CellRangeAddress 对象来表示需要合并的单元格区域。 4. 使用 Sheet 对象的 addMergedRegion 方法将定义好的单元格区域添加到 Sheet 中。 5. 将创建好的 Sheet 对象添加到 Workbook 中。 6. 通过输出流将 Workbook 写入到文件中,即可实现导出带有动态合并单元格的 Excel 文件。 动态合并单元格是一种非常实用的功能,可以使导出的 Excel 文件更加简洁和易读。而 Easypoi 提供的动态合并单元格功能,可以帮助我们通过简单的代码实现这一操作,提高了开发的效率和便利性。 总之,通过 Easypoi导出功能,我们可以轻松地实现动态合并单元格的操作,使导出的 Excel 文件更加美观和易于查看。 ### 回答3: Easypoi是一款强大的Java Excel工具库,可以实现Excel的导入和导出操作。导出动态合并单元格是指根据特定条件对Excel中的单元格进行合并操作。 在使用Easypoi导出Excel时,可以通过特定的逻辑判断来确定需要合并的单元格,并使用Easypoi提供的合并单元格API来实现。 首先,需要创建一个Excel导出模板,可以使用Excel模板工具直接在Excel中编辑,或者使用Easypoi提供的Excel模板注解来标注需要合并的单元格。 然后,在Java代码中使用Easypoi的相关API来进行导出操作。首先,使用Workbook对象来创建一个Excel工作簿,然后创建Sheet对象。接着,使用Easypoi的相关API来设置表头、填充数据等操作。当需要合并单元格时,可以使用Sheet对象的merge方法来进行合并操作。 在设置完所有需要合并的单元格后,使用Easypoi的相关API将工作簿导出为Excel文件。 最后,可以通过HttpServletResponse将导出的Excel文件返回给前端,或者将文件保存到本地。 总而言之,使用Easypoi导出动态合并单元格的步骤主要包括:创建Excel模板、使用Easypoi的API进行数据填充和合并单元格操作、导出Excel文件。通过这些步骤,就可以轻松实现动态合并单元格导出操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值