第7章:页面静态化-02-生成移动端静态页面
5. 生成移动端静态页面前面我们已经学习了Freemarker的基本使用方法下面我们就可以将Freemarker应用到项目中帮我们生成移动端套餐列表静态页面和套餐详情静态页面。接下来我们需要思考几个问题1什么时候生成静态页面比较合适呢2将静态页面生成到什么位置呢3应该生成几个静态页面呢对于第一个问题应该是当套餐数据发生改变时需要生成静态页面即我们通过后台系统修改套餐数据包括新增、删除、编辑时。对于第二个问题如果是在开发阶段可以将文件生成到项目工程中如果上线后可以将文件生成到移动端系统运行的tomcat中。对于第三个问题套餐列表只需要一个页面就可以了在这个页面中展示所有的套餐列表数据即可。套餐详情页面需要有多个即一个套餐应该对应一个静态页面。5.1 环境搭建在health_common工程的pom文件中导入Freemarker的maven坐标!--freemarker-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-freemarker/artifactId /dependency5.2 创建模板文件在health_service_provider工程的templates目录中创建模板文件mobile_setmeal.ftl和mobile_setmeal_detail.ftl文件前者是用于生成套餐列表页面的模板文件后者是生成套餐详情页面的模板文件1mobile_setmeal.ftl!DOCTYPE html html langzh-CN head meta charsetutf-8 meta http-equivX-UA-Compatible contentIEedge !-- 上述3个meta标签*必须*放在最前面任何其他内容都*必须*跟随其后 -- meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0,user-scalableno,minimal-ui meta namedescription content meta nameauthor content link relicon href../img/asset-favico.ico title预约/title link relstylesheet href../css/page-health-order.css / /head body data-spyscroll data-target#myNavbar data-offset150 div classapp idapp !-- 页面头部 -- div classtop-header span classf-lefti classicon-back onclickhistory.go(-1)/i/span span classcenter健康管理系统/span span classf-righti classicon-more/i/span /div !-- 页面内容 -- div classcontentBox div classlist-column1 ul classlist #list setmealList as setmeal li classlist-item a classlink-page hrefsetmeal_detail_${setmeal.id}.html img classimg-object f-left srchttp://r9peo7bmf.hn-bkt.clouddn.com/${setmeal.img} alt div classitem-body h4 classellipsis item-title${setmeal.name}/h4 p classellipsis-more item-desc${setmeal.remark}/p p classitem-keywords span #if setmeal.sex 0 性别不限 #else #if setmeal.sex 1 男 #else 女 /#if /#if /span span${setmeal.age}/span /p /div /a /li /#list /ul /div /div /div !-- 页面 css js -- script src../plugins/vue/vue.js/script script src../plugins/vue/axios-0.18.0.js/script /body /html注意上面模板文件中每个套餐对应的超链接如下a classlink-page hrefsetmeal_detail_${setmeal.id}.html可以看到链接的地址是动态构成的如果套餐的id为1则对应的超链接地址为setmeal_detail_1.html如果套餐的id为5则对应的超链接地址为setmeal_detail_5.html。所以我们需要为每个套餐生成一个套餐详情静态页面。2mobile_setmeal_detail.ftl!DOCTYPE html html langzh-CN head meta charsetutf-8 meta http-equivX-UA-Compatible contentIEedge !-- 上述3个meta标签*必须*放在最前面任何其他内容都*必须*跟随其后 -- meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalable0,user-scalableno,minimal-ui meta namedescription content meta nameauthor content link relicon href../img/asset-favico.ico title预约详情/title link relstylesheet href../css/page-health-orderDetail.css / script src../plugins/vue/vue.js/script script src../plugins/vue/axios-0.18.0.js/script script src../plugins/healthmobile.js/script /head body data-spyscroll data-target#myNavbar data-offset150 div idapp classapp !-- 页面头部 -- div classtop-header span classf-lefti classicon-back onclickhistory.go(-1)/i/span span classcenter健康管理系统/span span classf-righti classicon-more/i/span /div !-- 页面内容 -- div classcontentBox div classcard div classproject-img img srchttp://r9peo7bmf.hn-bkt.clouddn.com/${setmeal.img} width100% height100% / /div div classproject-text h4 classtit${setmeal.name}/h4 p classsubtit${setmeal.remark}/p p classkeywords span #if setmeal.sex 0 性别不限 #else #if setmeal.sex 1 男 #else 女 /#if /#if /span span${setmeal.age}/span /p /div /div div classtable-listbox div classbox-title i classicon-zhenspan classpath1/spanspan classpath2/span/i span套餐详情/span /div div classbox-table div classtable-title div classtit-item flex2项目名称/div div classtit-item flex3项目内容/div div classtit-item flex3项目解读/div /div div classtable-content ul classtable-list #list setmeal.checkGroups as checkgroup li classtable-item div classitem flex2${checkgroup.name}/div div classitem flex3 #list checkgroup.checkItems as checkitem label ${checkitem.name} /label /#list /div div classitem flex3${checkgroup.remark}/div /li /#list /ul /div div classbox-button a clicktoOrderInfo() classorder-btn立即预约/a /div /div /div /div /div script var vue new Vue({ el:#app, methods:{ toOrderInfo(){ window.location.href orderInfo.html?id${setmeal.id}; } } }); /script /body /html5.3 配置文件1在health_service_provider工程application.yml 配置freemarkerspring: freemarker: # 模板后缀名 suffix: .ftl # 文档类型 content-type: text/html # 页面编码 charset: UTF-8 # 页面缓存 cache: false # 模板路径 template-loader-path: classpath:/templates/ #freemarker文件输出地址 out_put_path: D:/IdeaProjects9/health_parent/health_mobile/src/main/resources/static/pages通过上面的配置可以指定将静态HTML页面生成的目录位置5.4 生成静态页面修改health_service_provider工程中的SetmealServiceImpl类的add方法加入生成静态页面的逻辑。package com.liusp.service.impl; import com.alibaba.dubbo.config.annotation.Service; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import com.liusp.constant.RedisConstant; import com.liusp.dao.SetmealDao; import com.liusp.entity.PageResult; import com.liusp.pojo.Setmeal; import com.liusp.service.SetmealService; import freemarker.template.Configuration; import freemarker.template.Template; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import java.io.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 体检套餐服务实现类 */ Service(interfaceClass SetmealService.class) Transactional public class SetmealServiceImpl implements SetmealService { Autowired private SetmealDao setmealDao; Autowired private RedisTemplate redisTemplate; Autowired private FreeMarkerConfigurer freeMarkerConfigurer; Value(${out_put_path})//从属性文件读取输出目录的路径 private String outputpath ; //新增套餐 public void add(Setmeal setmeal, Integer[] checkgroupIds) { setmealDao.add(setmeal); if(checkgroupIds ! null checkgroupIds.length 0){ //绑定套餐和检查组的多对多关系 setSetmealAndCheckGroup(setmeal.getId(),checkgroupIds); } //将图片名称保存到Redis savePic2Redis(setmeal.getImg()); //新增套餐后需要重新生成静态页面 generateMobileStaticHtml(); } //生成静态页面 public void generateMobileStaticHtml() { //准备模板文件中所需的数据 ListSetmeal setmealList this.findAll(); //生成套餐列表静态页面 generateMobileSetmealListHtml(setmealList); //生成套餐详情静态页面多个 generateMobileSetmealDetailHtml(setmealList); } //生成套餐列表静态页面 public void generateMobileSetmealListHtml(ListSetmeal setmealList) { MapString, Object dataMap new HashMapString, Object(); dataMap.put(setmealList, setmealList); this.generateHtml(mobile_setmeal.ftl,m_setmeal.html,dataMap); } //生成套餐详情静态页面多个 public void generateMobileSetmealDetailHtml(ListSetmeal setmealList) { for (Setmeal setmeal : setmealList) { MapString, Object dataMap new HashMapString, Object(); dataMap.put(setmeal, this.findById(setmeal.getId())); this.generateHtml(mobile_setmeal_detail.ftl, setmeal_detail_setmeal.getId().html, dataMap); } } public void generateHtml(String templateName,String htmlPageName,MapString, Object dataMap){ Configuration configuration freeMarkerConfigurer.getConfiguration(); Writer out null; try { // 加载模版文件 Template template configuration.getTemplate(templateName); // 生成数据 File docFile new File(outputpath \\ htmlPageName); out new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // 输出文件 template.process(dataMap, out); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null ! out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } //将图片名称保存到Redis private void savePic2Redis(String pic){ redisTemplate.opsForSet().add(RedisConstant.SETMEAL_PIC_DB_RESOURCES,pic); } //绑定套餐和检查组的多对多关系 private void setSetmealAndCheckGroup(Integer id, Integer[] checkgroupIds) { for (Integer checkgroupId : checkgroupIds) { MapString,Integer map new HashMap(); map.put(setmeal_id,id); map.put(checkgroup_id,checkgroupId); setmealDao.setSetmealAndCheckGroup(map); } } public PageResult pageQuery(Integer currentPage, Integer pageSize, String queryString) { PageHelper.startPage(currentPage,pageSize); PageSetmeal page setmealDao.selectByCondition(queryString); return new PageResult(page.getTotal(),page.getResult()); } public ListSetmeal findAll() { return setmealDao.findAll(); } public Setmeal findById(int id) { return setmealDao.findById(id); } }测试新增套餐查看生成的html页面。注意tomcat运行报Unable to process Jar entry [module-info.class] from Jar ...... jaxb-api-2.3.0.jar for annotations这个错误解决办法找到Maven仓库中的jaxb-api-2.3.0.jar包右击这个jar用压缩工具打开删除打开文件里的module-info.class文件夹删除的时候要停掉Tomcat服务否则无法删除。删除后重启服务。

