excel 动态表头与合并列

这篇具有很好参考价值的文章主要介绍了excel 动态表头与合并列。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

零、希望Springboot-java导出excel文件,包括动态表头与下边合并的列

使用 org.apache.poi 与自己封装工具类实现相关功能。代码如下

excel 动态表头与合并列,SpringBoot,excel

一、代码

1、依赖
    implementation(group: 'org.apache.poi',name: 'poi-ooxml',version: '4.1.0')
    implementation(group: 'org.apache.poi',name: 'poi',version: '4.1.0')
    implementation(group: 'cn.hutool', name: 'hutool-all', version: '5.8.3')
2、工具类 ExcelMergeUtil.java

import cn.hutool.json.JSONUtil;
import com.longze.fengqx.HeaderNode;
import com.longze.fengqx.PoiModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author Fengqx
 * @version 1.0
 * @description: excel文件合并
 * @date 2023/8/20 13:13
 */
public class ExcelMergeUtil {
    public static SXSSFSheet createExcelHead(SXSSFWorkbook book, String sheetName, String headJson){
        List<HeaderNode> headerNodes = JSONUtil.toList(headJson, HeaderNode.class);
        SXSSFSheet sxssfSheet = book.createSheet(sheetName);
        CellStyle headStyle = book.createCellStyle();
        defaultHeadStyle(headStyle);
        //表头层级
        int deep = headerNodes.stream().map(HeaderNode::getRow).reduce(Integer::max).orElse(1);
        for (int i = 0; i < deep; i++) {
            sxssfSheet.createRow(i);
        }
        //创建单元格
        for (HeaderNode headerNode : headerNodes) {
            int row = headerNode.getRow();
            int col = headerNode.getColumn();
            SXSSFCell sxssfCell = sxssfSheet.getRow(row).createCell(col);
            sxssfSheet.setColumnWidth(col, headerNode.getWidth() * 256);
            sxssfCell.setCellStyle(headStyle);
            sxssfCell.setCellValue(headerNode.getHeaderName());

            CellRangeAddress region;
            //是否跨列
            if (headerNode.isOverNode()) {
                region = new CellRangeAddress(row, deep, col, col);
            } else {
                region = new CellRangeAddress(row, row, col, (col + headerNode.getOverNodeCount() - 1));
            }
            if (region.getNumberOfCells() > 1) {
                sxssfSheet.addMergedRegionUnsafe(region);
                //合并后设置下边框
                RegionUtil.setBorderTop(BorderStyle.THIN, region, sxssfSheet);
                RegionUtil.setBorderLeft(BorderStyle.THIN, region, sxssfSheet);
                RegionUtil.setBorderBottom(BorderStyle.THIN, region, sxssfSheet);
                RegionUtil.setBorderRight(BorderStyle.THIN, region, sxssfSheet);
            }
        }
        return sxssfSheet;
    }
    public static void mergeCellFunc(Sheet sheet, String[] title, String[] field, List<Map<String, String>> list, Integer deep,List<Integer> mergeIndex){
        Map<String, List<Map<String, String>>> map = Maps.newHashMap();
        map.put("测试合并数据", list);
        // 模拟大数据量情况下,任务中心可分页查询接口,分批返回数据
        List<List<Map<String, String>>> listList =excelPageByNum(list, 5);
        // 数据总数 + 表头
        int size = listList.stream().mapToInt(List::size).sum() + deep;

        List<PoiModel> poiModels = Lists.newArrayList();
        for (List<Map<String, String>> listmid : listList) {
            for (Map<String, String> mapMid : listmid) {
                int index = sheet.getLastRowNum()+1;
                Row row = sheet.createRow(index);
                for (int i = 0; i < title.length; i++) {
                    String titleField = field[i];
                    String old = null;
                    if (index > deep+1) {
                        old = poiModels.get(i) == null ? null : poiModels.get(i).getContent();
                    }
                    for (int k : mergeIndex) {
                        if (index == deep+1) {
                            PoiModel poiModel =new PoiModel(mapMid.get(titleField),mapMid.get(titleField),null,deep+1,i);
                            poiModels.add(poiModel);
                            break;
                        }
                        PoiModel poiModel = poiModels.get(i);
                        String content = mapMid.get(titleField);
                        // 当前行的当前列与上一行的当前列的内容不一致时,则把当前行以上的合并
                        if (i > 0 && k == i) {
                            // 如果不需要考虑当前行与上一行内容相同,但是它们的前一列内容不一样则不合并的情况,把或条件删除
                            if (!StringUtils.equalsIgnoreCase(content, poiModel.getContent())
//                                    || (StringUtils.equalsIgnoreCase(content, poiModel.getContent()) && !StringUtils.equalsIgnoreCase(poiModels.get(i - 1).getOldContent(),mapMid.get(field[i - 1])))
                            ) {
                                get(poiModel, content, index, i, sheet);
                            }
                        }
                        // 处理第一列的情况
                        if (k == i && i == 0 && !StringUtils.equalsIgnoreCase(content,poiModel.getContent())) {
                            get(poiModel, content, index, i, sheet);
                        }
                        // 最后一行没有后续的行与之比较,所有当到最后一行时则直接合并对应列的相同内容
                        if (k == i && index == size && poiModels.get(i).getRowIndex() != index) {
                            CellRangeAddress cra = new CellRangeAddress(poiModels.get(i).getRowIndex(), index, poiModels.get(i).getCellIndex(), poiModels.get(i).getCellIndex());
                            sheet.addMergedRegion(cra);
                        }
                    }
                    Cell cell = row.createCell(i);
                    cell.setCellValue(mapMid.get(titleField));
                    poiModels.get(i).setOldContent(old);
                }
            }
        }
    }

