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

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

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

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

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

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

公司 项目 姓名 有效工作日 合计 人数
寿险 社保平台 张三 12 12000.0 2
李四 13 12000.0 2
王五 14 12000.0 2
合计 4 120000 120000 240000 2
           
产险 公积金平台 赵六 13 12000.0 2
僧其 16 12000.0 2
合计 8 120000 120000 240000 2

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

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

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

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

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

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

公司 项目 姓名 有效工作日 合计 人数
{{$fe:zkr.list t.companyName t.projectName t.name t.effectDays t.totalAmount t.emplNum}}
合计 {{zkr.projectNum}} 120000 120000 240000 2
           
{{$fe:bat.list t.companyName t.projectName t.name t.effectDays t.totalAmount t.emplNum}}
合计 {{bat.projectNum}} 120000 120000 240000 2

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

 /**
     * 根据模板生成excel后导出
     *
     * @param templatePath  模板路径
     * @param map 数据集合
     * @param fileName 文件名
     * @param response
     * @throws IOException
     */
    public static void exportExcel(TemplateExportParams templatePath, Map 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 map, String fileName, HttpServletResponse response, List 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 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 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 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 deptExportMap = new HashMap<>();
            // title的参数为ExportParams类型,目前仅仅在ExportParams中设置了sheetName
            deptExportMap.put("title", deptExportParams);
            // 模版导出对应得实体类型
            deptExportMap.put("entity", TestEntityVo.class);
            // sheet中要填充得数据
            deptExportMap.put("data", exportList);

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

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

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

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


        return ResultDomain.ok();
    }


public static void exportMutiSheet(List> 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());
        }
    }

你可能感兴趣的:(学习,java)