分页查询和分页缓存查询,List<Map<String, Object>>遍历和Map遍历

本文介绍了一种实现分页查询的方法,并通过ROWNUM进行限制。此外还展示了如何使用缓存来提高查询效率,包括如何初始化缓存及从缓存中检索数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分页查询

String sql = "返回所有符合条件记录的待分页SQL语句";
 int start = (page - 1) * limit + 1;
 int end = page * limit;
 sql = "select * from (select fulltable.*, ROWNUM RN from (" + sql + ") fulltable where ROWNUM <= " + end + ") where RN >= " + start;

分页缓存查询

private static List<Map<String, Object>> storageList;
    @PostConstruct//在需要自动启动的方法前加注释 @PostConstruct
    @Override
    public void refreshStorageMemory() {
        String sql = "select * from DM_STORAGE";
        storageList = dmJdbcTemplate.queryForList(sql);
    }
    @Override
    public List<Map<String, Object>> selectStorage(Integer page, Integer limit, Map<String, Object> operator) {
        if (page != null && limit != null && limit > 0) {
            int start = (page - 1) * limit;
            int end = page * limit;
            if (end > storageList.size()) {
                end = storageList.size();
            }
            return storageList.subList(start, end);
        }
        else {
            return storageList;
        }
    }

List<Map<,>>遍历取出Map

Map<String, Object> matAuxPlanRec;
            for (int i = 0; i < matAuxPlanRecList.size(); i++) {
                matAuxPlanRec = matAuxPlanRecList.get(i);
}

 


Map的get()方法获取key对应的value:String UNIT_ = (String) matAuxPlanRec.get("UNIT_");
Map遍历:

for (Map.Entry<String, Object> entry : matAuxPlanRec .entrySet()) {
            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}

 

转载于:https://www.cnblogs.com/sunnyroot/p/5930530.html

