一、效果示範及相關說明
由于項目資訊不能洩露,這裡采用測試資料,下面的測試資料是手動輸入的,僅用來輔助說明下面的解釋
測試資料原始效果

合并後的效果
二、首先建立注解類。
作用:加載導出字段上,order 表示分組次數。0表示一次分組,1表示二次分組,依次類推
isflag 表示分組政策,如果為true,則用來這個字段作為目前合并的依據,是以和該字段order相同的字段都會合并
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author hhb
* @date :2021/9/16 13:58
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MergeFlag {
int order() default 0;
boolean isflag() default false;
}
二、自定義合并政策
這裡使用的是alibabaEasyExcel的Excel處理架構,相關導出、導入實作如果不懂請檢視官方文檔
https://www.yuque.com/easyexcel/doc/easyexcel
根據官方提供的單次合并政策OnceAbsoluteMergeStrategy可知,實作合并的核心代碼如下
CellRangeAddress cellRangeAddress = new CellRangeAddress(this.firstRowIndex, this.lastRowIndex, this.firstColumnIndex, this.lastColumnIndex);
writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress);
是以我們接下來需要想辦法先知道那些資料是需要合并的,是以我們要預處理導出的資料.
然後根據資料上面的注解解析出一個合并map,map的key是 order,value是一個對象,包含通過反射得到的Field[]數組的index索引、合并辨別。
詳細見代碼
/**
* 生成合并政策Map
* @param dataList
* @return
*/
private void generateMergeMap(List dataList,Class<?> type){
//解析分組依據
List<MergeFlagDTO> mergeFlagDTOList = this.analyzeData(type);
Object preObj=null;
for (int i=0;i<dataList.size();i++){
//擷取合并表示
Object currObj=dataList.get(i);
if (null!=preObj){
//處理資料
try {
this.handData(0,mergeFlagDTOList,currObj,preObj,i);
} catch (IllegalAccessException e) {
throw new BadRequestAlertException("導出時發生解析錯誤!");
}
}
preObj=currObj;
}
}
/**
* 解析資料
* @param type
* @return
*/
private List<MergeFlagDTO> analyzeData(Class<?> type) {
Map<Integer, MergeFlagDTO> mergeOrderMap=new HashMap<>();
Field[] fields = type.getDeclaredFields();
//擷取注解清單
for (int i=0;i<fields.length;i++){
MergeFlag mergeFlag = fields[i].getAnnotation(MergeFlag.class);
if (null!=mergeFlag){
//加入索引
MergeFlagDTO dto =mergeOrderMap.get(mergeFlag.order())==null?new MergeFlagDTO():mergeOrderMap.get(mergeFlag.order());
dto.getMergeIndexs().add(i);
dto.setOrder(mergeFlag.order());
if (mergeFlag.isflag()){
dto.setField(fields[i]);
}
mergeOrderMap.put(mergeFlag.order(),dto);
}
}
return new ArrayList<>(mergeOrderMap.values()).stream().filter(item -> null != item.getField()).sorted(Comparator.comparing(MergeFlagDTO::getOrder)).collect(Collectors.toList());
}
/**
* 處理資料
* @param start
* @param mergeFlagDTOList
* @param currObj
* @param preObj
* @param index
* @throws IllegalAccessException
*/
private void handData(int start, List<MergeFlagDTO> mergeFlagDTOList, Object currObj, Object preObj, int index) throws IllegalAccessException {
if (start<mergeFlagDTOList.size()){
MergeFlagDTO dto = mergeFlagDTOList.get(start);
Field field = dto.getField();
field.setAccessible(true);
Object currValue = field.get(currObj);
Object preValue = field.get(preObj);
if (currValue.equals(preValue)) {
this.fillMergeMap(dto.getMergeIndexs(),index);
//繼續遞歸
this.handData(start+1,mergeFlagDTOList,currObj,preObj,index);
}
}
}
/**
* 填充合并map
* @param key
* @param index
*/
private void fillMergeMap(Integer key, Integer index){
List<RowRangeDTO> rangeDTOS = mergeMap.get(key)==null?new ArrayList<>():mergeMap.get(key);
//判斷是否需要新加
boolean needAdd=true;
//周遊
for (RowRangeDTO rangeDTO:rangeDTOS){
if (rangeDTO.getEndIndex().equals(index)){
rangeDTO.setEndIndex(index+1);
needAdd=false;
}
}
//如果沒有比對上的就說明需要
if (needAdd){
rangeDTOS.add(new RowRangeDTO(index,index+1));
}
mergeMap.put(key,rangeDTOS);
}
最後根據合并map對資料進行合并操作
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer integer){
//每一個cell隻合并一次
if (cell.getRowIndex()==1&&cell.getColumnIndex()==0){
mergeMap.keySet().forEach(key-> mergeMap.get(key).forEach(rowRangeDTO -> sheet.addMergedRegionUnsafe(new CellRangeAddress(rowRangeDTO.getStartIndex(),rowRangeDTO.getEndIndex(),key,key))));
}
}