Skip to content

Commit

Permalink
feat: ExcelUtils功能,添加自定义单元格合并规则接口。
Browse files Browse the repository at this point in the history
  • Loading branch information
wangliang181230 committed Jan 7, 2025
1 parent 64eda17 commit 2001856
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import icu.easyj.poi.excel.hook.ICellMerger;
import icu.easyj.poi.excel.hook.NoneCellMerger;

/**
* excel表格的列对应属性的注解
*
Expand Down Expand Up @@ -58,6 +61,9 @@
// 格式化(应用于Date数据、浮点数据等等的格式化输出)
String format() default "";

// 合并单元格控制器
Class<? extends ICellMerger<?>> cellMergerClass() default NoneCellMerger.class;


// 显示效果相关属性

Expand All @@ -81,4 +87,5 @@

// 是否隐藏列
boolean hidden() default false;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package icu.easyj.poi.excel.hook;

import icu.easyj.poi.excel.model.ExcelCellMapping;

public interface ICellMerger<T> {

/**
* 是否合并单元格
*
* @param cellMapping 当前单元格
* @param previous 上一条数据
* @param current 当前数据
* @return 是否合并:true=合并 | false=不合并
*/
boolean needMerge(ExcelCellMapping cellMapping, T previous, T current);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package icu.easyj.poi.excel.hook;

import icu.easyj.poi.excel.model.ExcelCellMapping;

public class NoneCellMerger implements ICellMerger<Object> {

@Override
public boolean needMerge(ExcelCellMapping cellMapping, Object previous, Object current) {
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import icu.easyj.poi.excel.annotation.Excel;
import icu.easyj.poi.excel.annotation.ExcelCell;
import icu.easyj.poi.excel.annotation.ExcelCells;
import icu.easyj.poi.excel.hook.ICellMerger;
import icu.easyj.poi.excel.style.ExcelFormats;
import icu.easyj.poi.excel.util.ExcelColorUtils;
import org.apache.commons.lang3.ArrayUtils;
Expand Down Expand Up @@ -68,6 +69,8 @@ public class ExcelCellMapping implements Serializable {
private String falseText; // boolean型数据为false时,显示的文字
private Map<String, String> convertMap; // 值转换
private Map<String, String> convertMap2; // 值反向转换
private Class<? extends ICellMerger<?>> cellMergerClass;


//******************************** getter and setter *********************************/

Expand Down Expand Up @@ -231,6 +234,18 @@ public void setConvertMap2(Map<String, String> convertMap2) {
this.convertMap2 = convertMap2;
}

public Class<? extends ICellMerger<?>> getCellMergerClass() {
return cellMergerClass;
}

public void setCellMergerClass(Class<? extends ICellMerger<?>> cellMergerClass) {
this.cellMergerClass = cellMergerClass;
}

public String getColumnFromFieldOrColumn() {
return StringUtils.isNotBlank(column) ? column : field.getName();
}


//******************************** static *********************************/

Expand Down Expand Up @@ -382,6 +397,7 @@ public static ExcelCellMapping toMapping(ExcelCell anno, Class<?> clazz, Field f
cellMapping.setConvertMap(convertMap);
cellMapping.setConvertMap2(convertMap2);
}
cellMapping.setCellMergerClass(anno.cellMergerClass());

// 处理映射信息,初始化部分情况的映射内容
if (StringUtils.isEmpty(cellMapping.getFormat())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import icu.easyj.core.util.ArrayUtils;
import icu.easyj.core.util.ReflectionUtils;
import icu.easyj.core.util.StringUtils;
import icu.easyj.poi.excel.hook.ICellMerger;
import icu.easyj.poi.excel.hook.NoneCellMerger;
import icu.easyj.poi.excel.model.ExcelCellMapping;
import icu.easyj.poi.excel.model.ExcelMapping;
import org.apache.poi.ss.usermodel.Cell;
Expand Down Expand Up @@ -336,50 +338,105 @@ public static void createDataRows(Sheet sheet, List<?> dataList, ExcelMapping ma
/**
* @since 0.7.8
*/
public static void mergeSameCells(Sheet sheet, ExcelMapping mapping) {
if (ArrayUtils.isEmpty(mapping.getMergeSameCells())) {
return; // 未定义需要合并的单元格
public static void mergeSameCells(Sheet sheet, List<?> dataList, ExcelMapping mapping) {
if (dataList.size() < 2) {
return;
}

String[] mergeFieldNames = mapping.getMergeSameCells();
int[] mergeCellNums = getMergeCellNums(mapping, mergeFieldNames);
if (ArrayUtils.isNotEmpty(mapping.getMergeSameCells())) {
//region 方案1:指定列合并单元格

// 列号进行排序
Arrays.sort(mergeCellNums);
String[] mergeFieldNames = mapping.getMergeSameCells();
int[] mergeCellNums = getMergeCellNums(mapping, mergeFieldNames);

if (mapping.isNeedNumberCell()) {
for (int i = 0; i < mergeCellNums.length; i++) {
mergeCellNums[i]++;
// 列号进行排序
Arrays.sort(mergeCellNums);

if (mapping.isNeedNumberCell()) {
for (int i = 0; i < mergeCellNums.length; i++) {
mergeCellNums[i]++;
}
}
}

int mergeStartRowNum = -1;
int mergeEndRowNum = -1;
int startRowNum = mapping.isNeedHeadRow() ? 2 : 1;
for (int i = startRowNum; i < sheet.getPhysicalNumberOfRows(); i++) {
if (isSameCells(sheet.getRow(i - 1), sheet.getRow(i), mergeCellNums)) {
if (mergeStartRowNum == -1) {
mergeStartRowNum = i - 1;
mergeEndRowNum = i;
} else {
mergeEndRowNum++;
int mergeStartRowNum = -1;
int mergeEndRowNum = -1;
int startRowNum = mapping.isNeedHeadRow() ? 2 : 1;
for (int i = startRowNum; i < sheet.getPhysicalNumberOfRows(); i++) {
if (isSameCells(sheet.getRow(i - 1), sheet.getRow(i), mergeCellNums)) {
if (mergeStartRowNum == -1) {
mergeStartRowNum = i - 1;
mergeEndRowNum = i;
} else {
mergeEndRowNum++;
}
continue;
}

if (mergeStartRowNum >= 0) {
for (int mergeCellNum : mergeCellNums) {
sheet.addMergedRegion(new CellRangeAddress(mergeStartRowNum, mergeEndRowNum, mergeCellNum, mergeCellNum));
}
mergeStartRowNum = -1;
mergeEndRowNum = -1;
}
continue;
}

if (mergeStartRowNum >= 0) {
for (int mergeCellNum : mergeCellNums) {
sheet.addMergedRegion(new CellRangeAddress(mergeStartRowNum, mergeEndRowNum, mergeCellNum, mergeCellNum));
}
mergeStartRowNum = -1;
mergeEndRowNum = -1;
}
}

if (mergeStartRowNum >= 0) {
for (int mergeCellNum : mergeCellNums) {
sheet.addMergedRegion(new CellRangeAddress(mergeStartRowNum, mergeEndRowNum, mergeCellNum, mergeCellNum));
//endregion 方案1:指定列合并单元格 end
} else {
//region 方案2:自定义合并单元格

for (ExcelCellMapping cellMapping : mapping.getCellMappingList()) {
if (cellMapping.getCellMergerClass() == null || cellMapping.getCellMergerClass() == NoneCellMerger.class) {
continue;
}

int cellNum = cellMapping.getCellNum() + (mapping.isNeedNumberCell() ? 1 : 0);
ICellMerger cellMerger = ReflectionUtils.getSingleton(cellMapping.getCellMergerClass());

int mergeStartRowNum = -1;
int mergeEndRowNum = -1;
int startRowNum = mapping.isNeedHeadRow() ? 2 : 1;
if (mapping.isNeedHeadRow()) {
Integer headRowNum = ExcelUtils.findHeadRowNum(sheet, 0, mapping);
if (headRowNum != null) {
startRowNum = headRowNum + 2;
}
}


for (int i = startRowNum; i < sheet.getPhysicalNumberOfRows(); i++) {
Object previous = dataList.get(i - startRowNum);
Object current = dataList.get(i - startRowNum + 1);

if (cellMerger.needMerge(cellMapping, previous, current)) {
if (mergeStartRowNum == -1) {
mergeStartRowNum = i - 1;
mergeEndRowNum = i;
} else {
mergeEndRowNum++;
}
continue;
}

if (mergeStartRowNum >= 0) {
sheet.addMergedRegion(new CellRangeAddress(mergeStartRowNum, mergeEndRowNum, cellNum, cellNum));
mergeStartRowNum = -1;
mergeEndRowNum = -1;
}
}

if (mergeStartRowNum >= 0) {
sheet.addMergedRegion(new CellRangeAddress(mergeStartRowNum, mergeEndRowNum, cellNum, cellNum));
}
}

//endregion
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public static boolean getHasNumberCell(Sheet sheet, ExcelMapping mapping) {
* @return 头行号
*/
@Nullable
private static Integer findHeadRowNum(Sheet sheet, int firstRowNum, ExcelMapping mapping) {
static Integer findHeadRowNum(Sheet sheet, int firstRowNum, ExcelMapping mapping) {
// 只检测前3行
int i = 0;
while (i < 3) {
Expand Down Expand Up @@ -261,7 +261,7 @@ private static Sheet generateSheet(Workbook book, List<?> dataList, ExcelMapping
// 创建数据行
ExcelRowUtils.createDataRows(sheet, dataList, mapping);
// 合并单元格 @since 0.7.8
ExcelRowUtils.mergeSameCells(sheet, mapping);
ExcelRowUtils.mergeSameCells(sheet, dataList, mapping);
}

// 触发勾子:afterCreateDataRows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ public void testMapToExcel() throws Exception {
// list to excel
List<TestClass> list = new ArrayList<>();
list.add(new TestClass("aaa", 1, 0, new Date(2020 - 1900, 1 - 1, 1), "desc111", Long.parseLong(Integer.MIN_VALUE + ""), 1.1D, new BigDecimal("1.1")));
list.add(new TestClass("aaa", 1, 0, new Date(2020 - 1900, 1 - 1, 1), "desc111", Long.parseLong(Integer.MIN_VALUE + ""), 1.1D, new BigDecimal("1.1")));
list.add(new TestClass("aaa", 0, 0, new Date(2020 - 1900, 1 - 1, 1), "desc111", Long.parseLong(Integer.MIN_VALUE + ""), 1.1D, new BigDecimal("1.1")));
list.add(new TestClass("aaa", 0, 0, new Date(2020 - 1900, 1 - 1, 1), "desc111", Long.parseLong(Integer.MIN_VALUE + ""), 1.1D, new BigDecimal("1.1")));
list.add(new TestClass("bbb", 2, 1, new Date(2019 - 1900, 2 - 1, 2), "desc222", Long.parseLong(Integer.MAX_VALUE + ""), -1.1D, new BigDecimal("-1.1")));
list.add(new TestClass("ccc", 3, 2, new Date(2018 - 1900, 3 - 1, 3), "desc333", Long.MIN_VALUE, -Double.MAX_VALUE, new BigDecimal(-Double.MAX_VALUE)));
list.add(new TestClass("ddd", 4, 3, new Date(2017 - 1900, 4 - 1, 4), "desc444", Long.MAX_VALUE, Double.MAX_VALUE, new BigDecimal(Double.MAX_VALUE)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package icu.easyj.poi.excel.util.hook;

import java.util.Objects;

import icu.easyj.poi.excel.hook.ICellMerger;
import icu.easyj.poi.excel.model.ExcelCellMapping;
import icu.easyj.poi.excel.util.model.TestClass;

public class MyCellMerger implements ICellMerger<TestClass> {

@Override
public boolean needMerge(ExcelCellMapping cellMapping, TestClass previous, TestClass current) {
switch (cellMapping.getColumnFromFieldOrColumn()) {
case "name":
return Objects.equals(previous.getName(), current.getName());
case "age":
return Objects.equals(previous.getName(), current.getName()) &&
Objects.equals(previous.getAge(), current.getAge());
case "bClass.age":
return Objects.equals(previous.getName(), current.getName()) &&
Objects.equals(
previous.getbClass() == null ? null : previous.getbClass().getAge(),
current.getbClass() == null ? null : current.getbClass().getAge()
);
default:
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,12 @@ public Integer getAge() {
public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "TestBClass{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import icu.easyj.poi.excel.annotation.Excel;
import icu.easyj.poi.excel.annotation.ExcelCell;
import icu.easyj.poi.excel.annotation.ExcelCustomRowConfig;
import icu.easyj.poi.excel.util.hook.MyCellMerger;
import icu.easyj.poi.excel.util.hook.TestListToExcelHook;

/**
Expand All @@ -31,18 +32,18 @@
@Excel(
toExcelHookClasses = {TestListToExcelHook.class},
customFirstRow = @ExcelCustomRowConfig(fontSize = 20, fontBold = false, rowHeight = 40, align = "left", verAlign = "top"),
showFooterRow = true,
mergeSameCells = {"name", "age", "bClass.age"}
showFooterRow = true
//mergeSameCells = {"name", "age", "bClass.age"}
)
public class TestClass {

@ExcelCell(headName = "姓名", cellNum = 0)
@ExcelCell(headName = "姓名", cellNum = 0, cellMergerClass = MyCellMerger.class)
private String name;

@ExcelCell(headName = "年龄", cellNum = 1)
@ExcelCell(headName = "年龄", cellNum = 1, cellMergerClass = MyCellMerger.class)
private Integer age;

@ExcelCell(headName = "周岁", cellNum = 2, column = "age")
@ExcelCell(headName = "周岁", cellNum = 2, column = "age", cellMergerClass = MyCellMerger.class)
private TestBClass bClass;

@ExcelCell(headName = "出生日期", cellNum = 3, format = "yyyy-MM-dd")
Expand Down

0 comments on commit 2001856

Please sign in to comment.