    /**
     * 表头样式
     *
     * @param headStyle
     */
    private static void defaultHeadStyle(CellStyle headStyle) {
        headStyle.setBorderTop(BorderStyle.THIN);
        headStyle.setBorderLeft(BorderStyle.THIN);
        headStyle.setBorderBottom(BorderStyle.THIN);
        headStyle.setBorderRight(BorderStyle.THIN);
        headStyle.setAlignment(HorizontalAlignment.CENTER);
        headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
    }
    //合并单元格
    private static void get(PoiModel poiModel, String content, int index, int i, Sheet sheet) {
        if (poiModel.getRowIndex() != index - 1) {
            CellRangeAddress cra = new CellRangeAddress(poiModel.getRowIndex(), index - 1, poiModel.getCellIndex(), poiModel.getCellIndex());
            //在sheet里增加合并单元格
            sheet.addMergedRegion(cra);
        }
        /*重新记录该列的内容为当前内容,行标记改为当前行标记,列标记则为当前列*/
        poiModel.setContent(content);
        poiModel.setRowIndex(index);
        poiModel.setCellIndex(i);
    }
    public static <T> List<List<T>> excelPageByNum(List<T> list, int pageSize) {
        return IntStream.range(0, list.size()).boxed().filter(t -> t % pageSize == 0).map(t -> list.stream().skip(t).limit(pageSize).collect(Collectors.toList())).collect(Collectors.toList());
    }
}
3、实体对象

HeaderNode.java  和 PoiModel.java

public class PoiModel {
    private String content;

    private String oldContent;

    private String primaryKey;

    private int rowIndex;

    private int cellIndex;

    public PoiModel() {
    }

    public PoiModel(String content, String oldContent, String primaryKey, int rowIndex, int cellIndex) {
        this.content = content;
        this.oldContent = oldContent;
        this.primaryKey = primaryKey;
        this.rowIndex = rowIndex;
        this.cellIndex = cellIndex;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getOldContent() {
        return oldContent;
    }

    public void setOldContent(String oldContent) {
        this.oldContent = oldContent;
    }

    public String getPrimaryKey() {
        return primaryKey;
    }

    public void setPrimaryKey(String primaryKey) {
        this.primaryKey = primaryKey;
    }

    public int getRowIndex() {
        return rowIndex;
    }

    public void setRowIndex(int rowIndex) {
        this.rowIndex = rowIndex;
    }

    public int getCellIndex() {
        return cellIndex;
    }

    public void setCellIndex(int cellIndex) {
        this.cellIndex = cellIndex;
    }
}




public class HeaderNode {

    /**
     * 标题头
     */
    private String headerName;

    /**
     * 层级
     */
    private int row;

