• (9)规格参数


    1.1.页面布局

    1.1.1.整体布局

    打开规格参数页面,看到如下内容:

    商品分类树我们之前已经做过,所以这里可以直接展示出来。

    因为规格是跟商品分类绑定的,因此首先会展现商品分类树,并且提示你要选择商品分类,才能看到规格参数的模板。一起了解下页面的实现:

    页面结构:

    这里使用了v-layout来完成页面布局,并且添加了row属性,代表接下来的内容是行布局(左右)。

    可以看出页面分成1个部分:

    • <v-flex xs3>:左侧,内部又分上下两部分:商品分类树及标题

      • v-card-title:标题部分,这里是提示信息,告诉用户要先选择分类,才能看到模板

      • v-tree:这里用到的是我们之前讲过的树组件,展示商品分类树,

    • <v-flex xs9 class="px-1">:右侧:内部是规格参数展示

    1.1.2.右侧规格

    当我们点击一个分类时,最终要达到的效果:

    可以看到右侧分为上下两部分:

    • 上部:面包屑,显示当前选中的分类

    • 下部:table,显示规格参数信息

    页面实现:

    可以看到右侧并不是我们熟悉的 v-data-table,而是一个spec-group组件(规格组)和spec-param组件(规格参数),这是我们定义的独立组件:

    在SpecGroup中定义了表格:

     

    1.2.规格组的查询

    1.2.1.树节点的点击事件

    当我们点击树节点时,要将v-dialog打开,因此必须绑定一个点击事件:(Specification.vue)

    我们来看下handleClick方法:(Specification.vue)

    点击事件发生时,发生了两件事:

    • 记录当前选中的节点,选中的就是商品分类

    • showGroup被置为true,则规格组就会显示了。

    同时,我们把被选中的节点(商品分类)的id传递给了SpecGroup组件:(Specification.vue)

     

    1.2.2.页面查询规格组

    来看下SpecGroup.vue中的实现:

    我们查看页面控制台,可以看到请求已经发出:

     

    1.2.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;
    ​
       // getter和setter省略
    }
    @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;
        @Column(name = "`numeric`")
        private Boolean numeric;
        private String unit;
        private Boolean generic;
        private Boolean searching;
        private String segments;
        
        // getter和setter ...
    }

    leyou-item-service中编写业务:

    mapper

    public interface SpecGroupMapper extends Mapper<SpecGroup> {
    }

    controller

    先分析下需要的东西,在页面的ajax请求中可以看出:

    • 请求方式:get

    • 请求路径:/spec/groups/{cid} ,这里通过路径占位符传递商品分类的id

    • 请求参数:商品分类id

    • 返回结果:页面是直接把resp.data赋值给了groups:

    那么我们返回的应该是规格组SpecGroup的集合

    代码:

    @RestController
    @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);
        }
    }

    页面访问测试:

    目前,我们数据库只为手机分类(76)提供了规格组:

    我们访问:http://api.leyou.com/api/item/spec/groups/76

    然后在后台系统中测试:

     

    1.3.规格参数查询

    1.3.1.表格切换

    当我们点击规格组,会切换到规格参数显示,肯定是在规格组中绑定了点击事件:

    我们看下事件处理:

    可以看到这里是使用了父子通信,子组件触发了select事件

    再来看下父组件的事件绑定:

    事件处理:

    这里我们记录了选中的分组,并且把标记设置为false,这样规格组就不显示了,而是显示:SpecParam

    并且,我们把group也传递到spec-param组件:

     

    1.3.2.页面查询规格参数

    我们来看SpecParam.vue的实现:

    查看页面控制台,发现请求已经发出:

    报404,因为我们还没有实现后台逻辑,接下来就去实现。

    1.3.3.后台实现

    SpecificationController

    分析:

    • 请求方式:GET

    • 请求路径:/spec/params

    • 请求参数:gid,分组id

    • 返回结果:该分组下的规格参数集合List<SpecParam>

    代码:

    /**
         * 根据条件查询规格参数
         * @param gid
         * @return
         */
    @GetMapping("params")
    public ResponseEntity<List<SpecParam>> queryParams(@RequestParam("gid")Long gid){
        List<SpecParam>  params = this.specificationService.queryParams(gid);
        if (CollectionUtils.isEmpty(params)){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(params);
    }

    SpecificationService

    @Autowired
    private SpecParamMapper paramMapper;
    ​
    /**
         * 根据条件查询规格参数
         * @param gid
         * @return
         */
    public List<SpecParam> queryParams(Long gid) {
        SpecParam param = new SpecParam();
        param.setGroupId(gid);
        return this.paramMapper.select(param);
    }

    SpecParamMapper

    public interface SpecParamMapper extends Mapper<SpecParam> {
    }

    测试:

    2. spu sku

    sku:标准产品单元,商品集,spuDetail(generic_spec:通用规格参数(id,value) special_spec:特殊规格参数(id,[value]))

    spu:标准库存单元,具体的商品(indexes:0_0_0 own_spec:具体的值) stock

    3. 商品查询

    1. 添加查询条件
    2. 添加过滤条件
    3. 添加分页
    4. 执行查询
    5. List<spu> --> List<spuBo>
    6. new PageResult<>(total,spuBos)

    1.效果预览

    接下来,我们实现商品管理的页面,先看下我们要实现的效果:

     可以看出整体是一个table,然后有新增按钮。

    2.页面请求

    先看整体页面结构(Goods.vue):

     并且在Vue实例挂载后就会发起查询(mounted调用getDataFromServer方法初始化数据):

     我们刷新页面,可以看到浏览器发起已经发起了查询商品数据的请求:

     因此接下来,我们编写接口即可。

    3.后台提供接口

    页面已经准备好,接下来在后台提供分页查询SPU的功能。

     

    3.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;// 最后修改时间
        // 省略getter和setter
    }

    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;// 售后服务
        // 省略getter和setter
    }

    3.2.mapper

    public interface SpuMapper extends Mapper<Spu> {
    }

    3.3.controller

    先分析:

    • 请求方式:GET

    • 请求路径:/spu/page

    • 请求参数:

      • page:当前页

      • rows:每页大小

      • key:过滤条件

      • saleable:上架或下架

    • 返回结果:商品SPU的分页信息。

      • 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办?

        我们可以新建一个类,继承SPU,并且拓展cname和bname属性,写到leyou-item-interface

    public class SpuBo extends Spu {
    
        String cname;// 商品分类名称
        
        String bname;// 品牌名称
        
        // 略 。。
    }

    编写controller代码:

    我们把与商品相关的一切业务接口都放到一起,起名为GoodsController,业务层也是这样

    @Controller
    public class GoodsController {
    
        @Autowired
        private GoodsService goodsService;
    
        @GetMapping("spu/page")
        public ResponseEntity<PageResult<SpuBo>> querySpuBoByPage(
                @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> pageResult = this.goodsService.querySpuBoByPage(key, saleable, page, rows);
            if(CollectionUtils.isEmpty(pageResult.getItems())){
                return ResponseEntity.notFound().build();
            }
            return ResponseEntity.ok(pageResult);
        }
    
    }

    3.4.service

    所有商品相关的业务(包括SPU和SKU)放到一个业务下:GoodsService。

    @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);
    
        }
    }

    4.5.Category中拓展查询名称的功能

    页面需要商品的分类名称需要在这里查询,因此要额外提供查询分类名称的功能,

    在CategoryService中添加功能:

    public List<String> queryNamesByIds(List<Long> ids) {
        List<Category> list = this.categoryMapper.selectByIdList(ids);
        List<String> names = new ArrayList<>();
        for (Category category : list) {
            names.add(category.getName());
        }
        return names;
        // return list.stream().map(category -> category.getName()).collect(Collectors.toList());
    }

    mapper的selectByIdList方法是来自于通用mapper。不过需要我们在mapper上继承一个通用mapper接口:

    public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> { 
    }

    4.测试

    刷新页面,查看效果:

    基本与预览的效果一致,OK!

    学习中,博客都是自己学习用的笔记,持续更新改正。。。
  • 相关阅读:
    软件开发各列阶段需要达到的目标和生成的成果
    SQL Server 2005 Express附加数据库为“只读”的解决方法
    System.Web.HttpException: Request timed out.
    [收藏]javascript keycode大全
    MS SQL Server中的CONVERT日期格式化大全
    转贴 对于大型公司项目平台选择j2ee的几层认识(一)
    项目经理:做好项目开始阶段的九条经验(1) 项目 技术应用
    .Net Core 实现账户充值,还款,用户登录(WebApi的安全)
    JS如何通过月份,计算月份相差几个月
    .Net core Api后台获取数据,异步方法中,数据需采用Linq分页
  • 原文地址:https://www.cnblogs.com/Tunan-Ki/p/11923588.html
Copyright © 2020-2023  润新知