当前位置: 首页 > news >正文

easy-poi 一对多导出

1. 需求:

某一列上下两行单元格A,B值一样且这两个单元格, 前面所有列对应单元格值一样的话,

就对A,B 两个单元格进行纵向合并单元格

1. 核心思路:

先对数据集的国家,省份,城市...... id 身份证进行排序

国家一列,值相同就合并单元格(直接调用:2 是指的下标 是2 开始;0 是0列

PoiMergeCellUtil.mergeCells(sheet,2,0);

省份一列:

两行数据,前一列的值相同(国家列相同),且当前列对应值也相同就合并单元格

城市一列:

两行数据,第一列+第二列值相同(国家省份值相同),且当前列对应值也相同就合并单元格

其他类似:

POM文件如下:

  <!-- EasyPoi 核心库 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.2.0</version>
        </dependency>
        <!-- EasyPoi Web 支持 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.2.0</version>
        </dependency>
        <!-- 如果需要使用注解 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>

代码如下:

实体类:

package com.example.demo.entity;


import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author guoyiguang
 * @description $
 * @date 2025/4/5$
 */
@Data
public class Boy {

    @Excel(name = "国家",orderNum = "1")
    private String country;

    @Excel(name = "省份",orderNum = "2")
    private String province;

    @Excel(name = "城市",orderNum = "3")
    private String city;

    @Excel(name = "县",orderNum = "4")
    private String county;

    @Excel(name = "城镇",orderNum = "5")
    private String town; // 镇

    @Excel(name = "村",orderNum = "6")
    private String village; // 村

    @Excel(name = "街道",orderNum = "7")
    private String street;

    @Excel(name = "性别",orderNum = "8")
    private String sex;

    @Excel(name = "名称",orderNum = "9")
    private String name;

    @Excel(name = "出生年份",orderNum = "10")
    private String birthYear;

    @Excel(name = "出生月份份",orderNum = "11")
    private String birthMonth; //

    @Excel(name = "ID身份证",orderNum = "12")
    private String idCard; // 身份证标识









}

模拟从数据库获取业务数据:

    public List<Boy> getBoysList(){
        List<Boy> boyList = new ArrayList<>();
        Boy boy = new Boy();
        boy.setCountry("中国");
        boy.setProvince("山西省");
        boy.setCity("晋中市");
        boy.setCounty("平遥县");
        boy.setTown("岳壁乡");
        boy.setVillage("金村");
        boy.setStreet("向阳街道");
        boy.setBirthYear("1990");
        boy.setBirthMonth("02");
        boy.setSex("男");
        boy.setName("张三1");
        boy.setIdCard("张三1");
        boyList.add(boy);



        Boy boy7 = new Boy();
        boy7.setCountry("中国");
        boy7.setProvince("山西省");
        boy7.setCity("晋中市");
        boy7.setCounty("平遥县");
        boy7.setTown("岳壁乡");
        boy7.setVillage("金村");
        boy7.setStreet("向阳街道");
        boy7.setBirthYear("1990");
        boy7.setBirthMonth("02");
        boy7.setSex("男");
        boy7.setName("张三1");
        boy7.setIdCard("张三111");
        boyList.add(boy7);


        Boy boy2 = new Boy();
        boy2.setCountry("中国");
        boy2.setProvince("山西省");
        boy2.setCity("晋中市");
        boy2.setCounty("平遥县");
        boy2.setTown("岳壁乡");
        boy2.setVillage("金村");
        boy2.setStreet("向阳街道-2");
        boy2.setBirthYear("1990");
        boy2.setBirthMonth("02");
        boy2.setSex("男");
        boy2.setName("张三2");
        boy2.setIdCard("张三2");
        boyList.add(boy2);


        Boy boy8 = new Boy();
        boy8.setCountry("中国");
        boy8.setProvince("山西省");
        boy8.setCity("晋中市");
        boy8.setCounty("平遥县");
        boy8.setTown("岳壁乡");
        boy8.setVillage("金村");
        boy8.setStreet("向阳街道-3");
        boy8.setBirthYear("1990");
        boy8.setBirthMonth("02");
        boy8.setSex("男");
        boy8.setName("张三3");
        boy8.setIdCard("张三33");
        boyList.add(boy8);

        Boy boy4 = new Boy();
        boy4.setCountry("中国");
        boy4.setProvince("陕西省");
        boy4.setCity("渭南市");
        boy4.setCounty("渭南县");
        boy4.setTown("渭南乡");
        boy4.setVillage("渭南村");
        boy4.setStreet("渭南向阳街道");
        boy4.setBirthYear("1990");
        boy4.setBirthMonth("02");
        boy4.setSex("男");
        boy4.setName("张三1");
        boy4.setIdCard("渭南张三1");
        boyList.add(boy4);


        Boy boy10 = new Boy();
        boy10.setCountry("中国");
        boy10.setProvince("陕西省");
        boy10.setCity("渭南市");
        boy10.setCounty("渭南县");
        boy10.setTown("渭南乡");
        boy10.setVillage("渭南村");
        boy10.setStreet("渭南向阳街道");
        boy10.setBirthYear("1990");
        boy10.setBirthMonth("02");
        boy10.setSex("男");
        boy10.setName("李四");
        boy10.setIdCard("渭南李四");
        boyList.add(boy10);


        Boy boy5 = new Boy();
        boy5.setCountry("中国");
        boy5.setProvince("陕西省");
        boy5.setCity("渭南市");
        boy5.setCounty("渭南县2");
        boy5.setTown("渭南乡2");
        boy5.setVillage("渭南村2");
        boy5.setStreet("渭南向阳街道");
        boy5.setBirthYear("1990");
        boy5.setBirthMonth("02");
        boy5.setSex("男");
        boy5.setName("张三1");
        boy5.setIdCard("渭南张三1");
        boyList.add(boy5);


        Boy boy9 = new Boy();
        boy9.setCountry("中国");
        boy9.setProvince("陕西省");
        boy9.setCity("咸阳市");
        boy9.setCounty("咸阳县2");
        boy9.setTown("咸阳乡2");
        boy9.setVillage("咸阳村2");
        boy9.setStreet("咸阳向阳街道");
        boy9.setBirthYear("1990");
        boy9.setBirthMonth("02");
        boy9.setSex("男");
        boy9.setName("张三1");
        boy9.setIdCard("咸阳张三1");
        boyList.add(boy9);


        Boy boy3 = new Boy();
        boy3.setCountry("美国");
        boy3.setProvince("美国省");
        boy3.setCity("美国市");
        boy3.setCounty("美国县");
        boy3.setTown("美国乡");
        boy3.setVillage("美国村");
        boy3.setStreet("美国街道");
        boy3.setBirthYear("1990");
        boy3.setBirthMonth("02");
        boy3.setSex("男");
        boy3.setName("美国张三2");
        boy3.setIdCard("美国张三2");
        boyList.add(boy3);

        Boy boy6 = new Boy();
        boy6.setCountry("美国");
        boy6.setProvince("美国省");
        boy6.setCity("美国市");
        boy6.setCounty("美国县");
        boy6.setTown("美国乡");
        boy6.setVillage("美国村-2");
        boy6.setStreet("美国街道");
        boy6.setBirthYear("1990");
        boy6.setBirthMonth("02");
        boy6.setSex("男");
        boy6.setName("美国张三2");
        boy6.setIdCard("美国张三2");
        boyList.add(boy6);



        return boyList;
    }

某一列两个单元格是否合并的工具方法:

    public void setMergeStartEndRow(LinkedList<Pair> list,int curRow,String preLastContent, String preCurContents,String lastContent,String curContent){
        if(!ObjectUtils.isEmpty(preLastContent) && !ObjectUtils.isEmpty(preCurContents) &&  preLastContent.equals(preCurContents)){
            if(lastContent.equals(curContent)){
                if(!CollectionUtils.isEmpty(list)){
                    Pair lastPair = list.getLast();
                    // 某一列要合并的单元格增加了一行
                    if((int)lastPair.getValue() == curRow-1 ){
                        Pair pair = list.removeLast();
                        list.add(Pair.of(pair.getLeft(),curRow));
                    }else{
                        // 某一列这两行要合并
                        list.add(Pair.of(curRow-1,curRow));
                    }
                }else{
                    // 某一列这两行要合并
                    list.add(Pair.of(curRow-1,curRow));
                }
            }else{

            }
        }else{
            // 不相等不处理
        }
    }

测试方法:

 public void exportBoys(HttpServletResponse response) throws IOException {

        List<Boy> boysList = getBoysList();
        //  0 行,0列
        // TOTO 根据字段排序
        boysList.sort(Comparator.comparing(Boy::getCountry)
                        .thenComparing(Boy::getProvince)
                        .thenComparing(Boy::getCity)
                        .thenComparing(Boy::getCounty)
                        .thenComparing(Boy::getTown));

        // 第二列到第十列合并依据:两行数据前一列值相同(更准确的说法:某两行某一列,之前所有列对应的两行数据都相同)且两行数据当前列的value一样
        // eg
        //row1: 中国  北京市  海淀区  西二旗(当前列)
        //row2: 中国  北京市  海淀区  西二旗(当前列)
        // 核心代码:构建 sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, column, column)); 的 startRow 和 endRow
        LinkedList<Pair> secondList = new LinkedList<>();
        LinkedList<Pair> list2 = new LinkedList<>();
        LinkedList<Pair> list3 = new LinkedList<>();
        LinkedList<Pair> list4 = new LinkedList<>();
        LinkedList<Pair> list5 = new LinkedList<>();
        LinkedList<Pair> list6 = new LinkedList<>();
        LinkedList<Pair> list7 = new LinkedList<>();
        LinkedList<Pair> list8 = new LinkedList<>();
        LinkedList<Pair> list9 = new LinkedList<>();
        LinkedList<Pair> list10 = new LinkedList<>();
        for(int row = 0;row <= boysList.size()-1;row++){
            if(row ==0 ){
                continue;
            }
            Boy curBoy = boysList.get(row);
            Boy lastBoy = boysList.get(row-1); // 上一行数据
            // 省份合并(要看前面国家和当前省份是否一样+当前行值和上一行值一样)
           setMergeStartEndRow(secondList,row,lastBoy.getCountry(),curBoy.getCountry(),lastBoy.getProvince(),curBoy.getProvince());

//          城市合并(要看前面国家和前面省份是否一样(前面所有字段值都一样才合并)+当前行值和上一行值一样)
            setMergeStartEndRow(list2,row,lastBoy.getProvince(),curBoy.getProvince(),lastBoy.getCity(),curBoy.getCity());
            // 县合并
            setMergeStartEndRow(list3,row,lastBoy.getCity(),curBoy.getCity(),lastBoy.getCounty(),curBoy.getCounty());
            // 城镇合并
            setMergeStartEndRow(list4,row,lastBoy.getCounty(),curBoy.getCounty(),lastBoy.getTown(),curBoy.getTown());
            // 村合并()
            setMergeStartEndRow(list5,row,lastBoy.getTown(),curBoy.getTown(),lastBoy.getVillage(),curBoy.getVillage());
            // 街道合并
            setMergeStartEndRow(list6,row,lastBoy.getVillage(),curBoy.getVillage(),lastBoy.getStreet(),curBoy.getStreet());
            // 性别合并(城镇+村+街道 都一样才认为横向条件满足)
            setMergeStartEndRow(list7,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet()),lastBoy.getSex(),curBoy.getSex());
            // name 合并 (城镇+村+街道+性别 都一样才认为横向条件满足)
            setMergeStartEndRow(list8,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet(),lastBoy.getSex()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet(),curBoy.getSex()),lastBoy.getName(),curBoy.getName());
            //年份合并
            setMergeStartEndRow(list9,row,lastBoy.getName(),curBoy.getName(),lastBoy.getBirthYear(),curBoy.getBirthYear());
            // 月份合并 (城镇+村+街道+性别+名称+年份 都一样才认为横向条件满足)
            setMergeStartEndRow(list10,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet(),lastBoy.getSex(),lastBoy.getName(),lastBoy.getBirthYear()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet(),curBoy.getSex(),curBoy.getName(),curBoy.getBirthYear()),lastBoy.getBirthMonth(),curBoy.getBirthMonth());

        }
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("标题", "副标题"), Boy.class, boysList);
        Sheet sheet = workbook.getSheet("副标题");

        // 第一列
        PoiMergeCellUtil.mergeCells(sheet,2,0);

        secondList.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 1, 1));

        });
        list2.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 2, 2));

        });
        list3.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 3, 3));

        });
        list4.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 4, 4));

        });
        list5.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 5, 5));

        });
        list6.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 6, 6));

        });
        list7.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 7, 7));

        });
        list8.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 8, 8));

        });
        list9.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 9, 9));

        });
        list10.forEach(pair->{
            // 标题占了两行,+2
            sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 10, 10));

        });
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-disposition", "attachment;filename=data.xlsx");
        workbook.write(response.getOutputStream());


    }
 // 获取前面列集合对应的值字符串