    /**
     * 非叶子节点列跨度
     */
    private int overNodeCount;
    /**
     * 当前列没有子节点
     */
    private boolean overNode = true;

    /**
     * 列
     */
    private int column;

    /**
     * 宽度
     */
    private int width = 13;

    public String getHeaderName() {
        return headerName;
    }

    public void setHeaderName(String headerName) {
        this.headerName = headerName;
    }

    public int getRow() {
        return row;
    }

    public void setRow(int row) {
        this.row = row;
    }

    public int getOverNodeCount() {
        return overNodeCount;
    }

    public void setOverNodeCount(int overNodeCount) {
        this.overNodeCount = overNodeCount;
    }

    public boolean isOverNode() {
        return overNode;
    }

    public void setOverNode(boolean overNode) {
        this.overNode = overNode;
    }

    public int getColumn() {
        return column;
    }

    public void setColumn(int column) {
        this.column = column;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }
}
4、下载Controller
 @GetMapping(value = "/downExcel")
    @ResponseBody
    public void downExcel(HttpServletResponse response,@RequestParam(required = true) String type) throws Exception {
        try {
            tengxunService.downExcel(response, type);
        } catch (Exception ex) {
            throw ex;
        }
    }
5、下载service
  @Override
    public void downloadExcel(HttpServletResponse response, String type)throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = URLEncoder.encode("腾讯充值文件", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
 OutputStream os = response.getOutputStream();
//加工数据
        List<Map<String, String>> list = Lists.newArrayList();
  for(int i=0;i<chongzhiList.size();i++){
            Chongzhi dto=new Chongzhi ();
            list.add(JSONObject.parseObject(JSON.toJSONString(dto),Map.class));
        }

        String weekStart ="08.01";
        String weekEnd ="08.07";
        String nextWeekStart = "08.08";
        String nextWeekEnd ="08.15";
        //合并单元格方法
        try {
            String customizeLabel = "[{\"headerName\":\"区域\",\"column\":0,\"row\":0}," +
                    "{\"headerName\":\"用户姓名\",\"column\":1,\"row\":0}," +
                    "{\"headerName\":\"本周("+weekStart+"-"+weekEnd+")充值记录\",\"column\":2,\"row\":0}," +
                    "{\"headerName\":\"本周("+weekStart+"-"+weekEnd+")充值详情\",\"column\":3,\"row\":0,\"overNodeCount\":4,\"overNode\":false},{\"headerName\":\"充值时间\",\"column\":3,\"row\":1}," +
                    "{\"headerName\":\"充值项目\",\"column\":4,\"row\":\"1\"}," +
                    "{\"headerName\":\"充值方式\",\"column\":5,\"row\":1}," +
                    "{\"headerName\":\"充值金额\",\"column\":6,\"row\":1}," +
                    "{\"headerName\":\"下周("+nextWeekStart+"-"+nextWeekEnd+")充值计划\",\"column\":7,\"row\":0}]";
            //1、生book
            SXSSFWorkbook book = new SXSSFWorkbook();
            //2、生成动态标题
            SXSSFSheet sxssfSheet = ExcelMerge.createExcelHead(book,"Sheet1", customizeLabel);
            //3、取数据对应字段值  汇总
            String[] title = {"区域", "用户姓名", "本周("+weekStart+"-"+weekEnd+")充值记录", "充值时间", "充值项目", "充值方式", "充值金额", "下周("+nextWeekStart+"-"+nextWeekEnd+")充值计划"};
            //4、取数据对应属性的字段值  汇总
            String[] field = {"areaName", "name", "chongzjilu", "chongzTime", "chongzProject", "chongzMethod", "chongzMoney", "nextChongz"};
            //5、需要合并的列
            List<Integer> mergeIndex = Arrays.asList(0,1,7);
            //6、合并
            ExcelMerge.mergeCellFunc(sxssfSheet,title,field, list, sxssfSheet.getLastRowNum(),mergeIndex);
            //创建excel文件 下载
            book.write(os);
        } catch (IOException e){
            logger.error("文件导出失败,错误信息{}",e);
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<>();
            map.put("statusCode", "500");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

三、下载

完事通过controller调用下载接口,直接可以下载出文件

可以任意改表头,与选择是否要合并的字段,当做参数传入,将需要合并的列顺序传入即可完成合并,一步到位,十分方便

//5、需要合并的列  
List<Integer> mergeIndex = Arrays.asList(0,1,7);
ExcelMerge.mergeCellFunc(sxssfSheet,title,field, list, sxssfSheet.getLastRowNum(),mergeIndex);

截图如下

excel 动态表头与合并列,SpringBoot,excel

荆轲刺秦王!文章来源地址https://www.toymoban.com/news/detail-660022.html

到了这里,关于excel 动态表头与合并列的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端-layui动态渲染表格行列与复杂表头合并

    说在前面: 最近一直在用layui处理表格 写的有些代码感觉还挺有用的,顺便记录下来方便以后查看使用; HTML处代码 拿到id 渲染位置表格 CSS 重点来了 JS代码 合并表头需要在 cols中传入[ [ ],[ ] ]这种数组类型的格式; 主要就是不合并的表头都加上rowspan:2(代表行跨度为2) c

    2024年02月07日
    浏览(49)
  • 前端vue+elementui导出复杂(单元格合并,多级表头)表格el-table转为excel导出

    需求 :前端对el-table表格导出 插件 : npm install xlsx -S npm install file-saver --save 原理 :直接导出el-table的表格里面的数据,这样就会存在缺点,只会导出当前页面的数据,如果需要导出全部数据,可以自己重新渲染一个全部数据不可见的el-table表格,来导出就可以了 扩展 :经过

    2024年02月04日
    浏览(66)
  • 【Go】excelize库实现excel导入导出封装(一),自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头

    最近在学go操作excel,毕竟在web开发里,操作excel是非常非常常见的。这里我选择用 excelize 库来实现操作excel。 为了方便和通用,我们需要把导入导出进行封装,这样以后就可以很方便的拿来用,或者进行扩展。 我参考的是这篇文章:【GO】excelize导入导出封装 这个导入导出封

    2024年02月04日
    浏览(49)
  • java动态生成excel并且需要合并单元格

    java动态生成excel并且需要合并单元格 先上图看一下预期效果 集成poi 通过poi手动制作excel

    2024年02月13日
    浏览(42)
  • 基于 SheetJS 的前端合并单元格复杂表头导入

    项目功能 任意层级合并单元格复杂表头解析 自动转化为目标层级的数据结构 自动生成基于 antdv 的 table 列配置数据 columns 及对于数据源 dataSource。在页面端复现 Excel 效果。 在线示例 步骤零:如需快速测试,可点击顶部的示例按钮,可快速填充测试数据,并自动下载对应的

    2024年01月16日
    浏览(48)
  • element-ui 表格(table)合并表头下面合并列且可以收缩展开

    百度了一大堆,发现了首行不能合并,想到了用dom做,找到了下面这个链接 1、表头合并 —— 给table添加属性:header-cell-style=\\\"headerStyle\\\",里面给首行设置跨行 element-ui表头合并 - ^Mao^ - 博客园 2、表内合并 —— 给table添加属性:span-method=\\\"arraySpanMethod\\\",里面设置合并 Element - The wor

    2024年02月16日
    浏览(61)
  • hutool 导出复杂表头excel

    假如已这样的表头导出数据  1.把包含表头的excel添加到项目资源目录  2.编写代码读取表头所在sheet,并且加入需导出的数据

    2024年02月13日
    浏览(52)
  • POI实现Excel多行复杂表头导出

    1. 定义表头标题 2. 编写导出/生成Excel工具类 3. 测试 测试结果

    2024年01月19日
    浏览(56)
  • Excel(1):表头或列头冻结

    对于较大的excel,通常需要固定一部分内容,另一份内容为可翻动。 在视图中选择冻结窗格,需要注意的是,选择冻结窗格时,窗格的左上方的表格区域是固定不动的,只可以向下或者向右活动。 

    2024年02月13日
    浏览(44)
  • java poi实现Excel多级表头导出

    最近碰到一个导出,比较繁琐,也查询了许多博客,在其中一篇博客的基础上修改,实现了最终想要的效果。话不多说,直接上效果图 1.主代码: 2.合并单元格 3.设置表头单元格的宽度 4.填充数据(注:我这里的数据格式是ListMapString, Object类型,可以根据自己的实际情况来封

    2024年02月03日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包