前提:为啥要做这么个折磨人的导出呢?因为我们是乙方!好了,废话不多说了,直接进入正题吧。
版本
implementation "com.alibaba:easyexcel:3.1.0"
List<List<String>> headTitles = Lists.newArrayList();
headTitles.add(Lists.newArrayList("项目名称"));
headTitles.add(Lists.newArrayList("品牌"));
headTitles.add(Lists.newArrayList("车身形式"));
headTitles.add(Lists.newArrayList("阀点组"));
headTitles.add(Lists.newArrayList("2021", "1"));
headTitles.add(Lists.newArrayList("2021", "2"));
headTitles.add(Lists.newArrayList("2021", "3"));
headTitles.add(Lists.newArrayList("2021", "4"));
headTitles.add(Lists.newArrayList("2021", "5"));
headTitles.add(Lists.newArrayList("2021", "6"));
headTitles.add(Lists.newArrayList("2022", "1"));
headTitles.add(Lists.newArrayList("2022", "2"));
headTitles.add(Lists.newArrayList("2022", "3"));
headTitles.add(Lists.newArrayList("2022", "4"));
headTitles.add(Lists.newArrayList("2022", "5"));
headTitles.add(Lists.newArrayList("2022", "6"));
//定义数据体
List<List<Object>> dataList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Object[] data = new Object[16];
//a1代表蓝色 a2代表绿色
if (i % 2 == 0) {
data[0] = "油电混动jy";
data[1] = "L";
data[2] = "SUV";
data[3] = "整车阀点";
data[6] = "a1G" + i;
data[8] = "a1G" + i;
data[10] = "a2G" + i;
data[12] = "a2G" + i;
} else {
data[0] = "纯电动jy";
data[1] = "MG";
data[2] = "三厢车";
data[3] = "整车阀点";
data[4] = "a1G" + i;
data[7] = "a2G" + i;
data[14] = "a2G" + i;
}
dataList.add(Arrays.asList(data));
}
public void export(HttpServletResponse response) throws IOException {
//表头
List<List<String>> headTitles = getTitles();
//数据
List<List<Object>> data = getData();
try {
String sheetName = URLEncoder.encode("表名", "UTF-8");
String fileName = sheetName.concat(String.valueOf(System.currentTimeMillis())).concat(".xlsx");
//response输出文件流
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
//需要合并的列
int[] mergeColumnIndex = {0, 1};
//从哪一行开始合并
int mergeRowIndex = 2;
//写入表头,表数据
EasyExcel.write(response.getOutputStream()).excelType(ExcelTypeEnum.XLSX).head(headTitles).sheet("Sheet0")
//设置整体表头和内容的样式
.registerWriteHandler(new ProjectExportUtil().cell())
//自适应高度
.registerWriteHandler(new CustomCellWriteHeightConfig())
//自适应宽度
.registerWriteHandler(new CustomCellWriteWidthConfig(0))
//合并策略、自定义单元格样式
.registerWriteHandler(new ExcelFillCellMergeStrategy(mergeColumnIndex, mergeRowIndex))
.doWrite(data);
} catch (Exception e) {
log.error("", e);
}
}
public HorizontalCellStyleStrategy cell() {
//1、 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为蓝灰色
headWriteCellStyle.setFillForegroundColor(IndexedColors.DARK_TEAL.getIndex());
//字体
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 14);
headWriteFont.setColor(IndexedColors.WHITE.getIndex());
headWriteCellStyle.setWriteFont(headWriteFont);
//居中
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 2、内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontName("宋体");
contentWriteFont.setFontHeightInPoints((short) 12);
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
}
public class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy {
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map maxColumnWidthMap = (Map) CACHE.get(writeSheetHolder.getSheetNo());
if (maxColumnWidthMap == null) {
maxColumnWidthMap = new HashMap(16);
CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
}
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
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(), columnWidth * 250);
}
}
}
}
private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData cellData = (CellData) cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return 6;
} 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 6;
}
}
}
}
}
这里我把默认值给的是6,是为了调整月份单元格的大小,可以根据实际需要进行调整
public class ExcelFillCellMergeStrategy implements CellWriteHandler {
/**
* 合并列的下标
*/
private int[] mergeColumnIndex;
/**
* 合并列开始行下标
*/
private int mergeRowIndex;
public ExcelFillCellMergeStrategy() {
}
public ExcelFillCellMergeStrategy(int[] mergeColumnIndex, int mergeRowIndex) {
this.mergeColumnIndex = mergeColumnIndex;
this.mergeRowIndex = mergeRowIndex;
}
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
//锁定前四列
Sheet sheet = writeSheetHolder.getSheet();
sheet.createFreezePane(4, 0);
}
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Head headData = context.getHeadData();
List<String> nameList = headData.getHeadNameList();
String headName = null;
if (null != nameList && nameList.size() == 2) {
headName = nameList.get(1);
}
Cell cell = context.getCell();
Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
//自定义单元格样式
if (BooleanUtils.isNotTrue(context.getHead())) {
Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
short colorIndex = 0;
if (!ObjectUtils.isEmpty(curData)) {
String value = String.valueOf(curData);
//蓝色
if (value.startsWith("a1")) {
value = value.replace("a1", "");
cell.setCellValue(value);
//加样式
colorIndex = IndexedColors.SKY_BLUE.getIndex();
} else if (value.startsWith("a2")) {
value = value.replace("a2", "");
cell.setCellValue(value);
//加样式
colorIndex = IndexedColors.BRIGHT_GREEN1.getIndex();
}
context.getFirstCellData().setWriteCellStyle(null);
}
cell.setCellStyle(getCellStyle(workbook, colorIndex, headName));
}
//合并单元格
WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
int curRowIndex = cell.getRowIndex();
int curColIndex = cell.getColumnIndex();
if (curRowIndex > mergeRowIndex) {
for (int i = 0; i < mergeColumnIndex.length; i++) {
if (curColIndex == mergeColumnIndex[i]) {
mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
break;
}
}
}
}
/**
* 获取自定义样式
*
* @param workbook
* @param bg 背景色
* @return
*/
private CellStyle getCellStyle(Workbook workbook, short bg, String headName) {
CellStyle cellStyle = workbook.createCellStyle();
if (bg != 0) {
cellStyle.setFillForegroundColor(bg);
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
if (StringUtils.isNotBlank(headName) && StringUtils.equals(headName, "1")) {
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
}
return cellStyle;
}
/**
* 当前单元格向上合并
*
* @param writeSheetHolder
* @param cell 当前单元格
* @param curRowIndex 当前行
* @param curColIndex 当前列
*/
private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
if (null == preCell) {
return;
}
Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
// 将当前单元格数据与上一个单元格数据比较
boolean dataBool = preData.equals(curData);
//如果内容相同,则合并
if (dataBool) {
Sheet sheet = writeSheetHolder.getSheet();
List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
boolean isMerged = false;
for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
CellRangeAddress cellRangeAddr = mergeRegions.get(i);
// 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
sheet.removeMergedRegion(i);
cellRangeAddr.setLastRow(curRowIndex);
sheet.addMergedRegion(cellRangeAddr);
isMerged = true;
}
}
// 若上一个单元格未被合并,则新增合并单元
if (!isMerged) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
sheet.addMergedRegion(cellRangeAddress);
}
}
}
}
整体上来说已经达到了那个效果,但可以还有些细枝末节的地方需要再调整调整吧,比如那个excel固定列之后的那条竖线能不能根据内容动态延伸之类的可能还需要时间再去做研究了。
再就是,easyexcel本身的功能就已经能够满足大部分场景下的导出了。少部分比较特别的,也有很多方法和策略让我们自己去实现,总体来说很赞了!
好了,文章到这里就结束了,欢迎各位小伙伴提出宝贵的建议!
最后,如果转载请记得注明出处哦!