IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> EasyExcel导出excel合并表头和数据 -> 正文阅读

[移动开发]EasyExcel导出excel合并表头和数据

maven依赖

 <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>

数据结构与element表格数据结构类似
多级表头使用children渲染
合并行或列通过cols和rows控制

关键代码

try (OutputStream out = new FileOutputStream(file)) {
EasyExcel.write(out).head(excelHead(tableVO.getTableHead())).sheet("Sheet1")
            .registerWriteHandler(new MyMergeStrategy(tableVO))//自定义合并单元格
            .registerWriteHandler(new CustomCellWriteUtil())//表头宽度自适应
            .doWrite(excelData(tableVO));
} catch (IOException e) {
    e.printStackTrace();
}
/**
* 创建表头,这里会自动合并表头
 *
 * @return
 */
private List<List<String>> excelHead(List<TableHeadVO> headVOS) {
    boolean needMerge = false;
    for (TableHeadVO headVO : headVOS) {
        if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
            needMerge = true;
        }
    }
    List<List<String>> headList = new ArrayList<>();
    for (TableHeadVO headVO : headVOS) {
        if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
            headVO.getChildren().forEach(child -> {
                List<String> head = new ArrayList<>();
                head.add(headVO.getLabel());
                head.add(child.getLabel());
                headList.add(head);
            });
        } else {
            List<String> head = new ArrayList<>();
            head.add(headVO.getLabel());
            if (needMerge) {
                head.add(headVO.getLabel());
            }
            headList.add(head);
        }
    }
    return headList;
}

创建数据

/**
 * 创建数据
 *
 * @return
 */
private List<List<Object>> excelData(TableVO tableVO) {
    int maxExcel = 32767;
    List<List<Object>> list = new ArrayList<>();
    if (Objects.isNull(tableVO) || CollectionUtils.isEmpty(tableVO.getTableData())) {
        return list;
    }
    List<TableHeadVO> heads = tableVO.getTableHead();
    for (Map<String, Object> map : tableVO.getTableData()) {
        List<Object> data = new ArrayList<>();
        for (TableHeadVO head : heads) {
            if (CollectionUtils.isNotEmpty(head.getChildren())) {
                head.getChildren().forEach(child -> {
                    Object value = map.get(child.getProp());
                    if ((value + "").length() >= maxExcel) {
                        data.add((value + "").substring(0, 255) + "字符过长");
                    } else {
                        data.add(value);
                    }
                });
            } else {
                Object value = map.get(head.getProp());
                if ((value + "").length() >= maxExcel) {
                    data.add((value + "").substring(0, 255) + "字符过长");
                } else {
                    data.add(value);
                }
            }
        }
        list.add(data);
    }
    return list;
}

表格数据

import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Map;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class TableVO {
    @ApiModelProperty("表头信息")
    private List<TableHeadVO> tableHead;
    @ApiModelProperty("表格数据信息")
    private List<Map<String, Object>> tableData;
    @ApiModelProperty("总数据")
    private long total;
    @ApiModelProperty("总页数")
    private long totalPage;
    @ApiModelProperty("当前页:从1开始")
    private long pageIndex;
    @ApiModelProperty("页大小")
    private long pageSize;

    public TableVO(List<TableHeadVO> tableHead, List<Map<String, Object>> tableData) {
        this.tableHead = tableHead;
        this.tableData = tableData;
    }

    public static TableVO of(PageResult result) {
        TableVO tableVO = new TableVO();
        tableVO.setPageIndex(result.getPageIndex());
        tableVO.setPageSize(result.getPageSize());
        tableVO.setTotal(result.getTotalCount());
        tableVO.setTotalPage(result.getTotalPage());
        return tableVO;
    }
}

表头对象


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("表头")
@Data
public class TableHeadVO {
    @ApiModelProperty("是否合并")
    private boolean merge = true;
    @ApiModelProperty("表格的字段")
    private String prop;
    @ApiModelProperty("表格字段名称")
    private String label;
    @ApiModelProperty("字段类型:input,textarea,number,radio,checkbox,time,date,rate,color,select,switch,slider,text,link,imgupload,fileupload,table,grid,report,divider")
    private String type;
    @ApiModelProperty("是否为导出excel字段")
    private Boolean excel;
    @ApiModelProperty("宽度")
    private String width;
    @ApiModelProperty("后台自定义")
    private Boolean custom = false;
    @ApiModelProperty("子表单")
    private List<TableHeadVO> children;

