使用EasyExcel实现导出excel文件时生成多级下拉选

前言

公司有个需求本来只涉及到两个下拉选项,后面就想能不能实现多个下拉选,当然我这里说的多个下拉选是联动的,比如省、地市、区县这种。

实现步骤

1、添加EasyExcel的Maven依赖

 <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
 </dependency>

2、一个具有多级关联的数据项


/**
 * excel下拉框数据项
 * @author lcy
 */
@Data
public class SelectItem {

    public SelectItem(Integer columnIndex) {
        this.columnIndex = columnIndex;
    }

    /**
     * 下拉框所在列的索引,从0开始
     */
    private Integer columnIndex;

    /**
     * 下拉框的值列表
     */
    private List<DataItem> dataItems;


    /**
     * 子级对应的下拉框数据
     */
    private SelectItem subSelect;


    public  void addDataItem(String mappingKey,List<String> values){
        if (this.dataItems == null){
            this.dataItems = new ArrayList<>();
        }
        this.dataItems.add(new DataItem(mappingKey,values));
    }
    public  void addDataItem(List<String> values){
        this.addDataItem("_"+UUID.randomUUID().toString().replaceAll("-",""),values);
    }


    @Data
    public static class DataItem{

        /**
         * 关联上级的key
         */
        private String mappingKey;

        /**
         * 当前下拉框的值
         */
        private List<String> values;

        /**
         * 当前下拉框的引用,隐藏页单元格地址
         */
        private String hiddenFormulaRef;

        public DataItem(String mappingKey, List<String> values) {
            Assert.notBlank(mappingKey,"mappingKey is not blank");
            Assert.notEmpty(values,"values is not empty");
            this.mappingKey = mappingKey;
            this.values = values;
        }
    }

3、定义一个SheetWriteHandler,这是EasyExcel提供的一个组件,允许我们在sheet页生成前后做一些干预动作。


/**
 * @author lcy
 */
public class SelectWriteHandler implements SheetWriteHandler , CellWriteHandler {

    private static final int ROW_SIZE = 10000;

    private final  WriteFont redFont;

    private final  List<SelectItem> selectItems;

    private final String HIDDEN_SHEET_NAME = "hidden_sheet";

    private final Set<Integer> selectColumns = new HashSet<>();

    private boolean isLoadSelectColumns = false;

    private int rowIndex = 0;

