easyexcel工具+表格标题自定义+表格合并

  1. excel导出工具类
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * Excel相关处理
 * @author dmw
 */
public class ExcelUtil {
	/**
	 *  单个sheet页导出
	 */
	public static void writeExcel(HttpServletResponse response, List<?> data, String fileName, String sheetName, Class<?> clazz) throws Exception {
		 EasyExcel.write(getOutputStream(fileName, response), clazz)
				 .excelType(ExcelTypeEnum.XLSX).sheet(sheetName)
				 .relativeHeadRowIndex(1)
				 .registerWriteHandler(new CustomTitleWriteHandler(clazz,fileName))
				 .registerWriteHandler(getCellStyle())
				 .doWrite(data);
	}

	/**
	 *  合并导出
	 */
	public static void writeExcelCustomMerge(HttpServletResponse response, List<?> data, String fileName, String sheetName, Class<?> clazz) throws Exception {
		EasyExcel.write(getOutputStream(fileName, response), clazz)
				.excelType(ExcelTypeEnum.XLSX)
				.sheet(sheetName)
				//表格标题占位
				.relativeHeadRowIndex(1)
				.registerWriteHandler(getCellStyle())
				.head(clazz)
				//自定义表格标题处理
				.registerWriteHandler(new CustomTitleWriteHandler(clazz,fileName))
				//自定义表格合并处理
				.registerWriteHandler(new CustomMergeStrategy(clazz))
				.doWrite(data);
	}

	/**
	 *   多个sheet页导出
	 */
	public static void writeExcels(HttpServletResponse response,List<List<?>> data, String fileName, Class<?> clazz) throws Exception{
		ExcelWriter excelWriter = null;
		try{
			excelWriter = EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).registerWriteHandler(getCellStyle()).build();
			for(int i=0;i<data.size();i++){
				WriteSheet writeSheet = EasyExcel.writerSheet(i, "sheet"+i).build();
				excelWriter.write(data.get(i), writeSheet);
			}
		} finally {
			if(excelWriter != null){
				excelWriter.finish();
			}
		}
	}

	/**
	 * 设置请求
	 */
	private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
		fileName = URLEncoder.encode(fileName +"."+ ExcelTypeEnum.XLSX, "UTF-8");
		// 告诉浏览器用什么软件可以打开此文件
		response.setHeader("content-Type", "application/vnd.ms-excel");
		response.setCharacterEncoding("utf8");
		response.setHeader("content-disposition",  "attachment;filename="+fileName+";"+"filename*=utf-8''"+fileName);
		return response.getOutputStream();
	}
	/**
	 * 设置样式
	 */
	private static HorizontalCellStyleStrategy getCellStyle(){
		//表头样式
		WriteCellStyle headWriteCellStyle = new WriteCellStyle();
		//设置表头居中对齐
		headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
		//内容样式
		WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
		//设置内容垂直居中对齐
		contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
		//设置内容水平居中对齐
		contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
		return new HorizontalCellStyleStrategy(headWriteCellStyle,contentWriteCellStyle);
	}
}

  1. 自定义表格标题处理
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.lang.reflect.Field;

/**
 * @description: 表格标题处理
 * @author: dmw
 * @date: 2023/3/2 17:00
 **/
public class CustomTitleWriteHandler implements SheetWriteHandler {

    /**
     * 标题
     */
    private final String fileName;

    /**
     * DTO数据类型
     */
    private final Class<?> elementType;

    public CustomTitleWriteHandler(Class<?> elementType,String fileName) {
        this.fileName = fileName;
        this.elementType = elementType;
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 获取clazz所有的属性
        Field[] fields = this.elementType.getDeclaredFields();
        Workbook workbook = writeWorkbookHolder.getWorkbook();
        Sheet sheet = workbook.getSheetAt(0);
        Row row1 = sheet.createRow(0);
        row1.setHeight((short) 800);
        Cell cell = row1.createCell(0);
        //设置标题
        cell.setCellValue(fileName);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        Font font = workbook.createFont();
        font.setBold(true);
        font.setFontHeight((short) 400);
        font.setFontName("宋体");
        cellStyle.setFont(font);
        cell.setCellStyle(cellStyle);
        sheet.addMergedRegionUnsafe(new CellRangeAddress(0, 0, 0, fields.length-1));
    }
}

4 . 自定义表格合并注解

import java.lang.annotation.*;

/**
 * 自定义注解,用于判断是否需要合并以及合并的主键
 * @author dmw
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CustomMerge {

    /**
     * 是否是主键,即该字段相同的行合并
     */
    boolean isPk() default false;
    
    /**
     * 是否需要合并单元格
     */
    boolean needMerge() default false;
}

3.2 表格合并策略

package com.springcloud.aj.mall.common.util.easyexcel;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义单元格合并策略
 * @author dmw
 */
@Slf4j
public class CustomMergeStrategy implements RowWriteHandler {
    /**
     * 主键下标
     */
    private Integer pkIndex;

    /**
     * 需要合并的列的下标集合
     */
    private final List<Integer> needMergeColumnIndex = new ArrayList<>();

    /**
     * DTO数据类型
     */
    private final Class<?> elementType;