相关新闻

npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案

npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案

npm-g_nosudo:彻底告别Ubuntu下npm全局安装sudo依赖的终极解决方案 【免费下载链接】npm-g_nosudo A shell script which will fix the problem where you want to stop using sudo for npm -g on Ubuntu. 项目地址: https://gitcode.com/gh_mirrors/np/npm-g_nos…

2026/10/11 23:27:32 阅读更多 →
网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定

网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定

网页里的视频、音频、图片怎么保存?猫抓浏览器资源嗅探扩展帮你一次搞定 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你在网页上看到…

2026/10/13 16:52:10 阅读更多 →
Maven生命周期全解析:从compile到deploy,掌握IDEA构建命令精髓

Maven生命周期全解析:从compile到deploy,掌握IDEA构建命令精髓

1. 项目概述:从“构建工具”到“工程基座”的认知跃迁提到Maven,很多刚入行的Java开发者第一反应就是“一个用来下载jar包的工具”。这个认知没错,但太浅了。我干了十多年后端,从Ant、Maven到Gradle一路用过来,可以很负…

2026/10/9 13:12:42 阅读更多 →

最新新闻

The Nature of Code 极坐标与三角函数实战:用 sin/cos 在 p5.js 中绘制圆周、螺旋与任意形状

The Nature of Code 极坐标与三角函数实战:用 sin/cos 在 p5.js 中绘制圆周、螺旋与任意形状