    public SelectWriteHandler(List<SelectItem> selectItems) {
        Assert.notEmpty(selectItems, "selectItems can not be empty");
        this.selectItems = selectItems;
        redFont = getRedFont();
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        Workbook workbook = writeWorkbookHolder.getWorkbook();
        Sheet hiddenSheet = workbook.getSheet(HIDDEN_SHEET_NAME);
        if (hiddenSheet != null){
            return ;
        }
        hiddenSheet = workbook.createSheet(HIDDEN_SHEET_NAME);
        workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet),true);
        Sheet sheet = writeSheetHolder.getSheet();
        for (SelectItem selectItem : selectItems) {
            buildHiddenSheetSelectRef(workbook,sheet,hiddenSheet,selectItem,null);
        }
        if (!isLoadSelectColumns){
            isLoadSelectColumns = true;
        }
    }

    private void buildHiddenSheetSelectRef(Workbook workbook,Sheet sheet,Sheet hiddenSheet, SelectItem selectItem,String formulaRef ) {
        if (!isLoadSelectColumns){
            selectColumns.add(selectItem.getColumnIndex());
        }
        List<SelectItem.DataItem> dataItems = selectItem.getDataItems();
        for (SelectItem.DataItem dataItem : dataItems) {
            setDataAndName(workbook, hiddenSheet, dataItem);
        }
        // 单元格地址引用
        if (formulaRef == null || formulaRef.isEmpty()){
            formulaRef = dataItems.get(0).getHiddenFormulaRef();
        }
        // 创建检验器
        DataValidation dataValidation = getDataValidation(sheet, selectItem, formulaRef);
        sheet.addValidationData(dataValidation);
        SelectItem subSelect = selectItem.getSubSelect();
        if (subSelect != null){
            buildHiddenSheetSelectRef(workbook,sheet,hiddenSheet,subSelect,getInDirectFormulaRef(selectItem.getColumnIndex()));
        }
    }

    private  DataValidation getDataValidation(Sheet sheet, SelectItem selectItem, String formulaRef) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = helper.createFormulaListConstraint(formulaRef);
        CellRangeAddressList rangeAddressList = new CellRangeAddressList(1,ROW_SIZE, selectItem.getColumnIndex(), selectItem.getColumnIndex());
        DataValidation dataValidation = helper.createValidation(constraint, rangeAddressList);
        dataValidation.setShowErrorBox(true);
        return dataValidation;
    }

    private void setDataAndName(Workbook workbook, Sheet hiddenSheet, SelectItem.DataItem dataItem) {
        // 构建隐藏数据
        Row row = hiddenSheet.createRow(rowIndex);
        List<String> values = dataItem.getValues();
        for (int i = 0; i < values.size(); i++) {
            row.createCell(i).setCellValue(values.get(i));
        }
        // 创建名称命名器
        Name name = workbook.createName();
        name.setNameName(dataItem.getMappingKey());
        name.setRefersToFormula(getFormulaRef(row));
        dataItem.setHiddenFormulaRef(name.getRefersToFormula());
        rowIndex++;
    }

    private String getInDirectFormulaRef(Integer columnIndex){
        CellReference slectCellReference = new CellReference(1, columnIndex);
        return  "INDIRECT("+joinFormulaRef(slectCellReference, false)+")";
    }

    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        if (!context.getHead()){
            Integer columnIndex = context.getColumnIndex();
            if (selectColumns.contains(columnIndex)){
                // 设置红色字体
                context.getFirstCellData().getOrCreateStyle().setWriteFont(redFont);
            }
        }
        CellWriteHandler.super.afterCellDispose(context);
    }

    private   String getFormulaRef(Row prvRow) {
        Cell startCell = prvRow.getCell(prvRow.getFirstCellNum());
        Cell endCell = prvRow.getCell(prvRow.getLastCellNum() - 1);
        return HIDDEN_SHEET_NAME + "!" + joinFormulaRef(new CellReference(startCell),true) + ":" + joinFormulaRef(new CellReference(endCell),true);
    }

    public  String joinFormulaRef(CellReference cellReference,boolean isAbsolute){
        StringBuilder sb = new StringBuilder();
        String[] refs = cellReference.getCellRefParts();
        for (int i = refs.length -1 ; i >= 1; i--) {
            if (isAbsolute){
                sb.append("$");
            }
            sb.append(refs[i]);
        }
        return sb.toString();
    }

    /**
     * 返回一个红色字体
     * @return
     */
    private WriteFont getRedFont() {
        WriteFont redFont =  new WriteFont();
        redFont.setColor(IndexedColors.RED.getIndex());
        return redFont;
    }
}


4、准备数据

       // 准备数据
        SelectItem selectItem = new SelectItem(0);
        selectItem.addDataItem(List.of("浙江省","河南省"));

        SelectItem subSelectItem = new SelectItem(1);
        subSelectItem.addDataItem("浙江省",List.of("杭州市","宁波市"));
        subSelectItem.addDataItem("河南省",List.of("郑州市","洛阳市","开封市"));
        selectItem.setSubSelect(subSelectItem);

        SelectItem selectItem3 = new SelectItem(2);
        selectItem3.addDataItem("杭州市",List.of("滨江区","西湖区"));
        selectItem3.addDataItem("宁波市",List.of("宁波市1","宁波市2"));
        selectItem3.addDataItem("郑州市",List.of("金水区","二七区"));
        selectItem3.addDataItem("洛阳市",List.of("洛阳市1","洛阳市2"));
        selectItem3.addDataItem("开封市",List.of("开封市1","开封市2"));
        subSelectItem.setSubSelect(selectItem3);

5、测试

EasyExcel.write("d:\\5555.xlsx")
                .registerWriteHandler(new SelectWriteHandler(List.of(selectItem)))
                .sheet()
                .doWrite(Collections.emptyList());


完整的测试代码

public class SelectExcelTest {


    public static void main(String[] args) {

        // 准备数据
        SelectItem selectItem = new SelectItem(0);
        selectItem.addDataItem(List.of("浙江省","河南省"));

        SelectItem subSelectItem = new SelectItem(1);
        subSelectItem.addDataItem("浙江省",List.of("杭州市","宁波市"));
        subSelectItem.addDataItem("河南省",List.of("郑州市","洛阳市","开封市"));
        selectItem.setSubSelect(subSelectItem);

        SelectItem selectItem3 = new SelectItem(2);
        selectItem3.addDataItem("杭州市",List.of("滨江区","西湖区"));
        selectItem3.addDataItem("宁波市",List.of("宁波市1","宁波市2"));
        selectItem3.addDataItem("郑州市",List.of("金水区","二七区"));
        selectItem3.addDataItem("洛阳市",List.of("洛阳市1","洛阳市2"));
        selectItem3.addDataItem("开封市",List.of("开封市1","开封市2"));
        subSelectItem.setSubSelect(selectItem3);



        EasyExcel.write("d:\\5555.xlsx")
                .registerWriteHandler(new SelectWriteHandler(List.of(selectItem)))
                .sheet()
                .doWrite(Collections.emptyList());
    }

}

6、结果


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/913328.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

海量小文件挑战下的CephFS:优化策略与实践探索

文章目录 1.背景2.基本概念2.1 CephFS IO流程2.2 Ceph-FUSE 3. 问题3.1 问题源起3.2 理论分析3.3 原因排查3.3.1 Ceph-FUSE日志分析3.3.2 提出猜想3.3.3 代码验证3.3.3.1 MDS端3.3.3.2 Ceph-FUSE端 3.4 小结 1.背景 随着大数据、人工智能技术的蓬勃发展&#xff0c;人类对于算…

编写一个脚本实现参数的远程主机网络探测python test_ip.py 192.168.0.10~192.168.0.100(sys模块)

""" 编写一个脚本实现参数的远程主机网络探测python test_ip.py 192.168.0.10~192.168.0.100 """ #导入模块 #读取起始IP&#xff0c;结束IP import sys start_ip sys.argv[1] end_ip sys.argv[2] # print(start_ip,end_ip)##########组装数据…

lvgl: 示例入门

目录 1. A very simple hello world label 2. A button with a label and react on click event 3. Create styles from scratch for buttons 4. Create a slider and write its value on a label 1. A very simple hello world label void _lv_example_get_started_1(void) …

Redis2:Redis数据结构介绍、通用命令、String类型、Key的层级格式

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

云计算基础

声明 学习视频来自B站UP主泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 目录 一、云架构介绍 二、云服务 三、云分类 四、共享责任模型 五、云架构 六、云架构设计 七、集…

【超级详细】基于Zynq FPGA对雷龙SD NAND的测试

目录 一、SD NAND特征1.1 SD卡简介1.2 SD卡Block图 二、SD卡样片三、Zynq测试平台搭建3.1 测试流程3.2 SOC搭建 一、SD NAND特征 1.1 SD卡简介 雷龙的SD NAND有很多型号&#xff0c;在测试中使用的是CSNP4GCR01-AMW与CSNP32GCR01-AOW。芯片是基于NAND FLASH和 SD控制器实现的…

python中常见的8种数据结构之一列表

列表是Python中最常见的数据结构之一。它是一种有序的集合&#xff0c;可以包含不同类型的数据。 以下是列表的一些特点和常见操作&#xff1a; 1. 定义列表&#xff1a;可以使用方括号&#xff08;[]&#xff09;来定义一个空列表&#xff0c;也可以在方括号中添加元素来初始…

Python 在PDF中绘制形状(线条、矩形、椭圆形等)

在PDF中绘制图形可以增强文档的视觉效果。通过添加不同类型的形状&#xff0c;如实线、虚线、矩形、圆形等&#xff0c;可以使文档更加生动有趣&#xff0c;提高读者的阅读兴趣。这对于制作报告、演示文稿或是教材特别有用。本文将通过以下几个示例介绍如何使用Python 在PDF中绘…

AndroidStudio-滚动视图ScrollView

