每一个商品都有cid 每一个spu 都作为一个 spuId.html 静态化,以减少服务器压力和访问速率、前端转发路径如下:
1、通过nginx 代理,代理静态化微服务端口8084以及路径 /item
商品详情的数据,需要封装在model 中,通过thymeleaf和vue 来动态展示在item.html 上,需要对spu 、skus 、skuDetails 、以及商品对应的specs(通用规格参数、特殊规格参数)等一系列数据进行封装;
一般封装成map
1、创建接口client,在 item-service商品微服务中GoodsController,创建querySpuById 查询spu 的方法、item-interface微服务对外提供api接口、
/**
* 通过spuid 查询
* @param id spuid
* @return
*/
@GetMapping("spu/{spuId}")
public ResponseEntity<Spu> querySpuById(@PathVariable("spuId") Long id){
Spu spu=this.goodsService.querySpuById(id);
if (spu != null) {
return ResponseEntity.ok(spu);
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
2、model中封装数据
/**
* @auther SyntacticSugar
* @data 2018/12/23 0023上午 11:26
*/
@Service
public class GoodsService {
@Autowired
private BrandClient brandClient;
@Autowired
private CategoryClient categoryClient;
@Autowired
private GoodsClient goodsClient;
@Autowired
private SpecClient specClient;
//日志
private static final Logger logger = LoggerFactory.getLogger(GoodsService.class);
public Map<String, Object> loadModel(Long goodsId) {
try {
Map<String, Object> map = new HashMap<>();
List<Sku> skus = this.goodsClient.querySkuBySpuId(goodsId);
SpuDetail spuDetail = this.goodsClient.querySpuDetailById(goodsId);
Spu spu = this.goodsClient.querySpuById(goodsId);
// 对查询的goods 封装
map.put("skus", skus);
map.put("spuDetail", spuDetail);
map.put("spu", spu);
return map;
} catch (Exception e) {
// public void error(String format, Object arg1, Object arg2);
logger.error("spuId:{}", goodsId,e);
e.printStackTrace();
}
return null;
}
@Controller
@RequestMapping("item")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("{id}.html")
public String get(Model model, @PathVariable("id") Long spuId) {
model.addAllAttributes(goodsService.loadModel(spuId));
return "item";
}
}
在item.html中
<script th:inline="javascript">
const skus=/*[[${skus}]]*/{};
const spuDetail=/*[[${spuDetail}]]*/{};
const spu=/*[[${spu}]]*/{};
console.log(skus);
console.log(spuDetail);
console.log(spu);
script>
前端页面console ,可以看到skus、spuDetail、spu 对象,在spuDetail中取值通用规范参数genericSpec,特殊规格参数specialSpec、
通用规范参数genericSpec,特殊规格参数specialSpec、
String genericSpec = spuDetail.getGenericSpec();
String specialSpec = spuDetail.getSpecialSpec();
Map<Long, Object> genericMap = JsonUtils.parseMap(genericSpec, Long.class, Object.class);
Map<Long, List<String>> specMap = JsonUtils.nativeRead(specialSpec, new TypeReference<Map<Long, List<String>>>() {
});
map.put("genericMap", genericMap);
map.put("specMap", specMap);
// 查询特有规格参数、普通规格参数
List<SpecParam> params = this.specClient.querySpecParam(null, spu.getCid3(), null, false);
// 处理成 kv
HashMap<Long, String> paramMap = new HashMap<>();
params.forEach(param->{
paramMap.put(param.getId(),param.getName());
});
map.put("paramMap", paramMap);
//value为每一个sku的规格参数数组
//key 是param.getId()
<dl v-for="(value,key,index) in specMap " :key="index">
<dt>
<div class="fl title">
<i>{{key}}i>
div>
dt>
<dd v-for="v in value">
<a href="javascript:;" class="selected">
{{v}}<span title="点击取消选择"> span>
a>
dd>
dl>
{{key}}
更改为{{paramMap[key]}}
通过计算,计算skus的每一个sku的indexes中的index 等于值的index的话,给其赋值;
computed:{
sku(){
const index = Object.values(this.indexes).join("_");
return this.skus.find(s => s.indexes == index);
}
},
展示图片,在skus数组中的对象中有images
计算属性中存在每一个sku 、sku中的images 是字符串,转化为数组展示。
1、需要判断图片是否存在
2、需要绑定选择的规格参数 和数组中图片的下标 一致,即
这样就能够选择什么机身颜色,对应哪一个图片,默认选择第一张即images[0];
<div class="zoom">
<div id="preview" class="spec-preview">
<span class="jqzoom">
<img :jqimg="images[0]" :src="images[0]" width="400px" height="400px"/>
span>
div>
<div class="spec-scroll">
<a class="prev"><a>
<div class="items">
<ul>
<img :src="image" :bimg="image" onmousemove="preview(this)" />
li>
ul>
div>
<a class="next">>a>
div>
div>
查询规格参数,当然这处没有什么问题,但是vue显示规格参数组specGroups:null 然后断点debug发现是有值的,放行以后值展示出来了,有时候代码没错,思路没错,刷新就不出来,就debug跟着走一走;
/**
* 查规格参数组
* @param cid 分类id
* @return
*/
@GetMapping("/groups/{cid}")
public ResponseEntity<List<SpecGroup>> querySpecGroups(@PathVariable("cid")Long cid){
List<SpecGroup> specGroups = this.specService.querySpecGroups(cid);
if (specGroups != null && 0!=specGroups.size()) {
return ResponseEntity.ok(specGroups);
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
在service 中封装规格参数组数据进去
在pojo中SpecGroup,添加业务字段 List< SpecParam>
@Table(name = "tb_spec_group")
public class SpecGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private String name;
//改组下所有的规格参数
@Transient
private List<SpecParam> params;
// getter setter
}
@Service
public class SpecService {
@Autowired
private SpecGroupMapper specGroupMapper;
@Autowired
private SpecParamMapper specParamMapper;
public List<SpecGroup> querySpecGroups(Long cid) {
SpecGroup specGroup = new SpecGroup();
specGroup.setCid(cid);
List<SpecGroup> specGroups = this.specGroupMapper.select(specGroup);
// 把specParams参数规格设置到speGroup中
specGroups.forEach(speGroup -> {
SpecParam specParam = new SpecParam();
specParam.setGroupId(speGroup.getId());
specParam.setCid(cid);
speGroup.setParams(this.specParamMapper.select(specParam));
});
return specGroups;
}
}
const specGroups=/*[[${specGroups}]]*/{}
console.log(specGroups)
data中声明属性、
specGroups
<div class="intro-detail" v-html="spuDetail.description">
div>
处理规格预包装、
<div class="Ptable" v-for="group in specGroups">
<div class="Ptable-item">
<h3>主体h3>
<dl v-for="param in group.params">
<dt>{{param.name}}dt>
<dd>{{param.generic?genericMap[param.id]:specMap[param.id]}}dd>
dl>
div>
div>
参数组下params中对通用规格做判断,true就在通用规格参数map中取值,false就在特殊规格参数map中取值、
通用规格参数、
特有规格参数、
显然不合适,当展示当前sku的规格属性,在计算属性中存在sku 的特有规格参数、
<dd>
{{param.generic?genericMap[param.id]:JSON.parse(sku.ownSpec)[param.id]}}
dd>
处理组名字,使用插值表达式取出组名
<h3>{{group.name}}h3>