Loading

搜索页面渲染

搜索分析

搜索页面要显示的内容主要分为3块。

  1)搜索的数据结果

  2)筛选出的数据搜索条件

  3)用户已经勾选的数据条件

搜索实现

搜索的业务流程如上图,用户每次搜索的时候,先经过搜索业务工程,搜索业务工程调用搜索微服务工程。

搜索工程搭建

(1)引入依赖

在changgou-service_search工程中的pom.xml中引入如下依赖:

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

(2)静态资源导入

将资源中的页面资源/所有内容拷贝到工程的resources目录下如下图:

(3) 更改配置文件,在spring下添加内容

thymeleaf:
    cache: false

基础数据渲染

(1)更新SearchController,定义跳转搜索结果页面方法

代码如下:

    //搜索页面   http://localhost:9009/search/list?keywords=手机&brand=三星&spec_颜色=粉色&
    //入参:Map
    //返回值 Map
    //由于页面是thymeleaf 完成的 属于服务器内页面渲染 跳转页面
    @GetMapping("/list")
    public String search(@RequestParam Map<String, String> searchMap, Model model) throws Exception {
​
        //特殊符号处理
        handlerSearchMap(searchMap);
​
        //执行查询返回值
        Map<String, Object> resultMap = searchService.search(searchMap);
​
        model.addAttribute("searchMap", searchMap);
        model.addAttribute("result", resultMap);
        return "search";
    }
​
}

(2) 搜索结果页面渲染

(2.1)用户选择条件回显

<div class="bread">
                <ul class="fl sui-breadcrumb">
                    <li>
                        <a href="#">全部结果</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>
                    <!-- 价格-->
                    <li class="with-x" th:if="${#maps.containsKey(searchMap,'price')}">
                        价格:<span th:text="${searchMap.price}"></span>
                        <i>×</i>
                    </li>
                    <!-- 规格-->
                    <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)商品属性及规格显示

更新搜索业务层实现

 public Map<String, Set<String>> formartSpec(List<String> specList){
        Map<String,Set<String>> resultMap = new HashMap<>();
        if (specList!=null && specList.size()>0){
            for (String specJsonString : specList) {  //"{'颜色': '黑色', '尺码': '250度'}"
                //将获取到的json转换为map
                Map<String,String> specMap = JSON.parseObject(specJsonString, Map.class);
                for (String specKey : specMap.keySet()) {
                    Set<String> specSet = resultMap.get(specKey);
                    if (specSet == null){
                        specSet = new HashSet<String>();
                    }
                    //将规格信息存入set中
                    specSet.add(specMap.get(specKey));
                    //将set存入map
                    resultMap.put(specKey,specSet);
                }
            }
        }
        return resultMap;
    }

更新页面

<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,brandStat:${result.brandList}">
                                <a th:text="${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:${result.specList}" 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}"></a>
                            </li>
                        </ul>
                    </div>
                    <div class="fl ext"></div>
                </div><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:text="0-500元"></a>
                            </li>
                            <li>
                                <a th:text="500-1000元"></a>
                            </li>
                            <li>
                                <a th:text="1000-1500元"></a>
                            </li>
                            <li>
                                <a th:text="1500-2000元"></a>
                            </li>
                            <li>
                                <a th:text="2000-3000元"></a>
                            </li>
                            <li>
                                <a th:text="3000元以上"></a>
                            </li>
                        </ul>
                    </div>
                    <div class="fl ext">
                    </div>
                </div>
                <div class="type-wrap">
                    <div class="fl key">更多筛选项</div>
                    <div class="fl value">
                        <ul class="type-list">
                            <li>
                                <a>特点</a>
                            </li>
                            <li>
                                <a>系统</a>
                            </li>
                            <li>
                                <a>手机内存 </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:${result.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}">Apple苹果iPhone 6s (A1699)Apple苹果iPhone 6s (A1699)Apple苹果iPhone 6s (A1699)Apple苹果iPhone 6s (A1699)</a>
                                </div>
                                <div class="commit">
                                    <i class="command">已有<span>2000</span>人评价</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>

关键字搜索

修改search.html

<form  th:action="@{/search/list}" class="sui-form form-inline">
  <!--searchAutoComplete-->
  <div class="input-append">
    <input 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>

测试

搜索华为关键字,效果如下:

条件搜索实现

用户每次点击搜索的时候,其实在上次搜索的基础之上加上了新的搜索条件,也就是在上一次请求的URL后面追加了新的搜索条件,我们可以在后台每次拼接组装出上次搜索的URL,然后每次将URL存入到Model中,页面每次点击不同条件的时候,从Model中取出上次请求的URL,然后再加上新点击的条件参数实现跳转即可。

(1)后台记录搜索URL

修改SkuController,添加组装URL的方法,并将组装好的URL存储起来,代码如下:

 //拼装url
        StringBuilder url = new StringBuilder("/search/list");
        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("&");
                }
            }
            //http://localhost:9009/search/list?keywords=手机&spec_网络制式=4G&
            String urlString = url.toString();
            //去除路径上的最后一个&
            urlString=urlString.substring(0,urlString.length()-1);
            model.addAttribute("url",urlString);
        }else{
            model.addAttribute("url",url);
        }

(2)页面搜索对接

<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:${result.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:${result.specList}" 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">价格</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">更多筛选项</div>
                    <div class="fl value">
                        <ul class="type-list">
                            <li>
                                <a>特点</a>
                            </li>
                            <li>
                                <a>系统</a>
                            </li>
                            <li>
                                <a>手机内存 </a>
                            </li>
                            <li>
                                <a>单卡双卡</a>
                            </li>
                            <li>
                                <a>其他</a>
                            </li>
                        </ul>
                    </div>
                    <div class="fl ext">
                    </div>
                </div>
            </div>

移除搜索条件

如上图,用户点击条件搜索后,要将选中的条件显示出来,并提供移除条件的x按钮,显示条件我们可以从searchMap中获取,移除其实就是将之前的请求地址中的指定条件删除即可。

修改search.html,移除分类、品牌、价格、规格搜索条件,代码如下:

<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')}">
                        价格:<span th:text="${searchMap.price}"></span>
                        <a th:href="@{${#strings.replace(url,'&price='+searchMap.price,'')}}">×</a>
                    </li>
                    <!--规格-->
                    <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>

排序

修改search.html,实现排序,代码如下:

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

分页

真实的分页应该像百度那样,如下图:

(1)分页工具类定义

在comm工程中添加Page分页对象,代码如下:

package com.changgou.entity;
​
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;// 从数据库查处的总记录数
// 每页查5条
    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) {
        //可以整除的情况下
        long pagecount =  total/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;
        }
​
        //计算start
        this.start = (this.currentpage-1)*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);
    }
​
    /****
     *
     * @param total   总记录数
     * @param currentpage   当前页
     * @param pagesize  每页显示多少条
     */
    public Page(long total,int currentpage,int pagesize) {
        initPage(total,currentpage,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;
    }
​
    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());
    }
}
​

(2)分页实现

修改SkuController,实现分页信息封装,代码如下:

(3)页面分页实现

修改search.html,实现分页查询,代码如下:

posted @ 2021-08-04 10:16  1640808365  阅读(47)  评论(0编辑  收藏  举报