恭喜小花生夺得LCK冠军!
前言
在最近的一次迭代中 ,因为业务频繁的提增加标签的需求,再也受不了了于是跟产品说把标签做成了一个可配置的系统,配置好即可对物资需求的物料进行打标,大概的数据结构是如下图这样的:
底层就是一棵多叉树,每个标签分类下可以配置本树的层级,可以无限配置标签分类和每个标签的深度,因为每个物料都需要展示出各自绑定的标签,这样会导致一个情况,列表展示的列不是固定的,加上这个配置可以无限延伸,在前端展示比较好处理,但是在excel上比较繁琐,所以记录一下动态列的扩展方法: 这是之前的代码,就是很普通的easyExcel写:
EasyExcel.write(filePathName, BoardGlobalResponse.class)
.sheet()
.doWrite(retBoardGlobalResponseList);
在对标签列进行改造之后,数据库将所有的标签进行了聚合采用了json结构,效果如下:
{
"tags": [
{
"tagId": 23,
"value": "正常盯盘池子-不盯盘-/",
"tcCode": "物料分类",
"tcName": "物料分类",
"tagName": "/",
"tagIdPath": "8-12-23"
},
{
"tagId": 138,
"value": "测试多分类1",
"tcCode": "物料分级",
"tcName": "物料分级",
"tagName": "/",
"tagIdPath": "138"
}
]
}
因为有多个分类都冗余在了一个字段中,所以在导出的时候,需要将该结构打平成Map方式,每个对象的map里key是标题,value是标签值,在充血模型里加入转换方法:
@ExcelIgnore
// 动态列
private Map<String, String> dynamicColumns = new HashMap<>();
public void setTagInfo(TagInfoDto tagInfo) {
this.tagInfo = tagInfo;
if (tagInfo == null) {
return;
}
Map<String, String> maps = new HashMap<>();
tagInfo.getTags().forEach(tag -> {
String tagPath = tag.getValue();
if (StringUtils.isBlank(tagPath)) {
return;
}
String[] split = tagPath.split("-");
for (int i = 1; i <= split.length; i++) {
maps.put(String.format(tag.getTcCode() + "(%s级标签)", i), split[i - 1
]);
}
this.dynamicColumns = maps;
});
}
由于我的物料很多,每个物料都会有自己所绑定的标签分类,数量各不相同,为了统一列,我把数据库中所有的配置分类都拿了出来:
private List<String> getDynamicHead() {
// 获取header
List<TagCategoryDO> categoryDOList = tagCategoryMapper.allTagCategory();
List<String> list = new ArrayList<>();
categoryDOList.forEach(category ->{
for (int i = 1; i <= category.getLevelNumb(); i++) {
list.add(String.format(category.getTcCode() + "(%s级标签)", i));
}
});
return list;
}
接下来实现自定义的行处理器,对字段往后进行拼接:
public class DynamicColumnsWriteHandler implements RowWriteHandler {
private final List<String> dynamicHeaders;
private Integer headerSize = 0;
private final List<BoardGlobalResponse> dataList;
public DynamicColumnsWriteHandler(List<String> dynamicHeaders, List<BoardGlobalResponse> dataList) {
this.dynamicHeaders = dynamicHeaders;
this.dataList = dataList;
}
@Override
public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex,
Integer relativeRowIndex, Boolean isHead) {}
@Override
public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {}
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
// 设置头
if (Boolean.TRUE.equals(isHead)) {
// 获取偏移列数
int lastRowNum = row.getLastCellNum() < 0 ? 0 : row.getLastCellNum();
headerSize = lastRowNum + dynamicHeaders.size();
// 获取头部列的样式模版
Cell cellTemplate = row.getCell(relativeRowIndex);
// 列后追加
for (int i = 0; i < dynamicHeaders.size(); i++) {
Cell cell = row.createCell(lastRowNum + i );
cell.setCellValue(dynamicHeaders.get(i));
cell.setCellStyle(cellTemplate.getCellStyle());
}
}
if (Boolean.FALSE.equals(isHead)) {
if (relativeRowIndex < dataList.size()) {
BoardGlobalResponse dto = dataList.get(relativeRowIndex);
// 在表头后面追加
Map<String, String> dynamicColumns = dto.getDynamicColumns();
int lastCellIndex = row.getLastCellNum() <= 0 ? 0 : row.getLastCellNum();
for (int i = 0; i < dynamicHeaders.size(); i++) {
Cell cell = row.createCell(lastCellIndex + i);
String value = dynamicColumns.get(dynamicHeaders.get(i));
cell.setCellValue(value == null ? "": value);
}
}
}
}
}
这里主要是对表头和每行的value值进行特殊的拼接,因为这一步已经是在带有@ExcelProperty注解处理之后的处理,所以往后拼接就可以,但是要注意拼接的位置,easyExcel的拼接是从0作为索引开始的,在外层改造之后的结构如下:
List<String> dynamicHead = getDynamicHead();
String filePathName = "globalDashBoard-" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(filePathName, BoardGlobalResponse.class).sheet()
.registerWriteHandler(new DynamicColumnsWriteHandler(dynamicHead, retBoardGlobalResponseList))
.doWrite(retBoardGlobalResponseList);
最后测试结果如下:
现在写出来也没啥难的,主要是刚开始的动态列让人脑袋头大,所以记录一下,没啥东西,如果对你有用还请给我个小小的赞 ,或者你有更好的方法也可以留言评论~