app和微网站的对比分析王也台球

当前位置: 首页 > news >正文

app和微网站的对比分析,王也台球,namecheap建站wordpress,中国第一营销网在上一篇博客中主要是使用SpringBootApache POI实现了BOM物料清单Excel表格导出#xff0c;详见以下博客#xff1a; Spring Boot Apache POI 实现 Exc#xff08;#xff09;el 导出#xff1a;BOM物料清单生成器#xff08;支持中文文件名、样式美化、数据合并#…在上一篇博客中主要是使用SpringBootApache POI实现了BOM物料清单Excel表格导出详见以下博客 Spring Boot Apache POI 实现 Excel 导出BOM物料清单生成器支持中文文件名、样式美化、数据合并 目录 引言 项目结构 源代码展示 1.WordController 2.WordUtil工具类 3.FreeMarker模版 4.POM依赖 WordController类深度解析 1.类结构 2.main方法 3.generateWordFile方法 4.addTestData方法 WordUtil类深度解析 1.类结构和静态成员 2.静态初始化块 3.私有构造函数 4.exportMillCertificateWord方法 5.createDoc方法 6.WordUtil类总结 FreeMarker模板深度解析 1.文档结构和样式 2.表格结构和动态数据插入 总结 引言 在电缆行业生成供货清单是一项常见但繁琐的任务。本教程将介绍如何使用现代Java技术栈自动化这一过程大幅提高工作效率和准确性。我们将使用SpringBoot作为框架Apache POI处理Word文档以及FreeMarker作为模板引擎来实现这一功能 让我们先了解一下这个问题的背景: 在电缆行业手动创建供货清单是一个复杂且重复的过程。这个过程不仅耗时还容易出错影响工作效率和数据准确性。 为了解决这个问题,我们提出了一个技术方案,结合了以下几个关键技术: SpringBoot: 作为我们的主要开发框架Apache POI: 用于生成和操作Word文档FreeMarker模板引擎: 用于生成Word文件的内容 这个方案的主要优势包括: 灵活性: 使用FreeMarker模板可以轻松调整文档格式,而无需修改程序代码。效率: 自动化生成过程大大减少了人工操作,提高了办公效率。准确性: 自动化处理确保了数据的准确性和一致性。适用性: 特别适合电缆行业的业务需求生成符合要求的.doc文件。 通过阅读这篇博客您将学习如何实现这个解决方案从而帮助您或您的团队简化工作流程,提高生产效率。 效果图 项目结构 src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── pw/ │ │ ├── WordController.java #负责生成测试数据并调用WordUtil工具类来生成Word文档 │ │ └── utils/ │ │ └── WordUtil.java #这个工具类封装了使用FreeMarker生成Word文档的核心功能 │ └── resources/ │ └── templates/ │ └── template.ftl #模版定义了Word文档的结构和样式使用HTML和CSS来格式化内容 1.WordController类这个类是我们应用的入口点负责生成测试数据并调用WordUtil来生成Word文档。 2.WordUtil类这个工具类封装了使用FreeMarker生成Word文档的核心逻辑。 3.FreeMarker模版template.ftl:这个模版定义了Word文档的结构和样式使用HTML和CSS来格式化内容。 源代码展示 1.WordController import com.pw.utils.WordUtil;import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;public class WordController {public static void main(String[] args) throws IOException {// 指定保存Word文件的目录String filePath F:\Poi2Word\src\main\resources\output; // 更改为您希望的目录new WordController().generateWordFile(filePath);}public void generateWordFile(String directory) throws IOException {ListMapString, Object listMap new ArrayList();//测试数据addTestData(listMap, 4600025747, 绝缘导线, AC10kV,JKLYJ,300, 1500, 米, 盘号A1);addTestData(listMap, 4600025748, 绝缘导线, AC10kV,JKLGYJ,15030, 2500, 米, 盘号A2);addTestData(listMap, 4600025749, 绝缘导线, AC10kV,JKLGYJ,15030, 3500, 米, 盘号A3);addTestData(listMap, 4600025750, 绝缘导线, AC10kV,JKLGYJ,15030, 4500, 米, 盘号A4);addTestData(listMap, 4600025751, 绝缘导线, AC10kV,JKLGYJ,15030, 3800, 米, 盘号A5);addTestData(listMap, 4600025752, 绝缘导线, AC10kV,JKLYJ,180, 2000, 米, 盘号A6);addTestData(listMap, 4600025753, 绝缘导线, AC10kV,JKLYJ,120, 4200, 米, 盘号A7);addTestData(listMap, 4600025754, 绝缘导线, AC10kV,JKLYJ,120, 3700, 米, 盘号A8);addTestData(listMap, 4600025755, 绝缘导线, AC10kV,JKLYJ,120, 4300, 米, 盘号A9);addTestData(listMap, 4600025756, 绝缘导线, AC10kV,JKLGYJ,10020, 2800, 米, 盘号A10);addTestData(listMap, 4600025757, 绝缘导线, AC10kV,JKLGYJ,10020, 2400, 米, 盘号A11);addTestData(listMap, 4600025758, 绝缘导线, AC10kV,JKLGYJ,10020, 2600, 米, 盘号A12);HashMapString, Object map new HashMap();map.put(qdList, listMap); // 添加供货清单数据map.put(contacts, 张三); // 联系人map.put(contactsPhone, 13988887777); // 联系电话map.put(date, 2025年01月18日); // 日期map.put(company, 新电缆科技有限公司); // 公司名称map.put(customer, 国网北京市电力公司); // 客户String wordName template.ftl; // FreeMarker模板文件名String fileName 供货清单 System.currentTimeMillis() .doc; // 带时间戳的文件名String name name; // 临时文件名// 确保输出目录存在File directoryFile new File(directory);if (!directoryFile.exists()) {directoryFile.mkdirs(); // 如果目录不存在则创建}// 生成Word文件WordUtil.exportMillCertificateWord(directory, map, wordName, fileName, name);System.out.println(文件成功生成在 directory fileName);}private void addTestData(ListMapString, Object listMap, String danhao, String name, String model, int num, String unit, String remark) {MapString, Object item new HashMap();item.put(serNo, listMap.size() 1); // 序号item.put(danhao, danhao); // 单号item.put(name, name); // 产品名称item.put(model, model); // 规格型号item.put(num, String.valueOf(num)); // 数量转换为字符串item.put(unit, unit); // 单位item.put(remark, remark); // 备注listMap.add(item); // 将数据添加到列表} }2.WordUtil工具类 package com.pw.utils;import freemarker.template.Configuration; import freemarker.template.Template;import java.io.; import java.util.Map;public class WordUtil {private static Configuration configuration null;// 模板文件夹路径private static final String templateFolder WordUtil.class.getResource(/templates).getPath();static {configuration new Configuration();configuration.setDefaultEncoding(utf-8);try {System.out.println(templateFolder);configuration.setDirectoryForTemplateLoading(new File(templateFolder)); // 设置模板加载路径} catch (IOException e) {e.printStackTrace();}}private WordUtil() {throw new AssertionError(); // 防止实例化}/** 导出Word文档* param map Word文档中参数* param wordName 模板的名字例如xxx.ftl* param fileName Word文件的名字 格式为xxxx.doc* param outputDirectory 输出文件的目录路径* param name 临时的文件夹名称作为Word文件生成的标识* throws IOException/public static void exportMillCertificateWord(String outputDirectory, Map map, String wordName, String fileName, String name) throws IOException {Template freemarkerTemplate configuration.getTemplate(wordName); // 获取模板文件File file null;try {// 调用工具类的createDoc方法生成Word文档file createDoc(map, freemarkerTemplate, name);// 确保输出目录存在File dir new File(outputDirectory);if (!dir.exists()) {dir.mkdirs(); // 如果目录不存在则创建}// 定义完整的文件路径File outputFile new File(outputDirectory, fileName);// 重命名并移动文件到指定目录file.renameTo(outputFile);System.out.println(文件成功生成在: outputFile.getAbsolutePath());} finally {if (file ! null file.exists()) {file.delete(); // 删除临时文件}}}private static File createDoc(Map?, ? dataMap, Template template, String name) {File f new File(name);try {// 使用OutputStreamWriter来指定编码防止特殊字符出问题Writer w new OutputStreamWriter(new FileOutputStream(f), utf-8);template.process(dataMap, w); // 使用FreeMarker处理模板w.close();} catch (Exception ex) {ex.printStackTrace();throw new RuntimeException(ex);}return f; // 返回生成的文件} }3.FreeMarker模版 !DOCTYPE html html headmeta charsetUTF-8title\({company}送货清单/titlestylebody { font-family: SimSun, serif; } !-- 设置字体 --table { border-collapse: collapse; width: 100%; } !-- 设置表格样式 --th, td { border: 1px solid black; padding: 5px; text-align: center; } !-- 设置表格的单元格样式 --th { background-color: #f2f2f2; } !-- 设置表头背景色 --.subtotal { font-weight: bold; } !-- 小计行加粗 --.total { font-weight: bold; font-size: 1.1em; } !-- 总计行加粗并设置字体大小 --/style /head body h1 styletext-align: center;\){company}送货清单/h1 !– 顶部公司名称 –tabletrth序号/th !– 表头序号 –th供货单号/th !– 表头供货单号 –th产品名称/th !– 表头产品名称 –th规格型号/th !– 表头规格型号 –th数量/th !– 表头数量 –th单位/th !– 表头单位 –th备注/th !– 表头备注 –/tr#assign totalQuantity 0 !– 总数量初始化 –#assign totalItems 0 !– 总项数初始化 –#assign sortedList qdList?sortby(model) !– 按照规格型号排序 –#assign currentModel !– 当前型号初始化 –#assign subtotalQuantity 0 !– 小计数量初始化 –#assign subtotalItems 0 !– 小计项数初始化 –#list sortedList as item !– 遍历排序后的列表 –#if item.model ! currentModel !– 如果规格型号变了 –#if currentModel ! !– 如果当前规格型号不是空 –tr classsubtotaltd colspan4小计\({subtotalQuantity}\){sortedList[0].unit} \({subtotalItems}轴/tdtd\){subtotalQuantity}/tdtd\({sortedList[0].unit}/tdtd/td/tr/#if#assign currentModel item.model !-- 更新当前型号 --#assign subtotalQuantity 0 !-- 重置小计数量 --#assign subtotalItems 0 !-- 重置小计项数 --/#iftrtd\){item?counter}/td !– 序号 –td\({item.danhao}/td !-- 单号 --td\){item.name}/td !– 产品名称 –td\({item.model}/td !-- 规格型号 --td\){item.num}/td !– 数量 –td\({item.unit}/td !-- 单位 --td\){item.remark}/td !– 备注 –/tr#assign itemNum item.num?replace(,, )?number !– 将数量转为数字并处理逗号 –#assign subtotalQuantity subtotalQuantity itemNum !– 累加小计数量 –#assign subtotalItems subtotalItems 1 !– 累加小计项数 –#assign totalQuantity totalQuantity itemNum !– 累加总数量 –#assign totalItems totalItems 1 !– 累加总项数 –/#list#if currentModel ! !– 如果当前规格型号不是空 –tr classsubtotaltd colspan4小计\({subtotalQuantity}\){sortedList[0].unit} \({subtotalItems}轴/tdtd\){subtotalQuantity}/tdtd\({sortedList[0].unit}/tdtd/td/tr/#iftr classtotaltd colspan4合计\){totalQuantity}\({qdList[0].unit} \){totalItems}轴/tdtd\({totalQuantity}/tdtd\){qdList[0].unit}/tdtd/td/tr /tablep发货联系人\({contacts}/p !-- 发货联系人 -- p联系电话\){contactsPhone}/p !– 联系电话 – p日期${date}/p !– 日期 –p styletext-align: right;收货人签字______________/p !– 收货人签字 – p styletext-align: right;联系电话_______________/p !– 收货人联系电话 – p styletext-align: right;\({customer}/p !-- 客户 -- /body /html4.POM依赖 !-- freemarker依赖用于模板引擎方便进行页面的渲染和数据的展示等操作 -- dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-freemarker/artifactId /dependency !-- Apache POI 的核心依赖用于操作 Microsoft Office 格式的文档如 Excel、Word 等文件 -- dependencygroupIdorg.apache.poi/groupIdartifactIdpoi/artifactIdversion5.0.0/version /dependency !-- Apache POI 的 OOXML 扩展依赖主要用于处理 Office 2007 及以后版本的 OOXML 格式的文件例如.xlsx 等 -- dependencygroupIdorg.apache.poi/groupIdartifactIdpoi-ooxml/artifactIdversion5.0.0/version /dependency !-- OOXML 模式相关的依赖提供了对 OOXML 文档结构和内容模式的支持有助于 Apache POI 更好地操作 OOXML 格式文件 -- dependencygroupIdorg.apache.poi/groupIdartifactIdooxml-schemas/artifactIdversion1.4/version /dependency WordController类深度解析 WordController类是整个应用的核心控制器负责协调数据生成和文档创建的过程。让我们逐步分析它的主要组成部分 1.类结构 public class WordController {// 方法定义... }这个类没有继承任何其他类也没有实现任何接口是一个独立的控制器类。 2.main方法 public static void main(String[] args) throws IOException {String filePath F:\\Poi2Word\\src\\main\\resources\\output;new WordController().generateWordFile(filePath); }这是应用的入口点。它设置了输出文件的路径,然后调用generateWordFile方法。请注意在常规的 Spring Boot 实际应用场景下我们一般不会直接在控制器类中使用 main 方法。此处之所以将 main 方法置于控制器中纯粹是出于演示目的旨在让相关流程更加直观易懂。 而当进入到正式开发环节时有几个关键要点务必落实 其一需要引入数据库集成功能将当前所使用的测试数据全面替换为从数据库中精准查询获取的真实数据以此确保数据的准确性与时效性 其二要对控制器进行优化改造摒弃现有的演示模式将其转换为遵循标准规范的请求接口实现方式进而满足实际业务需求提升系统的稳定性与可扩展性。 3.generateWordFile方法 此方法的只要目的是生成Word文件首先需要先收集和存储测试数据存储表格数据是将一条数据存储在Map集合中再将每一条数据存储到List集合中。将其他数据存储到单独的一个Map集合中。然后确保输出目录存在最后调用WordUtil中的exportMillCertificateWord方法生成文件并输出文件的生成位置。 // 生成 Word 文件的方法 public void generateWordFile(String directory) throws IOException {// 存储测试数据的列表每个元素都是一个 Map存储了具体的信息ListMapString, Object listMap new ArrayList();// 添加测试数据调用 addTestData 方法添加一条记录addTestData(listMap, 4600025747, 绝缘导线, AC10kV,JKLYJ,300, 1500, 米, 盘号A1);//... 可以继续调用 addTestData 方法添加更多测试数据...// 存储最终要填充到 Word 模板的数据的 Map包含各种信息HashMapString, Object map new HashMap();// 将测试数据列表添加到 map 中键为 qdListmap.put(qdList, listMap);// 联系人信息map.put(contacts, 张三);// 联系人电话map.put(contactsPhone, 13988887777);// 日期信息map.put(date, 2025年01月18日);// 公司名称map.put(company, 新电缆科技有限公司);// 客户名称map.put(customer, 国网北京市电力公司);// Word 模板文件的名称String wordName template.ftl;// 生成的 Word 文件的名称使用当前时间戳保证文件名的唯一性String fileName 供货清单 System.currentTimeMillis() .doc;// 名称信息具体含义可能根据实际情况而定String name name;// 创建一个文件对象用于表示输出目录File directoryFile new File(directory);// 检查输出目录是否存在如果不存在则创建目录if (!directoryFile.exists()) {directoryFile.mkdirs();}// 调用 WordUtil 的 exportMillCertificateWord 方法生成 Word 文件// 传入目录、数据 Map、模板名称、生成的文件名称和名称信息WordUtil.exportMillCertificateWord(directory, map, wordName, fileName, name);// 打印生成文件的成功信息System.out.println(文件成功生成在 directory fileName); }这个方法完成以下任务 创建一个一个ListMapString,Object集合来存储供货清单数据使用addTestData方法添加多条测试数据创建一个Map集合来存储企业名称发货联系人联系电话等信息确保输出目录存在调用WordUtil.exportMillCertificateWord方法来生成Word文档 4.addTestData方法 这个方法用于创建单个供货项目的数据 // 添加一条测试数据到 listMap 中 private void addTestData(ListMapString, Object listMap, String danhao, String name, String model, int num, String unit, String remark) {// 创建一个新的 HashMap用于存储每一条数据MapString, Object item new HashMap();// 将数据项依次放入 HashMap 中serNo 表示序号使用 listMap 的大小1 生成序号item.put(serNo, listMap.size() 1); // 序号是当前列表的大小 1item.put(danhao, danhao); // 供货单号item.put(name, name); // 产品名称item.put(model, model); // 规格型号item.put(num, String.valueOf(num)); // 数量将整数转为字符串item.put(unit, unit); // 单位item.put(remark, remark); // 备注// 将该条数据项添加到 listMap 列表中listMap.add(item); }这个方法完成以下任务 它接收多个参数,代表一个供货项目的各个属性。创建一个新的Map来存储这个项目的数据。自动计算序号(serNo)基于当前列表的大小。将所有数据添加到Map中。将这个Map添加到供货清单列表中。 WordUtil类深度解析 WordUtil类是整个文档生成过程的核心它封装了FreeMarker模板引擎的配置和使用逻辑。让我们逐步分析它的主要组成部分 1.类结构和静态成员 public class WordUtil {private static Configuration configuration null;private static final String templateFolder WordUtil.class.getResource(/templates).getPath();// 其他方法... }configuration这是FreeMarker的核心配置对象用于设置模版加载路径。 templateFolder定义了模版文件的存储路径。使用getResource()方法确保在不同环境下都能正确找到模版文件。 2.静态初始化块 这段代码的作用是初始化FreeMarker的Configuration对象设置模版加载目录以及编码格式以便FreeMarker后续能够正确加载和处理模版文件。 // 静态初始化块用于初始化 FreeMarker 配置 static {// 创建一个 FreeMarker 配置对象用于后续模板处理configuration new Configuration();// 设置 FreeMarker 配置对象的默认编码为 utf-8configuration.setDefaultEncoding(utf-8);try {// 输出模板文件夹路径帮助调试System.out.println(templateFolder);// 设置模板加载目录为 templateFolder 指定的路径模板文件会从该目录加载configuration.setDirectoryForTemplateLoading(new File(templateFolder));} catch (IOException e) {// 如果加载模板目录时出现异常打印错误堆栈信息e.printStackTrace();} }这个静态初始化块在类加载时执行主要完成以下任务 创建FreeMarker的Configuration对象设置默认编码为UTF-8确保正确处理中文等字符设置模版加载目录这样FreeMarker就知道从哪里查找加载模版文件了错误处理如果执行过程中出现了IO异常就会打印堆栈跟踪 3.私有构造函数 这个构造函数防止类被实例化,确保WordUtil只能通过其静态方法使用。 private WordUtil() {throw new AssertionError(); }私有构造函数的好处包括 防止类被实例化 当类的构造函数被声明为private时外部代码无法直接创建该类的实例。这就意味着该类只能公国静态方法访问确保类的功能是全局共享的。 实现单例模式的基础 在一些设计模式中例如单例模式类只允许有一个实例私有构造函数确保了这一点。通过private构造函数我们可以控制类的实例化过程并确保只有一个实例被创建。 封装类的内部实现 私有构造函数可以帮助隐藏类的具体实现细节外部代码不需要关心如何创建类的实例只需要使用类提供的静态方法即可。这增加了类的封装性降低了与外部代码的耦合度。 避免多余的对象创建 由于无法实例化类每次调用静态方法时都会使用已有的类实例这可以避免无意义的对象创建节省内存和资源。 4.exportMillCertificateWord方法 这个方法的主要功能是通过加载指定的 FreeMarker 模板生成一个临时的 Word 文档确保输出目录存在后将临时文件重命名并保存到指定的位置同时在过程结束后清理临时文件并打印文件生成的成功消息。 // 导出 Word 文档的方法 public static void exportMillCertificateWord(String outputDirectory, Map map, String wordName, String fileName, String name) throws IOException {// 获取 FreeMarker 模板文件Template freemarkerTemplate configuration.getTemplate(wordName);// 初始化一个 File 对象用于存储生成的临时文件File file null;try {// 使用模板和数据创建 Word 文档返回临时文件file createDoc(map, freemarkerTemplate, name);// 创建目标目录的 File 对象File dir new File(outputDirectory);// 如果目录不存在则创建该目录if (!dir.exists()) {dir.mkdirs(); // 创建目录及其父目录}// 定义最终输出文件的完整路径包括目录和文件名File outputFile new File(outputDirectory, fileName);// 将临时生成的文件重命名为目标文件并将其移动到指定目录file.renameTo(outputFile);// 打印输出文件的绝对路径a通知文件生成成功System.out.println(文件成功生成在: outputFile.getAbsolutePath());} finally {// 最后无论是否成功生成文件都确保临时文件被删除if (file ! null file.exists()) {file.delete(); // 删除临时文件}} }这个方法是文档导出的主要入口主要实现了以下功能 加载指定的FreeMarker模版调用createDoc方法生成临时文档文件确保输出目录存在将临时文件重命名并移动到指定的输出位置使用finally块确保临时文件被删除无论过程是否成功 5.createDoc方法 这个方法是创建文档的核心方法主要是通过创建一个临时文件使用指定的FreeMarker模版和数据模型将内容填充到文件中并确保文件使用UTF-8编码进行写入。该方法在执行过程中捕获异常并打印堆栈信息确保发生错误时能够正确处理。最后。方法返回生成的文件对象以便后续操作或保存。 // 创建文档的方法使用 FreeMarker 模板生成内容并写入文件 private static File createDoc(Map?, ? dataMap, Template template, String name) {// 创建一个新的 File 对象表示生成的文档文件文件名由参数 name 提供File f new File(name);try {// 使用 OutputStreamWriter 创建一个写入文件的 Writer 对象设置编码为 utf-8Writer w new OutputStreamWriter(new FileOutputStream(f), utf-8);// 使用 FreeMarker 模板将数据填充到文件中template.process(dataMap, w);// 关闭 Writer确保所有内容写入文件w.close();} catch (Exception ex) {// 捕获异常并打印错误堆栈信息ex.printStackTrace();// 抛出 RuntimeException确保错误被传播到调用者throw new RuntimeException(ex);}// 返回生成的文件对象return f; }这个方法是实际创建文档的核心主要实现以下功能 创建一个临时文件。 使用OutputStreamWriter设置UTF-8编码,确保正确处理所有字符。 调用FreeMarker的template.process()方法,将数据模型(dataMap)应用到模板上。 关闭写入器。 如果过程中发生异常,打印堆栈跟踪并抛出RuntimeException。 返回生成的文件对象。 6.WordUtil类总结 WordUtil 类通过封装 FreeMarker 模板引擎的配置和文件操作提供了一个简洁的文档生成工具。它加载指定模板使用数据模型填充内容创建临时文件并确保文件按照指定路径保存。该类通过静态方法确保全局共享功能使用 UTF-8 编码处理字符捕获异常并清理临时文件确保文档生成过程的稳定性和高效性。 FreeMarker模板深度解析 FreeMarker模板是整个文档生成过程的核心它定义了最终Word文档的结构和样式。让我们来逐步分析模板的主要组成部分 1.文档结构和样式 !DOCTYPE html !-- 声明文档类型为 HTML5 -- html head!-- 设置文档字符编码为 UTF-8支持中文和其他字符集 --meta charsetUTF-8!-- 设置页面标题动态插入公司名称 --title\){company}送货清单/titlestyle/ 设置页面正文的字体为 SimSun宋体如果没有则使用 serif /body { font-family: SimSun, serif; }/ 设置表格样式表格边框合并宽度100% /table { border-collapse: collapse; width: 100%; }/ 设置表格头部和单元格的边框、内边距和文本居中对齐 /th, td { border: 1px solid black; padding: 5px; text-align: center; }/ 设置表头背景色为浅灰色 /th { background-color: #f2f2f2; }/ 设置小计行字体加粗 /.subtotal { font-weight: bold; }/ 设置合计行字体加粗字体大小稍大 */.total { font-weight: bold; font-size: 1.1em; }/style /head body!– 页面标题居中显示公司名称和送货清单 –h1 styletext-align: center;\({company}送货清单/h1!-- 表格内容将在这里生成动态插入数据 -- /body /html这段代码通过HTML和内嵌CSS定义了页面布局和样式 动态公司名称title标签使用\){company}插入动态的公司名称显示在浏览器标签中。 字体和表格样式 设置页面字体为宋体Simsun定义表格边框合并、100%宽度并使单元格内容居中 小计和总计行样式为小计行加粗字体并为总计行加粗且增大字体突出显示重要数据。 2.表格结构和动态数据插入 table!– 表头定义表格的列名 –trth序号/th !– 序号 –th供货单号/th !– 供货单号 –th产品名称/th !– 产品名称 –th规格型号/th !– 规格型号 –th数量/th !– 数量 –th单位/th !– 单位 –th备注/th !– 备注 –/tr!– 初始化总计和小计相关变量 –#assign totalQuantity 0 !– 总数量 –#assign totalItems 0 !– 总项数 –#assign sortedList qdList?sort_by(model) !– 按照规格型号对数据进行排序 –#assign currentModel !– 当前规格型号 –#assign subtotalQuantity 0 !– 小计数量 –#assign subtotalItems 0 !– 小计项数 –!– 遍历排序后的列表 –#list sortedList as item!– 如果当前项的规格型号与上一项不同则输出上一项的小计 –#if item.model ! currentModel#if currentModel ! !– 输出上一规格型号的小计行 –tr classsubtotaltd colspan4小计\({subtotalQuantity}\){sortedList[0].unit} \({subtotalItems}轴/tdtd\){subtotalQuantity}/tdtd\({sortedList[0].unit}/tdtd/td/tr/#if!-- 更新当前规格型号为当前项的规格型号并重置小计 --#assign currentModel item.model#assign subtotalQuantity 0#assign subtotalItems 0/#if!-- 输出当前行数据 --trtd\){item?counter}/td !– 序号使用 FreeMarker 的 counter 计数 –td\({item.danhao}/td !-- 供货单号 --td\){item.name}/td !– 产品名称 –td\({item.model}/td !-- 规格型号 --td\){item.num}/td !– 数量 –td\({item.unit}/td !-- 单位 --td\){item.remark}/td !– 备注 –/tr!– 更新小计和总计的数量和项数 –#assign itemNum item.num?replace(,, )?number !– 将数量转为数字并处理逗号 –#assign subtotalQuantity subtotalQuantity itemNum !– 累加小计数量 –#assign subtotalItems subtotalItems 1 !– 累加小计项数 –#assign totalQuantity totalQuantity itemNum !– 累加总数量 –#assign totalItems totalItems 1 !– 累加总项数 –/#list!– 如果最后一项有数据输出最后的规格型号小计 –#if currentModel ! tr classsubtotaltd colspan4小计\({subtotalQuantity}\){sortedList[0].unit} \({subtotalItems}轴/tdtd\){subtotalQuantity}/tdtd\({sortedList[0].unit}/tdtd/td/tr/#if!-- 输出最终的合计行 --tr classtotaltd colspan4合计\){totalQuantity}\({qdList[0].unit} \){totalItems}轴/td !– 显示合计的数量和项数 –td\({totalQuantity}/td !-- 合计数量 --td\){qdList[0].unit}/td !– 单位 –td/td/tr /table表格结构 使用 table 标签创建表格并通过 th 定义表头包含7列序号、供货单号、产品名称等。 动态数据插入 使用 FreeMarker #list 遍历排序后的清单数据并通过 \({item.属性名} 动态插入每项数据如 \){item.danhao} 插入供货单号。 小计和总计计算 通过 #assign 定义变量如 totalQuantity 和 subtotalQuantity在循环中累加数量。使用 #if 判断条件插入小计行并在循环结束后插入总计行。 数据处理 使用 sortedList qdList?sortby(model) 按型号对清单数据进行排序。处理数量 itemNum item.num?replace(,, )?number移除逗号并转换为数字确保计算正确。 格式化输出 小计和总计行使用 colspan 属性合并单元格确保表格显示整洁。使用 CSS 类 subtotal 和 total 为小计和总计行应用加粗和突出显示的样式。 总结此表格通过 FreeMarker 动态插入数据、计算小计和总计并通过合适的排序和格式化样式确保清单展示清晰且易于阅读。 最后模板还包括了一些额外信息 p发货联系人\({contacts}/p p联系电话\){contactsPhone}/p p日期${date}/pp styletext-align: right;收货人签字______________/p p styletext-align: right;联系电话_______________/p p styletext-align: right;${customer}/p 这部分添加了额外的联系信息和签名区域,进一步完善了文档的实用性。 总的来这个FreeMarker模板展示了如何结合HTML、CSS和FreeMarker的模板语法来创建一个复杂、动态且格式良好的文档。它不仅能够准确地呈现数据还能执行必要的计算和格式化从而生成一个专业的供货清单文档。 总结 通过使用SpingBoot、Apache POI和FreeMarker我们成功自动化了电缆供货清单的生成过程。这不仅提高了效率还减少了人为错误。本解决方案的模块化设计使其易于维护和扩展。 希望本教程能够帮助您理解如何使用Java技术来解决实际业务问题。