有的表格表头要求多层级的动态的表头,已经知道表头excel坐标,只需要配置要表头坐标即可!下面是具体代码demo
poi依赖、hutool工具jar
org.apache.poi
poi-ooxml
4.1.0
cn.hutool
hutool-all
5.7.18
是不是很美观
简单代码实现
package com.example.demo.excel;
import cn.hutool.json.JSONUtil;
import lombok.Data;
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.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
/**
* @Description:
* @Auther: tsy
* @Date: 2022/10/09/5:27 下午
*/
public class CustomHeader {
public static void main(String[] args) {
String customizeLabel = "[{\"headerName\":\"机构名称\",\"column\":0,\"row\":0},{\"headerName\":\"卡类型代码\",\"column\":1,\"row\":0},{\"headerName\":\"卡类型名称\",\"column\":2,\"row\":0},{\"headerName\":\"期初库存量\",\"column\":3,\"row\":0},{\"headerName\":\"本期入库情况\",\"column\":4,\"row\":0,\"overNodeCount\":4,\"overNode\":false},{\"headerName\":\"本期入库小计\",\"column\":4,\"row\":1},{\"headerName\":\"本期入库明细\",\"column\":5,\"row\":\"1\",\"overNodeCount\":3,\"overNode\":false},{\"headerName\":\"印刷入库\",\"column\":5,\"row\":2},{\"headerName\":\"领用入库\",\"column\":6,\"row\":2},{\"headerName\":\"回收入库\",\"column\":7,\"row\":2},{\"headerName\":\"本期出库情况\",\"column\":8,\"row\":0,\"overNodeCount\":7,\"overNode\":false},{\"headerName\":\"本期出库小计\",\"column\":8,\"row\":\"1\"},{\"headerName\":\"本期出库明细\",\"column\":9,\"row\":\"1\",\"overNodeCount\":6,\"overNode\":false},{\"headerName\":\"机构/部门下发出库\",\"column\":9,\"row\":2},{\"headerName\":\"员工下发出库\",\"column\":10,\"row\":2},{\"headerName\":\"回收提交出库\",\"column\":11,\"row\":2},{\"headerName\":\"清理出库\",\"column\":12,\"row\":2},{\"headerName\":\"销毁出库\",\"column\":13,\"row\":2},{\"headerName\":\"其他方式出库\",\"column\":14,\"row\":2},{\"headerName\":\"剩余库存量\",\"column\":15,\"row\":0}]";
List headerNodes = JSONUtil.toList(customizeLabel, HeaderNode.class);
SXSSFWorkbook book = new SXSSFWorkbook();
SXSSFSheet sxssfSheet = book.createSheet("测试");
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);
}
}
FileOutputStream fileOut = null;
try {
File file = new File("/Users/tangshanyuan/test/tsy-2.xls");
fileOut = new FileOutputStream(file);
book.write(fileOut);
System.out.println("----Excle文件已生成------");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 表头样式
*
* @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());
}
@Data
static class HeaderNode {
/**
* 标题头
*/
private String headerName;
/**
* 层级
*/
private int row;
/**
* 非叶子节点列跨度
*/
private int overNodeCount;
/**
* 当前列没有子节点
*/
private boolean overNode = true;
/**
* 列
*/
private int column;
/**
* 宽度
*/
private int width = 13;
}
}
主要配置表头名称、表头层级(其起始行)、表头所属列、表头是否跨列、表头跨列列数、宽度
[
{
"headerName": "机构名称",
"column": 0,
"row": 0
},
{
"headerName": "卡类型代码",
"column": 1,
"row": 0
},
{
"headerName": "卡类型名称",
"column": 2,
"row": 0
},
{
"headerName": "期初库存量",
"column": 3,
"row": 0
},
{
"headerName": "本期入库情况",
"column": 4,
"row": 0,
"overNodeCount": 4,
"overNode": false
},
{
"headerName": "本期入库小计",
"column": 4,
"row": 1
},
{
"headerName": "本期入库明细",
"column": 5,
"row": "1",
"overNodeCount": 3,
"overNode": false
},
{
"headerName": "印刷入库",
"column": 5,
"row": 2
},
{
"headerName": "领用入库",
"column": 6,
"row": 2
},
{
"headerName": "回收入库",
"column": 7,
"row": 2
},
{
"headerName": "本期出库情况",
"column": 8,
"row": 0,
"overNodeCount": 7,
"overNode": false
},
{
"headerName": "本期出库小计",
"column": 8,
"row": "1"
},
{
"headerName": "本期出库明细",
"column": 9,
"row": "1",
"overNodeCount": 6,
"overNode": false
},
{
"headerName": "机构/部门下发出库",
"column": 9,
"row": 2
},
{
"headerName": "员工下发出库",
"column": 10,
"row": 2
},
{
"headerName": "回收提交出库",
"column": 11,
"row": 2
},
{
"headerName": "清理出库",
"column": 12,
"row": 2
},
{
"headerName": "销毁出库",
"column": 13,
"row": 2
},
{
"headerName": "其他方式出库",
"column": 14,
"row": 2
},
{
"headerName": "剩余库存量",
"column": 15,
"row": 0
}
]
是否可以直接配置表头坐标,这样是不是更方便、适用更强?因为上述实现存在一个问题,因为代码实现合并仅仅是按照单列合并、按照单行合并,并没有实现多行多列合并,所以考虑可以修改配置,直接配置表头坐标,代码不需要判断是不是跨列合并,可直接合并。
我又思考了一下,觉得不存在多行多列的情况,因为如果存在,excel多列是展示那些数据呢?所以我考虑不存在,那么上述实现是完全够用的!
代码实现:CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
如果文章有幸能对大家有一些帮助或参考价值,麻烦点点赞,哈哈,希望大家能够多多交流。