此博客用于个人学习,来源于网上,对知识点进行一个整理。
1. 商品规格参数管理:
1.1 页面布局:
1)整体布局:
因为规格是跟商品分类绑定的,因此首先会展现商品分类树,并且提示你要选择商品分类,才能看到规格参数的模板。页面结构:
这里使用了 v-layout 来完成页面布局,并且添加了 row 属性,代表接下来的内容是行布局(左右)。
可以看出页面分成2个部分:
- <v-flex xs3> :左侧,内部又分上下两部分:商品分类树及标题
- v-card-title:标题部分,这里是提示信息,告诉用户要先选择分类,才能看到模板
- v-tree:这里用到的是树组件,展示商品分类树
- <v-flex xs9 class=“px-1”>:右侧:内部是规格参数展示
2)右侧规格:
右侧分为上下两部分:
- 上部:面包屑,显示当前选中的分类
- 下部:table,显示规格参数信息
页面实现:
可以看到右侧并不是 v-data-table ,而是一个 spec-group 组件(规格组)和 spec-param 组件(规格参数),这是定义的一个独立组件:SpecGroup.vue。
在 SpecGroup 中定义了表格:
1.2 规格组的查询:
1)树节点的点击事件:
当点击树节点时,要将 v-dialog 打开,因此必须绑定一个点击事件:(Specification.vue)
handleClick 方法:(Specification.vue)
点击事件发生时,发生了两件事:
- 记录当前选中的节点,选中的就是商品分类
- showGroup 被置为true,则规格组就会显示了
同时,被选中的节点(商品分类)的 id 传递给了 SpecGroup 组件:(Specification.vue)
2)页面查询规格组:
来看下 SpecGroup.vue 中的实现:
3)后端代码:
- 在 leyou-item-interface 中添加实体类:
@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;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCid() {
return cid;
}
public void setCid(Long cid) {
this.cid = cid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<SpecParam> getParams() {
return params;
}
public void setParams(List<SpecParam> params) {
this.params = params;
}
}
@Table(name = "tb_spec_param")
public class SpecParam {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long cid;
private Long groupId;
private String name;
//numeric在mysql中是关键字,要加符号和column注解
@Column(name = "`numeric`")
private Boolean numeric;
private String unit;
private Boolean generic;
private Boolean searching;
private String segments;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getCid() {
return cid;
}
public void setCid(Long cid) {
this.cid = cid;
}
public Long getGroupId() {
return groupId;
}
public void setGroupId(Long groupId) {
this.groupId = groupId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getNumeric() {
return numeric;
}
public void setNumeric(Boolean numeric) {
this.numeric = numeric;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public Boolean getGeneric() {
return generic;
}
public void setGeneric(Boolean generic) {
this.generic = generic;
}
public Boolean getSearching() {
return searching;
}
public void setSearching(Boolean searching) {
this.searching = searching;
}
public String getSegments() {
return segments;
}
public void setSegments(String segments) {
this.segments = segments;
}
}
- mapper:
public interface SpecGroupMapper extends Mapper<SpecGroup> {
}
- controiller:
先分析下需要的东西,在页面的 ajax 请求中可以看出:
-
请求方式:get
-
请求路径:/spec/groups/{cid} ,这里通过路径占位符传递商品分类的id
-
请求参数:商品分类 id
-
返回结果:页面是直接把 resp.data 赋值给了 groups
返回的应该是规格组 SpecGroup 的集合:
@Controller
@RequestMapping("spec")
public class SpecificationController {
@Autowired
private SpecificationService specificationService;
/**
* 根据分类id查询参数数组
* @param cid
* @return
*/
@GetMapping("groups/{cid}")
public ResponseEntity<List<SpecGroup>> queryGroupsByCid(@PathVariable("cid")Long cid){
List<SpecGroup> groups = this.specificationService.queryGroupsByCid(cid);
if (CollectionUtils.isEmpty(groups)){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(groups);
}
}
- service:
@Service
public class SpecificationService {
@Autowired
private SpecGroupMapper groupMapper;
/**
* 根据分类id查询分组
* @param cid
* @return
*/
public List<SpecGroup> queryGroupsByCid(Long cid) {
SpecGroup specGroup = new SpecGroup();
specGroup.setCid(cid);
return this.groupMapper.select(specGroup);
}
}
1.3 规格参数查询:
1)表格切换:
当我们点击规格组,会切换到规格参数显示,肯定是在规格组中绑定了点击事件:
我们看下事件处理,使用了父子通信,子组件触发了 select 事件:
父组件的事件绑定:
事件处理:
这里我们记录了选中的分组,并且把标记设置为 false,这样规格组就不显示了,而是显示: SpecParam。并且,我们把 group 也传递到 spec-param 组件:
2)页面查询规格参数:
我们来看 SpecParam.vue 的实现:
3)后台实现:
分析:
- Controller:
- 请求方式:GET
- 请求路径:/spec/params
- 请求参数:gid,分组 id
- 返回结果:该分组下的规格参数集合 List<SpecParam>
/**
* 根据条件查询规格参数
* @param gid
* @return
*/
@GetMapping("params")
public ResponseEntity<List<SpecParam>> queryParams(
@RequestParam(value = "gid",required = false)Long gid,
@RequestParam(value = "cid",required = false)Long cid,
@RequestParam(value = "generic",required = false)Boolean generic,
@RequestParam(value = "searching",required = false)Boolean searching
){
List<SpecParam> params = this.specificationService.queryParamsByGid(gid,cid,generic,searching);
if (CollectionUtils.isEmpty(params)){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(params);
}
- service:
@Autowired
private SpecParamMapper paramMapper;
/**
* 根据条件查询规格参数
* @param gid
* @return
*/
public List<SpecParam> queryParamsByGid(Long gid,Long cid,Boolean generic,Boolean searching) {
SpecParam record = new SpecParam();
record.setGroupId(gid);
record.setCid(cid);
record.setGeneric(generic);
record.setSearching(searching);
return this.specParamMapper.select(record);
}
- Mapper:
public interface SpecParamMapper extends Mapper<SpecParam> {
}
2. 商品查询:
2.1 页面请求:
先看整体页面结构(Goods.vue):
并且在 Vue 实例挂载后就会发起查询( mounted 调用 getDataFromServer 方法初始化数据):
2.2 后台提供接口:
在后台提供分页查询 SPU 的功能
1)实体类:
在 leyou-item-interface 工程中添加实体类:
SPU:
@Table(name = "tb_spu")
public class Spu {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long brandId;
private Long cid1;// 1级类目
private Long cid2;// 2级类目
private Long cid3;// 3级类目
private String title;// 标题
private String subTitle;// 子标题
private Boolean saleable;// 是否上架
private Boolean valid;// 是否有效,逻辑删除用
private Date createTime;// 创建时间
private Date lastUpdateTime;// 最后修改时间
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getBrandId() {
return brandId;
}
public void setBrandId(Long brandId) {
this.brandId = brandId;
}
public Long getCid1() {
return cid1;
}
public void setCid1(Long cid1) {
this.cid1 = cid1;
}
public Long getCid2() {
return cid2;
}
public void setCid2(Long cid2) {
this.cid2 = cid2;
}
public Long getCid3() {
return cid3;
}
public void setCid3(Long cid3) {
this.cid3 = cid3;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSubTitle() {
return subTitle;
}
public void setSubTitle(String subTitle) {
this.subTitle = subTitle;
}
public Boolean getSaleable() {
return saleable;
}
public void setSaleable(Boolean saleable) {
this.saleable = saleable;
}
public Boolean getValid() {
return valid;
}
public void setValid(Boolean valid) {
this.valid = valid;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(Date lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
}
SPU 详情:
@Table(name="tb_spu_detail")
public class SpuDetail {
@Id
private Long spuId;// 对应的SPU的id
private String description;// 商品描述
private String specialSpec;// 商品特殊规格的名称及可选值模板
private String genericSpec;// 商品的全局规格属性
private String packingList;// 包装清单
private String afterService;// 售后服务
public Long getSpuId() {
return spuId;
}
public void setSpuId(Long spuId) {
this.spuId = spuId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getSpecialSpec() {
return specialSpec;
}
public void setSpecialSpec(String specialSpec) {
this.specialSpec = specialSpec;
}
public String getGenericSpec() {
return genericSpec;
}
public void setGenericSpec(String genericSpec) {
this.genericSpec = genericSpec;
}
public String getPackingList() {
return packingList;
}
public void setPackingList(String packingList) {
this.packingList = packingList;
}
public String getAfterService() {
return afterService;
}
public void setAfterService(String afterService) {
this.afterService = afterService;
}
}
2)mapper:
public interface SpuMapper extends Mapper<Spu> {
}
3)controller:
-
请求方式:GET
-
请求路径:/spu/page
-
请求参数:
- page:当前页
- rows:每页大小
- key:过滤条件
- saleable:上架或下架
-
返回结果:商品SPU的分页信息
页面展示的是商品分类和品牌名称,而数据库中保存的是 id,可以新建一个类,继承 SPU,并且拓展 cname 和 bname 属性,写到 leyou-item-interface。
public class SpuBo extends Spu {
private String cname;
private String bname;
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
}
与商品相关的一切业务接口都放到一起,起名为 GoodsController:
@Controller
public class GoodsController {
@Autowired
private GoodService goodService;
/**
* 根据条件分页查询spu
* @param key
* @param saleable
* @param page
* @param rows
* @return
*/
@GetMapping("spu/page")
public ResponseEntity<PageResult<SpuBo>> querySpuBoPage(
@RequestParam(value = "key",required = false)String key,
@RequestParam(value = "saleable",required = false)Boolean saleable,
@RequestParam(value = "page",defaultValue = "1")Integer page,
@RequestParam(value = "rows",defaultValue = "5")Integer rows
){
PageResult<SpuBo> result = this.goodService.querySpuBoPage(key,saleable,page,rows);
if (result == null || CollectionUtils.isEmpty(result.getItems())){
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(result);
}
}
4)service:
@Service
public class GoodsService {
@Autowired
private SpuMapper spuMapper;
@Autowired
private CategoryService categoryService;
@Autowired
private BrandMapper brandMapper;
public PageResult<SpuBo> querySpuBoByPage(String key, Boolean saleable, Integer page, Integer rows) {
Example example = new Example(Spu.class);
Example.Criteria criteria = example.createCriteria();
// 搜索条件
if (StringUtils.isNotBlank(key)) {
criteria.andLike("title", "%" + key + "%");
}
if (saleable != null) {
criteria.andEqualTo("saleable", saleable);
}
// 分页条件
PageHelper.startPage(page, rows);
// 执行查询
List<Spu> spus = this.spuMapper.selectByExample(example);
PageInfo<Spu> pageInfo = new PageInfo<>(spus);
List<SpuBo> spuBos = new ArrayList<>();
spus.forEach(spu->{
SpuBo spuBo = new SpuBo();
// copy共同属性的值到新的对象
BeanUtils.copyProperties(spu, spuBo);
// 查询分类名称
List<String> names = this.categoryService.queryNamesByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
spuBo.setCname(StringUtils.join(names, "/"));
// 查询品牌的名称
spuBo.setBname(this.brandMapper.selectByPrimaryKey(spu.getBrandId()).getName());
spuBos.add(spuBo);
});
return new PageResult<>(pageInfo.getTotal(), spuBos);
}
}
5)Category 中拓展查询名称的功能:
页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,在 CategoryService 中添加功能:
public List<String> queryNamesByIds(List<Long> ids){
List<Category> categories = this.categoryMapper.selectByIdList(ids);
return categories.stream().map(category -> category.getName()).collect(Collectors.toList());
}
mapper 的 selectByIdList 方法是来自于通用 mapper,不过需要我们在 mapper 上继承一个通用 mapper 接口:
public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category,Long> {
}