第十二章:商品规格参数与商品查询

此博客用于个人学习,来源于网上,对知识点进行一个整理。

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)后端代码:

  1. 在 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;
    }
}
  1. mapper:
public interface SpecGroupMapper extends Mapper<SpecGroup> {
}
  1. 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);
    }
}
  1. 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)后台实现:

分析:

  1. 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);
}
  1. 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);
}
  1. 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> {
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值