EasyExcel自定义动态下拉框(附加业务对象转换功能)

全文直接复制粘贴即可,测试无误

一、注解类
1、ExcelSelected.java

设置下拉框

@Documented
@Target({ElementType.FIELD})//用此注解用在属性上。
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
public @interface ExcelSelected {/*** 固定下拉内容*/String[] source() default {};/*** 方式二:提供动态下拉选项的类*/Class<? extends WhExcelDynamicSelect>[] sourceClass() default {};/*** 设置下拉框的起始行,默认为第二行*/int firstRow() default 1;/*** 设置下拉框的结束行,默认为最后一行*/int lastRow() default 0x10000;
}
2、TextColumn.java

设置单元格为文本格式

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TextColumn {int index(); // 列索引
}
二、Controller类

分为导入数据与导出模板

@Tag(name = "xxxx管理")
@RestController
@RequestMapping("/api/v1/project/people")
@RequiredArgsConstructor
@Slf4j
public class WhProjectPeopleController {private final WhProjectPeopleService whProjectPeopleService;@GetMapping("/exportDownload")@Operation(summary = "导出excel模板")public void downloadExcel(HttpServletResponse response) throws IOException {//获取输入流,原始模板位置String name = "人员信息模板";//假如以中文名下载的话,设置下载文件名称String filename = "人员信息模板.xlsx";//转码,免得文件名中文乱码filename = URLEncoder.encode(filename, "UTF-8");//设置文件下载头response.addHeader("Content-Disposition", "attachment;filename=" + filename);response.setContentType("multipart/form-data");ExcelUtils.writeExcel(response, filename, name, WhProjectPeopleVO.class, null);}@PostMapping("/importExcel")@Operation(summary = "导入人员数据")public Result importExcel(MultipartFile file, HttpServletRequest request) throws IOException {if (file.isEmpty()) {throw new SzException("excel文件内容为空!");}List<WhProjectPeopleVO> vos = ExcelUtils.read(file, WhProjectPeopleVO.class);if (CollectionUtils.isEmpty(vos)){throw new SzException("导入数据失败!");}Map<String,Integer> result = whProjectPeopleService.importExcel(vos);return Result.success(result);}}
三、Service方法

向excel导入数据

