通用 Excel 导出功能设计与实现:动态列选择与灵活配置

在企业级应用开发中,数据导出是高频需求。本文介绍一种支持动态列选择、灵活配置的通用 Excel 导出方案,通过前后端协同设计,实现导出字段、列顺序、数据格式的自定义,满足多样化业务场景。

一、功能架构设计

核心特性

  1. 动态字段选择:支持通过前端勾选动态指定导出字段,包含字段名(逻辑标识)与显示名(业务含义)的映射
  1. 行数据过滤:支持按用户 ID 筛选导出特定行数据
  1. 多 Sheet 支持:可扩展支持单个 Excel 文件包含多个 Sheet 页
  1. 格式自适应:自动处理日期、数字等数据类型的格式化显示

技术栈

  • 前端:Thymeleaf 模板引擎 + XMLHttpRequest 文件下载
  • 后端:Spring Boot + EasyExcel + Hutool 工具集
  • 核心组件
  • 请求参数:ExcelExportRequest(包含基础配置与字段列表)
  • 响应结构:ExcelExportResponse(封装文件元信息与 Sheet 数据)

二、核心实现细节

1. 前后端数据协议设计

入参结构(ExcelExportRequest)
@Datapublic class UserExportRequest extends ExcelExportRequest {private List<Integer> userIdList; // 待导出的用户ID列表(可选)}@Datapublic class ExcelExportRequest {private String excelName; // Excel文件名private String sheetName; // Sheet页名称private List<ExcelExportField> fieldList; // 导出字段列表(有序)}@Datapublic class ExcelExportField {private String fieldName; // 实体类字段名(如"userId")private String fieldDesc; // 表格显示名称(如"用户ID")}
出参结构(ExcelExportResponse)
@Datapublic class ExcelExportResponse {private String excelName; // 导出文件名private List<ExcelSheet> sheetList; // Sheet数据集合@Datapublic static class ExcelSheet {private String sheetName; // Sheet名称private List<ExcelHead> headList; // 表头信息private List<Map<String, String>> dataList; // 行数据(键值对形式)@Datapublic static class ExcelHead {private String fieldName; // 字段名private String fieldDesc; // 显示名}}}

2. 前端交互实现

动态列选择组件

<!-- 案例1:仅列选择 --><table border="1"><caption><span class="title">案例1:勾选需要导出的列</span><button onclick="exportExcel1(event)">导出</button></caption><tr><th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th><th><label><input type="checkbox" class="exportCol" data-field-name="userName" data-field-desc="用户名">用户名</label></th><!-- 更多字段... --></tr></table><!-- 案例2:列选择+行筛选 --><table border="1"><caption><span class="title">案例2:勾选需要导出的列 & 行</span><button onclick="exportExcel2(event)">导出</button></caption><tr><th>选择记录</th><th><label><input type="checkbox" class="exportCol" data-field-name="userId" data-field-desc="用户id"> 用户id</label></th><!-- 更多字段... --></tr><tr th:each="user:${userList}"><td><input type="checkbox" class="userId" th:data-user-id="${user.userId}"></td><td th:text="${user.userId}"></td><!-- 数据行展示... --></tr></table>
文件下载逻辑
function download(data, url) {const xhr = new XMLHttpRequest();xhr.open("POST", url);xhr.responseType = 'blob';xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8');xhr.onload = function() {if (this.status === 200) {const blob = this.response;if (blob.size > 0) {// 从响应头解析文件名const fileName = getFileNameFromResponse(this.getResponseHeader("content-disposition"));// 创建临时链接下载const a = document.createElement('a');a.href = URL.createObjectURL(blob);a.download = fileName;a.click();}}};xhr.send(JSON.stringify(data));}// 文件名解析工具function getFileNameFromResponse(disposition) {const match = /filename=(.*)/.exec(disposition);return decodeURIComponent(match[1].replace(/['"]/g, ''));}

3. 后端核心处理

控制器设计
@Controller@CrossOriginpublic class UserController {@Resource private UserService userService;// 页面跳转@GetMapping("/userList")public String userList(Model model) {model.addAttribute("userList", userService.getUserList());return "userList";}// 导出接口@PostMapping("/userExport")public void userExport(@RequestBody UserExportRequest request) throws IOException {ExcelExportResponse response = userService.userExport(request);ExcelExportUtils.writeExcelToResponse(response);}}
业务层逻辑
@Servicepublic class UserServiceImpl implements UserService {@Overridepublic ExcelExportResponse userExport(UserExportRequest request) {List<User> dataList;// 处理行筛选逻辑if (CollectionUtil.isEmpty(request.getUserIdList())) {dataList = getUserList(); // 导出全部数据} else {dataList = getUserList(request.getUserIdList()); // 按ID筛选}// 构建导出数据return ExcelExportUtils.build(dataList, request);}// 模拟数据获取private List<User> getUserList() {List<User> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new User(i, "用户名-" + i, 20 + i, "地址-" + i));}return list;}}
导出工具类
public class ExcelExportUtils {public static ExcelExportResponse build(List<?> dataList, ExcelExportRequest request) {ExcelExportResponse result = new ExcelExportResponse();result.setExcelName(request.getExcelName());List<ExcelSheet> sheetList = new ArrayList<>();ExcelSheet sheet = new ExcelSheet();sheet.setSheetName(request.getSheetName());// 构建表头(保持字段顺序)sheet.setHeadList(buildSheetHeadList(request.getFieldList()));// 构建数据行(通过反射获取字段值)sheet.setDataList(buildSheetDataList(dataList, request.getFieldList()));sheetList.add(sheet);result.setSheetList(sheetList);return result;}private static List<ExcelSheet.ExcelHead> buildSheetHeadList(List<ExcelExportField> fields) {return fields.stream().map(field -> new ExcelSheet.ExcelHead(field.getFieldName(), field.getFieldDesc())).collect(Collectors.toList());}// 反射获取对象字段值private static List<Map<String, String>> buildSheetDataList(List<?> dataList, List<ExcelExportField> fields) {return dataList.stream().map(obj -> {Map<String, String> row = new HashMap<>();fields.forEach(field -> {Object value = ReflectUtil.getFieldValue(obj, field.getFieldName());row.put(field.getFieldName(), Objects.toString(value, ""));});return row;}).collect(Collectors.toList());}// 响应输出处理public static void writeExcelToResponse(ExcelExportResponse result) throws IOException {HttpServletResponse response = getResponse();response.setContentType("application/vnd.ms-excel");response.setHeader("Content-Disposition","attachment; filename=" + URLEncodeUtil.encode(result.getExcelName() + ".xlsx"));try (ExcelWriter writer = EasyExcel.write(response.getOutputStream()).build()) {result.getSheetList().forEach(sheet -> {WriteSheet writeSheet = EasyExcel.writerSheet(sheet.getSheetName()).build();// 写入表头与数据writer.write(buildEasyExcelData(sheet), writeSheet);});}}}

三、方案优势分析

  1. 灵活性:通过fieldList实现导出字段的动态排序与筛选,适应不同业务视图需求
  1. 扩展性:支持添加多 Sheet、数据格式化(如日期转换)、样式配置等扩展功能
  1. 易用性:前端可视化勾选操作,后端自动处理反射映射,降低使用门槛
  1. 性能优化:通过工具类封装重复逻辑,减少代码冗余,提升开发效率

四、应用场景

  • 数据报表导出:支持不同角色用户自定义报表字段
  • 批量数据下载:结合行筛选功能实现精准数据提取
  • 系统对接:为第三方系统提供标准化 Excel 数据输出接口

通过该方案,开发者可快速实现具备灵活配置能力的 Excel 导出功能,同时保持代码的可维护性与扩展性。实际应用中可根据业务需求,进一步扩展数据格式化、单元格样式、多语言支持等高级功能。

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

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

相关文章

安全壁垒 - K8s 的 RBAC、NetworkPolicy 与 SecurityContext 精要

安全壁垒 - K8s 的 RBAC、NetworkPolicy 与 SecurityContext 精要 如果说 Kubernetes 是我们构建云原生应用的“城市”,那么我们已经学会了如何规划道路(网络)、建设住宅(Pod 调度)、提供水电(存储)以及智能调节城市规模(自动伸缩)。现在,是时候为这座城市安装“城门…

服务器开放端口如何设置,本地内网开通应用端口让外网访问连接步骤

在互联网时代&#xff0c;服务器扮演着至关重要的角色&#xff0c;为了让本地搭建部署的服务器能够正常提供互联网服务&#xff0c;我们需要开放特定端口以供外部网络的客户端访问&#xff0c;本文将带领大家深入了解内网本地服务器如何设置端口开放给公网访问。 服务器开放端…

【深度学习新浪潮】什么是上下文工程?

什么是上下文工程? 上下文工程(Context Engineering) 是指通过设计、优化与大语言模型(LLM)交互时的输入内容(即“上下文”),引导模型生成更符合预期、更精准回答的系统性方法。这里的“上下文”通常包括 提示词(Prompt)、示例(Few-Shot Examples)、历史对话记录、…

Ansible ad-hoc模式常用三大模块“script、shell、command“应用笔记

script模块 - 外卖厨师 相当于你把做好的菜谱&#xff08;脚本文件&#xff09;分发给别人厨房执行 适合场景&#xff1a; ✓ 需要复杂菜谱&#xff08;多步骤脚本&#xff09; ✓ 保证每家分店味道一致&#xff08;环境标准化&#xff09; 示例&#xff1a;把《红烧肉制作指…

双重检查锁定实现的单例模式为什么需要volatile

今天介绍一下 单例模式(Singleton) 应用场景&#xff1a;配置管理类、数据库连接池、线程池 实现方式&#xff1a;双重检查锁定、静态内部类、枚举 public class ConfigManager {private static volatile ConfigManager instance;private ConfigManager() {}public static C…

Flink流水线+Gravitino+Paimon集成

1.数据源管理 1.1 添加Gravitino数据源 添加成功之后&#xff0c;会在Gravitino中创建一个名为配置的中的meatalake 1.2. 添加Paimon数据源 属性gravitinoId可以关联前面创建的Gravitino数据源&#xff0c;关联后&#xff0c;会在gravitino下创建一个该数据源的catalog。 2. …

关系代数详解与SQL示例

关系代数详解与SQL示例 关系代数是关系数据库的理论基础&#xff0c;它提供了一组操作符用于操作关系&#xff08;表&#xff09; 1. 基本操作 1.1 选择 (Selection, σ) 选择操作从关系中选择满足特定条件的元组&#xff08;行&#xff09;。 关系代数表示&#xff1a;σ条…

Android14音频子系统-Linux音频子系统ASoC-ALSA

文章目录 1、术语2、概述1&#xff09;资料快车 3、预备工作1&#xff09;codec - UDA1340 - 硬件规格2&#xff09;ASOC-ALSA代码重点目录介绍3&#xff09;ASOC-ALSA层级介绍4&#xff09;了解基本的软硬件架构 4、数据结构5、代码分析1&#xff09;Machine1、总体流程介绍2、…

零基础入门Java+大模型(持续更新)

0.初始一些常见的概念 AI&#xff1a;人工智能 大模型划分&#xff1a;&#xff08;本章了解一下这个就行&#xff09;NLP模型-->自然语言模型&#xff08;AI现在爆火的原因&#xff0c;就是自然语言模型这一块取得了很大的成就&#xff09;。 LLM&#xff1a;大语言模型…

数据库系统总结

数据库系统概述 数据库系统&#xff08;Database System, DBS&#xff09;是用于高效管理、存储和检索数据的软件系统。 数据库系统的组成包括&#xff1a;数据库、硬件、软件、人员。 三级模式-两级映像 内模式&#xff1a;管理如何存储物理的数据&#xff0c;对数据的存储…

2026-软件工程-《软件质量测试与保证》-期末复习—习题汇总

题量: 20 满分: 100 作答时间:06-04 17:30至06-22 23:59 智能分析 80分 一. 单选题&#xff08;共10题&#xff0c;50分&#xff09; (单选题)白盒测试设计测试用例的依据是( )。 A. 代码逻辑结构 B. 代码注释说明 C. 需求规格说明书 D. 用户使用场景 我的答案:A:代码逻辑结构…

量化面试绿皮书:35. 蒙蒂霍尔问题

文中内容仅限技术学习与代码实践参考&#xff0c;市场存在不确定性&#xff0c;技术分析需谨慎验证&#xff0c;不构成任何投资建议。 35. 蒙蒂霍尔问题 蒙提霍尔问题是一个基于美国老电视节目《让我们做个交易》的概率谜题&#xff0c;该问题以该节目的主持人命名。假设你现在…

如何防范 SQL 注入攻击以及SQL 注入防范技巧

在互联网高度发展的时代&#xff0c;网络安全问题日益突出&#xff0c;SQL 注入攻击成为众多网站和应用程序面临的严重威胁之一。本文将详细介绍如何防范 SQL 注入攻击&#xff0c;通过多个关键方面的详细阐述&#xff0c;帮助开发者和网站管理者构建更安全的网络环境&#xff…

k8s从入门到放弃之数据存储

k8s从入门到放弃之数据存储 在Kubernetes中&#xff0c;数据存储主要通过持久卷&#xff08;Persistent Volumes, PVs&#xff09;和持久卷声明&#xff08;Persistent Volume Claims, PVCs&#xff09;机制来实现。这种设计允许存储与计算分离&#xff0c;使得容器可以根据需…

用户体验驱动的3D设计:从功能实现到情感共鸣的设计升级

你有没有想过&#xff1a; 为什么有些产品看起来“平平无奇”&#xff0c;却能赢得用户喜爱&#xff1f;同样是3D建模&#xff0c;为什么有些人做的模型总让人觉得“有温度”&#xff1f;设计师只是关注功能和结构就够了吗&#xff1f;还是应该让作品“打动人心”&#xff1f;…

【图床配置记录】——pigo+gitee+typroa

记录一下安装的过程 基本上blog1可以解决 blog1 但是如果pigo上面没有gitee 或者下载gitee没啥反应 手动安装&#xff0c;在c:/用户/用户名/AppData/Roaming/picgo这个目录下,以管理员身份打开powershell&#xff08;或cmd&#xff1b; 文件——以管理员打开ps——输入npm in…

Python问题汇总(发个库存)

常见问题&#xff1a; 文章目录 IDE与1.如何注释2.python运行代码后没有输出&#xff0c;但无报错&#xff0c;有exit code 0标志3.导入txt文件&#xff0c;但是出现错误&#xff1a;UnicodeDecodeError: gbk codec cant decode byte 0x80 in position 205: illegal multibyte…

Java课后习题(编程题)

第一题&#xff1a; import java.util.Scanner;class Test {public static void main(String args[]) {Scanner scnew Scanner(System.in);System.out.println("请输入一个x值&#xff1a;");int xsc.nextInt();int y0;if(x>0){y3*x-1;}else if(x<0){y2*x-1;}e…

Spring--spring事务在什么情况下会失效,以及对应的解决方案

前言 一般失效是使用了Transaction注解的情况下&#xff0c;这篇博客就带你详解一下&#xff0c;哪些情况下注解会失效&#xff0c;在开发过程中要避免这些问题和可以及时发现这些问题&#xff0c;并且知道如何去规避和解决 一、Spring事务的基本原理 在深入了解事务失效的场…

在智慧教育行业中,OPS插拔式电脑启到什么作用

在全球数字化浪潮的推动下&#xff0c;教育行业正经历前所未有的深刻变革。目前&#xff0c;智慧校园已不再是简单的信息化升级&#xff0c;而是涵盖AI、云计算、大数据、物联网等技术的系统性创新。而作为智慧教育发展的助手之一——国产OPS插拔式电脑&#xff0c;能助力传统教…