乐优商城(十八)——搜索微服务

目录

二、实现基本搜索

2.1 页面分析

2.1.1 页面跳转

2.1.2 发起异步请求

2.2 后台提供搜索接口

2.2.1 Controller

2.2.2 Service

2.2.3 测试

2.3 页面渲染

2.3.1 保存搜索结果

2.3.2 循环展示商品

2.3.3 多sku展示

2.3.4 展示sku其它属性

2.3.5 修改


二、实现基本搜索

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就只监视这些属性,后来添加的属性发生改变,是不会被监视到的。

所以对加载数据函数做一下小修改:

结果:

猜你喜欢

转载自blog.csdn.net/lyj2018gyq/article/details/83028917
今日推荐