滚动视图 滚动视图有两种: 1.ScrollView&#xff0c;它是垂直方向的滚动视图;垂直方向滚动时&#xff0c;layout_width属性值设置为match_parent&#xff0c;layout_height属性值设置为wrap_content。 例如&#xff1a; &#xff08;1&#xff09;XML文件中: <?xml ve…

【后端速成Vue】computed计算属性

前言&#xff1a; 本期将会介绍 Vue 中的计算属性&#xff0c;他和 methods 方法又会有什么区别呢&#xff1f;在这里都会给你一一讲解。 篮球哥找工作专属IT岗位内部推荐&#xff1a; 专属内推链接&#xff1a;内推通道 1、computed计算属性 概念&#xff1a; 基于现有的数据…

mysql 配置文件 my.cnf 增加 lower_case_table_names = 1 服务启动不了的原因

原因&#xff1a;在MySQL8.0之后的版本&#xff0c;只允许在数据库初始化时指定&#xff0c;之后不允许修改了 mysql 配置文件 my.cnf 增加 lower_case_table_names 1 服务启动不了 报错信息&#xff1a;Job for mysqld.service failed because the control process exited …

优化时钟网络之时钟偏移

Note&#xff1a;文章内容以Xilinx 7系列FPGA进行讲解 1、基本介绍 所谓时钟偏移&#xff08;Clock Skew&#xff09;&#xff0c;是指在同步时序电路中&#xff0c;同一个时钟信号到达各个寄存器时钟端口的时间不一致的现象。如下图所示&#xff1a; 时钟从源端到达寄存器FF1的…

npm镜像的常用操作

查看当前配置的 npm 镜像 npm config get registry切换官方镜像 npm config set registry https://registry.npmjs.org/切换淘宝镜像(推荐) npm config set registry https://registry.npmmirror.com/切换腾讯云镜像 npm config set registry http://mirrors.cloud.tencent…

Notepad++ 最新官网中文版在线下载 附文本编辑器安装与基础使用教程

Notepad &#xff08;记事本&#xff09;是一个简单的文本编辑器&#xff0c;预装在所有版本的 Microsoft Windows 操作系统中。它的主要功能是创建、编辑和存储纯文本文件&#xff0c;通常以 .txt 格式保存。Notepad 的设计旨在提供一个轻量级的文本处理工具&#xff0c;适合快…

Deprecated Gradle features were used in this build

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 今天我使用gradle搭建springboot项目使用&#xff0c;报警告Deprecated Gradle features were used in this build, making it inco…

crond 任务调度 (Linux相关指令:crontab)

相关视频链接 crontab 进行 定时任务 的设置 概述 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序 任务调度的分类&#xff1a; 1.系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等。 2.个别用户可能希望执行某些程序&#xff0c;比如…

比流计算资源效率最高提升 1000 倍,“增量计算”新模式能否颠覆数据分析?

作者 | 关涛 云器科技CTO 数据平台领域发展 20 年&#xff0c;逐渐成为每个企业的基础设施。作为一个进入“普惠期”的领域&#xff0c;当下的架构已经完美了吗&#xff0c;主要问题和挑战是什么&#xff1f;在 2023 年 AI 跃变式爆发的大背景下&#xff0c;数据平台又该如何演…

区块链技术在供应链管理中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在供应链管理中的应用 区块链技术在供应链管理中的应用 区块链技术在供应链管理中的应用 引言 区块链技术概述 定义与…

微搭低代码入门01变量

目录 1 变量的定义2 变量的赋值3 变量的类型4 算术运算符5 字符串的连接6 模板字符串7 检查变量的类型8 解构赋值8.1 数组的解构赋值8.2 对象的解构赋值 9 类型转换9.1 转换为字符串9.2 转换为数字9.3 转换为布尔值 总结 好些零基础的同学&#xff0c;在使用低代码的时候&#…

大数据机器学习算法与计算机视觉应用04:多项式

The Algorithm Magic of Polynomial PolynomialsThe Root of PolynomialA Delete ChannelPolynomials for Finding Maximum Matchings Polynomials 多项式 一个 d d d 次多项式可以用一个 d 1 d1 d1 元组 c i {c_i} ci​ 表达。在这种情况下&#xff0c;两个多项式相加的…