    public CustomMergeStrategy(Class<?> elementType) {
        this.elementType = elementType;
    }

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
        // 如果是标题,则直接返回
        if (isHead) {
            return;
        }
        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();
        if (null == pkIndex) {
            this.lazyInit(writeSheetHolder);
        }
        // 不能和标题合并,只能数据行之间合并
        int rowNum = row.getRowNum();
        if (rowNum <= 1) {
            return;
        }
        // 获取上一行数据
        Row lastRow = sheet.getRow(rowNum - 1);
        // 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并
        if (lastRow.getCell(pkIndex).getStringCellValue().equalsIgnoreCase(row.getCell(pkIndex).getStringCellValue())) {
            boolean isMerged = false;

            for (Integer needMerIndex : needMergeColumnIndex) {
                List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
                for (int i = 0; i < mergeRegions.size(); i++) {
                    CellRangeAddress cellRangeAddress = mergeRegions.get(i);
                    // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                    if (cellRangeAddress.isInRange(rowNum - 1, needMerIndex)) {
                        log.info("已合并移除:{}",i);
                        sheet.removeMergedRegion(i);
                        cellRangeAddress.setLastRow(rowNum);
                        sheet.addMergedRegionUnsafe(cellRangeAddress);
                        isMerged = true;
                    }
                }
                // 若上一个单元格未被合并,则新增合并单元
                if (!isMerged) {
                    CellRangeAddress cellRangeAddress = new CellRangeAddress(rowNum - 1, rowNum,    needMerIndex, needMerIndex);
                    sheet.addMergedRegionUnsafe(cellRangeAddress);
                }
            }
        }
    }

    /**
     * 初始化主键下标和需要合并字段的下标
     */
    private void lazyInit(WriteSheetHolder writeSheetHolder) {

        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();

        // 获取标题行
        Row titleRow = sheet.getRow(1);

        // 获取DTO所有的属性
        Field[] fields = this.elementType.getDeclaredFields();

        // 遍历所有的字段,因为是基于DTO的字段来构建excel,所以字段数 >= excel的列数
        for (Field theField : fields) {
            // 获取@ExcelProperty注解,用于获取该字段对应在excel中的列的下标
            ExcelProperty easyExcelAnn = theField.getAnnotation(ExcelProperty.class);
            // 为空,则表示该字段不需要导入到excel,直接处理下一个字段
            if (null == easyExcelAnn) {
                continue;
            }
            // 获取自定义的注解,用于合并单元格
            CustomMerge customMerge = theField.getAnnotation(CustomMerge.class);

            // 没有@CustomMerge注解的默认不合并
            if (null == customMerge) {
                continue;
            }
            for (int index = 0; index < fields.length; index++) {
                Cell theCell = titleRow.getCell(index);
                // 当配置为不需要导出时,返回的为null,这里作一下判断,防止NPE
                if (null == theCell) {
                    continue;
                }
                // 将字段和excel的表头匹配上
                if (easyExcelAnn.value()[0].equalsIgnoreCase(theCell.getStringCellValue())) {
                    if (customMerge.isPk()) {
                        pkIndex = index;
                    }
                    if (customMerge.needMerge()) {
                        needMergeColumnIndex.add(index);
                    }
                }
            }
        }
        // 没有指定主键,则异常
        if (null == this.pkIndex) {
            throw new IllegalStateException("使用@CustomMerge注解必须指定主键");
        }

    }
}

  1. 表格实体vo
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.springcloud.aj.mall.common.util.RebateStatusConverter;
import com.springcloud.aj.mall.common.util.easyexcel.CustomMerge;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;

@Data
public class FinanceRebateExportVo {

	@ColumnWidth(20)
	@ExcelProperty("订单编号")
	@CustomMerge(needMerge = true,isPk = true)
	private String orderCode;
	
	//......其他字段
	
	@ColumnWidth(15)
	@CustomMerge(needMerge = true)
	@ExcelProperty(value = "实际返利金额" )
	private BigDecimal realRebateAmount;
  1. 实际使用
	@Override
	public void financeRebateExport(HttpServletResponse response,FinanceRebateForm form) {
		String sheetName = "返利报表";
		if (Objects.nonNull(form.getBeginTime()) && Objects.nonNull(form.getEndTime())){
			String begin = form.getBeginTime().substring(0, 10);
			String end = form.getEndTime().substring(0, 10);
			sheetName=sheetName+"("+begin+"至"+end+")";
		}
		List<FinanceRebateVo> rebateList = ajOrderMapper.getFinanceRebateList(form);
		// 转换成execl 对象输出
		List<FinanceRebateExportVo> list = rebateList.stream().map(record -> {
			FinanceRebateExportVo excelVO = new FinanceRebateExportVo();
			BeanUtils.copyProperties(record, excelVO);
			return excelVO;
		}).collect(Collectors.toList());
		try {
			ExcelUtil.writeExcelCustomMerge(response,list, sheetName, sheetName, FinanceRebateExportVo.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  1. 导出效果如下

easyexcel工具+表格标题自定义+表格合并_第1张图片

end…
1.已参考并修复导出合并问题 easyExcel导入导出
2.添加自定义表格标题
感谢

你可能感兴趣的:(java,easyexcel,java)