下拉单选框的实现
继承SheetWriteHandler接口
java
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.util.Map;
public class SpinnerWriteHandler implements SheetWriteHandler {
private Map<Integer, String[]> mapDropDown;
public SpinnerWriteHandler(Map<Integer, String[]> mapDropDown) {
this.mapDropDown = mapDropDown;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
// 空实现,不需要前置操作
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
DataValidationHelper helper = sheet.getDataValidationHelper();
for (Map.Entry<Integer, String[]> entry : mapDropDown.entrySet()) {
int columnIndex = entry.getKey(); // 列索引(从0开始)
String[] options = entry.getValue(); // 下拉选项
// 设置单元格范围:从第1行到第1000行,该列
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, columnIndex, columnIndex);
// 创建下拉约束
DataValidationConstraint constraint = helper.createExplicitListConstraint(options);
DataValidation dataValidation = helper.createValidation(constraint, addressList);
// 错误提示设置
dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
dataValidation.setShowErrorBox(true);
dataValidation.setSuppressDropDownArrow(true);
// 错误提示框内容
dataValidation.createErrorBox("提示", "输入值与单元格定义格式不一致");
dataValidation.createPromptBox("填写说明", "填写内容只能为下拉选集中的类型");
// 添加到 sheet
sheet.addValidationData(dataValidation);
}
}
}
在实际业务中使用
java
@RestController
@RequestMapping("/test")
public class DmFieldController extends BaseController {
@PostMapping("/downloadTemplate")
public void downloadTemplate(HttpServletResponse response) {
dmFieldService.downloadTemplate(response);
}
}
java
public interface DmFieldService {
/**
* 下载模板
*/
void downloadTemplate(HttpServletResponse response);
}
java
@Service
public class DmFieldServiceImpl implements DmFieldService {
@Override
public void downloadTemplate(HttpServletResponse response) {
try {
Map<Integer, String[]> mapDropDown = new HashMap<>();
String[] yesOrNo = {"是", "否"};//你下拉框的内容
mapDropDown.put(7, yesOrNo);//表示的第几列
mapDropDown.put(9, yesOrNo);//表示的第几列
response.setCharacterEncoding("utf-8");
String fileName = "测试模板.xlsx";
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); // 防止空格变+
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
response.setContentType("application/vnd.ms-excel");
EasyExcel.write(response.getOutputStream(), DmFieldExcel.class)//改为你实际的的excel类
.sheet("sheet1")
.registerWriteHandler(new SpinnerWriteHandler(mapDropDown))//这里使用你的实现类
.doWrite(new ArrayList<>());
} catch (Exception e) {
throw new RuntimeException("生成模板失败");
}
}
}
下拉多选框
excel中并不支持下拉多选框,所以我们只能通过其他方式实现类似的实现
我们这里采用,让用户手动输入并逗号隔开(有提示需要输入的内容,和输入后的校验)
继承SheetWriteHandler接口
java
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.util.Map;
public class SpinnerWriteHandler implements SheetWriteHandler {
private final Map<Integer, String[]> dropdownOptions;
public SpinnerWriteHandler(Map<Integer, String[]> dropdownOptions) {
this.dropdownOptions = dropdownOptions;
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Workbook workbook = writeWorkbookHolder.getWorkbook();
Sheet mainSheet = writeSheetHolder.getSheet();
// 创建一个隐藏的选项表(用于存储所有下拉值)
String hiddenSheetName = "__OPTIONS__";
Sheet hiddenSheet = workbook.createSheet(hiddenSheetName);
workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);
int optionStartCol = 0; // 隐藏表从第0列开始写选项
for (Map.Entry<Integer, String[]> entry : dropdownOptions.entrySet()) {
int colIndex = entry.getKey();
String[] options = entry.getValue();
if (options == null || options.length == 0) continue;
// 1. 将选项写入隐藏 Sheet 的某一列
String rangeName = "ValidList_Col" + colIndex;
for (int i = 0; i < options.length; i++) {
Row row = hiddenSheet.getRow(i);
if (row == null) row = hiddenSheet.createRow(i);
row.createCell(optionStartCol).setCellValue(options[i]);
}
// 2. 定义名称(Named Range),供公式引用
Name name = workbook.createName();
name.setNameName(rangeName);
// 生成类似:__OPTIONS__!$A$1:$A$3
String ref = String.format("%s!$%s$1:$%s$%d",
hiddenSheetName,
getExcelColumnName(optionStartCol),
getExcelColumnName(optionStartCol),
options.length);
name.setRefersToFormula(ref);
// 3. 构造自定义验证公式(核心!)
// 公式逻辑:如果单元格为空,允许;否则,拆分后每个值都必须在 ValidList 中
// 兼容旧版 Excel 的公式(不使用 TEXTSPLIT)
String cellRef = getExcelColumnName(colIndex) + "2"; // 从第2行开始(第1行为标题)
String formula =
"OR(" +
"ISBLANK(" + cellRef + ")," +
"SUMPRODUCT(--ISERROR(MATCH(" +
"TRIM(MID(SUBSTITUTE(" + cellRef + ",\",\",REPT(\" \",99)),(ROW(INDIRECT(\"1:\"&LEN(" + cellRef + ")-LEN(SUBSTITUTE(" + cellRef + ",\",\",\"\"))+1))-1)*99+1,99))," +
rangeName + ",0)))=0" +
")";
// 4. 应用数据验证
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, colIndex, colIndex);
DataValidationHelper helper = mainSheet.getDataValidationHelper();
DataValidationConstraint constraint = helper.createCustomConstraint(formula);
DataValidation validation = helper.createValidation(constraint, addressList);
validation.setShowErrorBox(true);
validation.createErrorBox("输入错误", "请输入有效的选项,多个值请用英文逗号分隔(如:A,B)。");
validation.setSuppressDropDownArrow(false); // 显示下拉箭头(虽然不能点选多选,但可提示)
validation.setShowPromptBox(true);
validation.createPromptBox("填写说明", "可选值:" + String.join(",\n", options));
mainSheet.addValidationData(validation);
optionStartCol++; // 下一组选项写入下一列
}
}
/**
* 将列索引转为 Excel 列名(0->A, 1->B, ..., 26->AA)
*/
private String getExcelColumnName(int columnIndex) {
StringBuilder sb = new StringBuilder();
while (columnIndex >= 0) {
sb.insert(0, (char) ('A' + (columnIndex % 26)));
columnIndex = columnIndex / 26 - 1;
}
return sb.toString();
}
}
在实际业务中使用
- 和上面单选框的使用一样
单选框和复选框也可以同时实现,需要指定不同的SheetWriteHandler的实现类