excel单元格合并策略

excel单元格合并策略

证明1+1=2?

要证明1+1=2这个问题,首先我们要找到问题的关键。所谓问题的关键呢,就是关键的问题,那么如何找到问题的关键就是这个问题的关键。

比如说,你有一个苹果,我也有一个苹果,如果我把我的苹果给你,当然我是不可能给你的,所以你把你的苹果给我的话,我可能会吃不完,但是我可以明天吃啊。

总而总之,言而言之,要证明1+1=2这个问题,关键就是我们就得先找到问题的关键。所以如何找到问题的关键就是关键的问题。

综上所知,你应该知道为什么1+1=2了吧。

上一篇我们讲了excel动态列的导出,今天我们继续来填之前挖下的坑。

所以补一下excel的单元格合并策略

上一篇在这里 excel动态列的导出

依赖

我们这里依然使用的是easyexcel来做导出

xml 复制代码
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>

先看效果吧

给两个效果吧,在接下来的代码中会顺序讲解这两个效果的实现过程。

第一个效果是只有标题第一行的单元格合并 ,并且内容靠右


第二个效果是标题合并,第二行的数据要随着导出的数据 所在公司的名称和日期进行变化

标题下的内容需要进行 一对多 指定单元格的合并

第一种效果

先看一下导出的excel对象,截了其中一部分字段

使用value= {标题,子标题}的形式定义了第一级第二级 的标题 ,第一级的内容需要相同,相同的内容会自动合并成一个单元格。

java 复制代码
@Data
public class ImportPmsPriceSeaExcelDto {

    @ExcelProperty(value = {"填写须知:\n" +
            "1. 请勿修改表格结构;\n" +
            "2. 带*字段为必填项;\n" +
            "3. 开船日,截关日:填写数字,多个使用[/]隔开;\n" +
            "4. 生效日期,失效日期:请按YYYY-MM-DD的格式填写,如2023-01-01\n" +
            "5. 航程,免堆期:请填写数字,例如 2\n" +
            "6. 推荐价格:请填入「是」、「否」,若不填则默认为「否」;","合约号"})
    private String ctrNo;

    @ExcelProperty(value = {"填写须知:\n" +
            "1. 请勿修改表格结构;\n" +
            "2. 带*字段为必填项;\n" +
            "3. 开船日,截关日:填写数字,多个使用[/]隔开;\n" +
            "4. 生效日期,失效日期:请按YYYY-MM-DD的格式填写,如2023-01-01\n" +
            "5. 航程,免堆期:请填写数字,例如 2\n" +
            "6. 推荐价格:请填入「是」、「否」,若不填则默认为「否」;","开船日*"})
    @ColumnWidth(20)
    private String sailingWeekdays;

    @ExcelProperty(value = {"填写须知:\n" +
            "1. 请勿修改表格结构;\n" +
            "2. 带*字段为必填项;\n" +
            "3. 开船日,截关日:填写数字,多个使用[/]隔开;\n" +
            "4. 生效日期,失效日期:请按YYYY-MM-DD的格式填写,如2023-01-01\n" +
            "5. 航程,免堆期:请填写数字,例如 2\n" +
            "6. 推荐价格:请填入「是」、「否」,若不填则默认为「否」;","截关日"})
    private String customsCutoffWeekdays;
}

定义完导出对象后,我们就可以接着写导出了。

为了控制第一行标题的样式,我们使用了 .registerWriteHandler(new PmsPriceSeaTemplateMergeStrategy()) 自定义合并策略,来控制excel的样式


java 复制代码
@Override
    public void exportPmsPriceSeaTemplate(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf8");
        response.setHeader("Content-disposition", "attachment;filename=" + "全部数据.xlsx");

        List<ImportPmsPriceSeaExcelDto> excelVoList = createImportPmsPriceSeaExcelDto();

        EasyExcel.write(response.getOutputStream())
                .head(ImportPmsPriceSeaExcelDto.class)
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(new PmsPriceSeaTemplateMergeStrategy())
                .sheet("运单模板")
                .doWrite(excelVoList);
    }

自定义策略类

接着定义自定义策略

titleHandle()方法中,我们将标题的所有列,设置了内容靠左换行字体大小


java 复制代码
/**
 * @Author: tfxing
 * @Description: RowWriteHandler
 */
public class PmsPriceSeaTemplateMergeStrategy implements RowWriteHandler {


    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
        if (row.getRowNum() > 1) {
            return;
        }
        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();

        titleHandle(sheet);

        titleHandle2(sheet);

    }

    private void titleHandle2(Sheet sheet) {

        Row row = sheet.getRow(1);

        if(null == row) {
            return;
        }

        row.setHeightInPoints(45);
    }

    private void titleHandle(Sheet sheet) {
        Workbook workbook = sheet.getWorkbook();
        Font font = workbook.createFont();

        CellStyle cellStyle = workbook.createCellStyle();
        // 内容靠左
        cellStyle.setAlignment(HorizontalAlignment.LEFT);
        cellStyle.setFont(font);
        // 是否换行
        cellStyle.setWrapText(true);

        Row row = sheet.getRow(0);
        for (int i = 0; i < 24; i++) {
            Cell cell0 = row.getCell(i);
            if(null == cell0) {
                continue;
            }
            cell0.setCellStyle(cellStyle);
        }

		// 设置字体大小
        row.setHeightInPoints(125);
    }

}

第二种效果

第一种效果的实现还是挺简单的是吧,那么要来实现第二种效果吧

复习一下

同样的我们先定义一下导出的对象,同样是截取了其中一部分

java 复制代码
@Data
public class AcctSettingExcelVo {

    @ExcelProperty({"凭证列表","公司名称","日期"})
    @ColumnWidth(12)
    private String vchDateStr;

    @ExcelProperty({"凭证列表","公司名称","凭证字号"})
    @ColumnWidth(20)
    private String vchWordStr;

    @ExcelProperty({"凭证列表","公司名称","凭证类型"})
    @ColumnWidth(20)
    private String vchTypeStr;

    @ExcelProperty({"凭证列表","公司名称","摘要"})
    @ColumnWidth(12)
    private String enTryDesc;
}

直接看合并策略吧

通过构造器传参,将三个关键的参数传递到类中。map,companyName,glpName,这三个参数分别是,需要合并的行列的map,公司名称,日期

map的处理是在调用处处理的的,我这里的处理过程就是通过将将数据需要合并的列和行解析出来,map的key就是行的下标value就是列的下标

具体实现我这里就不放出来了,每个人的业务不一样逻辑也不一样,大家自己想办法写吧。

通过titlehandle()方法将第二行的数据替换成companyNameglpName,公司名称和日期。

java 复制代码
		Workbook workbook = sheet.getWorkbook();
        Font font = workbook.createFont();
        font.setBold(true); // 字体加粗
        font.setFontHeightInPoints((short)14); // 字体大小

        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.LEFT); // 内容居左
        cellStyle.setFont(font);

		// 前7行的内容设置为公司,内容相同时单元格会自动合并,并设置上风格 内容居左
        for (int i = 0; i < 7; i++) {
            Cell cell0 = sheet.getRow(1).getCell(i);
            cell0.setCellValue(companyName);
            cell0.setCellStyle(cellStyle);
        }

        CellStyle cellStyle1 = workbook.createCellStyle();
        cellStyle1.setAlignment(HorizontalAlignment.RIGHT); // 内容居右
        cellStyle1.setFont(font);

		// 第7行后的数据内容设置为日期,并设置上风格 内容居右
        for (int i = 7; i < 12; i++) {
            Cell cell7 = sheet.getRow(1).getCell(i);
            cell7.setCellValue(glpName);
            cell7.setCellStyle(cellStyle1);
        }

**decimalHandle()**方法是将小数转为千分位数显示的格式,三分一个分隔符。例如:10,010


前三行和最后两行需要单元格合并,

将map中的行和列取出来作为合并的参数。

new CellRangeAddress(rowNum, lastRowNum, i, i) 这个对象中的四个参数分别是:第一行,最后一行,第一列,最后一列

在我们往期写的一篇博客中有详细说明,在这里

java 复制代码
Integer lastRowNum = map.get(rowNum);
if(-1 == lastRowNum) {
    return;
}

for (int i = 0; i < 3; i++) {
    //合并单元格区域只有一个单元格时,不合并
    if (rowNum == lastRowNum && i == i) {
        return;
    }
    CellRangeAddress cellRangeAddress = new CellRangeAddress(rowNum, lastRowNum, i, i);
    sheet.addMergedRegionUnsafe(cellRangeAddress);
}

for (int i = 10; i < 12; i++) {
    //合并单元格区域只有一个单元格时,不合并
    if (rowNum == lastRowNum && i == i) {
        return;
    }
    CellRangeAddress cellRangeAddress1 = new CellRangeAddress(rowNum, lastRowNum, i, i);
    sheet.addMergedRegionUnsafe(cellRangeAddress1);
}
}

自定义策略详细代码


java 复制代码
package com.yunwuyun.easy.settlement.strategy;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.yunwuyun.easy.commons.utils.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Author: Carl
 * @Date: 2022/12/10/10:20
 * @Description: RowWriteHandler
 */
public class CustomMergeStrategy1 implements RowWriteHandler {


    private Map<Integer,Integer> map = new HashMap<>();

    private String companyName;

    private String glpName;


    public CustomMergeStrategy1(Map<Integer,Integer> map,String companyName,String glpName) {
        this.map = map;
        this.companyName = companyName;
        this.glpName = glpName;
    }


    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
        // 如果是标题,则直接返回
        if (aBoolean) {
            return;
        }

        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();

        int rowNum = row.getRowNum();
        titleHandle(sheet,companyName,glpName);
        
        decimalHandle(sheet,integer,aBoolean);

        if (rowNum <= 2) {
            return;
        }
        Integer lastRowNum = map.get(rowNum);
        if(-1 == lastRowNum) {
            return;
        }
        
        //合并单元格区域只有一个单元格时,不合并
        for (int i = 0; i < 3; i++) {
            //合并单元格区域只有一个单元格时,不合并
            if (rowNum == lastRowNum && i == i) {
                return;
            }
            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowNum, lastRowNum, i, i);
            sheet.addMergedRegionUnsafe(cellRangeAddress);
        }

        for (int i = 10; i < 12; i++) {
            //合并单元格区域只有一个单元格时,不合并
            if (rowNum == lastRowNum && i == i) {
                return;
            }
            CellRangeAddress cellRangeAddress1 = new CellRangeAddress(rowNum, lastRowNum, i, i);
            sheet.addMergedRegionUnsafe(cellRangeAddress1);
        }
    }
    
    private void titleHandle(Sheet sheet, String companyName, String glpName) {
        Workbook workbook = sheet.getWorkbook();
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short)14);

        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.LEFT);
        cellStyle.setFont(font);

        for (int i = 0; i < 7; i++) {
            Cell cell0 = sheet.getRow(1).getCell(i);
            cell0.setCellValue(companyName);
            cell0.setCellStyle(cellStyle);
        }

        CellStyle cellStyle1 = workbook.createCellStyle();
        cellStyle1.setAlignment(HorizontalAlignment.RIGHT);
        cellStyle1.setFont(font);

        for (int i = 7; i < 12; i++) {
            Cell cell7 = sheet.getRow(1).getCell(i);
            cell7.setCellValue(glpName);
            cell7.setCellStyle(cellStyle1);
        }


    }

    /**
     * 金额列处理
     * @param sheet
     * @param integer
     * @param aBoolean
     */
    private void decimalHandle(Sheet sheet, Integer integer, Boolean aBoolean) {
        if(!aBoolean) {
            Workbook workbook = sheet.getWorkbook();
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.RIGHT);
            Cell cell = sheet.getRow(integer+3).getCell(6);
            cell.setCellStyle(cellStyle);
            handleDecimalValue(cell);

            Cell cell8 = sheet.getRow(integer+3).getCell(8);
            cell8.setCellStyle(cellStyle);
            handleDecimalValue(cell8);

            Cell cell9 = sheet.getRow(integer+3).getCell(9);
            cell9.setCellStyle(cellStyle);
            handleDecimalValue(cell9);
        }
    }

    private void handleDecimalValue(Cell cell) {
        String stringCellValue = cell.getStringCellValue();

        String[] split = stringCellValue.split("\\.");
        String preValue = split[0];
        char[] chars = preValue.toCharArray();

        String str = "";
        for (int i = chars.length - 1,j=1; i >= 0; i--,j++) {
            str += chars[i];
            if(j % 3 == 0 && i != 0) {
                str += ",";
            }
        }
        str = StringUtils.reverse(str);
        str = str+"."+split[1];
        cell.setCellValue(str);
    }

}

下一篇咱们来写一下(我还没想好)吧

(相别容易见时难,别后相思独凄然,千山万水总是情,点个关注行不行)

相关推荐
从心归零6 分钟前
sshj使用代理连接服务器
java·服务器·sshj
IT毕设梦工厂1 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Ylucius2 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
七夜zippoe2 小时前
分布式系统实战经验
java·分布式
是梦终空2 小时前
JAVA毕业设计176—基于Java+Springboot+vue3的交通旅游订票管理系统(源代码+数据库)
java·spring boot·vue·毕业设计·课程设计·源代码·交通订票
落落落sss2 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
码爸2 小时前
flink doris批量sink
java·前端·flink
Monodye3 小时前
【Java】网络编程:TCP_IP协议详解(IP协议数据报文及如何解决IPv4不够的状况)
java·网络·数据结构·算法·系统架构
一丝晨光3 小时前
逻辑运算符
java·c++·python·kotlin·c#·c·逻辑运算符
无名指的等待7124 小时前
SpringBoot中使用ElasticSearch
java·spring boot·后端