目录
二、实现基本搜索
2.1 页面分析
2.1.1 页面跳转
在首页的顶部,有一个输入框:
当输入文本后,点击搜索就会跳转到搜索页面search.html了:
并且将搜索关键字以请求参数的方式携带过来:
2.1.2 发起异步请求
要想在页面加载后,就展示出搜索结果。应该在页面加载时,获取地址栏请求参数,并发起异步请求,查询后台数据,然后在页面渲染。
首先在data中定义一个对象,记录请求的参数:
然后通过生命周期函数created,在页面加载时获取请求参数,并记录下来
created(){
//1.判断是否有请求参数
if(!location.search){
return;
}
//2.将请求参数转为对象
const search = ly.parse(location.search.substring(1));
//3.记录在data的search对象中
this.search = search;
//4.发起请求,根据条件搜索
this.loadData();
}
发起请求,搜索数据
methods:{
loadData(){
ly.http.post("/search/page",this.search).then(resp => {
console.log(resp);
});
}
}
-
这里使用
ly
是common.js中定义的工具对象。 -
这里使用的是post请求,这样可以携带更多参数,并且以json格式发送
在leyou-gateway中,添加允许信任域名,以解决跨域问题:
并添加网关映射:
刷新页面试试:
2.2 后台提供搜索接口
2.2.1 Controller
-
请求方式:Post
-
请求路径:/search/page,不过前面的/search应该是网关的映射路径,因此真实映射路径page,代表分页查询
-
请求参数:json格式,目前只有一个属性:key-搜索关键字,但是搜索结果页一定是带有分页查询的,所以将来肯定会有page属性,因此可以用一个对象来接收请求的json数据:
package com.leyou.bo;
/**
* @Author: 98050
* Time: 2018-10-12 20:08
* Feature: 搜索业务对象
*/
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 getDefaultSize() {
return DEFAULT_SIZE;
}
}
-
返回结果:作为分页结果,一般都两个属性:当前页数据、总条数信息,我们可以使用之前定义的PageResult类
package com.leyou.controller;
import com.leyou.bo.SearchRequest;
import com.leyou.common.pojo.PageResult;
import com.leyou.pojo.Goods;
import com.leyou.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: 98050
* Time: 2018-10-12 20:21
* Feature:
*/
@RestController
@RequestMapping
public class SearchController {
@Autowired
private SearchService searchService;
@PostMapping("page")
public ResponseEntity<PageResult<Goods>> search(@RequestBody SearchRequest searchRequest){
PageResult<Goods> result = this.searchService.search(searchRequest);
if (result == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}else {
return ResponseEntity.ok(result);
}
}
}
2.2.2 Service
public PageResult<Goods> search(SearchRequest searchRequest) {
String key = searchRequest.getKey();
/**
* 判断是否有搜索条件,如果没有,直接返回null。不允许搜索全部商品
*/
if (StringUtils.isBlank(key)){
return null;
}
//构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//1.对关键字进行全文检索查询
queryBuilder.withQuery(QueryBuilders.matchQuery("all",key).operator(Operator.AND));
//2.通过sourceFilter设置返回的结果字段,只需要id,skus,subTitle
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));
//3.分页
int page = searchRequest.getPage();
int size = searchRequest.getDefaultSize();
//elasticsearch分页从0开始
queryBuilder.withPageable(PageRequest.of(page - 1,size));
//4.查询、获取结果
Page<Goods> pageInfo = this.goodsRepository.search(queryBuilder.build());
//5.封装结果,返回
return new PageResult<>(pageInfo.getTotalElements(), (long)pageInfo.getTotalPages(),pageInfo.getContent());
}
2.2.3 测试
刷新页面:
返回数据:
其中有许多空值,解决方法:在leyou-search的application.yml中添加一行配置,json处理时忽略空值:
spring:
jackson:
default-property-inclusion: non_null # 配置json处理时忽略空值
结果:
2.3 页面渲染
2.3.1 保存搜索结果
首先,在data中定义属性,保存搜索的结果:
在loadData的异步查询中,将查询结果赋值给goodsList:
2.3.2 循环展示商品
2.3.3 多sku展示
这里可以发现,一个商品位置,是多个sku的信息集合。当用户鼠标选择某个sku,对应的图片、价格、标题会随之改变!
可以看到,在列表中默认第一个是被选中的,所以需要做两件事情:
-
在搜索到数据时,先默认把第一个sku作为被选中的,记录下来
-
记录当前被选中的是哪一个sku,记录在哪里比较合适呢?显然是遍历到的goods对象自己内部,因为每一个goods都会有自己的sku信息。
在查询成功的回调函数中,对goods进行遍历,把skus转化成对象,并添加一个selected属性保存被选中的sku:
多图片展示
<!--多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其它属性
刷新页面:
2.3.5 修改
sku点击切换
Vue的自动渲染是基于对象的属性变化的。比如页面使用GoodsList进行渲染,如果GoodsList变化,或者其内部的任何子对象变化,都会Vue感知,从而从新渲染页面。
然而,这一切有一个前提,那就是当你第一次渲染时,对象中有哪些属性,Vue就只监视这些属性,后来添加的属性发生改变,是不会被监视到的。
所以对加载数据函数做一下小修改:
结果: