title: 乐优商城学习笔记十四-搜素微服务(二)
date: 2019-04-19 15:01:59
tags:
- 乐优商城
- java
- springboot
categories:
- 乐优商城
2.实现基本搜索
2.1.页面分析
2.1.1.页面跳转
在首页的顶部,有一个输入框:
1526629923970当我们输入任何文本,点击搜索,就会跳转到搜索页search.html
了:
并且将搜索关键字以请求参数携带过来:
[图片上传失败...(image-d459-1555658500384)]
我们打开search.html
,在最下面会有提前定义好的Vue实例:
我们打开search.html
,在最下面会有提前定义好的Vue实例:
<script type="text/javascript">
var vm = new Vue({
el: "#searchApp",
data: {
},
components:{
// 加载页面顶部组件
lyTop: () => import("./js/pages/top.js")
}
});
</script>
2.1.2.发起异步请求
要想在页面加载后,就展示出搜索结果。我们应该在页面加载时,获取地址栏请求参数,并发起异步请求,查询后台数据,然后在页面渲染。
我们在data中定义一个对象,记录请求的参数:
data: {
search:{
key:"", // 搜索页面的关键字
}
}
我们通过钩子函数created,在页面加载时获取请求参数,并记录下来。
created(){
// 判断是否有请求参数
if(!location.search){
return;
}
// 将请求参数转为对象
const search = ly.parse(location.search.substring(1));
// 记录在data的search对象中
this.search = search;
// 发起请求,根据条件搜索
this.searchFromServer();
}
然后发起请求,搜索数据。
methods:{
searchFromServer(){
// 发起异步请求
ly.http.post("/search/page",this.search)
.then(resp => {
console.log(resp.data);
})
}
}
- 我们这里使用
ly
是common.js中定义的工具对象。 - 这里使用的是post请求,这样可以携带更多参数,并且以json格式发送
刷新页面试试:
[图片上传失败...(image-7e64ab-1555658500384)]
请求体:
[图片上传失败...(image-a22bcf-1555658500384)]
因为后台没有提供接口,所以无法访问。没关系,接下来我们实现后台接口
2.2.后台提供搜索接口
2.2.1.controller
首先分析几个问题:
-
请求方式:Post
-
请求路径:/search/page,不过前面的/search应该是网关的映射路径,因此真实映射路径page,代表分页查询
-
请求参数:json格式,目前只有一个属性:key,搜索关键字,但是搜索结果页一定是带有分页查询的,所以将来肯定会有page属性,因此我们可以用一个对象来接收请求的json数据:
public class SearchRequest { private String key;// 搜索条件 private Integer page;// 当前页 private static final Integer DEFAULT_SIZE = 20;// 每页大小,不从页面接收,而是固定大小 private static final Integer DEFAULT_PAGE = 1;// 默认页 public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Integer getPage() { if(page == null){ return DEFAULT_PAGE; } // 获取页码时做一些校验,不能小于1 return Math.max(DEFAULT_PAGE, page); } public void setPage(Integer page) { this.page = page; } public Integer getSize() { return DEFAULT_SIZE; } }
-
返回结果:作为分页结果,一般都两个属性:当前页数据、总条数信息,我们可以使用之前定义的PageResult类
代码:
@RestController
@RequestMapping
public class SearchController {
@Autowired
private IndexService indexService;
/**
* 搜索商品
*
* @param request
* @return
*/
@PostMapping("page")
public ResponseEntity<PageResult<Goods>> search(@RequestBody SearchRequest request) {
PageResult<Goods> result = this.searchService.search(request);
if (result == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(result);
}
}
2.2.2.service
public PageResult<Goods> search(SearchRequest request) {
Integer page = request.getPage() - 1;
Integer size = request.getSize();
//创建查询构建器
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//过滤
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"}, null));
//分页
queryBuilder.withPageable(PageRequest.of(page,size));
//过滤
//queryBuilder.withQuery(QueryBuilders.matchQuery("all",request.getKey()));
//查询
Page<Goods> result = repository.search(queryBuilder.build());
//解析结果
long total = result.getTotalElements();
Integer totalPage =result.getTotalPages();
List<Goods> goodsList = result.getContent();
return new PageResult<>(total, totalPage, goodsList);
}
注意点:对于过滤字段,如果添加 会报500错误
2.2.3.测试
刷新页面测试:
[图片上传失败...(image-bd11a2-1555658500384)]
[图片上传失败...(image-272ece-1555658500384)]
数据是查到了,但是因为我们只查询部分字段,所以结果json 数据中有很多null,这很不优雅。
解决办法很简单,在application.yml中添加一行配置,json处理时忽略空值:
spring:
jackson:
default-property-inclusion: non_null # 配置json处理时忽略空值
结果:
[图片上传失败...(image-380655-1555658500384)]
2.3.页面渲染
页面已经拿到了结果,接下来就要渲染样式了。
2.3.1.保存搜索结果
首先,在data中定义属性,保存搜索的结果goodList:
var vm = new Vue({
el: "#searchApp",
data: {
ly,
key:"",
search:{},
goodList:{},
total: 0,
totalPage: 0,
selectedSku:{}
},
在loadData
的异步查询中,将结果赋值给goodsList
:
loadData(){
//发送到后台
ly.http.post("/search/page",this.search).then(resp =>{
//保存分页结果
this.total = resp.data.total;
this.totalPage=resp.data.totalPage;
this.goodList = resp.data.item;
console.log(resp);
}).catch(error => {
})
}
2.3.2.循环展示商品
在search.html的中部,有一个div
,用来展示所有搜索到的商品:
[图片上传失败...(image-f2dcad-1555658500384)]
可以看到,div
中有一个无序列表ul
,内部的每一个li
就是一个商品spu了。
我们删除多余的,只保留一个li
,然后利用vue的循环来展示搜索到的结果:
<li class="yui3-u-1-5" v-for="goods in goodList" :key="goods.id" >
2.3.3.多sku展示
分析
接下来展示具体的商品信息,来看图:
[图片上传失败...(image-f69db6-1555658500384)]
这里我们可以发现,一个商品位置,是多个sku的信息集合。当用户鼠标选择某个sku,对应的图片、价格、标题会随之改变!
我们先来实现sku的选择,才能去展示不同sku的数据。
[图片上传失败...(image-14d752-1555658500384)]
可以看到,在列表中默认第一个是被选中的,那我们就需要做两件事情:
- 记录当前被选中的是哪一个sku,记录在哪里比较合适呢?显然是遍历到的goods对象自己内部,因为每一个goods都会有自己的sku信息。
- 在搜索到数据时,先默认把第一个sku作为被选中的,记录下来
初始化sku
我们在查询成功的回调函数中,对goods进行遍历,然后添加一个selected属性,保存被选中的sku:
loadData(){
//发送到后台
ly.http.post("/search/page",this.search).then(resp =>{
//保存分页结果
this.total = resp.data.total;
this.totalPage=resp.data.totalPage;
//保存当前页商品
resp.data.item.forEach(goods =>{
goods.skus = JSON.parse(goods.skus);
goods.selectedSku = goods.skus[0];
});
this.goodList = resp.data.item;
console.log(resp);
}).catch(error => {
})
}
多sku图片列表
接下来,我们看看多个sku的图片列表位置:
[图片上传失败...(image-53c581-1555658500384)]
看到又是一个无序列表,这里我们也一样删掉多余的,保留一个li
,需要注意选中的项有一个样式类:selected
我们的代码:
<!--多sku图片列表-->
<ul class="skus">
<li :class="{selected: sku.id == goods.selected.id}" v-for="sku in goods.skus" :key="sku.id"
@mouseEnter="goods.selected=sku">
<img :src="sku.image">
</li>
</ul>
注意:
- class样式通过 goods.selected的id是否与当前sku的id一致来判断
- 绑定了鼠标事件,鼠标进入后把当前sku赋值到goods.selected
2.3.4.展示sku其它属性代码
div class="goods-list">
<ul class="yui3-g">
<li class="yui3-u-1-5" v-for="goods in goodList" :key="goods.id" >
<div class="list-wrap">
<div class="p-img">
<a href="item.html" target="_blank"><img :src="goods.selectedSku.image" height="200"/></a>
<ul class="skus">
<li :class="{selected:goods.selectedSku.id == sku.id}"
@mouseenter="goods.selectedSku=sku" v-for="sku in goods.skus" :key="sku.id">
<img :src="sku.image">
</li>
</ul>
</div>
<div class="clearfix"></div>
<div class="price">
<strong>
<em>¥</em>
<i v-text="goods.selectedSku.price"></i>
</strong>
</div>
<div class="attr">
<em v-text="goods.selectedSku.title.substring(0,21) + '..'"></em>
</div>
<div class="cu">
<em><span>促</span>{{goods.subTitle.substring(0,15)+'..'}}</em>
</div>
<div class="commit">
<i class="command">已有2000人评价</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>
<a href="javascript:void(0);" class="sui-btn btn-bordered">关注</a>
</div>
</div>
</li>
</ul>
</div>
页面效果:
image
网友评论