2.3.页面渲染
页面已经拿到了结果,接下来就要渲染样式了。
2.3.1.保存搜索结果
首先,在data中定义属性,保存搜索的结果:
这个页面渲染主要是涉及到Vue的相关的知识,相对来说如果前端的基础较差的话,理解起来还是比较费事的,因为没有JavaSSM三层结构那么的明显,所以这个前端的框架的部分的代码写起来相对来说比较费劲,不过也是有迹可循的,首先,要在Vue中定义到获取的模型的对象,之后才能对模型进行操作,操作完之后,在把处理的结果集合返回到页面,然后页面在进行渲染,这个流程,在执行的过程中,就代码的书写比较难受。
首先,在data中定义属性,保存搜索的结果: 这个事定义了一个goodsList的一个集合,这个集合里面就用来存放后端响应的所有的数据
其实这个操作其实非常的简单,就是分了两个步骤,第一个步骤定义一个goodsLsit,集合,把响应的数据存入到goodsList集合中,第二个步骤就是循环遍历goodsList,将数据渲染到前台的模板上
2.3.3.多sku展示
2.3.3.1.分析
接下来展示具体的商品信息,来看图:
因为一个Spu下有多个SKu,所以存在多SKU的问题,这个时候处理的方法就事将Sku转换为单个的对象,然后在将对象显示到页面上,之后处理起来就比较简单了
这里我们可以发现,一个商品位置,是多个sku的信息集合。当用户鼠标选择某个sku,对应的图片、价格、标题会随之改变!
我们先来实现sku的选择,才能去展示不同sku的数据。
可以看到,在列表中默认第一个是被选中的,那我们就需要做两件事情:
-
在搜索到数据时,先默认把第一个sku作为被选中的,记录下来
-
记录当前被选中的是哪一个sku,记录在哪里比较合适呢?显然是遍历到的goods对象自己内部,因为每一个goods都会有自己的sku信息。
这个时候只要在给响应过的goodsList中的单个的goods对象在附加一个select的属性就可以l了
这个鼠标的动态渲染,和鼠标划过图片要有变化的效果注意两个地方,因为Vue不能监听后来附加上的属性,应该事先转化对象,在添加属性,最后在把对象赋值给goodsList,这样就可以监听了
3.页面分页效果
刚才的查询中,我们默认了查询的页码和每页大小,因此所有的分页功能都无法使用,接下来我们一起看看分页功能条
该如何制作。
这里要分两步,
-
第一步:如何生成分页条
-
第二步:点击分页按钮,我们做什么
这个分页在写的时候有一些坑,这个前端这个代码不好调试的问题就是,你单词拼错,或则是少一个分号,页面直接就不显示了,也不提示什么异常,排查错误很难排查,非常建议这块提前把代码备份好,或者调试一步,在走下一步,否则非常大的可能会出现,上一次调试还好好的,突然加了一个方法,直接页面就不显示了,可能就是少了一个分号,或者多了一个大括号,页面就渲染不出来了,比如这种错误;
开始进行前端分页的页面渲染:
因为后台已经提供过了totalPage:所以只用去过了,存到模型对象里面,然后在对这个模型对象里面的当前页做处理就可以了
分页正常显示出来:
wahtch:{
search:{
deep:true,
handler(){
this.loadData();
}
}
},
翻页过后出现无限循环已经地址栏和底部栏的数字页码对不上
刷新之后出现问题
找到原因:watch单词拼写错误,这个前端出现异常真的很难排查出来错误在哪
将这个地方的监听函数修改之后,分页渲染正常,这个Vue客户端的错误真的很难找到,前端的页面不号排查
顶部分页条信息显示正常:
接下来就是对分类和品牌的聚合,聚合之后进行对页面的渲染,因为用的是查询里面的聚合词条的方法,所以主要是对两个函数内容的编写:
根据BrandId进行聚合:
/**
* 解析品牌的聚合结果集合
* @param aggregation
* @return
*/
private List<Brand> gerBrandAggResult(Aggregation aggregation) {
LongTerms terms= (LongTerms) aggregation;
List<Brand> brands=new ArrayList<>();
terms.getBuckets().forEach(bucket -> {
Brand brand = this.brandClient.queryNameById(bucket.getKeyAsNumber().longValue());
brands.add(brand);
});
return brands;
}
根据分类Id进行聚合:
/**
* 解析分类的聚合结果集
* @param aggregation
* @return
*/
private List<Map<String, Object>> getCategoryAggResult(Aggregation aggregation) {
LongTerms terms= (LongTerms) aggregation;
List<Map<String,Object>> maps=new ArrayList<>();
terms.getBuckets().forEach(bucket -> {
Map<String,Object> map=new HashMap<>();
long id = bucket.getKeyAsNumber().longValue();
List<String> names = this.categoryClient.queryNameByids(Arrays.asList(id));
map.put("id",id);
map.put("name",names);
maps.add(map);
});
return maps;
}
可以看到在进行分类聚合的时候有出现了异常
这个远程调用的异常经成出现:问题还是出现在根据分类查询的时候参数的个数不匹配
进行修改,这个Feign客户端抛出的异常,正常情况下都可以进行修改的
完成测试:品牌和分类聚合成功
聚合成功之后开始直接在页面上渲染数据
前端的代码渲写起来真的比较麻烦:
完成渲染成功:
开始封装规格参数封装规格参数的思路也是比较简单的,只要封装成,List<Map<String,Object>>这种机构就可以了,K为规格参数名,Value为规格参数值,这个数据结构有点复杂,首先是规格参数名,是Map结构,规格参数值,是Map<List>的结构,最后整个响应的结果集合是个List<Map>的结构,所以比较绕,但是也可也写的通
/**
* 根据查询条件聚合规格参数
* @param id
* @param baseQuery
* @return
*/
private List<Map<String, Object>> getParamAggResult(Long id, QueryBuilder baseQuery) {
//构建查询对象
NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();
//保留关键字查询
queryBuilder.withQuery(baseQuery);
List<Map<String, Object>>specs=new ArrayList<>();
//查询要执行聚合的规格参数
List<SpecParam> specParamList = this.specificationClient.querySpecParam(null, id, null, true);
//将每个规格参数放入聚合从查询条件中
specParamList.forEach(specParam -> {
queryBuilder.addAggregation(AggregationBuilders.terms(specParam.getName()).field("specs."+specParam.getName()+".keyword"));
});
//排除字段
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},null));
//执行查询
AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());
//解析聚合查询
Map<String, Aggregation> aggregationMap = goodsPage.getAggregations().asMap();
//遍历聚合查询出来的条件开始进行封装
for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()) {
Map<String,Object> map=new HashMap<>();
map.put("k",entry.getKey());
StringTerms options = (StringTerms) entry.getValue();
List<String> ops=new ArrayList<>();
options.getBuckets().forEach(bucket -> {
ops.add(bucket.getKeyAsString());
});
map.put("options",ops);
specs.add(map);
}
return specs;
}
完成测试
开始渲染出来静态页面:
data.specs.forEach(spec=>{
spec.options=spec.options.map(o=>({name:o}))
this.filters.push(spec);
})
、
更多收起页面优化
<div class="type-wrap" style="text-align: center">
<v-btn small flat @click="show=true" v-show="!show">
更多
<v-icon>arrow_drop_down</v-icon>
</v-btn>
<v-btn small="" flat @click="show=false" v-show="show">
收起
<v-icon>arrow_drop_up</v-icon>
</v-btn>
</div
发送请求,将规格参数的列表的值发送请求封装过去
selectFilter(k,option){
let obj={};
Object.assign(obj,this.search);
if (k=="品牌"|| k=="分类"){
obj.filter[k]=option.id;
}else{
obj.filter[k]=option.name
}
this.search=obj;
}
添加过滤查询:
private BoolQueryBuilder buildBoolQueryBuid(SearchRequest searchRequest) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//给布尔查询添加基本的查询条件
boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));
//添加过滤条件
Map<String, Object> filter = searchRequest.getFilter();
for (Map.Entry<String, Object> stringObjectEntry : filter.entrySet()) {
String key = stringObjectEntry.getKey();
if (StringUtils.equals("品牌",key)){
key="brandId";
}else if (StringUtils.equals("分类",key)){
key="cid3";
}else{
key="specs."+key+".keyword";
}
boolQueryBuilder.filter(QueryBuilders.termQuery(key,stringObjectEntry.getValue()));
}
return boolQueryBuilder;
}
完成测试: