导入excel功能,都需要对应的模板导入,也有动态的表头数据导入,可以看excel行数据不规则解析匹配。这里一般都是固定excel文件,存放在项目路径下面,下载模板的时候都是找对应的文件。
@GetMapping("/downloadTemplate")
public void downloadTemplate(HttpServletResponse response) {
String fileName = "导入模板.xlsx";
ClassPathResource classPathResource = new ClassPathResource("template/file.xlsx");
try (InputStream inputStream = classPathResource.getInputStream();
OutputStream out = response.getOutputStream();) {
setResponseAttribute(fileName, response);
Map<String, String> map = new HashMap<>(4);
int year = DateUtil.thisYear();
map.put("lastYear", String.valueOf(year - 1));
map.put("currentYear", String.valueOf(year));
ExcelWriterBuilder writerBuilder = EasyExcel.write(out).withTemplate(inputStream);
//这边第二个sheet页面里面有个参数需要动态生成,指定了往第二个sheet页里面填充值
writerBuilder.sheet(1).doFill(map);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
一般导出数据固定的话都可以使用对象导入,建立对应的对象,使用EasyExcel中相应的注解,对Excel中单元格样式,字体样式,背景颜色等等都可以实现。
/**
*
* @author 洛必达法则不会
* @since 2021-07-22
*/
@Data
@HeadStyle(horizontalAlignment = HorizontalAlignment.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignment.CENTER)
@HeadFontStyle(fontHeightInPoints = 10, fontName = "微软雅黑")
@ContentFontStyle(fontHeightInPoints = 10)
@HeadRowHeight(18)
@ColumnWidth(14)
public class TalentSituationHeadData {
/**
* 年份
*/
@ColumnWidth(8)
@ExcelProperty(value = "年份", index = 0)
private String year;
/**
* 事业部
*/
@ExcelProperty(value = "事业分部", index = 1)
private String deptName;
/**
* 待招岗位
*/
@ExcelProperty(value = "待招岗位", index = 2)
private String recruitedPost;
/**
* 姓名
*/
@HeadStyle(fillForegroundColor = 44)
@ExcelProperty(value = "姓名", index = 3)
private String name;
}
本次功能导入需要导出一个Excel包含多个sheet页,其中一个sheet页面的表头需要根据时间动态生成,直接使用@ExcelProperty注解,value值都是固定的。
// 首先尝试的通过 WriteSheet中head属性获取表头的字段,再修改里面对应的值,实际运行中属性值是null,此方法不可以
WriteSheet potentialCustomerSheet = EasyExcel.writerSheet(index,sheetNameEnum.getName()).head(PotentialCustomerHeadData.class).build();
List<List<String>> headList = potentialCustomerSheet.getHead();
//另一种就是重新构造List> headList,通过EasyExcel.write(fileName).head(headList).sheet("模板");写入表头,这种方式在对象的字体颜色注解就没有,需要重新构造对应的格式,也比较繁琐
List<List<String>> headList = buildHeadDataList();
EasyExcel.write(fileName).head(headList).sheet("模板");
//之前的逻辑写好,不想用上面的方法,后面就采用反射的方式,拿到对应属性上面的注解,修改里面的值再通过EasyExcel.writerSheet写入
String year = StrUtil.isNotBlank(param.getStartDate())
? param.getStartDate().substring(0, 4)
: Integer.toString(DateUtil.thisYear());
Field yearEstimateField = PotentialCustomerHeadData.class.getDeclaredField("yearEstimate");
yearEstimateField.setAccessible(true);
ExcelProperty excel = yearEstimateField.getAnnotation(ExcelProperty.class);
InvocationHandler excelH = Proxy.getInvocationHandler(excel);
// 获取 AnnotationInvocationHandler 的 memberValues 字段
Field excelF = excelH.getClass().getDeclaredField("memberValues");
excelF.setAccessible(true);
Map excelValues = (Map) excelF.get(excelH);
excelValues.put("value", new String[]{"项目利润", String.format("%s预估\n(万元/年)", year)});
WriteSheet potentialCustomerSheet = EasyExcel.writerSheet(index, sheetNameEnum.getName()).head(PotentialCustomerHeadData.class).build();
刚好一个表单导出的时候,需要实现单元格合并,实现AbstractMergeStrategy类的方法,具体合并策略根据业务需求实现。
//策略类
public class CellMergeStrategy extends AbstractMergeStrategy {
private Map<String, List<RowRange>> strategyMap;
private Sheet sheet;
public CellMergeStrategy() {
}
public CellMergeStrategy(Map<String, List<RowRange>> strategyMap) {
this.strategyMap = strategyMap;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer integer) {
this.sheet = sheet;
if (cell.getRowIndex() == 1 && cell.getColumnIndex() == 0) {
/**
* 保证每个cell被合并一次,如果不加上面的判断,因为是一个cell一个cell操作的,
* 例如合并A2:A3,当cell为A2时,合并A2,A3,但是当cell为A3时,又是合并A2,A3,
* 但此时A2,A3已经是合并的单元格了
*/
for (Map.Entry<String, List<RowRange>> entry : strategyMap.entrySet()) {
int columnIndex = Integer.parseInt(entry.getKey());
entry.getValue().forEach(rowRange -> {
//添加一个合并请求
sheet.addMergedRegionUnsafe(new CellRangeAddress(rowRange.getStart(),
rowRange.getEnd(), columnIndex, columnIndex));
});
}
}
}
}
/**
* Excel工具类
*/
public class ExcelUtil {
//获取需要合并的单元格
public static Map<String, List<RowRange>> getProfitReachedMergeStrategy(List<ProfitReachedHeadData> profitReachedHeadDataList) {
Map<String, List<RowRange>> strategyMap = new HashMap<>();
ProfitReachedHeadData previousData = null;
int size = profitReachedHeadDataList.size();
for (int i = 0; i < size; i++) {
ProfitReachedHeadData currentData = profitReachedHeadDataList.get(i);
if (previousData != null) {
if (currentData.getDeptName().equals(previousData.getDeptName())) {
fillStrategyMap(strategyMap, "0", i);
}
if (currentData.getOrgName().equals(previousData.getOrgName())) {
fillStrategyMap(strategyMap, "1", i);
}
}
previousData = currentData;
}
return strategyMap;
}
/**
* 增加合并策略
* @param strategyMap Map>
* @param key String
* @param index int
*/
private static void fillStrategyMap(Map<String, List<RowRange>> strategyMap, String key, int index) {
List<RowRange> rowRangeList = strategyMap.get(key) == null ? new ArrayList<>() : strategyMap.get(key);
boolean flag = false;
for (RowRange rowRange : rowRangeList) {
//分段list中是否有end索引是上一行索引的,如果有,则索引+1
if (rowRange.getEnd() == index) {
rowRange.setEnd(index + 1);
flag = true;
}
}
//如果没有,则新增分段
if (!flag) {
rowRangeList.add(new RowRange(index, index + 1));
}
strategyMap.put(key, rowRangeList);
}
public class RowRange {
private int start;
private int end;
}
}
//业务实现
List<ProfitReachedHeadData> profitSummaryHeadDataList = profitSummaryService.buildProfitReachedHeadDataList(entry.getValue());
Map<String, List<RowRange>> summaryStrategyMap = ExcelUtil.getProfitReachedMergeStrategy(profitSummaryHeadDataList);
WriteSheet profitSummarySheet = EasyExcel.writerSheet(index, String.format("%s-%s合计-%s月", sheetNameEnum.getName(),permissionOperate.getDeptName(),entry.getKey().substring(5))).registerWriteHandler(new CellMergeStrategy(summaryStrategyMap)).head(ProfitReachedHeadData.class).build();
excelWriter.write(profitSummaryHeadDataList, profitSummarySheet);