- 学习地址:https://d9bp4nr5ye.feishu.cn/wiki/O3obweIbgi2Rk1ksXJncpClTnAf
- B站视频:https://www.bilibili.com/video/BV1H34y1T7Lm
先来看看最终实现效果,如果效果是你想要的,再看看实现逻辑。
EasyExcel本身是支持设置下拉校验的,但有个问题,一旦下拉数据超过50个左右的时候就无法正常展示,当然,现在这个问题得到了解决。
来看整体的项目目录
如果你之前使用过EasyExcel那上面主流程代码你将会很熟悉,下拉选相较于之前的导出只是多注册了一个 Handler。
一、主流程代码
@RestController
@RequestMapping("/one")
public class OneController {
// 测试数据构建
private static List<String> countryList = Arrays.asList("中国","美国","俄罗斯","德国","日本");
private static List<String> cityList = Arrays.asList("深圳","广州","上海","北京","纽约","莫斯科","东京");
private static Map<String, List> dropDownData = new HashMap<>(2);
private static List<SheetOneVO> cityEntityList = new ArrayList<>(cityList.size());
static {
dropDownData.put("country", countryList);
dropDownData.put("city", cityList);
for (String item : cityList) {
cityEntityList.add(new SheetOneVO(item));
}
}
@GetMapping("/kk-one")
public void one(HttpServletResponse response) throws Exception {
// 通用内容设置
String fileName = URLEncoder.encode("templateOne.xlsx",CharEncoding.UTF_8);
response.setContentType("application/octet-stream");
response.setCharacterEncoding(CharEncoding.UTF_8);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
// 构建模板数据
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
WriteSheet writeSheet = EasyExcel
.writerSheet(0, "one")
.registerWriteHandler(new XdxCellWriteHandler(dropDownData))
.head(TemplateOneVO.class)
.build();
WriteSheet citySheet = EasyExcel
.writerSheet(1, "city")
.head(SheetOneVO.class)
.needHead(false)
.build();
excelWriter.write(Arrays.asList(new TemplateOneVO("张三","美国", "胡佛")), writeSheet)
.write(cityEntityList, citySheet).
finish();
}
}
二、处理器代码
- 这个handler重写了单元格拦截器,在遍历表头的时候,设置下拉选。
- 正常是超过50个下拉才会出问题,这里懒得造那么多数据所以设置了一个LIMIT,超过它就会换成联动sheet方式,从而就解决了个数限制的问题。
public class XdxCellWriteHandler implements CellWriteHandler {
/**
* 超过 LIMIT 的大小就使用 sheet关联下拉,否则直接设置下拉
*/
private static final Integer LIMIT = 6;
private Map<String, List> dropDownData;
public XdxCellWriteHandler(Map<String, List> dropDownData) {
this.dropDownData = dropDownData;
}
/**
* 设置下拉框数据
* @param writeSheetHolder
* @param key 当前列名
* @param rowIndex 行号
* @param columnIndex 列号
*/
private void setSelectDataList(WriteSheetHolder writeSheetHolder, String key, int rowIndex, int columnIndex) {
if (dropDownData.get(key) == null) {
return;
}
Sheet sheet = writeSheetHolder.getSheet();
DataValidationHelper helper = sheet.getDataValidationHelper();
// 设置下拉列表的行: 首行,末行,首列,末列
CellRangeAddressList rangeList = new CellRangeAddressList(rowIndex, 50000, columnIndex, columnIndex);
// 设置下拉列表的值
DataValidationConstraint constraint;
if (dropDownData.get(key).size() < LIMIT) {
// 直接设置下拉选
constraint = helper.createExplicitListConstraint((String[]) dropDownData.get(key).toArray(new String[0]));
} else {
// 联动到另外一个 sheet
constraint = helper.createFormulaListConstraint(key+ "!$A$1:$A$" + dropDownData.get(key).size());
}
// 设置约束
DataValidation validation = helper.createValidation(constraint, rangeList);
// 阻止输入非下拉选项的值
validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
validation.setShowErrorBox(true);
validation.setSuppressDropDownArrow(true);
validation.createErrorBox("提示", "请输入下拉选项中的内容");
sheet.addValidationData(validation);
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList, Cell cell, Head head,
Integer relativeRowIndex, Boolean isHead) {
// 设置隐藏 sheet
WriteSheet writeSheet = writeSheetHolder.getWriteSheet();
if (writeSheet.getSheetNo() > 0) {
Workbook workbook = writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook();
workbook.setSheetHidden(writeSheet.getSheetNo(), true);
return;
}
if (!isHead) {
//设置value下拉框
setSelectDataList(writeSheetHolder, head.getFieldName(), cell.getRowIndex(), cell.getColumnIndex());
}
}
}
三、两个VO
TemplateOneVO
@Data
@AllArgsConstructor
public class TemplateOneVO {
@ExcelProperty("姓名")
private String name;
@ExcelProperty("国家")
private String country;
@ExcelProperty("城市")
private String city;
}
SheetOneVO
@Data
@AllArgsConstructor
public class SheetOneVO {
private String key;
}