前言:
??今天我們要做的是基于Elasticsearch搜索和thymeleaf渲染頁面孤澎,在今天的任務(wù)中將簡(jiǎn)單的介紹thymeleaf的基本使用锈颗,以及結(jié)合項(xiàng)目如何使用thymeleaf渲染頁面齐苛。
觀看thymeleaf的基本使用我已經(jīng)寫了一篇博客引镊,連接地址:http://www.reibang.com/p/29afd801bcc4 通過上一篇《基于Elasticsearch商品搜索(模仿某東搜索頁面的簡(jiǎn)單實(shí)現(xiàn))》
基礎(chǔ)數(shù)據(jù)渲染
(1)更新SearchController,定義跳轉(zhuǎn)搜索結(jié)果頁面方法
@GetMapping("list")
public String list(@RequestParam Map<String, String> searchMap, Model model) {
//對(duì)搜索入?yún)в刑厥夥?hào)進(jìn)行處理
if (null != searchMap) {
Set<Map.Entry<String, String>> entries = searchMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
if (entry.getKey().startsWith("spec_")) {
searchMap.put(entry.getKey(), entry.getValue().replace("+", "%2B"));
}
}
}
Map resultMap = searchService.search(searchMap);
//設(shè)置視圖數(shù)據(jù)
//返回?cái)?shù)據(jù)
model.addAttribute("resultMap", resultMap);
//查詢條件數(shù)據(jù)
model.addAttribute("searchMap", searchMap);
return "search";
}
(2) 搜索結(jié)果頁面渲染
(2.1)用戶選擇條件回顯
<div class="bread">
<ul class="fl sui-breadcrumb">
<li>
<a href="#">全部結(jié)果</a>
</li>
<li class="active"><span th:text="${searchMap.keywords}"></span></li>
</ul>
<ul class="fl sui-tag">
<!-- 品牌-->
<li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}">品牌:
<span th:text="${searchMap.brand}"></span>
<i>×</i>
</li>
<!-- 價(jià)格-->
<li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}">價(jià)格:<span
th:text="${searchMap.price}"></span>
<i>×</i>
</li>
<!-- 規(guī)格-->
<li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}">
<span th:text="${#strings.replace(sm.key,'spec_','')}"></span>:
<span th:text="${#strings.replace(sm.value,'%2B','+')}"></span>
<i>×</i>
</li>
</ul>
<form class="fl sui-form form-dark">
<div class="input-control control-right">
<input type="text"/>
<i class="sui-icon icon-touch-magnifier"></i>
</div>
</form>
</div>
(2.2)商品屬性及規(guī)格顯示
<!--selector-->
<div class="clearfix selector">
<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,brandSate:${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>
<div class="type-wrap" th:each="spec,specStat:${resultMap.skuSpecList}"
th:unless="${#maps.containsKey(searchMap,'spec_'+spec.key)}">
<div class="fl key" th:text="${spec.key}"></div>
<div class="fl value">
<ul class="type-list">
<li th:each="op,opStat:${spec.value}">
<a th:text="${op}" th:href="@{${url}('spec_'+${spec.key}=${op})}"></a>
</li>
</ul>
</div>
<div class="fl ext"></div>
</div>
<div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}">
<div class="fl key">價(jià)格</div>
<div class="fl value">
<ul class="type-list">
<li>
<a th:text="0-500元" th:href="@{${url}(price='0-500')}"></a>
</li>
<li>
<a th:text="500-1000元" th:href="@{${url}(price='500-1000')}"></a>
</li>
<li>
<a th:text="1000-1500元" th:href="@{${url}(price='1000-1500')}"></a>
</li>
<li>
<a th:text="1500-2000元" th:href="@{${url}(price='1500-2000')}"></a>
</li>
<li>
<a th:text="2000-3000元" th:href="@{${url}(price='2000-3000')}"></a>
</li>
<li>
<a th:text="3000元以上" th:href="@{${url}(price='3000')}"></a>
</li>
</ul>
</div>
<div class="fl ext">
</div>
</div>
<div class="type-wrap">
<div class="fl key">更多篩選項(xiàng)</div>
<div class="fl value">
<ul class="type-list">
<li>
<a>特點(diǎn)</a>
</li>
<li>
<a>系統(tǒng)</a>
</li>
<li>
<a>手機(jī)內(nèi)存 </a>
</li>
<li>
<a>單卡雙卡</a>
</li>
<li>
<a>其他</a>
</li>
</ul>
</div>
<div class="fl ext">
</div>
</div>
</div>
(2.3)商品列表顯示
<!--商品列表-->
<div class="goods-list">
<ul class="yui3-g">
<li class="yui3-u-1-5" th:each="sku,skuStat:${resultMap.rows}">
<div class="list-wrap">
<div class="p-img">
<a href="item.html" target="_blank"><img src="/img/_/mobile01.png" /></a>
</div>
<div class="price">
<strong>
<em>¥</em>
<i th:text="${sku.price}"></i>
</strong>
</div>
<div class="attr">
<a target="_blank" href="item.html" th:title="${sku.spec}" th:utext="${sku.name}"></a>
</div>
<div class="commit">
<i class="command">已有<span>2000</span>人評(píng)價(jià)</i>
</div>
<div class="operate">
<a href="success-cart.html" target="_blank" class="sui-btn btn-bordered btn-danger">加入購物車</a>
<a href="javascript:void(0);" class="sui-btn btn-bordered">收藏</a>
</div>
</div>
</li>
</ul>
</div>
(3)關(guān)鍵字搜索
<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>
(4) 條件搜索實(shí)現(xiàn)
用戶每次點(diǎn)擊搜索的時(shí)候誉券,其實(shí)在上次搜索的基礎(chǔ)之上加上了新的搜索條件指厌,也就是在上一次請(qǐng)求的URL后面追加了新的搜索條件,我們可以在后臺(tái)每次拼接組裝出上次搜索的URL踊跟,然后每次將URL存入到Model中踩验,頁面每次點(diǎn)擊不同條件的時(shí)候,從Model中取出上次請(qǐng)求的URL商玫,然后再加上新點(diǎn)擊的條件參數(shù)實(shí)現(xiàn)跳轉(zhuǎn)即可箕憾。
(4.1)后臺(tái)記錄搜索URL
StringBuilder url = new StringBuilder("/search/list");
//拼接url
if (searchMap != null && searchMap.size() > 0) {
url.append("?");
for (String paramKey : searchMap.keySet()) {
if (!"sortRule".equals(paramKey) && !"sortField".equals(paramKey) && !"PageNum".equals(paramKey)) {
url.append(paramKey).append("=").append(searchMap.get(paramKey)).append("&");
}
}
String urlString = url.toString();
//去掉最后一個(gè)&
urlString = urlString.substring(0, urlString.length() - 1);
model.addAttribute("url", urlString);
} else {
model.addAttribute("url", url);
}
(4.2)頁面搜索對(duì)接
<div class="clearfix selector">
<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,brandSate:${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>
<div class="type-wrap" th:each="spec,specStat:${resultMap.skuSpecList}"
th:unless="${#maps.containsKey(searchMap,'spec_'+spec.key)}">
<div class="fl key" th:text="${spec.key}"></div>
<div class="fl value">
<ul class="type-list">
<li th:each="op,opStat:${spec.value}">
<a th:text="${op}" th:href="@{${url}('spec_'+${spec.key}=${op})}"></a>
</li>
</ul>
</div>
<div class="fl ext"></div>
</div>
<div class="type-wrap" th:unless="${#maps.containsKey(searchMap,'price')}">
<div class="fl key">價(jià)格</div>
<div class="fl value">
<ul class="type-list">
<li>
<a th:text="0-500元" th:href="@{${url}(price='0-500')}"></a>
</li>
<li>
<a th:text="500-1000元" th:href="@{${url}(price='500-1000')}"></a>
</li>
<li>
<a th:text="1000-1500元" th:href="@{${url}(price='1000-1500')}"></a>
</li>
<li>
<a th:text="1500-2000元" th:href="@{${url}(price='1500-2000')}"></a>
</li>
<li>
<a th:text="2000-3000元" th:href="@{${url}(price='2000-3000')}"></a>
</li>
<li>
<a th:text="3000元以上" th:href="@{${url}(price='3000')}"></a>
</li>
</ul>
</div>
<div class="fl ext">
</div>
</div>
<div class="type-wrap">
<div class="fl key">更多篩選項(xiàng)</div>
<div class="fl value">
<ul class="type-list">
<li>
<a>特點(diǎn)</a>
</li>
<li>
<a>系統(tǒng)</a>
</li>
<li>
<a>手機(jī)內(nèi)存 </a>
</li>
<li>
<a>單卡雙卡</a>
</li>
<li>
<a>其他</a>
</li>
</ul>
</div>
<div class="fl ext">
</div>
</div>
</div>
(5)移除搜索條件
用戶點(diǎn)擊條件搜索后,要將選中的條件顯示出來拳昌,并提供移除條件的 x 按鈕,顯示條件我們可以從searchMap中獲取袭异,移除其實(shí)就是將之前的請(qǐng)求地址中的指定條件刪除即可。
<ul class="fl sui-tag">
<li class="with-x" th:if="${#maps.containsKey(searchMap,'brand')}">品牌:
<span th:text="${searchMap.brand}"></span>
<a th:href="@{${#strings.replace(url,'&brand='+searchMap.brand,'')}}">×</a>
</li>
<li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}">價(jià)格:<span
th:text="${searchMap.price}"></span>
<a th:href="@{${#strings.replace(url,'&price='+searchMap.price,'')}}">×</a>
</li>
<!--規(guī)格-->
<li class="with-x" th:each="sm:${searchMap}" th:if="${#strings.startsWith(sm.key,'spec_')}">
<span th:text="${#strings.replace(sm.key,'spec_','')}"></span>:
<span th:text="${#strings.replace(sm.value,'%2B','+')}"></span>
<a th:href="@{${#strings.replace(url,'&'+sm.key+'='+sm.value,'')}}">×</a>
</li>
</ul>
(6)排序
<li>
<a th:href="@{${url}(sortRule='ASC',sortField='price')}">價(jià)格↑</a>
</li>
<li>
<a th:href="@{${url}(sortRule='DESC',sortField='price')}">價(jià)格↓</a>
</li>
(7)分頁
加入分頁工具類——傳送門:http://www.reibang.com/p/acdffb8b3859
實(shí)現(xiàn)分頁信息封裝:
//設(shè)置分頁查詢數(shù)據(jù)
Page<Skuinfo> page = new Page<Skuinfo>(
Long.parseLong(String.valueOf(resultMap.get("total"))),
Integer.parseInt(String.valueOf(resultMap.get("pageNum"))),
Page.pageSize
);
看累了吧地回,來幾張圖片放松一下
商品詳情頁靜態(tài)化
需求分析
??當(dāng)系統(tǒng)審核完成商品扁远,需要將商品詳情頁進(jìn)行展示俊鱼,那么采用靜態(tài)頁面生成的方能的web服務(wù)器中進(jìn)行訪問是比較合適的。所以畅买,開發(fā)流程如下圖所示:
執(zhí)行步驟解釋:
??系統(tǒng)管理員(商家運(yùn)維人員)修改或者審核商品的時(shí)候, 會(huì)更改數(shù)據(jù)庫中商品上架狀態(tài)并發(fā)送商品id給rabbitMq中的上架交換器并闲。
??上架交換器會(huì)將商品id發(fā)給靜態(tài)頁生成隊(duì)列。
??靜態(tài)頁微服務(wù)設(shè)置監(jiān)聽器, 監(jiān)聽靜態(tài)頁生成隊(duì)列, 根據(jù)商品id獲取商品詳細(xì)數(shù)據(jù)并使用thymeleaf的模板技術(shù)生成靜態(tài)頁谷羞。
靜態(tài)頁面生成服務(wù)中的具體代碼實(shí)現(xiàn):
1.創(chuàng)建PageListener監(jiān)聽類,監(jiān)聽page_create_queue隊(duì)列,獲取消息,并生成靜態(tài)化頁面
@Component
public class PageListener {
@Autowired
private PageService pageService;
@RabbitListener(queues = RabbitmqConfig.PAGE_CREATE_QUEUE)
public void receiveMessage(String spuId) {
System.out.println("生成靜態(tài)頁面的spuId:" + spuId);
pageService.generateHtml(spuId);
}
}
2.PageServiceImpl實(shí)現(xiàn):
@Service
public class PageServiceImpl implements PageService {
@Value("${pagepath}")
private String pagepath;
@Autowired
private TemplateEngine templateEngine;
/**
* @param spuId
* @return void
* @description: 生產(chǎn)靜態(tài)化頁面
* @author 大佬味的小男孩
* @date 2020/07/27 21:57
*/
@Override
public void generateHtml(String spuId) {
//獲取context對(duì)象
Context context = new Context();
//獲取靜態(tài)化頁面的數(shù)據(jù)
Map<String, Object> map = this.getItemData(spuId);
context.setVariables(map);
//獲取商品存儲(chǔ)位置(本地磁盤)
File file = new File(pagepath);
if (!file.exists()) {
//判斷存儲(chǔ)靜態(tài)化頁面的文件夾是否存在 如果不存在 就創(chuàng)建
file.mkdir();
}
//定義輸出流 生產(chǎn)文件
File file1 = new File(file + "/" + spuId + ".html");
Writer out = null;
try {
out = new PrintWriter(file1);
templateEngine.process("item", context, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
//關(guān)閉流
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Autowired
private SpuFeign spuFeign;
@Autowired
private CategoryFeign categoryFeign;
@Autowired
private SkuFeign skuFeign;
/**
* @param spuId
* @return java.util.Map<java.lang.String, java.lang.Object>
* @description: //獲取靜態(tài)化頁面的數(shù)據(jù)
* @author 大佬味的小男孩
* @date 2020/07/27 22:01
*/
private Map<String, Object> getItemData(String spuId) {
HashMap<String, Object> map = new HashMap<>();
//獲取spu數(shù)據(jù)
Spu spu = spuFeign.findSpuById(spuId).getData();
map.put("spu", spu);
//獲取圖片數(shù)據(jù)
if (spu != null) {
if (!StringUtils.isEmpty(spu.getImages())) {
map.put("ImageList", spu.getImages().split(","));
}
}
//獲取商品分類數(shù)據(jù)
Category category1 = categoryFeign.findById(spu.getCategory1Id());
Category category2 = categoryFeign.findById(spu.getCategory2Id());
Category category3 = categoryFeign.findById(spu.getCategory3Id());
map.put("category1", category1);
map.put("category2", category2);
map.put("category3", category3);
//獲取sku的數(shù)據(jù)
List<Sku> skuList = skuFeign.findSkuListByspuid(spuId);
map.put("skuList", skuList);
//獲取規(guī)格信息
map.put("specifcationList", JSON.parseObject(spu.getSpecItems(), Map.class));
return map;
}
}
3. 數(shù)據(jù)監(jiān)控服務(wù)我就不演示了
4 模板填充
(1)面包屑數(shù)據(jù)
<div class="crumb-wrap">
<ul class="sui-breadcrumb">
<li>
<a href="#" th:text="${category1.name}"></a>
</li>
<li>
<a href="#" th:text="${category2.name}"></a>
</li>
<li>
<a href="#" th:text="${category3.name}"></a>
</li>
</ul>
</div>
(2)商品圖片
<div class="fl preview-wrap">
<!--放大鏡效果-->
<div class="zoom">
<!--默認(rèn)第一個(gè)預(yù)覽-->
<div id="preview" class="spec-preview">
<span class="jqzoom"><img th:jqimg="${ImageList[0]}" th:src="${ImageList[0]}"/></span>
</div>
<!--下方的縮略圖-->
<div class="spec-scroll">
<a class="prev"><</a>
<!--左右按鈕-->
<div class="items">
<ul>
<li th:each="img:${ImageList}"><img th:src="${img}" th:bimg="${img}" onmousemove="preview(this)"/></li>
</ul>
</div>
<a class="next">></a>
</div>
</div>
</div>
(3)規(guī)格輸出
<div id="specification" class="summary-wrap clearfix">
<!--循環(huán)MAP-->
<dl th:each="spec,specStat:${specifcationList}">
<dt>
<div class="fl title">
<i th:text="${spec.key}"></i>
</div>
</dt>
<dd th:each="arrValue:${specStat.current.value}">
<a href="javascript:;"
th:v-bind:class="|{selected:sel('${spec.key}','${arrValue}')}|"
th:@click="|selectSpecification('${spec.key}','${arrValue}')|">
<i th:text="${arrValue}"></i>
<span title="點(diǎn)擊取消選擇"> </span>
</a>
</dd>
</dl>
</div>
(4)默認(rèn)SKU顯示
??靜態(tài)頁生成后帝火,需要顯示默認(rèn)的Sku,我們這里默認(rèn)顯示第1個(gè)Sku即可湃缎,這里可以結(jié)合著Vue一起實(shí)現(xiàn)犀填。可以先定義一個(gè)集合嗓违,再定義一個(gè)spec和sku九巡,用來存儲(chǔ)當(dāng)前選中的Sku信息和Sku的規(guī)格,代碼如下:
<script th:inline="javascript">
var item = new Vue({
el: '#itemArray',
data: {
skuList: [[${skuList}]], //SKU集合
sku: {}, //當(dāng)前選中的sku
spec: {}, //選中的sku的規(guī)格
num: 1
},
created: function () {
//默認(rèn)選中第1個(gè)SKU,深克隆
this.sku = JSON.parse(JSON.stringify(this.skuList[0]));
//第1個(gè)SKU的規(guī)格,深克隆
this.spec = JSON.parse(this.skuList[0].spec);
}
})
</script>
最后請(qǐng)?jiān)徫也恢涝趺纯偨Y(jié)前端頁面蹂季!