教程文档示例工程 【免费下载链接】website-archive Archive of the Coding Train website (first version) 项目地址: https://gitcode.com/gh_mirrors/we/website-archive 点击查看 免费下载 本文以 website-archive 仓库中归档的《Trigonometry and Polar Coord…

2026/10/14 16:09:19 阅读更多 →
MySQL视图原理与生产级应用:从数据抽象到权限治理

MySQL视图原理与生产级应用:从数据抽象到权限治理

1. 项目概述:为什么View不是“偷懒 shortcut”,而是数据库设计的隐形骨架你有没有遇到过这样的场景:业务部门隔三差五提需求——“把销售部上季度华东区TOP10客户订单金额、退货率、复购频次拉个表”;“财务要统计每个产品线的毛利…

2026/10/14 16:09:19 阅读更多 →
2027年度《中国科学家奋进新征程》大型科学家挂历编辑工作正式启动——弘扬新时代科学家精神

2027年度《中国科学家奋进新征程》大型科学家挂历编辑工作正式启动——弘扬新时代科学家精神

2026/10/14 16:09:19 阅读更多 →
MySQL View进阶实战:从性能陷阱到数据治理核心

MySQL View进阶实战:从性能陷阱到数据治理核心

1. 项目概述:为什么View不是“偷懒的快捷方式”,而是数据治理的第一道闸门你有没有遇到过这样的场景:业务方临时要一份“近30天活跃用户中,下单金额超5000元且完成至少2次售后的客户清单”,还要带所属城市、首次注册渠…

2026/10/14 16:09:19 阅读更多 →
错误率的三大陷阱:类别不平衡、分布偏移与阈值依赖

错误率的三大陷阱:类别不平衡、分布偏移与阈值依赖

1. 项目概述:为什么“错误率”不是一句定义就能糊弄过去“机器学习公式推导【Day4】性能度量-错误率”——这个标题乍看平平无奇,像是教科书里一页翻过就忘的章节。但我在带某高校实验室本科生做模型评估实训时发现,超过七成的同学能背出错误…

2026/10/14 16:09:19 阅读更多 →
数据中心机柜温湿度精细化监测:分层多点以太网采集系统实践

数据中心机柜温湿度精细化监测:分层多点以太网采集系统实践

1. 从"空调没报警但服务器降频"说起:粗粒度监测的盲区到底在哪去年夏天做机房巡检时碰到一个很典型的案例:动环平台显示机房平均温度24.5℃,一个告警都没有,但监控大屏上某一排机柜的服务器核心温度持续走高&#xff0c…

2026/10/14 16:08:13 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →