Thymeleaf静态化页面

    科技2022-07-11  88

    一、thymeleaf的基础使用

    当天的git地址:thymeleaf静态页面展示 thymeleaf的坐标

    <!--thymeleaf配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>

    springboot 相关配置

    spring: thymeleaf: cache: false #关闭缓存

    页面需要放在templates目录下。这是springboot自动配置作用

    注意controller,方法上不能加@ResponseBody了,返回的页面名称,页面的前缀后缀被springboot给你省略掉了

    @RequestMapping("demo") @Controller public class TestController { @GetMapping("test") public String test(Model model,String id) { System.out.println(id); model.addAttribute("name", "zs"); //集合存储 List<User> list = new ArrayList<>(); User jack = new User("jack", 23,"bj"); User lucy = new User("lucy", 21,"sh"); list.add(jack); list.add(lucy); model.addAttribute("list", list); //Map定义 Map<String,Object> dataMap = new HashMap<String,Object>(); dataMap.put("No","123"); dataMap.put("address","深圳"); model.addAttribute("dataMap",dataMap); //存储一个数组 String[] names = {"张三","李四","王五"}; model.addAttribute("names",names); //日期 model.addAttribute("now",new Date()); //if条件 model.addAttribute("age",22); return "index"; } } <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Thymeleaf的入门</title> </head> <body> <!--form表单,@{}作用是链接地址--> <p th:text="${name}"></p> <form th:action="@{/demo/test}"> <input th:type="text" th:name="id"> <button>提交</button> </form> <!--循环 userStart是状态,没什么用--> <table> <tr> <td>下标</td> <td>姓名</td> <td>年龄</td> <td>住址</td> </tr> <tr th:each="user,userStart:${list}"> <td th:text="${userStart.index}"></td> <td th:text="${user.getName()}"></td> <td th:text="${user.getAge()}"></td> <td th:text="${user.getAddress()}"></td> </tr> </table> <!--遍历map--> <div th:each="map:${dataMap}"> <div th:text="${map}"/> <br/>----------------- KEY:<span th:text="${map.key}"/> VALUE:<span th:text="${map.value}"/> <br/>----------------- </div> <!--遍历数组--> <div th:each="name,nameStat:${names}"> <span th:text="${nameStat.count}"/> <span th:text="${name}"/> </div> <!--日期转换--> <div th:text="${#dates.format(now,'yyyy-MM-dd hh:dd:ss')}"/> <!--条件判断--> <div th:if="${(age==22)}">我它娘22了</div> <div id="W" th:include="footer::copy"/> </body> </html>

    二、项目中的使用

    2.1、用户的条件搜索回显

    controller 将用户条件集合 查询上来的结果返回回去

    @GetMapping("/list") public String search(@RequestParam Map<String, String> searchMap, Model model) { //特殊符号处理 handleSearchMap(searchMap); //执行查询返回值 Map resultMap = searchService.search(searchMap); model.addAttribute("searchMap", searchMap); model.addAttribute("resultMap", resultMap); return "search"; }

    html :展示的样式

    2.1.1搜索框的条件回显

    获取条件map,搜索条件就行了

    <!--搜索框--> <li class="active"> <span th:text="${searchMap.keywords}"></span> </li>
    2.1.2 品牌的条件回显

    判断条件map里有没有品牌条件,有的话就显示

    <!--品牌--> <li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}"> 品牌:<span th:text="${searchMap.brand}"/> <a th:href="@{${#strings.replace(url,'&brand='+searchMap.brand,'')}}">×</a> </li>
    2.1.3规格的条件回显
    遍历条件map,获取所有的key,判断key有没有前缀为“spec_”将符合条件map的key作为条件的名称(颜色 :金色)需要将"spec_"前缀截取掉,value作为条件的内容 需要将+号转换为+ <!--规格--> <li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}"> <span th:text="${#strings.replace(sm.key,'spec_','')}"/> : <span th:text="${#strings.replace(sm.value,'+','+')}"/> <a th:href="@{${#strings.replace(url,'&'+sm.key+'='+sm.value,'')}}">×</a> </li>

    2.2商品的分类显示

    2.2.1 商品的品牌分组显示

    1、判断map集合条件里没有品牌。有的话就不显示条件分组2、获取返回上来的map结果集合,从里面获取品牌分组集合。遍历显示到页面中 <div class="type-wrap logo" th:unless="${#maps.containsKey(searchMap,'brand')}"> <div class="fl key brand">品牌</div> <div class="value logos"> <ul class="logo-list"> <li th:each="brand:${resultMap.brandList}"> <a th:text="${brand}" th:href="@{${url}(brand=${brand})}"></a> </li> </ul> </div> <div class="ext"> <a href="javascript:void(0);" class="sui-btn">多选</a> <a href="javascript:void(0);">更多</a> </div> </div>
    2.2.2 规格的分组显示

    1、判断map条件里没有规格条件2、service里对规格重新封装一下 封装的逻辑:(1)、重新定义一个resultMap<String,Set>。string 作为规格的名称 ,set集合具体规格 ( 尺码:[100度,150度,200度,250度,350度])(2)、遍历查询上来的规格字符串集合,将每行json字符串转为specMap(3)、获取这个specMap所有的key,拿着key到resultMap,取对应的规格详情。(4)、这个规格详情为空的话就创建一个set集合(5)、specMap根据这个key取出对应的value存入到set集合中(6)、把这个specMap的key存入到resultMap,value就是set集合 /* [ "{'颜色': '红色', '尺码': '150度'}", "{'颜色': '黑色', '尺码': '150度'}", "{'颜色': '黑色'}", "{'颜色': '红色', '尺码': '100度'}", "{'颜色': '红色', '尺码': '250度'}", "{'颜色': '红色', '尺码': '350度'}", "{'颜色': '黑色', '尺码': '200度'}", "{'颜色': '黑色', '尺码': '250度'}" ] 转成页面需要的格式: { 颜色:[黑色,红色], 尺码:[100度,150度,200度,250度,350度] } */ //将规格进行转换 private Map<String, Set<String>> formartSpec(List<String> specList) { //创建一个map,封装成页面需要的格式 Map<String, Set<String>> resultMap = new HashMap<>(); if (specList.size() > 0 && specList != null) { for (String spec : specList) { //将list的每条json字符串转换成map "{'颜色': '黑色', '尺码': '250度'}" Map<String,String> specMap= JSON.parseObject(spec, Map.class); //遍历获取所有的key (颜色,尺码) for (String key : specMap.keySet()) { //根据key取出规格相对应的信息 例如(颜色:[黑色,黄色]) Set<String> specSet = resultMap.get(key); //没有的话就创建一个,set集合 if (specSet == null) { specSet = new HashSet<>(); } //将这个key对应value取出,赋值给set specSet.add(specMap.get(key)); //将这个key,所对应的set封装到map中 resultMap.put(key, specSet); } } } return resultMap; } 3、前端:判断条件map里有没有,结果集的规格key,有的话不显示4、将查询上来的结果集进行,进行循环。key作为规格名称。value作为规格对应的详情。value是一个set集合也需要遍历 <!--规格分组展示--> <div class="type-wrap" th:each="map:${resultMap.specMap}" th:unless="${#maps.containsKey(searchMap,'spec_'+map.key)}"> <div class="fl key" th:text="${map.key}"> </div> <div class="fl value" > <ul class="type-list"> <li th:each="op:${map.value}"> <a th:text="${op}" th:href="@{${url}('spec_'+${map.key}=${op})}"></a> </li> </ul> </div> <div class="fl ext"></div> </div>
    2.2.3 价格分类显示

    1、判断条件map里没有价格条件2、价格展示写死就可以了 <div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}"> <div class="fl key">价格</div> <div class="fl value"> <ul class="type-list"> <li> <a th:href="@{${url}(price='0-500')}">0-500元</a> </li> <li> <a th:href="@{${url}(price='500-1000')}">500-1000元</a> </li> <li> <a th:href="@{${url}(price='1000-1500')}">1000-1500元</a> </li> <li> <a th:href="@{${url}(price='1500-2000')}">1500-2000元</a> </li> <li> <a th:href="@{${url}(price='2000-3000')}">2000-3000元 </a> </li> <li> <a th:href="@{${url}(price='3000')}">3000元以上</a> </li> </ul> </div> <div class="fl ext"> </div> </div>

    2.3 关键搜索(搜索框)

    1、是一个from表单,需要将条件回显到搜索框中

    <div class="search"> <form th:action="@{search/list}" class="sui-form form-inline"> <!--searchAutoComplete--> <div class="input-append"> <input th:type="text" id="autocomplete" name="keywords" th:value="${searchMap.keywords}" class="input-error input-xxlarge" /> <button class="sui-btn btn-xlarge btn-danger" th:type="submit">搜索</button> </div> </form> </div>

    2.4 条件搜索

    每次点击一个条件都。都会拼接上一次的请求。例如:http://localhost:9009/search/list?keywords=手机

    所以我们可以在后端拼接每次条件的url路径。

    1、/search/list 是写死的,后面条件慢慢拼接2、拼接一个?号3、遍历条件map,获取里面所有的4、分页,和排序不用拼接上去5、把条件的名称和对应的条件的内容,条件可能不止一个在拼接一个&符号6、拼接完毕,再把最后一个&截取掉 //字符串拼接 StringBuilder url = new StringBuilder("/search/list"); //map里有查询条件 if (searchMap.size() > 0 && searchMap != null) { //拼接? url.append("?"); //遍历查询条件的key for (String paramKey : searchMap.keySet()) { //排序和分页不用拼接到url上 if (!"sortRule".equals(paramKey) && !"sortField".equals(paramKey) && !"pageNum".equals(paramKey)) { //拼接url http://localhost:9009/search/list?keywords=手机&spec_颜色=金色 url.append(paramKey).append("=").append(searchMap.get(paramKey)).append("&"); } } //截取掉最后一位的连接符 & String urlString = url.toString(); urlString = urlString.substring(0, urlString.length() - 1); model.addAttribute("url", urlString);
    2.4.1品牌条件搜索
    通过标签进行跳转。获取返回上来的上次url请求路径,拼接这次要传入到品牌的名称 <div class="type-wrap logo" th:unless="${#maps.containsKey(searchMap,'brand')}"> <div class="fl key brand">品牌</div> <div class="value logos"> <ul class="logo-list"> <li th:each="brand:${resultMap.brandList}"> <a th:text="${brand}" th:href="@{${url}(brand=${brand})}"></a> </li> </ul> </div> <div class="ext"> <a href="javascript:void(0);" class="sui-btn">多选</a> <a href="javascript:void(0);">更多</a> </div> </div>
    2.4.2规格条件
    通过标签进行跳转。获取返回上来的上次url请求路径,条件:前缀需要通过返回上来的规格结果集的key拼接一下(spe_ 颜色),规格的详情就是返回上来的结果集value(spec_ 颜色=金色) <!--规格分组展示--> <div class="type-wrap" th:each="map:${resultMap.specMap}" th:unless="${#maps.containsKey(searchMap,'spec_'+map.key)}"> <div class="fl key" th:text="${map.key}"> </div> <div class="fl value" > <ul class="type-list"> <li th:each="op:${map.value}"> <a th:text="${op}" th:href="@{${url}('spec_'+${map.key}=${op})}"></a> </li> </ul> </div> <div class="fl ext"></div> </div>
    2.4.3价格条件

    通过标签进行跳转。获取返回上来的上次url请求路径。拿上次的url拼接这次的价格

    <div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}"> <div class="fl key">价格</div> <div class="fl value"> <ul class="type-list"> <li> <a th:href="@{${url}(price='0-500')}">0-500</a> </li> <li> <a th:href="@{${url}(price='500-1000')}">500-1000</a> </li> <li> <a th:href="@{${url}(price='1000-1500')}">1000-1500</a> </li> <li> <a th:href="@{${url}(price='1500-2000')}">1500-2000</a> </li> <li> <a th:href="@{${url}(price='2000-3000')}">2000-3000</a> </li> <li> <a th:href="@{${url}(price='3000')}">3000元以上</a> </li> </ul> </div> <div class="fl ext"> </div> </div>

    2.5 移出条件

    2.5.1移出品牌

    1、给x号 写入到标签

    2、当我点击x号。会把url路径上的品牌条件,替换为空字符串

    <!--品牌--> <li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}"> 品牌:<span th:text="${searchMap.brand}"/> <a th:href="@{${#strings.replace(url,'&brand='+searchMap.brand,'')}}">×</a> </li>
    2.5.2 移出价格
    1、给x号 写入到标签2、当我点击x号。会把url路径上的价格条件,替换为空字符串 <!--价格--> <li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}"> 价格:<span th:text="${searchMap.price}"/> <a th:href="@{${#strings.replace(url,'&price='+searchMap.price,'')}}">×</a> </li>
    2.5.3移出规格
    1、给x号 写入到标签2、当我点击x号。会把url路径上的规格条件。规格的key需要拼接一下,替换为空字符串 <!--规格--> <li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}"> <span th:text="${#strings.replace(sm.key,'spec_','')}"/> : <span th:text="${#strings.replace(sm.value,'+','+')}"/> <a th:href="@{${#strings.replace(url,'&'+sm.key+'='+sm.value,'')}}">×</a> </li>

    2.6 价格排序

    2.6.1价格升序

    2.6.2价格降序

    <li> <a th:href="@{${url}(sortRule='ASC',sortField='price')}">价格⬆</a> </li> <li> <a th:href="@{${url}(sortRule='DESC',sortField='price')}">价格⬇</a> </li>

    2.7 分页

    分页的工具类,创建再common下

    package com.changgou.util; import java.io.Serializable; import java.util.List; /** * 分页对象 * @param <T> */ public class Page <T> implements Serializable{ //当前默认为第一页 public static final Integer pageNum = 1; //默认每页显示条件 public static final Integer pageSize = 20; //判断当前页是否为空或是小于1 public static Integer cpn(Integer pageNum){ if(null == pageNum || pageNum < 1){ pageNum = 1; } return pageNum; } // 页数(第几页) private long currentpage; // 查询数据库里面对应的数据有多少条 private long total;// 从数据库查处的总记录数 // 每页显示多少分页标签 private int size; // 下页 private int next; private List<T> list; // 最后一页 private int last; private int lpage; private int rpage; //从哪条开始查 private long start; //全局偏移量 public int offsize = 2; public Page() { super(); } /**** * * @param currentpage 当前页 * @param total 总记录数 * @param pagesize 每页显示多少条 */ public void setCurrentpage(long currentpage,long total,long pagesize) { //如果整除表示正好分N页,如果不能整除在N页的基础上+1页 int totalPages = (int) (total%pagesize==0? total/pagesize : (total/pagesize)+1); //总页数 this.last = totalPages; //判断当前页是否越界,如果越界,我们就查最后一页 if(currentpage>totalPages){ this.currentpage = totalPages; }else{ this.currentpage=currentpage; } //计算起始页 this.start = (this.currentpage-1)*pagesize; } /**** * 初始化分页 * @param total * @param currentpage * @param pagesize */ public void initPage(long total,int currentpage,int pagesize){ //总记录数 this.total = total; //每页显示多少条 this.size=pagesize; //计算当前页和数据库查询起始值以及总页数 setCurrentpage(currentpage, total, pagesize); //分页计算 int leftcount =this.offsize, //需要向上一页执行多少次 rightcount =this.offsize; //起点页 this.lpage =currentpage; //结束页 this.rpage =currentpage; //2点判断 this.lpage = currentpage-leftcount; //正常情况下的起点 this.rpage = currentpage+rightcount; //正常情况下的终点 //页差=总页数和结束页的差 int topdiv = this.last-rpage; //判断是否大于最大页数 /*** * 起点页 * 1、页差<0 起点页=起点页+页差值 * 2、页差>=0 起点和终点判断 */ this.lpage=topdiv<0? this.lpage+topdiv:this.lpage; /*** * 结束页 * 1、起点页<=0 结束页=|起点页|+1 * 2、起点页>0 结束页 */ this.rpage=this.lpage<=0? this.rpage+(this.lpage*-1)+1: this.rpage; /*** * 当起点页<=0 让起点页为第一页 * 否则不管 */ this.lpage=this.lpage<=0? 1:this.lpage; /*** * 如果结束页>总页数 结束页=总页数 * 否则不管 */ this.rpage=this.rpage>last? this.last:this.rpage; } /**** * * @param total 总记录数 * @param currentpage 当前页 * @param pagesize 每页显示多少条 */ public Page(long total,int currentpage,int pagesize) { initPage(total,currentpage,pagesize); } //上一页 public long getUpper() { return currentpage>1? currentpage-1: currentpage; } //总共有多少页,即末页 public void setLast(int last) { this.last = (int) (total%size==0? total/size : (total/size)+1); } /**** * 带有偏移量设置的分页 * @param total * @param currentpage * @param pagesize * @param offsize */ public Page(long total,int currentpage,int pagesize,int offsize) { this.offsize = offsize; initPage(total, currentpage, pagesize); } public long getNext() { return currentpage<last? currentpage+1: last; } public void setNext(int next) { this.next = next; } public long getCurrentpage() { return currentpage; } public long getTotal() { return total; } public void setTotal(long total) { this.total = total; } public long getSize() { return size; } public void setSize(int size) { this.size = size; } public long getLast() { return last; } public long getLpage() { return lpage; } public void setLpage(int lpage) { this.lpage = lpage; } public long getRpage() { return rpage; } public void setRpage(int rpage) { this.rpage = rpage; } public long getStart() { return start; } public void setStart(long start) { this.start = start; } public void setCurrentpage(long currentpage) { this.currentpage = currentpage; } /** * @return the list */ public List<T> getList() { return list; } /** * @param list the list to set */ public void setList(List<T> list) { this.list = list; } public static void main(String[] args) { //总记录数 //当前页 //每页显示多少条 int cpage =17; Page page = new Page(1001,cpage,50,7); System.out.println("开始页:"+page.getLpage()+"__当前页:"+page.getCurrentpage()+"__结束页"+page.getRpage()+"____总页数:"+page.getLast()); } }

    controller层

    1、创建分页对象2、把当前页、一页显示的条目数全部传入到分页对象中 //分页数数据 /** * 1、总页数 * 2、当前页 * 3、一页显示的条目数 */ Page<SkuInfo> page= new Page<SkuInfo>( Long.parseLong(resultMap.get("total")+""), Integer.parseInt(resultMap.get("pageNum")+""), Page.pageSize ); model.addAttribute("page",page);

    前端html

    上一页 <li class="prev disabled"> <a th:href="@{${url}(pageNum=${page.upper})}">«上一页</a> </li>

    下一页

    <li class="next"> <a th:href="@{${url}(pageNum=${page.next})}">下一页»</a> </li>

    中间显示的条目数

    1、遍历获取当前显示的多少页,到多少页(例如2-6)2、判断那个是当前页,如果是当前页给一个当前页的样式3、展示显示页码4、每个页码,都拼接上了url路径 <li th:each="i:${#numbers.sequence(page.lpage,page.rpage)}" th:class="${i}==${page.currentpage}?'active':''"> <a th:href="@{${url}(pageNum=${i})}" th:text="${i}"></a> </li>

    fo> page= new Page( Long.parseLong(resultMap.get(“total”)+""), Integer.parseInt(resultMap.get(“pageNum”)+""), Page.pageSize );

    model.addAttribute(“page”,page);

    **前端html** * 上一页 ```java <li class="prev disabled"> <a th:href="@{${url}(pageNum=${page.upper})}">«上一页</a> </li>

    下一页

    <li class="next"> <a th:href="@{${url}(pageNum=${page.next})}">下一页»</a> </li>

    中间显示的条目数

    1、遍历获取当前显示的多少页,到多少页(例如2-6)2、判断那个是当前页,如果是当前页给一个当前页的样式3、展示显示页码4、每个页码,都拼接上了url路径 <li th:each="i:${#numbers.sequence(page.lpage,page.rpage)}" th:class="${i}==${page.currentpage}?'active':''"> <a th:href="@{${url}(pageNum=${i})}" th:text="${i}"></a> </li>
    Processed: 0.040, SQL: 8