【畅购商城】详情页详情之商品详情
1.构建详情页
步骤0:确定访问路径
http://localhost:3000/Goods?id=1
步骤二:复制 ~/static/goods.html 内容,导入第三方资源(css、js)
head: {
title: '列表页面',
link: [
{rel:'stylesheet',href: '/style/goods.css'},
{rel:'stylesheet',href: '/style/common.css'},
{rel:'stylesheet',href: '/style/bottomnav.css'},
{rel:'stylesheet',href: '/style/jqzoom.css'},
],
script: [
{ type: 'text/javascript', src: '/js/header.js' },
{ type: 'text/javascript', src: '/js/goods.js' },
{ type: 'text/javascript', src: '/js/jqzoom-core.js' },
]
},
步骤三:导入公共资源
<script>
import TopNav from '@/components/TopNav'
import HeaderSearch from '@/components/HeaderSearch'
import BottomNav from '@/components/BottomNav'
import Footer from '@/components/Footer'
export default {
components: {
TopNav,
HeaderSearch,
BottomNav,
Footer,
},
}
</script>
步骤四:添加原页面js特效
2.详情
2.1分析
2.2接口
GET http://localhost:10010/web-service/sku/goods/2600242
返回值
{
skuid:"商品ID,skuid",
spuid:"商品ID,skuid",
goods_name:"商品名称",
price:"价格",
on_sale_date:"上架时间",
comment_count:"评论数量",
comment_level:"评论级别(1-5)",
cat1_info:{
id:"分类ID",
cat_name:"分类名称"
},
cat2_info:{
id:"分类ID",
cat_name:"分类名称"
},
cat3_info:{
id:"分类ID",
cat_name:"分类名称"
},
logo:{
smlogo:"小LOGO(50x50)",
biglogo:"大LOGO(350x350)",
xbiglogo:"超大LOGO(800x800)"
},
photos:[
{
smimg:"商品图片(50x50)",
bigimg:"商品图片(350x350)",
xbigimg:"商品图片(800x800)"
},
...
],
description:"商品描述",
aftersale:"售后",
stock:"库存量",
spec_list:[
{
id:"规格ID",
spec_name:"规格名称",
options:[
{
id:"选项ID",
option_name:"选项名称"
}
...
]
}
...
],
spec_info:{
id_list:"规格ID:选项ID|规格ID:选项ID|...",
id_txt:"规格名称:规格选项|规格名称:规格选项|..."
},
sku_list:[
{
skuid:"SKUID",
id_list:"规格ID:选项ID|规格ID:选项ID|..."
},
...
]
}
2.3初始化数据
insert into tb_sku_photo(sku_id,url) values(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/100605/24/7603/222062/5dfc6d30Ec375bf0a/e29b6690731acb24.jpg');
insert into tb_sku_photo(sku_id,url) values(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/110371/2/1323/189888/5dfc6d30E073c3495/cb256ec2d3cf9ae2.jpg');
insert into tb_sku_photo(sku_id,url) values(2600242,'http://img12.360buyimg.com/n1/s450x450_jfs/t1/95005/38/7465/139593/5dfc6d2fEd2317126/63b5253237353618.jpg');
2.4后端实现:JavaBean
SkuPhoto : sku对应的所有图片
OneSkuResult:用于封装sku详情
步骤一:创建SkuPhoto,根据tb_sku_photo表编写内容
package com.czxy.changgou4.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* Created by liangtong.
*/
@TableName("tb_sku_photo")
@Data
public class SkuPhoto {
@TableId(type = IdType.AUTO)
private Integer id;
//外键
@TableField(value="sku_id")
@JsonProperty("sku_id")
private Integer skuId;
@TableField(exist = false)
private Sku sku;
@TableField(value="url")
private String url;
}
步骤二:创建OneSkuResult,根据接口返回结果编写内容
package com.czxy.changgou4.vo;
import com.czxy.changgou4.pojo.Category;
import com.czxy.changgou4.pojo.Specification;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@Data
public class OneSkuResult {
private Integer skuid;
private Integer spuid;
@JsonProperty("goods_name")
private String goodsName;
private Double price;
@JsonProperty("on_sale_date")
private Date onSaleDate;
@JsonProperty("comment_count")
private Integer commentCount;
@JsonProperty("comment_level")
private Integer commentLevel;
@JsonProperty("cat1_info")
private Category cat1Info;
@JsonProperty("cat2_info")
private Category cat2Info;
@JsonProperty("cat3_info")
private Category cat3Info;
private Map<String, String> logo;
private List<Map> photos;
private String description;
private String aftersale;
private Integer stock;
@JsonProperty("spec_list")
private List<Specification> specList;
// id_list:'规格ID:选项ID|规格ID:选项ID|...',
// id_txt:'规格名称:选项名称|规格名称:选项名称|...'
@JsonProperty("spec_info")
private Map<String, String> specInfo;
@JsonProperty("sku_list")
private List<Map<String, String>> skuList;
}
2.5后端实现:Mapper
步骤一:修改skuCommentMapper,完成“评论级别”功能
/**
* 通过spu查询评论打分(星星)的平均数
* @param spuId
* @return
*/
@Select("SELECT AVG(star) FROM tb_sku_comment WHERE spu_id = #{spuId}")
public Integer findAvgStarBySpuId(@Param("spuId") Integer spuId);
步骤二:创建SkuPhotoMapper,完成“通过skuId查询对应的所有的图片”功能
package com.czxy.changgou4.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.changgou4.pojo.SkuPhoto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* Created by liangtong.
*/
@Mapper
public interface SkuPhotoMapper extends BaseMapper<SkuPhoto> {
/**
* 通过skuId查询对应的所有的图片
* @param spuId
* @return
*/
@Select("select * from tb_sku_photo where sku_id = #{spuId}")
@Results({
@Result(property="id", column="id"),
@Result(property="skuId", column="sku_id"),
@Result(property="url", column="url")
})
public List<SkuPhoto> findSkuPhotoBySkuId(Integer spuId);
}
步骤三:修改SkuMapper,添加“查询指定spuId的所有sku”功能
/**
* 查询指定spuId的所有sku
* @param spuId
* @return
*/
@Select("select * from tb_sku where spu_id = #{spuId}")
@ResultMap("skuResult")
public List<Sku> findSkuBySpuId(Integer spuId);
2.6后端实现
步骤一:修改SkuService,添加findSkuById 方法
/**
* 查询详情
* @param skuid
* @return
*/
public OneSkuResult findSkuById(Integer skuid);
步骤二:修改SkuServiceImpl,完成“查询详情”功能
package com.czxy.changgou4.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.czxy.changgou4.mapper.*;
import com.czxy.changgou4.pojo.Sku;
import com.czxy.changgou4.pojo.SkuPhoto;
import com.czxy.changgou4.pojo.Specification;
import com.czxy.changgou4.pojo.Spu;
import com.czxy.changgou4.service.SkuService;
import com.czxy.changgou4.vo.ESData;
import com.czxy.changgou4.vo.OneSkuResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@Service
@Transactional
public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuService {
@Resource
private SkuCommentMapper skuCommentMapper;
@Resource
private SpuMapper spuMapper;
@Resource
private CategoryMapper categoryMapper;
@Resource
private SkuPhotoMapper skuPhotoMapper;
@Resource
private SpecificationMapper specificationMapper;
@Override
public List<ESData> findESData() {
//1 查询所有详情sku
List<Sku> skulist = baseMapper.findAllSkus();
//2 将SKU 转换成 ESData
List<ESData> esDataList = new ArrayList<>();
for (Sku sku:skulist){
ESData esData = new ESData();
// id
esData.setId(sku.getId());
// 图片地址
esData.setLogo(sku.getSpu().getLogo());
// 商品名称
esData.setSkuName(sku.getSkuName());
// all “华为xx {"机身颜色":"白色","内存":"3GB","机身存储":"16GB"} 荣耀 ”
esData.setAll(sku.getSkuName()+" " + sku.getSpecInfoIdTxt() + " " +sku.getSpu().getBrand().getBrandName());
// on_sale_time
esData.setOnSaleTime(sku.getSpu().getOnSaleTime());
// brand_id
esData.setBrandId(sku.getSpu().getBrandId());
// cat_id
esData.setCatId(sku.getSpu().getCat3Id());
// Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
Map<String,Object> specs = JSON.parseObject(sku.getSpecInfoIdTxt(), Map.class);
// Map newSpecs = new HashMap();
// for(String key : specs.keySet()){
// newSpecs.put("spec" + key , specs.get(key));
// }
esData.setSpecs(specs);
// price 价格
esData.setPrice(sku.getPrice());
// spu_name
esData.setSpuName(sku.getSpu().getSpuName());
// stock 库存
esData.setStock(sku.getStock());
// description
esData.setDescription(sku.getSpu().getDescription());
// packages;//规格与包装
esData.setPackages(sku.getSpu().getPackages());
// aftersale;//售后保障
esData.setAftersale(sku.getSpu().getAftersale());
// midlogo;
esData.setMidlogo(sku.getSpu().getLogo());
// comment_count; 评价数
Integer comment_count = skuCommentMapper.findNumBySpuId(sku.getSpu().getId());
esData.setCommentCount(comment_count);
//销售量
esData.setSellerCount(10);
esDataList.add(esData);
}
return esDataList;
}
@Override
public OneSkuResult findSkuById(Integer skuid) {
OneSkuResult skuResult = new OneSkuResult();
// 1 查找sku基本信息
Sku sku = baseMapper.selectById(skuid);
// 2 根据sku查找spu信息
Spu spu = spuMapper.findSpuById(sku.getSpuId());
// 3 赋值
// skuid;
skuResult.setSkuid(sku.getId());
// spuid;
skuResult.setSpuid(sku.getSpuId());
// 商品名称
skuResult.setGoodsName(sku.getSkuName());
// 价格
skuResult.setPrice(sku.getPrice());
// 上架时间
skuResult.setOnSaleDate(spu.getOnSaleTime());
// 评价数
Integer comment_count = skuCommentMapper.findNumBySpuId(spu.getId());
skuResult.setCommentCount(comment_count);
// 评论级别
skuResult.setCommentLevel(skuCommentMapper.findAvgStarBySkuId(sku.getId()));
// 一级分类
skuResult.setCat1Info(categoryMapper.selectById(spu.getCat1Id()));
// 二级分类
skuResult.setCat2Info(categoryMapper.selectById(spu.getCat2Id()));
// 三级分类
skuResult.setCat3Info(categoryMapper.selectById(spu.getCat3Id()));
// 第一张图片
Map<String,String> logo = new HashMap();
logo.put("smlogo",spu.getLogo());
logo.put("biglogo",spu.getLogo());
logo.put("xbiglogo",spu.getLogo());
skuResult.setLogo(logo);
// 通过skuId查询对应的所有的图片
List<SkuPhoto> skuPhotoList = skuPhotoMapper.findSkuPhotoBySkuId(sku.getId());
List<Map> photos = new ArrayList<>();
for(SkuPhoto sp:skuPhotoList){
Map<String,String> map = new HashMap();
map.put("smimg",sp.getUrl());
map.put("bigimg",sp.getUrl());
map.put("xbigimg",sp.getUrl());
photos.add(map);
}
skuResult.setPhotos(photos);
// 商品描述
skuResult.setDescription(spu.getDescription());
// 售后
skuResult.setAftersale(spu.getAftersale());
// 库存量
skuResult.setStock(sku.getStock());
// List<SpecResult> spec_list; 根据分类查找规格和规格选项
List<Specification> spec_list = specificationMapper.findSpecificationByCategoryId(spu.getCat3Id());
skuResult.setSpecList(spec_list);
// //id_list:'规格ID:选项ID|规格ID:选项ID|...',
// //id_txt:'规格名称:选项名称|规格名称:选项名称|...'
// Map<String, String> spec_info;
Map<String,String> spec_info = new HashMap<>();
spec_info.put("id_list",sku.getSpecInfoIdList());
spec_info.put("id_txt",sku.getSpecInfoIdTxt());
skuResult.setSpecInfo(spec_info);
// List<Map<String, String>> sku_list;
List<Sku> skuBySpuIdList = baseMapper.findSkuBySpuId(spu.getId());
List<Map<String, String>> sku_list = new ArrayList<>();
for(Sku s : skuBySpuIdList){
Map<String,String> map = new HashMap<>();
map.put("skuid",s.getId().toString());
map.put("id_list",s.getSpecInfoIdList());
sku_list.add(map);
}
skuResult.setSkuList(sku_list);
// 返回结果
return skuResult;
}
}
/**
* 查询详情
* @param skuid
* @return
*/
@GetMapping("/goods/{skuid}")
public BaseResult<OneSkuResult> findSkuById(@PathVariable("skuid") Integer skuid){
OneSkuResult sku = skuService.findSkuById(skuid);
return BaseResult.ok("查询成功", sku);
}
2.7前端实现
详情页面需要进行SSR
步骤一:修改 “apiserver.js”,查询详情
步骤二:修改 Goods.vue 页面,使用asyncData进行查询
步骤三:修改 Goods.vue 页面,显示当前位置
步骤四:修改 Goods.vue 页面,处理放大镜图片
步骤五:修改 Goods.vue 页面,商品详情
编写specOptionSelect方法
methods: {
specOptionSelect(spec,option) {
// 拼接标记符,规格id:选项id
let flag = spec.id + ':' + option.id
// 判断id_list中是否有‘标记符’,如果没有返回-1
return this.goodsInfo.spec_info.id_list.indexOf(flag) != -1
}
},
步骤六:修复bug,图片大小的原因,导致“放大镜”中等图太大,遮盖小图
问题图示
解决
<style>
.midpic img {
width: 100%;
}
</style>
3.规格操作
点击“规格”时,切换SKU的id
步骤一:修改 Goods.vue 页面,给每一个规格选项绑定点击事件
步骤二:修改 Goods.vue 页面,完成 selectSpec 函数