public String getPrexStr(String... strs){
        StringBuilder sb = new StringBuilder();
        for(String str:strs){
            if(!ObjectUtils.isEmpty(str)){
                sb.append("[");
                sb.append(str);
                sb.append("]");
            }else{
                sb.append("[");
                sb.append(str);
                sb.append("]");
            }

        }
        return sb.toString();

    }

相关文章:

  • 主流动态网站开发语言河北网站优化公司
  • 佛山家具网站建设公司抖音优化
  • 上海 网站建设优化设计数学
  • 中英文网站栏目修改云巅seo
  • js博客网站开发计划书互联网销售可以卖什么产品
  • 网页网络优化seo网络推广技术员招聘
  • 戴尔笔记本 ubuntu 22.04 开机后进入initramfs界面
  • 网络编程—TCP/IP模型(TCP协议)
  • JSONP跨域访问漏洞
  • #SVA语法滴水穿石# (013)关于 disable iff、matched 、expect 的用法
  • Mysql 数据库下载安装
  • 数字统计题解
  • 【C++奇遇记】C++中的进阶知识(继承(一))
  • Visual FoxPro 6.0学习笔记十五
  • 定制一款国密浏览器(1):Chromium 源码获取和构建
  • 常见前端GET请求以及对应的Spring后端接收接口写法
  • Docker快速安装MongoDB并配置主从同步
  • WiFi加密协议
  • RoMo: Robust Motion Segmentation Improves Structure from Motion
  • 【c语言】结构体
  • [自制调试工具]构建高效调试利器:Debugger 类详解
  • at定时任务(超详细)
  • SpringBoot整合JUnit
  • [ctfshow web入门] web1
  • 【学习笔记】深度学习环境部署相关
  • 提高MCU的效率方法