历史记录 avater 若依集成EasyPOI 实现创建多个工作薄list集合对象下载导出详细代码 在同一个EXPOR 回答 在若依框架中集成 EasyPOI 实现导出多个 List 数据为多个工作簿(Sheet)并保存在同一 Excel 文件中,可以通过以下步骤实现。EasyPOI 提供了便捷的注解方式 API 来处理 Excel 文件的导出操作,特别适合处理多个工作簿导出的需求。 实体类定义 首先,定义用于导出的数据模型类,例如 User Product,并使用 EasyPOI 的注解来标记字段对应的 Excel 列。 Java import cn.afterturn.easypoi.excel.annotation.Excel; public class User { @Excel(name = "用户ID") private int id; @Excel(name = "用户名") private String name; @Excel(name = "年龄") private int age; // Getter Setter } Java import cn.afterturn.easypoi.excel.annotation.Excel; public class Product { @Excel(name = "产品ID") private int productId; @Excel(name = "产品名称") private String productName; @Excel(name = "价格") private double price; // Getter Setter } 控制器方法实现导出功能 在控制器中编写导出方法,使用 ExcelExportUtil 类来构建 Excel 文件,并将多个 List 数据导出为多个工作簿。 Java import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.TemplateExportParams; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; @Controller public class ExportController { @GetMapping("/export") public void exportData(HttpServletResponse response) throws IOException { // 构建用户数据 List<User> userList = new ArrayList<>(); userList.add(new User(1, "张三", 25)); userList.add(new User(2, "李四", 30)); // 构建产品数据 List<Product> productList = new ArrayList<>(); productList.add(new Product(101, "手机", 2999.99)); productList.add(new Product(102, "电脑", 8999.99)); // 构建参数 Map<String, Object> map = new HashMap<>(); map.put("title", "数据导出"); // 构建用户工作簿参数 ExportParams userExportParams = new ExportParams("用户信息", "用户"); map.put("userList", ExcelExportUtil.exportExcel(userExportParams, User.class, userList)); // 构建产品工作簿参数 ExportParams productExportParams = new ExportParams("产品信息", "产品"); map.put("productList", ExcelExportUtil.exportExcel(productExportParams, Product.class, productList)); // 导出 Excel 文件 Workbook workbook = ExcelExportUtil.exportExcel(map, "template.xlsx"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=data.xlsx"); workbook.write(response.getOutputStream()); } } 模板文件配置 如果需要使用模板文件,可以在项目资源目录中放置一个名为 template.xlsx 的文件,该文件可以包含一些固定的样式或格式,EasyPOI 会在此基础上填充数据。 Maven 依赖配置 确保在 pom.xml 文件中添加 EasyPOI 的依赖项: XML <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>4.4.0</version> </dependency> 注意事项 模板文件:如果不需要模板文件,可以直接使用 ExcelExportUtil.exportExcel 方法生成 Excel 文件。 响应处理:在控制器方法中,通过 HttpServletResponse 将生成的 Excel 文件写入响应流,以便浏览器下载。 异常处理:在实际应用中,建议添加异常处理逻辑,以确保在发生错误时能够正确返回错误信息。 通过上述步骤,可以在若依框架中集成 EasyPOI 实现导出多个 List 数据为多个工作簿,并保存在同一 Excel 文件中。这种方法不仅代码简洁,而且易于维护扩展。 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 如何在若依框架中实现Excel文件的导入功能? 思维导图 参考来源 优快云 优快云 若依集成EasyPOI 实现创建多个工作薄list集合对象下载导出详细代码 在同一个EXPOR 回答 3.1 实体类定义与注解配置 在实现导出多个工作簿(Sheet)之前,需要定义用于导出的数据模型类。EasyPOI 通过注解方式实现字段与 Excel 列的映射。 Java import cn.afterturn.easypoi.excel.annotation.Excel; public class User { @Excel(name = "用户ID") private int id; @Excel(name = "用户名") private String name; @Excel(name = "年龄") private int age; // Getter Setter } Java import cn.afterturn.easypoi.excel.annotation.Excel; public class Product { @Excel(name = "产品ID") private int productId; @Excel(name = "产品名称") private String productName; @Excel(name = "价格") private double price; // Getter Setter } 3.2 控制器方法实现导出多个工作簿 在控制器中编写导出方法,通过 ExcelExportUtil 构建 Excel 文件,并将多个 List 数据导出为多个工作簿。通过 ExportParams 设置工作簿名称标题。 Java import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; @Controller public class ExportController { @GetMapping("/export") public void exportData(HttpServletResponse response) throws IOException { // 构建用户数据 List<User> userList = new ArrayList<>(); userList.add(new User(1, "张三", 25)); userList.add(new User(2, "李四", 30)); // 构建产品数据 List<Product> productList = new ArrayList<>(); productList.add(new Product(101, "手机", 2999.99)); productList.add(new Product(102, "电脑", 8999.99)); // 构建参数 Map<String, Object> map = new HashMap<>(); map.put("title", "数据导出"); // 构建用户工作簿参数 ExportParams userExportParams = new ExportParams("用户信息", "用户"); map.put("userList", ExcelExportUtil.exportExcel(userExportParams, User.class, userList)); // 构建产品工作簿参数 ExportParams productExportParams = new ExportParams("产品信息", "产品"); map.put("productList", ExcelExportUtil.exportExcel(productExportParams, Product.class, productList)); // 导出 Excel 文件 Workbook workbook = ExcelExportUtil.exportExcel(map, "template.xlsx"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=data.xlsx"); workbook.write(response.getOutputStream()); } } 3.3 Maven 依赖配置 确保在 pom.xml 文件中添加 EasyPOI 的依赖项,以便支持导出功能。 XML <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>4.4.0</version> </dependency> 3.4 模板文件配置(可选) 如果需要使用模板文件,可以在项目资源目录中放置一个名为 template.xlsx 的文件,该文件可以包含一些固定的样式或格式,EasyPOI 会在此基础上填充数据。若不需要模板,可直接使用 ExcelExportUtil.exportExcel 方法生成 Excel 文件。 3.5 响应处理与异常处理 在控制器方法中,通过 HttpServletResponse 将生成的 Excel 文件写入响应流,以便浏览器下载。在实际应用中,建议添加异常处理逻辑,以确保在发生错误时能够正确返回错误信息1。 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 如何在若依框架中实现Excel文件的导入功能? 思维导图 参考来源 优快云 优快云 优快云 优快云 若依集成EasyPOI 实现创建多个工作薄list集合对象下载导出详细代码 在同一个EXPOR 回答 3.1 实体类定义与注解配置 在实现导出多个工作簿(Sheet)之前,需要定义用于导出的数据模型类。EasyPOI 通过注解方式实现字段与 Excel 列的映射。 Java import cn.afterturn.easypoi.excel.annotation.Excel; public class User { @Excel(name = "用户ID") private int id; @Excel(name = "用户名") private String name; @Excel(name = "年龄") private int age; // Getter Setter } Java import cn.afterturn.easypoi.excel.annotation.Excel; public class Product { @Excel(name = "产品ID") private int productId; @Excel(name = "产品名称") private String productName; @Excel(name = "价格") private double price; // Getter Setter } 3.2 控制器方法实现导出多个工作簿 在控制器中编写导出方法,通过 ExcelExportUtil 构建 Excel 文件,并将多个 List 数据导出为多个工作簿。通过 ExportParams 设置工作簿名称标题。 Java import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.*; @Controller public class ExportController { @GetMapping("/export") public void exportData(HttpServletResponse response) throws IOException { // 构建用户数据 List<User> userList = new ArrayList<>(); userList.add(new User(1, "张三", 25)); userList.add(new User(2, "李四", 30)); // 构建产品数据 List<Product> productList = new ArrayList<>(); productList.add(new Product(101, "手机", 2999.99)); productList.add(new Product(102, "电脑", 8999.99)); // 构建参数 Map<String, Object> map = new HashMap<>(); map.put("title", "数据导出"); // 构建用户工作簿参数 ExportParams userExportParams = new ExportParams("用户信息", "用户"); map.put("userList", ExcelExportUtil.exportExcel(userExportParams, User.class, userList)); // 构建产品工作簿参数 ExportParams productExportParams = new ExportParams("产品信息", "产品"); map.put("productList", ExcelExportUtil.exportExcel(productExportParams, Product.class, productList)); // 导出 Excel 文件 Workbook workbook = ExcelExportUtil.exportExcel(map, "template.xlsx"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment;filename=data.xlsx"); workbook.write(response.getOutputStream()); } } 3.3 Maven 依赖配置 确保在 pom.xml 文件中添加 EasyPOI 的依赖项,以便支持导出功能。 XML <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>4.4.0</version> </dependency> 3.4 模板文件配置(可选) 如果需要使用模板文件,可以在项目资源目录中放置一个名为 template.xlsx 的文件,该文件可以包含一些固定的样式或格式,EasyPOI 会在此基础上填充数据。若不需要模板,可直接使用 ExcelExportUtil.exportExcel 方法生成 Excel 文件。 3.5 响应处理与异常处理 在控制器方法中,通过 HttpServletResponse 将生成的 Excel 文件写入响应流,以便浏览器下载。在实际应用中,建议添加异常处理逻辑,以确保在发生错误时能够正确返回错误信息1。 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 如何在若依框架中实现Excel文件的导入功能? 思维导图 参考来源 优快云 优快云 优快云 优快云 优快云 Workbook workbook = ExcelExportUtil.exportExcel(map, "template.xlsx"); 报错 回答 问题分析 在使用 EasyPOI 的 ExcelExportUtil.exportExcel(map, template.xlsx) 方法进行 Excel 导出时,可能会出现报错。这类问题通常与模板文件路径、数据结构匹配、EasyPOI 版本兼容性或模板格式有关。 1. 模板文件路径问题 若 template.xlsx 文件路径不正确,EasyPOI 将无法读取模板内容,导致导出失败。应确保模板文件存在于类路径下,例如 resources 目录,并通过 ClassPathResource 或相对路径加载。 Java String templatePath = "template.xlsx"; InputStream templateInputStream = new ClassPathResource(templatePath).getInputStream(); 在调用 ExcelExportUtil.exportExcel(map, templatePath) 时,若 templatePath 是相对路径,应确保程序运行时该路径可访问。若使用绝对路径,需确保路径字符串格式正确,避免因路径拼接错误导致文件读取失败1。 2. 数据结构与模板字段不匹配 EasyPOI 在导出时会将 map 中的键与模板中的字段进行匹配。若 map 中的键名与模板中定义的占位符不一致,或缺少必要的字段,会导致数据填充失败。 例如,若模板中包含 ${userList} ${productList},则 map 中必须包含这两个键,并且对应的值应为 List 类型。 Java map.put("userList", userList); map.put("productList", productList); 此外,若使用 @Excel 注解定义字段映射,需确保注解中的 name 属性与模板中的列标题一致,否则数据无法正确导出2。 3. EasyPOI 版本兼容性问题 不同版本的 EasyPOI 对模板处理方式可能不同。例如,旧版本可能不支持某些模板语法或导出方式。建议使用稳定版本(如 4.4.0)以确保兼容性。 XML <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> </dependency> 若使用较新版本仍存在问题,可尝试回退至已验证可用的版本,或查阅官方文档确认模板使用方式是否发生变化。 4. 模板文件格式问题 EasyPOI 支持 .xls .xlsx 两种格式的模板文件,但在使用时需注意: 若使用 .xls 格式,需确保模板未被损坏,且字段占位符格式正确(如 ${userList})。 若使用 .xlsx 格式,需注意其对样式合并单元格的支持更好,但对模板结构要求更高。 若模板中包含复杂的样式、合并单元格或条件格式,可能导致 EasyPOI 解析失败。建议先使用简单模板进行测试,再逐步增加复杂度。 5. 异常日志分析 当调用 ExcelExportUtil.exportExcel(map, template.xlsx) 报错时,应优先查看控制台日志或日志文件,定位具体错误信息。常见异常包括: FileNotFoundException:表示模板文件未找到,需检查路径是否正确。 NullPointerException:表示 map 或模板对象为空,需检查数据是否正确填充。 ExcelDataConvertException:表示数据类型转换失败,需检查实体类字段类型与模板字段是否匹配。 通过日志可快速定位问题根源,避免盲目修改代码。 6. 完整示例代码 以下为一个完整的导出方法示例,包含模板路径加载、数据填充与响应输出: Java @GetMapping("/export") public void exportData(HttpServletResponse response) throws IOException { // 构建用户数据 List<User> userList = Arrays.asList( new User(1, "张三", 25), new User(2, "李四", 30) ); // 构建产品数据 List<Product> productList = Arrays.asList( new Product(101, "手机", 2999.99), new Product(102, "电脑", 8999.99) ); // 构建参数 Map<String, Object> map = new HashMap<>(); map.put("userList", userList); map.put("productList", productList); // 加载模板文件 String templatePath = "template.xlsx"; InputStream templateInputStream = new ClassPathResource(templatePath).getInputStream(); // 导出 Excel 文件 Workbook workbook = ExcelExportUtil.exportExcel(map, templateInputStream); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=data.xlsx"); workbook.write(response.getOutputStream()); } 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 如何在若依框架中实现Excel文件的导入功能? 思维导图 参考来源 优快云 优快云 Workbook workbook = ExcelExportUtil.exportExcel(map, "template.xlsx"); 无法传值 Map<String, Object> 回答 在使用 ExcelExportUtil.exportExcel(map, template.xlsx) 时,若出现 map 传值失败的问题,通常是因为数据结构与模板字段不匹配或导出方式未正确使用。为确保 Map<String, Object> 参数能够正确传递给模板文件,需遵循以下要点。 使用 Map 传递数据的正确方式 ExcelExportUtil.exportExcel 方法支持将 Map<String, Object> 数据结构传递给模板进行填充。该 map 中的键应与模板中的占位符名称一致,值则为相应的数据对象或集合。例如,若模板中包含 ${userList} ${productList},则 map 中应包含这两个键,并且对应的值应为 List 类型。 Java Map<String, Object> map = new HashMap<>(); map.put("userList", userList); map.put("productList", productList); 通过这种方式,EasyPOI 能够正确识别并填充模板中的数据字段1。 创建多个 Sheet 的方式 若需要在导出时创建多个 Sheet,可使用 List<Map<String, Object>> 结构,每个 Map 表示一个 Sheet 的配置。每个配置应包含 sheetName、data entity 等信息。例如: Java List<Map<String, Object>> sheetsList = new ArrayList<>(); public Map<String, Object> createSheet(String sheetName, List<ExportVo> dataList) { if (StringUtils.isBlank(sheetName) || CollectionUtils.isEmpty(dataList)) { return new HashMap<>(); } ExportParams exportParams = new ExportParams(); exportParams.setSheetName(sheetName); exportParams.setHeight((short) 8); exportParams.setStyle(ExcelExportStylerDefaultImpl.class); Map<String, Object> map = new HashMap<>(); map.put("data", dataList); map.put("entity", ExportVo.class); map.put("title", exportParams); return map; } 将多个 Sheet 的配置加入 sheetsList 后,可通过 ExcelExportUtil.exportExcel(sheetsList, templatePath) 进行导出。 处理主从表结构 在处理主从表结构时,可使用 List<Map<String, Object>> 来表示每一行数据,其中主表字段从表字段均作为键值对存在。例如: Java List<Map<String, Object>> dataList = new ArrayList<>(); Map<String, Object> row = new HashMap<>(); row.put("mainField", "主表数据"); row.put("detailField1", "详情1"); row.put("detailField2", "详情2"); dataList.add(row); 通过这种方式,可实现动态导出主从表数据,且无需固定从表字段数量4。 示例代码 以下为一个完整的导出方法示例,包含模板路径加载、数据填充与响应输出: Java @GetMapping("/export") public void exportData(HttpServletResponse response) throws IOException { // 构建用户数据 List<User> userList = Arrays.asList( new User(1, "张三", 25), new User(2, "李四", 30) ); // 构建产品数据 List<Product> productList = Arrays.asList( new Product(101, "手机", 2999.99), new Product(102, "电脑", 8999.99) ); // 构建参数 Map<String, Object> map = new HashMap<>(); map.put("userList", userList); map.put("productList", productList); // 加载模板文件 String templatePath = "template.xlsx"; InputStream templateInputStream = new ClassPathResource(templatePath).getInputStream(); // 导出 Excel 文件 Workbook workbook = ExcelExportUtil.exportExcel(map, templateInputStream); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=data.xlsx"); workbook.write(response.getOutputStream()); } 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 如何在若依框架中实现Excel文件的导入功能? 思维导图 参考来源 优快云 优快云 优快云 优快云 优快云 Caused by: java.lang.NoClassDefFoundError: org/apache/poi/xssf/usermodel/XSSFCellStyle 怎样解决 回答 依赖缺失问题分析 java.lang.NoClassDefFoundError: org.apache.poi.xssf.usermodel.XSSFCellStyle 通常表明项目中缺少 Apache POI 的相关依赖。在使用 Apache POI 进行 Excel 导出时,必须确保所有必要的依赖库都已正确引入,尤其是 poi-ooxml 模块,该模块包含了 XSSFCellStyle 等用于处理 .xlsx 格式文件的核心类1。 依赖配置建议 在 Maven 项目中,需在 pom.xml 文件中添加如下依赖以确保 POI 的完整支持: XML <!-- Apache POI 核心依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <!-- POI 对 OOXML 格式(xlsx)的支持 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <!-- 可选:处理 Excel 样式复杂格式 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>5.2.3</version> </dependency> 上述依赖确保了 XSSFCellStyle、XSSFWorkbook、XSSFSheet 等类的可用性,是构建 Excel 导出功能的基础1。 依赖冲突排查 若项目中已引入 POI 依赖但仍出现 NoClassDefFoundError,则可能是依赖版本冲突或被其他依赖覆盖。建议使用以下命令查看当前依赖树: Bash mvn dependency:tree 通过分析输出结果,可识别出是否存在多个版本的 POI 被引入,进而通过排除冲突依赖或统一版本号来解决该问题。 打包构建问题处理 在使用 jar 包部署时,若依赖未被正确打包进最终的可执行文件中,也可能导致运行时报错。确保构建插件配置如下,以将所有依赖打包进 jar 文件: XML <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> 通过该方式,可确保所有 POI 依赖在构建时被正确包含进 jar 包中,避免因依赖缺失导致的运行时错误1。 示例代码 以下为使用 POI 创建 Excel 文件并设置单元格样式的简单示例: Java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileOutputStream; import java.io.IOException; public class ExcelExportExample { public static void main(String[] args) throws IOException { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); // 创建单元格样式 CellStyle headerStyle = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true); headerStyle.setFont(font); // 创建表头 Row headerRow = sheet.createRow(0); Cell headerCell = headerRow.createCell(0); headerCell.setCellValue("姓名"); headerCell.setCellStyle(headerStyle); // 创建数据行 Row dataRow = sheet.createRow(1); Cell dataCell = dataRow.createCell(0); dataCell.setCellValue("张三"); // 写出到文件 try (FileOutputStream fos = new FileOutputStream("example.xlsx")) { workbook.write(fos); } workbook.close(); } } 相关提问 如何在Spring Boot中使用EasyPOI导出Excel文件? EasyPOI支持哪些Excel导出功能? 在若依框架中使用EasyPOI导出Excel时如何处理大数据量? 思维导图 参考来源 优快云 easypoi 异常 org.apache.poi.common.Duplicatable 怎样解决 回答 我们注意到用户提到了两个引用: 引用[1]提到了使用POI时遇到的POIXMLException,但用户当前的问题是关于org.apache.poi.common.Duplicatable异常。 引用[2]则详细描述了当创建过多单元格样式时,会超过Excel的限制(64000个样式),并提出了通过复制样式(使用Duplicatable接口的copy方法)来避免计数增加的解决方案。 用户当前的问题:EasyPOI 报错 org.apache.poi.common.Duplicatable 异常解决方法 分析:根据引用[2]的内容,这个异常通常是由于在Excel中创建了超过64000个样式导致的。因为每个样式都会占用一个样式ID,而Excel限制了最大样式数量。在POI中,每次调用workbook.createCellStyle()都会创建一个新的样式并增加计数器,当超过最大限制时就会抛出异常。 解决思路:避免创建过多的样式对象,而是复用已有的样式。或者,当需要微调样式时,不要创建全新的样式,而是复制一个已有的样式并做修改,这样复制的样式不会增加计数(因为使用了Duplicatable.copy()方法)。 具体步骤: 定位问题:检查代码中创建单元格样式的地方,特别是在循环中创建样式的情况。 修改样式创建方式:将创建样式移到循环外部,然后通过复制样式来创建新的样式,而不是每次都调用createCellStyle()。 示例代码(引用[2]中的解决方案): 原始代码可能如下(在循环内部创建样式): Java for (int i = 0; i < 100000; i++) { CellStyle style = workbook.createCellStyle(); // 每次循环都创建一个新样式,计数增加 // ... 设置样式属性 cell.setCellStyle(style); } 修改后: Java // 先创建一个基础样式 CellStyle baseStyle = workbook.createCellStyle(); // 设置基础样式的一些通用属性,比如边框、字体等 for (int i = 0; i < 100000; i++) { // 复制基础样式,而不是创建新的样式 CellStyle newStyle = (CellStyle) ((Duplicatable) baseStyle).copy(); // 如果需要,可以修改newStyle的某些属性(注意:不能修改基础样式,否则会影响所有复制的样式) // 例如:设置不同的背景颜色等 // newStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); cell.setCellStyle(newStyle); } 注意:copy()方法返回的是Object,需要强制转换为CellStyle,并且前提是baseStyle实现了Duplicatable接口(实际上POI内置的样式类都实现了这个接口)。 这样,虽然我们创建了多个样式对象,但是通过复制的方式不会增加工作簿中样式ID的计数,因此可以避免超过64000个样式的限制。 在EasyPOI中的处理: EasyPOI是对POI的封装,因此我们可能需要查看EasyPOI的导出代码,看是否有地方在循环中创建样式。如果使用的是EasyPOI的注解导出,通常不会直接操作样式,但如果是自定义样式导出(比如使用IExcelExportStyler),则需要注意。 如果是在自定义的IExcelExportStyler实现中,需要避免在getStyles方法中频繁创建样式。EasyPOI在导出时会调用getStyles方法获取样式,因此应该在这个方法内部缓存样式,避免每次调用都创建新的样式。 例如,在IExcelExportStyler的实现类中,可以这样写: Java public class MyExcelExportStyler extends AbstractExcelExportStyler implements IExcelExportStyler { private CellStyle headerStyle; private CellStyle titleStyle; // 其他样式 public MyExcelExportStyler(Workbook workbook) { super(workbook); // 初始化样式 headerStyle = createHeaderStyle(); titleStyle = createTitleStyle(); // ... } @Override public CellStyle getHeaderStyle(short color) { // 如果需要根据颜色变化,可以复制基础样式并修改颜色 CellStyle style = (CellStyle) ((Duplicatable) headerStyle).copy(); style.setFillForegroundColor(color); return style; } // 其他方法类似 } 这样,在初始化时创建一次基础样式,后续通过复制来创建新样式,避免计数增加。 总结解决方法: 检查代码中创建单元格样式的位置,避免在循环中直接调用workbook.createCellStyle()。 使用Duplicatable.copy()方法来复制样式,而不是创建新样式。 在EasyPOI中,如果自定义了IExcelExportStyler,确保在构造方法中初始化基础样式,并在需要时复制这些样式。 通过以上方法,可以解决org.apache.poi.common.Duplicatable相关的异常(实际上是样式数量超过限制的问题)。 相关问题 §§相关问题§§ 如何优化EasyPOI导出大量数据时的性能? 在EasyPOI中如何自定义复杂的Excel样式? EasyPOI导出Excel时如何避免内存溢出? 解决 EasyPOI 报错 org.apache.poi.common.Duplicatable 异常 该异常通常是由于 Excel 样式数量超过限制(最大 64,000 个样式)导致的,以下是完整解决方案: 问题原因分析 Excel 样式限制:每个 .xlsx 文件最多支持 64,000 种独立样式2 样式创建方式:每次调用 workbook.createCellStyle() 会生成新样式并计数 循环中创建样式:在数据遍历中重复创建样式会快速耗尽样式配额 Java // 错误示范:在循环中创建样式(耗尽样式配额) for (Row row : sheet) { CellStyle style = workbook.createCellStyle(); // 每次循环都创建新样式 style.setFillForegroundColor(IndexedColors.YELLOW.index); cell.setCellStyle(style); } 解决方案:使用样式复制技术 通过 Duplicatable.copy() 复制样式而非创建新样式,避免计数增加: 1. 基础样式池(推荐) Java // 创建基础样式池 private Map<String, CellStyle> styleCache = new HashMap<>(); private CellStyle getOrCreateStyle(Workbook workbook, String styleKey) { if (!styleCache.containsKey(styleKey)) { CellStyle baseStyle = workbook.createCellStyle(); // 配置基础样式... styleCache.put(styleKey, baseStyle); } // 复制样式(不增加计数) return (CellStyle) ((Duplicatable) styleCache.get(styleKey)).copy(); } // 使用示例 for (Row row : sheet) { CellStyle style = getOrCreateStyle(workbook, "YELLOW_BG"); cell.setCellStyle(style); // 复制样式而非创建 } 2. 动态样式复制 Java // 创建基础样式(仅1次) CellStyle baseStyle = workbook.createCellStyle(); baseStyle.setFillForegroundColor(IndexedColors.YELLOW.index); for (Row row : sheet) { // 复制样式(关键步骤) CellStyle newStyle = (CellStyle) ((Duplicatable) baseStyle).copy(); // 按需修改特定属性(可选) if (specialCase) { newStyle.setFillForegroundColor(IndexedColors.RED.index); } cell.setCellStyle(newStyle); // 应用复制样式 } 优化建议 样式复用:相同样式的单元格共享样式实例 按需创建:仅在样式差异处创建新样式 样式池管理:对常用样式进行缓存(如标题样式、数据样式等) 批量处理:大数据量导出时使用 SXSSFWorkbook(流式导出) Java // 启用流式导出(避免OOM) SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保留100行在内存中 验证效果 通过以下代码检查样式计数: Java int styleCount = workbook.getNumCellStyles(); System.out.println("当前样式数量: " + styleCount); // 应保持在合理范围 关键点:使用 Duplicatable.copy() 复制样式不会增加 workbook 的样式计数器,从而避免触发 64,000 的样式上限2。 相关提问 EasyPOI 导出大数据量时如何避免内存溢出? 如何在 EasyPOI 中实现动态单元格样式? EasyPOI 与原生 POI 的性能对比如何? 思维导图 参考来源 优快云 优快云 若依框架 怎样Excel导出创个多工作薄导出不同数据详细可用代码
07-24
------------------------ test.html ------------------------ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材库存管理系统</title> <link rel="stylesheet" href="../js/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css"> <link rel="stylesheet" href="../js/bootstrap-icons-1.8.1/bootstrap-icons.css"> <link rel="stylesheet" href="../css/test.css"> </head> <body> <div class="container py-4"> <!-- 标题部分 --> <div class="text-center mb-4"> <h1 class="text-primary"><i class="bi bi-boxes"></i> 板材库存管理系统</h1> <p class="text-muted">查询订单、产品、板材及库存信息</p> </div> <!-- 统计卡片 --> <div class="row mb-4" id="statsCards"> <!-- 卡片内容由JS动态生成 --> </div> <!-- 搜索区域 --> <div class="card search-section mb-4"> <div class="card-header"> <h5 class="mb-0"><i class="bi bi-search me-2"></i>高级搜索</h5> </div> <div class="card-body"> <div class="row g-3"> <!-- 订单搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-clipboard-search search-icon"></i> <input type="text" class="form-control with-icon" id="orderSearch" placeholder="搜索订单号..." aria-label="订单号搜索"> </div> </div> <!-- 产品搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-grid search-icon"></i> <input type="text" class="form-control with-icon" id="productSearch" placeholder="搜索产品编号..." aria-label="产品编号搜索"> </div> </div> <!-- 板材搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-box search-icon"></i> <input type="text" class="form-control with-icon" id="materialSearch" placeholder="搜索板材ID或材质..." aria-label="板材搜索"> </div> </div> <!-- 木皮搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-tree search-icon"></i> <input type="text" class="form-control with-icon" id="woodSearch" placeholder="搜索木皮名称..." aria-label="木皮搜索"> </div> </div> <!-- 厚度搜索 --> <div class="col-md-4"> <div class="search-control"> <i class="bi bi-arrows-vertical search-icon"></i> <input type="number" class="form-control with-icon" id="thicknessSearch" placeholder="厚度(mm)" min="0" step="0.1"> </div> </div> <!-- 库存范围搜索 --> <div class="col-md-4"> <div class="input-group"> <span class="input-group-text"><i class="bi bi-box"></i></span> <input type="number" class="form-control" id="minStock" placeholder="最小库存" min="0"> <input type="number" class="form-control" id="maxStock" placeholder="最大库存" min="0"> <button class="btn btn-primary" type="button" id="stockStatusBtn"> <i class="bi bi-search"></i> </button> </div> </div> <!-- 高级搜索按钮 --> <div class="col-12 text-end"> <button class="btn btn-sm btn-outline-secondary" id="resetFilters"> <i class="bi bi-x-circle"></i> 重置筛选 </button> </div> </div> </div> </div> <!-- 结果区域 --> <div class="card"> <div class="card-header d-flex justify-content-between align-items-center"> <h5 class="mb-0"><i class="bi bi-table me-2"></i>查询结果</h5> <div class="text-secondary"> <span id="resultCount">0</span> 条记录 <span class="ms-2"><i class="bi bi-info-circle"></i> <small>数据更新时间: <span id="lastUpdate">--:--:--</span></small></span> </div> </div> <div class="card-body result-section"> <div class="table-responsive"> <table class="table table-hover" id="resultsTable"> <thead class="table-light sticky-top"> <tr> <th data-sort="dingdan.number">订单号 <span class="sort-indicator"></span></th> <th data-sort="chanpin.bianhao">产品信息 <span class="sort-indicator"></span></th> <th data-sort="dingdan_chanpin.shuliang">产品数量 <span class="sort-indicator"></span></th> <th data-sort="zujian.name">组件 <span class="sort-indicator"></span></th> <th data-sort="bancai.id">板材 <span class="sort-indicator"></span></th> <th data-sort="chanpin_zujian.one_howmany">单件用量 <span class="sort-indicator"></span></th> <th data-sort="orderUsage">订单用量 <span class="sort-indicator"></span></th> <th data-sort="kucun.shuliang">库存数量 <span class="sort-indicator"></span></th> <th>操作</th> </tr> </thead> <tbody id="resultBody"> <tr id="loadingRow"> <td colspan="9" class="text-center py-5"> <div class="d-flex align-items-center justify-content-center"> <div class="spinner-border text-primary" role="status"> <span class="visually-hidden">加载中...</span> </div> <div class="ms-3">正在加载数据,请稍候...</div> </div> </td> </tr> </tbody> </table> </div> <div id="noResults" class="no-results text-center py-5" style="display: none;"> <div> <i class="bi bi-inboxes text-muted" style="font-size: 3rem;"></i> <h4 class="mt-3 text-muted">没有找到匹配的记录</h4> <p class="text-muted">请尝试调整您的搜索条件</p> </div> </div> <!-- 分页控件 --> <nav aria-label="Results pagination" class="mt-3"> <ul class="pagination justify-content-center" id="pagination"> <!-- 分页由JS动态生成 --> </ul> </nav> </div> </div> </div> <!-- 模态框 - 板材详情 --> <div class="modal fade" id="materialDetailModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">板材详情</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body" id="materialDetailContent"> <!-- 详情内容由JS动态生成 --> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button> </div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="../js/bootstrap-5.3.0-alpha1-dist/js/bootstrap.bundle.min.js"></script> <script src="../js/dingdan.js"></script> </body> </html> ------------------------ DataManager.js ------------------------ //这个函数不许改,也禁止废话,属性名其他命名都哼规范不会出现意外, function resolveDataReferences(data) { console.log(data) // 获取 data 对象的所有顶层键 const keys = Object.keys(data); // 遍历每个顶层键(如 users, posts 等) for (const key of keys) { const entities = data[key]; // 遍历该顶层键下的每个实体(如每个 user 或 post) for (const entity of entities) { // 遍历实体的每个属性 for (const attribute in entity) { if (entity?.hasOwnProperty(attribute)) { var trpe=attribute?.replace(/\d/g, ''); // 确保属性属于当前实体 if (Array.isArray(entity[attribute])) { if(data[trpe]==null){ trpe+="s" } // 如果属性是一个数组,则将数组中的每个 ID 替换为对应的实际对象 entity[attribute] = entity[attribute].map(item => data[trpe ]?.find(updateItem => updateItem.id === item.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { // 如果属性是一个对象,则将其替换为对应的实际对象 entity[attribute] = data[trpe + "s"]?.find(updateItem => updateItem.id === entity[attribute].id); } } } } } return data; } /** * 数据管理器类,负责与后端API通信并管理数据 */ class DataManager { constructor(baseUrl) { this.baseUrl = baseUrl; this.data = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_bancais:[], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [] }; this.isSyncing = false; this.lastSync = null; this.callbacks = { all: [], bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], chanpin_zujian: [], dingdan_bancai:[], zujian: [], caizhi: [], dingdan_chanpin: [], user: [], jinhuo: [] }; this.syncQueue = Promise.resolve(); this.registerCallback('dingdan_bancai', async (operation, data) => { if (operation === 'add' || operation === 'update') { try { // 构造进货记录数据 const jinhuoData = { dingdan_bancai:data, shuliang: data.shuliang, date: new Date().toISOString(), user: { id: localStorage.getItem("userId") } }; // 创建进货记录 await this.addEntity('jinhuo', jinhuoData); } catch (error) { console.error('进货记录创建失败:', error); } } }); } /** * 获取所有数据 * @returns {Promise<boolean>} 是否成功 */ async fetchAll() { console.log(this) try { const response = await fetch(`${this.baseUrl}/app/all`); if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API error'); console.log(result.data) const resolvedData = resolveDataReferences(result.data); // 更新本地数据 Object.keys(this.data).forEach(key => { if (resolvedData[key]) { this.data[key] = resolvedData[key]; } }); this.lastSync = new Date(); // 关键改进:数据更新后触发刷新回调 this.triggerCallbacks('refresh', 'all', this.data); return true; } catch (error) { console.error('Fetch error:', error); // 触发错误回调 this.triggerCallbacks('fetch_error', 'all', { error }); return false; } } /** * 注册回调函数 * @param {string} entity - 实体类型(如'bancai')或'all'表示全局回调 * @param {Function} callback - 回调函数,参数为(operation, data) */ registerCallback(entity, callback) { if (!this.callbacks[entity]) { this.callbacks[entity] = []; } this.callbacks[entity].push(callback); } /** * 移除回调函数 * @param {string} entity - 实体类型单数性质 * @param {Function} callback - 要移除的回调函数 */ unregisterCallback(entity, callback) { if (!this.callbacks[entity]) return; const index = this.callbacks[entity].indexOf(callback); if (index !== -1) { this.callbacks[entity].splice(index, 1); } } /** * 触发回调 * @param {string} operation - 操作类型('add', 'update', 'delete') * @param {string} entity - 实体类型单数性质 * @param {Object} data - 相关数据 */ triggerCallbacks(operation, entity, data) { // 触发全局回调 this.callbacks.all.forEach(cb => cb(operation, entity, data)); // 触发特定实体回调 if (this.callbacks[entity]) { this.callbacks[entity].forEach(cb => cb(operation, data)); } } // /** // * 执行CRUD操作并触发回调 // */ // async crudOperation(operation, entity, data) { // try { // const response = await fetch(`${this.baseUrl}/app/${operation}/${entity}`, { // method: 'POST', // headers: {'Content-Type': 'application/json'}, // body: JSON.stringify(data) // }); // // if (!response.ok) throw new Error('Network response was not ok'); // // const result = await response.json(); // if (result.status !== 200) throw new Error(result.text || 'API error'); // // // // // 自动同步数据 // this.syncData(); // // 触发操作成功的回调 // this.triggerCallbacks(operation, entity, data); // return result; // } catch (error) { // console.error('CRUD error:', error); // // 触发操作失败的回调 // this.triggerCallbacks(`${operation}_error`, entity, { // data, // error: error.message // }); // throw error; // } // } /** * 执行CRUD操作 * @param {string} operation - 'add', 'delete', 'update' * @param {string} entity - 实体名称单数性质(小写) * @param {Object} data - 要发送的数据 后端要求数据格式为{属性: "值", 关联对象: {id:0}, 关联对象集: [{id:0}]} * @returns {Promise<Object>} 响应结果 */ async crudOperation(operation, entity, data) { try { const response = await fetch(`${this.baseUrl}/app/${operation}/${entity}`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data) }); if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API error'); console.log(data)//这了就没有id了 // 自动同步数据 this.syncQueue = this.syncQueue.then(async () => { await this.syncData(); // 同步完成后触发操作回调 this.triggerCallbacks(operation, entity, result.data); }); return result; } catch (error) { console.error('CRUD error:', error); // 触发操作失败的回调 this.triggerCallbacks(`${operation}_error`, entity, { data, error: error.message }); throw error; } } /** * 自动同步数据(防止频繁请求) */ async syncData() { if (this.isSyncing) { this.pendingSync = true; return; } this.isSyncing = true; try { await this.fetchAll(); } catch (error) { console.error('Sync failed:', error); } finally { this.isSyncing = false; // 处理等待中的同步请求 if (this.pendingSync) { this.pendingSync = false; setTimeout(() => this.syncData(), 1000); } } } /** * 添加实体 * @param {string} entity - 实体名称单数性质 * @param {Object} data - 实体数据 */ async addEntity(entity, data) { return this.crudOperation('add', entity, data); } /** * 更新实体 * @param {string} entity - 实体名称单数性质 * @param {Object} data - 实体数据(必须包含id) */ async updateEntity(entity, data) { return this.crudOperation('update', entity, data); } /** * 删除实体 * @param {string} entity - 实体名称单数性质 * @param {number} id - 实体ID */ async deleteEntity(entity, id) { return this.crudOperation('delete', entity, {id}); } /** * 新增方法:手动触发数据刷新 */ async refreshData() { return this.syncQueue = this.syncQueue.then(() => this.syncData()); } /** * 获取订单的可用板材信息 * @param {number} dingdanId - 订单ID * @returns {Object} 可用板材信息 */ getAvailableBancaisForOrder(dingdanId) { const dingdan = this.data.dingdans.find(d => d.id == dingdanId); if (!dingdan) return {}; return dingdan.availableBancais || {}; } /** * 获取产品的组件列表 * @param {number} chanpinId - 产品ID * @returns {Array} 组件列表 */ getZujiansForChanpin(chanpinId) { const chanpin = this.data.chanpins.find(c => c.id == chanpinId); if (!chanpin) return []; return (chanpin.chanpin_zujian_list || []) .map(cz => cz.zujian) .filter(z => z); } /** * 获取组件的板材信息 * @param {number} zujianId - 组件ID * @returns {Array} 板材列表 */ getBancaisForZujian(zujianId) { return (this.data.chanpin_zujians || []) .filter(cz => cz.zujian && cz.zujian.id == zujianId) .map(cz => cz.bancai) .filter(b => b); } /** * 创建进货记录 * @param {Object} jinhuoData - 进货数据 * @returns {Promise} 操作结果 */ async createJinhuo(jinhuoData) { return this.addEntity('jinhuo', jinhuoData); } /** * 获取订单列表 * @returns {Array} 订单列表 */ getDingdans() { return this.data.dingdans || []; } /** * 获取订单的产品列表 * @param {number} dingdanId - 订单ID * @returns {Array} 产品列表 */ getChanpinsForDingdan(dingdanId) { const dingdan = this.data.dingdans.find(d => d.id == dingdanId); if (!dingdan) return []; return (dingdan.dingdan_chanpin_list || []) .map(dc => dc.chanpin) .filter(c => c); } } export { DataManager }; // 创建单例实例 //const dataManager = new DataManager('http://127.0.0.1:8080/KuCun2'); //// 初始化时获取所有数据 //dataManager.fetchAll().then(() => { // console.log('Initial data loaded'); //}); // 导出数据对象,外部可以直接访问 data.bancais, data.dingdans 等 //export const data = dataManager.data; //// 导出操作方法 //export const addEntity = dataManager.addEntity.bind(dataManager); //export const updateEntity = dataManager.updateEntity.bind(dataManager); //export const deleteEntity = dataManager.deleteEntity.bind(dataManager); //export const fetchAll = dataManager.fetchAll.bind(dataManager); ------------------------ test.js ------------------------ // 创建DataManager实例 // 获取全局的DataManager实例 const dataManager = window.parent.dataManager; // 扁平化数据结构 function flattenData(data) { const result = []; // 遍历所有订单 data.dingdans.forEach(dingdan => { // 遍历订单中的产品 dingdan.dingdan_chanpin?.forEach(dc => { const chanpin = dc.chanpin; // 遍历产品中的组件 chanpin.chanpin_zujian.forEach(cz => { const zujian = cz.zujian; const bancai = cz.bancai; const kucun = bancai.kucun; // 计算订单用量 = 产品数量 × 组件数量 × 单件用量 const orderUsage = dc.shuliang * cz.zujianshu * cz.one_howmany; // 创建扁平化数据对象 result.push({ dingdan: dingdan, dingdan_chanpin: dc, chanpin: chanpin, chanpin_zujian: cz, zujian: zujian, bancai: bancai, kucun: kucun, orderUsage: orderUsage }); }); }); }); return result; } // 更新统计卡片 function updateStatistics(data) { // 订单总数 $('#orderCount').text(data.dingdans.length); // 产品种类 $('#productCount').text(data.chanpins.length); // 板材种类 $('#materialCount').text(data.bancais.length); // 库存总量 const totalStock = data.kucuns.reduce((sum, kucun) => sum + kucun.shuliang, 0); $('#totalStock').text(totalStock); } // 渲染表格 function renderTable(dataArray) { const $tbody = $('#resultBody'); $tbody.empty(); if (dataArray.length === 0) { $('#noResults').show(); $('#resultCount').text('0'); return; } $('#noResults').hide(); $('#resultCount').text(dataArray.length); dataArray.forEach(item => { const bancai = item.bancai; const dingdan = item.dingdan; const chanpin = item.chanpin; const zujian = item.zujian; const kucun = item.kucun; const row = ` <tr> <td>${dingdan.number}</td> <td>${chanpin.bianhao}</td> <td>${item.dingdan_chanpin.shuliang}</td> <td>${zujian.name}</td> <td> <div>ID: ${bancai.id}</div> <div>材质: ${bancai.caizhi.name}</div> <div>厚度: ${bancai.houdu}mm</div> <div>木皮1: ${bancai.mupi1.name}${bancai.mupi1.you ? '(油漆)' : ''}</div> <div>木皮2: ${bancai.mupi2.name}${bancai.mupi2.you ? '(油漆)' : ''}</div> </td> <td>${item.chanpin_zujian.one_howmany}</td> <td>${item.orderUsage.toFixed(2)}</td> <td class="${kucun.shuliang < item.orderUsage ? 'text-danger fw-bold' : ''}"> ${kucun.shuliang} </td> <td> <button class="btn btn-sm btn-outline-primary view-detail" data-bancai-id="${bancai.id}"> <i class="bi bi-info-circle"></i> 详情 </button> </td> </tr> `; $tbody.append(row); }); // 绑定详情按钮事件 $('.view-detail').off('click').on('click', function() { const bancaiId = $(this).data('bancai-id'); viewBancaiDetail(bancaiId); }); } // 查看板材详情 function viewBancaiDetail(bancaiId) { const bancai = dataManager.data.bancais.find(b => b.id === bancaiId); if (!bancai) return; const kucun = bancai.kucun; const reservedOrder = kucun.reservedOrder ? kucun.reservedOrder.number : '无'; // 构建详情内容 const detailContent = ` <div class="bancai-detail"> <h5 class="mb-3">板材详情 #${bancai.id}</h5> <div class="row"> <div class="col-md-6"> <div class="mb-2"><strong>材质:</strong> ${bancai.caizhi.name}</div> <div class="mb-2"><strong>厚度:</strong> ${bancai.houdu}mm</div> <div class="mb-2"><strong>木皮1:</strong> ${bancai.mupi1.name}${bancai.mupi1.you ? ' (油漆)' : ''}</div> <div class="mb-2"><strong>木皮2:</strong> ${bancai.mupi2.name}${bancai.mupi2.you ? ' (油漆)' : ''}</div> </div> <div class="col-md-6"> <div class="mb-2"><strong>当前库存:</strong> ${kucun.shuliang}</div> <div class="mb-2"><strong>预定订单:</strong> ${reservedOrder}</div> <div class="mb-2"><strong>库存状态:</strong> <span class="badge ${kucun.shuliang > 50 ? 'bg-success' : kucun.shuliang > 10 ? 'bg-warning' : 'bg-danger'}"> ${kucun.shuliang > 50 ? '充足' : kucun.shuliang > 10 ? '正常' : '不足'} </span> </div> </div> </div> <h6 class="mt-4 border-top pt-3">使用此板材的产品组件</h6> <ul class="list-group"> ${dataManager.data.chanpin_zujians .filter(cz => cz.bancai.id === bancaiId) .map(cz => `<li class="list-group-item">${cz.zujian.name} (产品: ${cz.chanpin.bianhao})</li>`) .join('')} </ul> </div> `; // 使用模态框显示详情 const modal = new bootstrap.Modal(document.getElementById('detailModal')); $('#detailModal .modal-body').html(detailContent); modal.show(); } // 搜索过滤数据 function filterData(flatData) { const orderSearch = $('#orderSearch').val().toLowerCase(); const productSearch = $('#productSearch').val().toLowerCase(); const materialSearch = $('#materialSearch').val().toLowerCase(); const woodSearch = $('#woodSearch').val().toLowerCase(); const thickness = parseFloat($('#thicknessSearch').val()); const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Infinity; return flatData.filter(item => { const bancai = item.bancai; const dingdan = item.dingdan; const chanpin = item.chanpin; const kucun = item.kucun; // 订单号搜索 if (orderSearch && !dingdan.number.toLowerCase().includes(orderSearch)) { return false; } // 产品编号搜索 if (productSearch && !chanpin.bianhao.toLowerCase().includes(productSearch)) { return false; } // 板材ID或材质搜索 if (materialSearch && !(bancai.id.toString().includes(materialSearch) || bancai.caizhi.name.toLowerCase().includes(materialSearch))) { return false; } // 木皮搜索 if (woodSearch && !(bancai.mupi1.name.toLowerCase().includes(woodSearch) || bancai.mupi2.name.toLowerCase().includes(woodSearch))) { return false; } // 厚度过滤 if (!isNaN(thickness) && Math.abs(bancai.houdu - thickness) > 0.1) { return false; } // 库存范围过滤 if (kucun.shuliang < minStock || kucun.shuliang > maxStock) { return false; } return true; }); } // 初始化排序功能 function initSorting() { let currentSort = { column: null, direction: 'asc' }; $('th[data-sortable]').on('click', function() { const column = $(this).index(); const $sortIndicator = $(this).find('.sort-indicator'); // 重置其他列的排序指示器 $('.sort-indicator').html(''); // 更新当前排序状态 if (currentSort.column === column) { currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc'; } else { currentSort.column = column; currentSort.direction = 'asc'; } // 更新排序指示器 $sortIndicator.html(currentSort.direction === 'asc' ? '↑' : '↓'); // 执行排序 sortTable(currentSort); }); } // 排序表格数据 function sortTable(sortConfig) { const flatData = window.currentFlatData || []; if (flatData.length === 0) return; flatData.sort((a, b) => { let valueA, valueB; switch (sortConfig.column) { case 0: // 订单号 valueA = a.dingdan.number; valueB = b.dingdan.number; break; case 1: // 产品信息 valueA = a.chanpin.bianhao; valueB = b.chanpin.bianhao; break; case 2: // 产品数量 valueA = a.dingdan_chanpin.shuliang; valueB = b.dingdan_chanpin.shuliang; break; case 3: // 组件 valueA = a.zujian.name; valueB = b.zujian.name; break; case 4: // 板材 valueA = a.bancai.id; valueB = b.bancai.id; break; case 5: // 单件用量 valueA = a.chanpin_zujian.one_howmany; valueB = b.chanpin_zujian.one_howmany; break; case 6: // 订单用量 valueA = a.orderUsage; valueB = b.orderUsage; break; case 7: // 库存数量 valueA = a.kucun.shuliang; valueB = b.kucun.shuliang; break; default: return 0; } // 数字排序 if (typeof valueA === 'number') { return sortConfig.direction === 'asc' ? valueA - valueB : valueB - valueA; } // 字符串排序 if (typeof valueA === 'string') { return sortConfig.direction === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA); } return 0; }); // 重新渲染排序后的表格 renderTable(flatData); } // 初始化页面 $(document).ready(async function() { // 创建详情模态框(如果不存在) if (!$('#detailModal').length) { $('body').append(` <div class="modal fade" id="detailModal" tabindex="-1"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">板材详情</h5> <button type="button" class="btn-close" data-bs-dismiss="modal"></button> </div> <div class="modal-body"></div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button> </div> </div> </div> </div> `); } // 显示加载状态 $('#loadingRow').show(); $('#noResults').hide(); try { // 加载数据 await dataManager.fetchAll(); // 更新最后更新时间 $('#lastUpdate').text(new Date().toLocaleTimeString()); // 扁平化数据 const flatData = flattenData(dataManager.data); window.currentFlatData = flatData; // 保存当前数据用于排序 // 更新统计卡片 updateStatistics(dataManager.data); // 渲染表格 renderTable(flatData); // 初始化排序 initSorting(); } catch (error) { console.error('初始化失败:', error); $('#resultBody').html(` <tr> <td colspan="9" class="text-center text-danger py-5"> <i class="bi bi-exclamation-circle fs-1"></i> <h4 class="mt-3">数据加载失败</h4> <p>${error.message || '请检查网络连接后重试'}</p> <button class="btn btn-primary mt-2" id="retryBtn">重试</button> </td> </tr> `); $('#retryBtn').on('click', function() { location.reload(); }); } finally { $('#loadingRow').hide(); } // 绑定搜索事件 $('#orderSearch, #productSearch, #materialSearch, #woodSearch, #thicknessSearch, #minStock, #maxStock').on('input', function() { performSearch(); }); $('#stockStatusBtn').on('click', performSearch); }); // 执行搜索 function performSearch() { if (!window.currentFlatData) return; const filteredData = filterData(window.currentFlatData); renderTable(filteredData); } 修改test.js实现页面功能 基本逻辑 一个订单有很多产品不同数量,每个产品可以再很多订单中,一个产品有很多组件,一个组件可以在很多产品中,因为每个组件因为在不同的产品中有不同的生产工艺,所以使用不同的板材板材能生产组件数量,每个板材有不同的材质两面木皮,木皮表面可能有油漆, 订购时可能直接购入板材,也可能按订单产品订购板材,也用可能按订单产品组件订购板材,每次采购不标准,一个订单可能订购几次,用户有姓名 账号 密码 权限, 一个记录进货消耗,查看的时候会查看订单下有多少板材可用
06-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值