    public TableHeadVO(String prop, String label) {
        this.prop = prop;
        this.label = label;
        this.type = "input";
        this.excel = true;
        this.width = "auto";
    }
}

最效果
在这里插入图片描述

数据合并


import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class MyMergeStrategy extends AbstractMergeStrategy {
    //合并坐标集合
    private List<CellRangeAddress> cellRangeAddresses;

    public MyMergeStrategy(TableVO tableVO) {
        List<CellRangeAddress> list = new ArrayList<>();

        // 判断是否需要合并,需要合并的再构造合并的地址
        boolean needMerge = false;
        for (TableHeadVO headVO : tableVO.getTableHead()) {
            if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
                needMerge = true;
            }
        }
        if (needMerge) {
            // 重新定义head
            List<TableHeadVO> heads = new ArrayList<>();
            for (TableHeadVO headVO : tableVO.getTableHead()) {
                if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
                    for (TableHeadVO child : headVO.getChildren()) {
                        heads.add(child);
                    }
                } else {
                    heads.add(headVO);
                }
            }
            if (CollectionUtils.isNotEmpty(tableVO.getTableData())) {
                for (int i = 0; i < tableVO.getTableData().size(); i++) {
                    Map<String, Object> data = tableVO.getTableData().get(i);
                    System.out.println("第" + i + "行数据" + data);

                    for (int j = 0; j < heads.size(); j++) {
                        TableHeadVO head = heads.get(j);

                        int cols = (int) data.getOrDefault("cols", 1);
                        int rows = (int) data.getOrDefault("rows", 1);
                        // 判断有没有孩子,有孩子的不用合并,没孩子的都要合并,根据所占列数合并
                        if (rows > 1 && head.isMerge()) {
                            System.out.print(head.getLabel());
                            log.info("{}, {} , {}, {}", i + 1 + 1, i + 1 + rows, j, j);
                            // row 行,col列
                            // int firstRow, int lastRow, int firstCol, int lastCol
                            CellRangeAddress item = new CellRangeAddress(i + 1 + 1, i + 1 + rows, j, j);
                            list.add(item);
                        }
                    }
                    System.out.println();
                }
            }
        }

        this.cellRangeAddresses = list;
    }

    /**
     * 合并操作:对每个单元格执行!!!
     *
     * @param sheet            sheet对象
     * @param cell             当前单元格
     * @param head             表头对象
     * @param relativeRowIndex 相关行索引
     */
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        /*
         * 合并单元格
         *
         * 由于merge()方法会在写每个单元格(cell)时执行,因此需要保证合并策略只被添加一次。否则如果每个单元格都添加一次
         * 合并策略,则会造成重复合并。例如合并A2:A3,当cell为A2时,合并A2:A3,但是当cell为A3时,又要合并A2:A3,而此时
         * 的A2已经是之前的A2和A3合并后的结果了。
         * 由于此处的表头占了两行,因此数据单元格是从(2, 0)开始的,所以就对这个单元格(cell.getRowIndex() == 2 && cell.getColumnIndex() == 0)
         * 添加一次合并策略就可以了。如果表头只有一行,则判断条件改为「cell.getRowIndex() == 1 && cell.getColumnIndex() == 0」就可以了。
         */
        if (cell.getRowIndex() == 2 && cell.getColumnIndex() == 0) {
            for (CellRangeAddress item : cellRangeAddresses) {
                sheet.addMergedRegion(item);
            }
        }

        /*
         * 如果不作判断,可以使用addMergedRegionUnsafe()方法,
         * 这样生成的Excel文件可以打开,只是打开时会提示内容有问题,修复后可以打开
         */
        // for (CellRangeAddress item : cellRangeAddresses) {
        //     sheet.addMergedRegionUnsafe(item);
        // }
    }
}

列宽自适应


import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * excel自适应列宽 拉过去就能直接套用自动适应列宽
 */
public class CustomCellWriteUtil extends AbstractColumnWidthStyleStrategy {
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);

    public CustomCellWriteUtil() {
    }

    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean needSetWidth = isHead;
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null || maxColumnWidthMap.isEmpty()) {
                maxColumnWidthMap = new HashMap(16);
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }

            Integer columnWidth = this.dataLength(cellDataList, cell, true);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = (Integer) ((Map) maxColumnWidthMap).get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    ((Map) maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), (int) ((columnWidth + 0.72) * 256));
                }
            }
        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

参考文献

easyexcel(十一):easyexcel动态表头,表头合并
EasyExcel 动态表头 + 数据单元格合并

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:54:07  更:2022-04-18 17:54:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:43:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码