发布时间:2026/8/14 20:38:41
第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服务否则无法删除。删除后重启服务。

相关新闻

2026/8/14 20:38:41

企业如何快速识别符合自身需求的科技成果?

观点作者:科易网-国家科技成果转化(厦门)示范基地 在当前新一轮科技革命和产业变革加速推进的背景下,科技成果的高效转化已成为推动经济高质量发展的重要引擎。然而,面对海量的科技成果、复杂的技术需求以及多元的创新…

2026/8/14 20:38:41

Git 忽略文件大小写

⏹遇到的情况 git提交文件的时候,默认是不区分大小写更改的, 这样会导致修改文件名大小写后,git提交不生效,导致代码运行失效。 也就是说默认情况下,在Git看来 SEB001.java 和 seb001.java 其实是一个文件 ⏹解决 使用…

2026/8/14 20:33:40

从0到1掌握DOM Distiller:Chrome阅读器模式核心技术完全指南

从0到1掌握DOM Distiller:Chrome阅读器模式核心技术完全指南 【免费下载链接】dom-distiller Distills the DOM 项目地址: https://gitcode.com/gh_mirrors/do/dom-distiller DOM Distiller是Chrome阅读器模式的核心技术,它通过提取网页核心文本并…

2026/8/14 21:23:48

Windows系统文件ulib.dll丢失找不到问题解决

在使用电脑系统时经常会出现丢失找不到某些文件的情况,由于很多常用软件都是采用 Microsoft Visual Studio 编写的,所以这类软件的运行需要依赖微软Visual C运行库,比如像 QQ、迅雷、Adobe 软件等等,如果没有安装VC运行库或者安装…

2026/8/14 21:23:48

JX3Toy零基础上手全攻略:剑网3自动化脚本如何帮你腾出双手

JX3Toy零基础上手全攻略:剑网3自动化脚本如何帮你腾出双手 【免费下载链接】JX3Toy 全功能减负工具 项目地址: https://gitcode.com/GitHub_Trending/jx/JX3Toy JX3Toy是一款开源的剑网3自动化脚本工具,基于Lua语言接管重复的技能循环操作。本文将…

2026/8/14 21:23:48

外卖平台商家入驻怎么审核?先核对资料、配送和结算条件

县城外卖平台审核商家入驻时,应把主体资料、经营品类、营业地址、配送范围、收款与结算信息分开核对,再决定是否开店。先完成资料审核并不等于马上开放接单;营业时间、商品、配送方式和结算周期都要在上线前单独配置,才能减少无效…

2026/8/14 21:18:47

OpenClaw 避坑指南,解决 Gateway 离线等各类问题

🤖本地桌面 AI 智能体:OpenClaw Windows2.9.3 实战部署 前言 现在各类大模型聊天工具遍地开花💬,但大多只局限文字对话,没办法真正操作本地电脑文件、浏览器。OpenClaw 作为一款桌面端 AI 智能体,最大价值…

2026/8/14 4:27:24

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/14 4:27:24

当 LLM 遇见大文档:主流开源项目如何处理上下文超限

从 Agentic Loop 到 Repo Map,七种策略与六类陷阱引言:128K vs 10MB 的硬冲突 2026 年的 LLM 上下文窗口已达到 128K ~ 1M token(≈ 0.5MB ~ 4MB 文本),但 LLM 想要处理的真实数据规模远远超过这个量级:真实…

2026/8/14 0:00:09

Flutter与OpenHarmony实现剧本杀组队表单开发实战

1. 项目概述在移动应用开发领域,跨平台框架Flutter因其高效的开发体验和出色的性能表现,已经成为众多开发者的首选。而OpenHarmony作为新兴的操作系统平台,其开放性和灵活性为开发者提供了全新的可能性。本文将聚焦于一个实际应用场景——剧本…

2026/8/14 0:00:09

VSCode高效Git管理:从入门到实战技巧

1. 为什么选择VSCode进行Git代码管理作为微软推出的轻量级代码编辑器,Visual Studio Code(简称VSCode)已经成为全球开发者使用率最高的编辑器之一。根据2023年Stack Overflow开发者调查,VSCode的市场占有率高达74.48%。它内置的Gi…

2026/8/14 4:27:24

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/14 4:27:24

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/14 4:27:24

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…