maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
数据结构与element表格数据结构类似 多级表头使用children渲染 合并行或列通过cols和rows控制
关键代码
try (OutputStream out = new FileOutputStream(file)) {
EasyExcel.write(out).head(excelHead(tableVO.getTableHead())).sheet("Sheet1")
.registerWriteHandler(new MyMergeStrategy(tableVO))
.registerWriteHandler(new CustomCellWriteUtil())
.doWrite(excelData(tableVO));
} catch (IOException e) {
e.printStackTrace();
}
private List<List<String>> excelHead(List<TableHeadVO> headVOS) {
boolean needMerge = false;
for (TableHeadVO headVO : headVOS) {
if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
needMerge = true;
}
}
List<List<String>> headList = new ArrayList<>();
for (TableHeadVO headVO : headVOS) {
if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
headVO.getChildren().forEach(child -> {
List<String> head = new ArrayList<>();
head.add(headVO.getLabel());
head.add(child.getLabel());
headList.add(head);
});
} else {
List<String> head = new ArrayList<>();
head.add(headVO.getLabel());
if (needMerge) {
head.add(headVO.getLabel());
}
headList.add(head);
}
}
return headList;
}
创建数据
private List<List<Object>> excelData(TableVO tableVO) {
int maxExcel = 32767;
List<List<Object>> list = new ArrayList<>();
if (Objects.isNull(tableVO) || CollectionUtils.isEmpty(tableVO.getTableData())) {
return list;
}
List<TableHeadVO> heads = tableVO.getTableHead();
for (Map<String, Object> map : tableVO.getTableData()) {
List<Object> data = new ArrayList<>();
for (TableHeadVO head : heads) {
if (CollectionUtils.isNotEmpty(head.getChildren())) {
head.getChildren().forEach(child -> {
Object value = map.get(child.getProp());
if ((value + "").length() >= maxExcel) {
data.add((value + "").substring(0, 255) + "字符过长");
} else {
data.add(value);
}
});
} else {
Object value = map.get(head.getProp());
if ((value + "").length() >= maxExcel) {
data.add((value + "").substring(0, 255) + "字符过长");
} else {
data.add(value);
}
}
}
list.add(data);
}
return list;
}
表格数据
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TableVO {
@ApiModelProperty("表头信息")
private List<TableHeadVO> tableHead;
@ApiModelProperty("表格数据信息")
private List<Map<String, Object>> tableData;
@ApiModelProperty("总数据")
private long total;
@ApiModelProperty("总页数")
private long totalPage;
@ApiModelProperty("当前页:从1开始")
private long pageIndex;
@ApiModelProperty("页大小")
private long pageSize;
public TableVO(List<TableHeadVO> tableHead, List<Map<String, Object>> tableData) {
this.tableHead = tableHead;
this.tableData = tableData;
}
public static TableVO of(PageResult result) {
TableVO tableVO = new TableVO();
tableVO.setPageIndex(result.getPageIndex());
tableVO.setPageSize(result.getPageSize());
tableVO.setTotal(result.getTotalCount());
tableVO.setTotalPage(result.getTotalPage());
return tableVO;
}
}
表头对象
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("表头")
@Data
public class TableHeadVO {
@ApiModelProperty("是否合并")
private boolean merge = true;
@ApiModelProperty("表格的字段")
private String prop;
@ApiModelProperty("表格字段名称")
private String label;
@ApiModelProperty("字段类型:input,textarea,number,radio,checkbox,time,date,rate,color,select,switch,slider,text,link,imgupload,fileupload,table,grid,report,divider")
private String type;
@ApiModelProperty("是否为导出excel字段")
private Boolean excel;
@ApiModelProperty("宽度")
private String width;
@ApiModelProperty("后台自定义")
private Boolean custom = false;
@ApiModelProperty("子表单")
private List<TableHeadVO> children;
public TableHeadVO(String prop, String label) {
this.prop = prop;
this.label = label;
this.type = "input";
this.excel = true;
this.width = "auto";
}
}
最效果
数据合并
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Slf4j
public class MyMergeStrategy extends AbstractMergeStrategy {
private List<CellRangeAddress> cellRangeAddresses;
public MyMergeStrategy(TableVO tableVO) {
List<CellRangeAddress> list = new ArrayList<>();
boolean needMerge = false;
for (TableHeadVO headVO : tableVO.getTableHead()) {
if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
needMerge = true;
}
}
if (needMerge) {
List<TableHeadVO> heads = new ArrayList<>();
for (TableHeadVO headVO : tableVO.getTableHead()) {
if (CollectionUtils.isNotEmpty(headVO.getChildren())) {
for (TableHeadVO child : headVO.getChildren()) {
heads.add(child);
}
} else {
heads.add(headVO);
}
}
if (CollectionUtils.isNotEmpty(tableVO.getTableData())) {
for (int i = 0; i < tableVO.getTableData().size(); i++) {
Map<String, Object> data = tableVO.getTableData().get(i);
System.out.println("第" + i + "行数据" + data);
for (int j = 0; j < heads.size(); j++) {
TableHeadVO head = heads.get(j);
int cols = (int) data.getOrDefault("cols", 1);
int rows = (int) data.getOrDefault("rows", 1);
if (rows > 1 && head.isMerge()) {
System.out.print(head.getLabel());
log.info("{}, {} , {}, {}", i + 1 + 1, i + 1 + rows, j, j);
CellRangeAddress item = new CellRangeAddress(i + 1 + 1, i + 1 + rows, j, j);
list.add(item);
}
}
System.out.println();
}
}
}
this.cellRangeAddresses = list;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (cell.getRowIndex() == 2 && cell.getColumnIndex() == 0) {
for (CellRangeAddress item : cellRangeAddresses) {
sheet.addMergedRegion(item);
}
}
}
}
列宽自适应
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* excel自适应列宽 拉过去就能直接套用自动适应列宽
*/
public class CustomCellWriteUtil extends AbstractColumnWidthStyleStrategy {
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);
public CustomCellWriteUtil() {
}
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
boolean needSetWidth = isHead;
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
if (maxColumnWidthMap == null || maxColumnWidthMap.isEmpty()) {
maxColumnWidthMap = new HashMap(16);
CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
}
Integer columnWidth = this.dataLength(cellDataList, cell, true);
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(), (int) ((columnWidth + 0.72) * 256));
}
}
}
}
private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData cellData = cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
} 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 -1;
}
}
}
}
}
参考文献
easyexcel(十一):easyexcel动态表头,表头合并 EasyExcel 动态表头 + 数据单元格合并
|