easyExcel实现下拉单选框和(变相的下拉多选框)

下拉单选框的实现

继承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的实现类

相关推荐
Ccuno2 小时前
Java 核心类库与数据结构
java·深度学习
辣机小司2 小时前
【踩坑记录:EasyExcel 生产级实战:策略模式重构与防御性导入导出校验指南(实用工具类分享)】
java·spring boot·后端·重构·excel·策略模式·easyexcel
better_liang2 小时前
每日Java面试场景题知识点之-RabbitMQ消息重复消费问题
java·分布式·消息队列·rabbitmq·幂等性
醒过来摸鱼2 小时前
Spring Cloud Gateway
java·spring·spring cloud
2501_944441752 小时前
Flutter&OpenHarmony商城App消息通知组件开发
java·javascript·flutter
we1less2 小时前
[audio] AudioTrack (四) getOutputForAttr 分析
android·java
计算机毕设指导62 小时前
基于微信小程序的博物馆文创系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
后端小张2 小时前
【JAVA 进阶】Spring Boot自动配置详解
java·开发语言·人工智能·spring boot·后端·spring·spring cloud
郝学胜-神的一滴2 小时前
Python面向对象编程:解耦、多态与魔法艺术
java·开发语言·c++·python·设计模式·软件工程