思路:
在若依分离版本的 Excel 注解内增加 isMerge() 方法,在导出时获取有该属性的字段,获取该字段的列所在位置后,对该sheet进行遍历,判断当前行的值是否与下一行的值一致,不一致则进行记录,遍历完成,再对数据进行合并。
代码:
1.对Excel注解增加属性
/**
* 自定义导出Excel数据注解
*
* @author yihong
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
..........
/**
* 是否合并单元格
* @return
*/
public boolean isMerge() default false;
......
}
2.在若依分离版本的ExcelUtil工具类内增加方法
public class ExcelUtil<T> {
/**------------1.新增属性----------*/
/**
* 需要合并的列
*/
List<Object[]> mergeFields = new ArrayList<>(30);
/**
* 数据从第几行开始
*/
private int dataRowNum;
/**------------2.新增方法----------*/
/**
* 合并单元格
*/
public void mergedCell() {
int lastRowNum = sheet.getLastRowNum();
//1.判断当前是否有需要合并单元格的列,判断sheet内是否有值
if (CollectionUtil.isEmpty(this.mergeFields) || lastRowNum == 0) return;
//2.获取需要合并的字段所在第几列
Map<Integer, Integer> mergeFileIndex = getMergeFileIndex();
//3.开始读取对应的数据
//创建更新列表集合,并塞入默认值
Map<Integer, List<String>> mergeMap = new HashMap<>();
for (Map.Entry<Integer, Integer> fileIndex : mergeFileIndex.entrySet()) {
mergeMap.put(fileIndex.getKey(), new ArrayList<>());
}
lastRowNum += 1;
for (int i = this.dataRowNum; i < lastRowNum; i++) {
//是否是最后一行了
Integer nextIndex = i + 1;
if(nextIndex >= lastRowNum){
for (Map.Entry<Integer, Integer> fileIndex : mergeFileIndex.entrySet()) {
Integer fileEndIndex = fileIndex.getValue();
if(fileEndIndex != i) {
mergeMap.get(fileIndex.getKey()).add(fileEndIndex + "-" + i);
}
}
continue;
}
Row thisRow = sheet.getRow(i);
Row nextRow = sheet.getRow(nextIndex);
for (Map.Entry<Integer, Integer> fileIndex : mergeFileIndex.entrySet()) {
String thisValue = thisRow.getCell(fileIndex.getKey()).getStringCellValue();
String nextValue = nextRow.getCell(fileIndex.getKey()).getStringCellValue();
Integer fileEndIndex = fileIndex.getValue();
if (!StringUtils.equals(thisValue, nextValue)) {
if(fileEndIndex != i){
mergeMap.get(fileIndex.getKey()).add(fileEndIndex + "-" + i);
}
fileIndex.setValue(nextIndex);
}
}
}
//4.开始合并
for (Map.Entry<Integer, List<String>> entry : mergeMap.entrySet()) {
if (CollectionUtil.isEmpty(entry.getValue())) continue;
for (String range : entry.getValue()) {
String[] split = range.split("-");
sheet.addMergedRegion(new CellRangeAddress(Integer.parseInt(split[0]), Integer.parseInt(split[1]),
entry.getKey(), entry.getKey()));
}
}
}
/**
* 获取需要合并的字段所在第几列
*/
public Map<Integer, Integer> getMergeFileIndex() {
Set<String> mergeFieldSet = new HashSet<String>();
Row titleRow = this.sheet.getRow(dataRowNum - 1);
for (Object[] mergeField : this.mergeFields) {
Excel attr = (Excel) mergeField[1];
mergeFieldSet.add(attr.name());
}
// 定义一个map用于存放列的位置与数据索引位置
Map<Integer, Integer> mergeFieldIndexSet = new HashMap<>();
for (int i = 0; i < titleRow.getPhysicalNumberOfCells(); i++) {
Cell cell = titleRow.getCell(i);
if (StringUtils.isNull(cell)) continue;
String headerName = String.valueOf(this.getCellValue(titleRow, i));
if (mergeFieldSet.contains(headerName)) mergeFieldIndexSet.put(i, this.dataRowNum);
}
return mergeFieldIndexSet;
}
/**------------3.调用该方法,在工具类内的writeSheet()该方法内进行改动----------*/
/**
* 创建写入数据到Sheet
*/
public void writeSheet() {
// 取出一共有多少个sheet.
int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
for (int index = 0; index < sheetNo; index++) {
createSheet(sheetNo, index);
// 产生一行
Row row = sheet.createRow(rownum);
dataRowNum = rownum + 1;
//创建需要合并的集合
int column = 0;
// 写入各个字段的列头名称
for (Object[] os : fields) {
Excel excel = (Excel) os[1];
this.createCell(excel, row, column++);
if(excel.isMerge()) mergeFields.add(os);
}
if (Type.EXPORT.equals(type)) {
fillExcelData(index, row);
addStatisticsRow();
mergedCell();
}
}
}
}
3.在你需要导出的实体类上进行引用 (如下)
/**
* 顺序号
*/
@Excel(name = "顺序号",isMerge = true)
private Long serialNumber;
4.效果展示
注意:
代码还有待完善,目前没有经历过大批量数据的合并测试