    @Override@Transactionalpublic Map<String, Integer> importExcel(List<WhProjectPeopleVO> vos) {Map<String, Integer> params = new HashMap<>();if (CollectionUtils.isEmpty(vos)) {throw new SzException("数据导入失败!");}log.info("导入数据:{}" + JSON.toJSONString(vos));int count = 0;List<WhProjectPeople> peoples = whProjectPeopleConverter.voListToEntity(vos);for (WhProjectPeople people : peoples) {WhProjectPeopleForm whProjectPeopleForm = whProjectPeopleConverter.entityToForm(people);saveProjectPeople(whProjectPeopleForm);count++;params.put("insert", count);}return params;}
}
四、工具类
1、ExcelUtils.java
public class ExcelUtils {/*** 将列表以 Excel 响应给前端** @param response  响应* @param filename  文件名* @param sheetName Excel sheet 名* @param head      Excel head 头* @param data      数据列表* @param <T>       泛型,保证 head 和 data 类型的一致性* @throws IOException 写入失败的情况*/public static <T> void writeExcel(HttpServletResponse response, String filename, String sheetName,Class<T> head, List<T> data) throws IOException {CellStyleStrategy cellStyleStrategy =new CellStyleStrategy(new WriteCellStyle());Map<Integer, WhExcelSelectedResolve> selectedMap = resolveSelectedAnnotation(head);// 输出 ExcelEasyExcel.write(response.getOutputStream(), head)// 不要自动关闭,交给 Servlet 自己处理.autoCloseStream(false)//设置表头和填充内容的样式.registerWriteHandler(cellStyleStrategy)//设置填充内容单元格格式为文本格式.registerWriteHandler(new CustomSheetWriteHandler()).registerWriteHandler(new SelectedSheetWriteHandler(selectedMap)).sheet(sheetName).doWrite(data);// 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));response.setContentType("application/vnd.ms-excel;charset=UTF-8");}/*** 将列表以 Excel 响应给前端** @param response  响应* @param filename  文件名* @param sheetName Excel sheet 名* @param head      Excel head 头* @param data      数据列表* @param <T>       泛型,保证 head 和 data 类型的一致性* @throws IOException 写入失败的情况*/public static <T> void write(HttpServletResponse response, String filename, String sheetName,Class<T> head, List<T> data) throws IOException {CellStyleStrategy cellStyleStrategy =new CellStyleStrategy(new WriteCellStyle());// 输出 ExcelEasyExcel.write(response.getOutputStream(), head).autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度.registerWriteHandler(cellStyleStrategy).sheet(sheetName).doWrite(data);// 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));response.setContentType("application/vnd.ms-excel;charset=UTF-8");}public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {return EasyExcel.read(file.getInputStream(), head, null)// 不要自动关闭,交给 Servlet 自己处理.autoCloseStream(false).doReadAllSync();}/*** 解析表头类中的下拉注解* @param head 表头类* @param <T> 泛型* @return Map<下拉框列索引, 下拉框内容> map*/private static <T> Map<Integer, WhExcelSelectedResolve> resolveSelectedAnnotation(Class<T> head) {Map<Integer, WhExcelSelectedResolve> selectedMap = new HashMap<>();// getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性Field[] fields = head.getDeclaredFields();for (int i = 0; i < fields.length; i++){Field field = fields[i];// 解析注解信息ExcelSelected selected = field.getAnnotation(ExcelSelected.class);ExcelProperty property = field.getAnnotation(ExcelProperty.class);if (selected != null) {WhExcelSelectedResolve excelSelectedResolve = new WhExcelSelectedResolve();String[] source = excelSelectedResolve.resolveSelectedSource(selected);if (source != null && source.length > 0){excelSelectedResolve.setSource(source);excelSelectedResolve.setFirstRow(selected.firstRow());excelSelectedResolve.setLastRow(selected.lastRow());if (property != null && property.index() >= 0){selectedMap.put(property.index(), excelSelectedResolve);} else {selectedMap.put(i, excelSelectedResolve);}}}}return selectedMap;}}
2、SpringContextUtil.java
@Component
public class SpringContextUtil implements ApplicationContextAware {/*** 获取ApplicationContext*/@Getterprivate static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextUtil.applicationContext = applicationContext;}/*** 通过class获取Bean*/public static <T> T getBean(Class<T> clazz) {return applicationContext.getBean(clazz);}/*** 通过name以及class获取Bean*/public static <T> T getBean(String name, Class<T> clazz) {return applicationContext.getBean(name, clazz);}
}
五、转换器
1、WhProjectExcelConverter.java

项目id与项目名称互相转化

public class WhProjectExcelConverter implements Converter<String> {private final WhProjectService whProjectService = SpringContextUtil.getBean(WhProjectService.class);private final List<WhProject> list = whProjectService.list();@Overridepublic Class<?> supportJavaTypeKey() {//实体类对象属性类型return String.class;}/*** 将单元格中数据转化为java对象属性* @param context* @return* @throws Exception*/@Overridepublic String convertToJavaData(ReadConverterContext<?> context) throws Exception {//创建集合用于存储项目id和名称对应关系Map<String,String> mapToJavaData = new HashMap<>();//遍历项目列表将项目id和名称放入mapfor (WhProject sysDeptEntity : list) {mapToJavaData.put(sysDeptEntity.getProjectName(),sysDeptEntity.getId());}//从cellData中读取数据 转换为实体类中的对象数值return mapToJavaData.get(context.getReadCellData().getStringValue());}/*** 将java对象转化为excel单元格数据* @param context* @return* @throws Exception*/@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) throws Exception {Map<String,String> mapToExcelData = new HashMap<>();for (WhProject sysDeptEntity : list) {mapToExcelData.put(sysDeptEntity.getId(),sysDeptEntity.getProjectName());}//将java属性转换为excel对应属性类型return new WriteCellData<>(mapToExcelData.get(context.getValue()));}
}
2、WhWorkTypeExcelConverter.java

int类型type值与String类型type名称相互转化

public class WhWorkTypeExcelConverter implements Converter<Integer> {private final WhDictItemService whDictItemService = SpringContextUtil.getBean(WhDictItemService.class);private final List<WhDictItem> list = whDictItemService.listByCode(WORK_TYPE);@Overridepublic Class<?> supportJavaTypeKey() {//实体类对象属性类型return Integer.class;}/*** 将单元格中数据转化为java对象属性* @param context* @return* @throws Exception*/@Overridepublic Integer convertToJavaData(ReadConverterContext<?> context) throws Exception {//创建集合用于存储部门id和名称对应关系Map<String,String> mapToJavaData = new HashMap<>();//遍历数据字典将属性名称和属性值放入mapfor (WhDictItem sysDeptEntity : list) {mapToJavaData.put(sysDeptEntity.getItemName(),sysDeptEntity.getItemValue());}//从cellData中读取数据 转换为实体类中的对象数值return Integer.valueOf(mapToJavaData.get(context.getReadCellData().getStringValue()));}/*** 将java对象转化为excel单元格数据* @param context* @return* @throws Exception*/@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) throws Exception {Map<String,String> mapToExcelData = new HashMap<>();for (WhDictItem sysDeptEntity : list) {mapToExcelData.put(sysDeptEntity.getItemValue(),sysDeptEntity.getItemName());}//将java属性转换为excel对应属性类型return new WriteCellData<>(mapToExcelData.get(context.getValue()));}
}
六、Handler类
1、SelectedSheetWriteHandler.java

设置下拉列表相关

@Data
public class SelectedSheetWriteHandler implements SheetWriteHandler {/*** 构建下拉选的map*/private final Map<Integer, WhExcelSelectedResolve> selectedMap;private final int columnSelectMaxLength = 255;@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {// 这里可以对cell进行任何操作Sheet sheet = writeSheetHolder.getSheet();DataValidationHelper helper = sheet.getDataValidationHelper();selectedMap.forEach((k, v) -> {// 设置下拉列表的行: 首行,末行,首列,末列CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), v.getLastRow(), k, k);// 设置下拉列表的值DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSource());// 设置约束DataValidation validation = helper.createValidation(constraint, rangeList);// 阻止输入非下拉选项的值validation.setErrorStyle(DataValidation.ErrorStyle.STOP);validation.setShowErrorBox(true);validation.setSuppressDropDownArrow(true);validation.createErrorBox("提示", "请输入下拉选项中的内容");sheet.addValidationData(validation);});}
}
2、CustomSheetWriteHandler.java

设置文本格式、表格式

public class CustomSheetWriteHandler implements SheetWriteHandler {// 设置100列columnprivate static final Integer COLUMN = 100;@Overridepublic void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {}@Overridepublic void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {// 获取带有 @TextColumn 注解的列索引Map<Integer, String> textColumns = getTextColumns();SXSSFSheet sxssfSheet = (SXSSFSheet) writeSheetHolder.getSheet();for (int i = 0; i < COLUMN; i++) {if (textColumns.containsKey(i)) {// 设置为文本格式CellStyle cellStyle = writeWorkbookHolder.getCachedWorkbook().createCellStyle();// 49为文本格式cellStyle.setDataFormat((short) 49);// i为列,一整列设置为文本格式sxssfSheet.setDefaultColumnStyle(i, cellStyle);}}}private Map<Integer, String> getTextColumns() {Map<Integer, String> textColumns = new HashMap<>();Class<?> dataClass = WhProjectPeopleVO.class;Field[] fields = dataClass.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(TextColumn.class)) {int columnIndex = field.getAnnotation(TextColumn.class).index();textColumns.put(columnIndex, field.getName());}}return textColumns;}
}
七、SelectedResolve类
@Data
@Slf4j
public class WhExcelSelectedResolve {/*** 下拉内容*/private String[] source;/*** 设置下拉框的起始行,默认为第二行*/private int firstRow;/*** 设置下拉框的结束行,默认为最后一行*/private int lastRow;public String[] resolveSelectedSource(ExcelSelected excelSelected) {if (excelSelected == null) {return null;}// 获取固定下拉框的内容String[] source = excelSelected.source();if (source.length > 0) {return source;}// 获取动态下拉框的内容Class<? extends WhExcelDynamicSelect>[] classes = excelSelected.sourceClass();if (classes.length > 0) {try {WhExcelDynamicSelect excelDynamicSelect = classes[0].newInstance();String[] dynamicSelectSource = excelDynamicSelect.getSource();if (dynamicSelectSource != null && dynamicSelectSource.length > 0) {return dynamicSelectSource;}} catch (InstantiationException | IllegalAccessException e) {log.error("解析动态下拉框数据异常", e);}}return null;}
}
八、实体类
@Data
@Schema(description = "项目随行人员表")
@HeadRowHeight(30)
@ContentRowHeight(18)
public class WhProjectPeopleVO {@ExcelIgnore@Schema(description = "id")private String id;@ExcelIgnore@Schema(description = "人员类型 0随行人员1负责人 默认0")private Integer type;@ExcelProperty(value = "姓名", index = 0)@Schema(description = "姓名")private String name;@ColumnWidth(20)@TextColumn(index = 1)@ExcelProperty(value = "身份证号", index = 1)@Schema(description = "身份证号")private String identityId;@ColumnWidth(20)@ExcelProperty(value = "手机号", index = 2)@Schema(description = "手机号")private String mobile;@ColumnWidth(20)@ExcelSelected(sourceClass = WhProjectSelect.class)@ExcelProperty(value = "所属项目", index = 3, converter = WhProjectExcelConverter.class)@Schema(description = "项目id")private String projectId;@ExcelIgnore@Schema(description = "微信openId")private String openId;@ExcelIgnore@Schema(description = "微信unionId")private String unionId;@ExcelIgnore@Schema(description = "开始时间")private Date startTime;@ExcelIgnore@Schema(description = "结束时间")private Date endTime;@ExcelIgnore@Schema(description = "项目名称")private String projectName;@ExcelIgnore@Schema(description = "最近入场时间")private Date lastArrivalTime;@ExcelSelected(sourceClass = WhWorkTypeSelect.class)@ExcelProperty(value = "工种", index = 4, converter = WhWorkTypeExcelConverter.class)@Schema(description = "工种")private Integer workType;
}

测试结果

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【2025最新计算机毕业设计】基于Spring Boot+Vue影院购票系统(高质量源码,提供文档,免费部署到本地)

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

信息科技伦理与道德1:研究方法

1 问题描述 1.1 讨论&#xff1f; 请挑一项信息技术&#xff0c;谈一谈为什么认为他是道德的/不道德的&#xff0c;或者根据使用场景才能判断是否道德。判断的依据是什么&#xff08;自身的道德准则&#xff09;&#xff1f;为什么你觉得你的道德准则是合理的&#xff0c;其他…

Web安全扫盲

1、建立网络思维模型的必要 1 . 我们只有知道了通信原理&#xff0c; 才能够清楚的知道数据的交换过程。 2 . 我们只有知道了网络架构&#xff0c; 才能够清楚的、准确的寻找漏洞。 2、局域网的简单通信 局域网的简单通信&#xff08;数据链路层&#xff09; 一般局域网都通…

Linux驱动开发(18):linux驱动并发与竞态

并发是指多个执行单元同时、并行执行&#xff0c;而并发的执行单元对共享资源(硬件资源和软件上的全局变量、静态变量等)的访问 则很容易导致竞态。对于多核系统&#xff0c;很容易理解&#xff0c;由于多个CPU同时执行&#xff0c;多个CPU同时读、写共享资源时很容易造成竞态。…

009:传统计算机视觉之边缘检测

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 本节来看一个利用传统计算机视觉方法来实现图片边缘检测的方法。 什么是边缘检测&#xff1f; 边缘检测是通过一些算法来识别图像中物体之间或者物体与背景之间的边界&…

QML使用Popup实现弹出Message

方案一&#xff1a;popup import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15ApplicationWindow {visible: truewidth: 640height: 480title: qsTr("Top Message Popup Example")ColumnLayout {anchors.centerIn: parentspacing: 10Butt…

idea java.lang.OutOfMemoryError: GC overhead limit exceeded

Idea build项目直接报错 java: GC overhead limit exceeded java.lang.OutOfMemoryError: GC overhead limit exceeded 设置 编译器 原先heap size 设置的是 700M , 改成 2048M即可

boot-126网易邮件发送

【SpringBoot整合JavaMail发送邮件】 一 . Java Mail基本概念 1.SMTP Simple Mail Transfer Protocol:简单邮件传输协议&#xff0c;用于发送邮件的协议。 2.POP3 Post office Protocol 3:邮局通讯协议第三版&#xff0c;用于接收邮件的标准协议。 3.IMAP Internet Message Acc…

【ArcGISPro/GeoScenePro】检查多光谱影像的属性并优化其外观

数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 操作 其他数据 检查影像的属性 熟悉检查您正在使用的栅格属性非常重要。

音视频入门基础:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现

一、引言 通过FFmpeg命令&#xff1a; ./ffmpeg -i XXX.ps 可以判断出某个文件是否为PS文件&#xff1a; 所以FFmpeg是怎样判断出某个文件是否为PS文件呢&#xff1f;它内部其实是通过mpegps_probe函数来判断的。从《FFmpeg源码&#xff1a;av_probe_input_format3函数和AVI…

[Python学习日记-74] 面向对象实战2——选课系统

[Python学习日记-74] 面向对象实战2——选课系统 简介 开发要求 实现&#xff1a;选课系统 简介 在前面的《年会答题系统》当中我们介绍了面向对象软件开发的一些流程&#xff0c;当然这一流程只是涵括了大部分的&#xff0c;目前在业界也没有一个统一的标准&#xff0c;每个…

用户注册模块(芒果头条项目进度4)

1 创建⽤户模块⼦应⽤ 1.1 在项⽬包⽬录下 创建apps的python包。 1.2 在apps包下 创建应⽤userapp $ cd 项⽬包⽬录/apps $ python ../../manage.py startapp userapp 1.3 配置导包路径 默认情况下导包路径指向项⽬根⽬录 # 通过下⾯语句可以打印当前导包路径 print(sys.pa…

Elasticsearch:利用 AutoOps 检测长时间运行的搜索查询

作者&#xff1a;来自 Elastic Valentin Crettaz 了解 AutoOps 如何帮助你调查困扰集群的长期搜索查询以提高搜索性能。 AutoOps 于 11 月初在 Elastic Cloud Hosted 上发布&#xff0c;它通过性能建议、资源利用率和成本洞察、实时问题检测和解决路径显著简化了集群管理。 Au…

关于Flutter应用国际化语言的设置

目录 1. Locale配置 2. 用户切换/启动自动加载缓存里面的locale 由于最近在开发app国际化设置的时候遇到一些问题&#xff0c;所以做出一些总结。 1. Locale配置 具体的初始化配置可以参考文档&#xff1a;i18n | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 值得…

基层医联体医院患者历史检验检查数据的快速Python编程分析

​​​​​​​ 一、引言 1.1 研究背景与意义 在当今数字化医疗时代,医疗数据呈爆炸式增长,涵盖患者的基本信息、病史、检验检查结果、治疗方案等各个维度。这些海量且复杂的数据蕴含着巨大价值,为精准医疗决策提供了关键依据。通过对患者历史检验检查数据的深入对比分析…

如何使用OpenCV进行抓图-多线程

前言 需求&#xff1a; 1、如何使用OpenCV捕抓Windows电脑上USB摄像头的流、 2、采用多线程 3、获知当前摄像头的帧率。 这个需求&#xff0c;之前就有做了&#xff0c;但是由于出现了一个问题&#xff0c;人家摄像头的帧率目前都可以达到60帧/s 了&#xff0c;而我的程序…

数势科技:解锁数据分析 Agent 的智能密码(14/30)

一、数势科技引领数据分析变革 在当今数字化浪潮中&#xff0c;数据已然成为企业的核心资产&#xff0c;而数据分析则是挖掘这一资产价值的关键钥匙。数势科技&#xff0c;作为数据智能领域的领军者&#xff0c;以其前沿的技术与创新的产品&#xff0c;为企业开启了高效数据分析…

C++11编译器优化以及引用折叠

1.左值与右值的意义 1.左值引用和右值引用最终目的是减少拷贝&#xff0c;提高效率 2.左值引用还可以修改参数/返回值 左值引用不足&#xff1a; 部分函数放回场景&#xff0c;只能传值返回&#xff0c;不能引用左值返回 当前函数局部对象&#xff0c;出了当前函数作用域生…

小程序学习06——uniapp组件常规引入和easycom引入语法

目录 一 组件注册 1.1 组件全局注册 1.2 组件全局引入 1.3 组件局部引入 页面引入组件方式 1.3.1 传统vue规范&#xff1a; 1.3.2 通过uni-app的easycom 二 组件的类型 2.1 基础组件列表 一 组件注册 1.1 组件全局注册 &#xff08;a&#xff09;新建compoents文件…

【时时三省】(C语言基础)常见的动态内存错误2

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 对非动态开辟空间内存使用free释放 示例&#xff1a; 这个arr数组是在栈上的 *p指向的就是arr 对非动态空间也用了free ferr只能在动态开辟空间使用 使用free释放一块动态开辟空间的一部分…