前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >商城项目-商品查询

商城项目-商品查询

作者头像
cwl_java
发布2020-02-11 13:51:17
1.4K0
发布2020-02-11 13:51:17
举报
文章被收录于专栏:cwl_Javacwl_Java

4.商品查询

4.1.效果预览

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

在这里插入图片描述
在这里插入图片描述

可以看出整体是一个table,然后有新增按钮。是不是跟昨天写品牌管理很像?

模板代码在分别在Goods.vue

在这里插入图片描述
在这里插入图片描述

4.2.从0开始

接下来,我们自己来实现一下,新建两个组件:MyGoods.vue和MyGoodsForm.vue

在这里插入图片描述
在这里插入图片描述

内容先随意:

代码语言:javascript
复制
<template>
  <v-card>
      MyGoods
  </v-card>
</template>

<script>

  export default {
    name: "my-goods",
    data() {
      return {
      }
    }
  }
</script>

<style scoped>

</style>

然后修改menu.js,新建一个菜单:

在这里插入图片描述
在这里插入图片描述

修改router/index.js,添加一个路由:

在这里插入图片描述
在这里插入图片描述

预览一下:

在这里插入图片描述
在这里插入图片描述

4.3.页面实现

4.3.1.页面基本表格

商品列表页与品牌列表页几乎一样,我们可以直接去复制一份过来,然后进行一些修改。

首先,字段不一样,商品列表也展示的SPU信息,包含以下字段:

代码语言:javascript
复制
id:
title:标题
cname:商品分类名称
bname:品牌名称

完整代码:

代码语言:javascript
复制
<template>
  <v-card>
    <v-card-title>
      <v-btn color="primary" @click="addGoods">新增商品</v-btn>
      <!--搜索框,与search属性关联-->
      <v-spacer/>
      <v-text-field label="输入关键字搜索" v-model.lazy="search" append-icon="search" hide-details/>
    </v-card-title>
    <v-divider/>
    <v-data-table
      :headers="headers"
      :items="goodsList"
      :search="search"
      :pagination.sync="pagination"
      :total-items="totalGoods"
      :loading="loading"
      class="elevation-1"
    >
      <template slot="items" slot-scope="props">
        <td>{{ props.item.id }}</td>
        <td class="text-xs-center">{{ props.item.title }}</td>
        <td class="text-xs-center">{{props.item.cname}}</td>
        <td class="text-xs-center">{{ props.item.bname }}</td>
        <td class="justify-center layout">
          <v-btn color="info" @click="editGoods(props.item)">编辑</v-btn>
          <v-btn color="warning">删除</v-btn>
          <v-btn >下架</v-btn>
        </td>
      </template>
    </v-data-table>
    <!--弹出的对话框-->
    <v-dialog max-width="500" v-model="show" persistent>
      <v-card>
        <!--对话框的标题-->
        <v-toolbar dense dark color="primary">
          <v-toolbar-title>{{isEdit ? '修改' : '新增'}}商品</v-toolbar-title>
          <v-spacer/>
          <!--关闭窗口的按钮-->
          <v-btn icon @click="closeWindow"><v-icon>close</v-icon></v-btn>
        </v-toolbar>
        <!--对话框的内容,表单-->
        <v-card-text class="px-5">
          <my-goods-form :oldGoods="oldGoods" />
        </v-card-text>
      </v-card>
    </v-dialog>
  </v-card>
</template>

<script>
  // 导入自定义的表单组件
  import MyGoodsForm from './MyGoodsForm'

  export default {
    name: "my-goods",
    data() {
      return {
        search: '', // 搜索过滤字段
        totalGoods: 0, // 总条数
        goodsList: [], // 当前页品牌数据
        loading: true, // 是否在加载中
        pagination: {}, // 分页信息
        headers: [
          {text: 'id', align: 'center', value: 'id'},
          {text: '标题', align: 'center', sortable: false, value: 'title'},
          {text: '商品分类', align: 'center', sortable: false, value: 'cname'},
          {text: '品牌', align: 'center', value: 'bname', sortable: false,},
          {text: '操作', align: 'center', sortable: false}
        ],
        show: false,// 控制对话框的显示
        oldGoods: {}, // 即将被编辑的商品信息
        isEdit: false, // 是否是编辑
      }
    },
    mounted() { // 渲染后执行
      // 查询数据
      this.getDataFromServer();
    },
    watch: {
      pagination: { // 监视pagination属性的变化
        deep: true, // deep为true,会监视pagination的属性及属性中的对象属性变化
        handler() {
          // 变化后的回调函数,这里我们再次调用getDataFromServer即可
          this.getDataFromServer();
        }
      },
      search: { // 监视搜索字段
        handler() {
          this.getDataFromServer();
        }
      }
    },
    methods: {
      getDataFromServer() { // 从服务的加载数的方法。
        // 发起请求
        this.$http.get("/item/spu/page", {
          params: {
            key: this.search, // 搜索条件
            page: this.pagination.page,// 当前页
            rows: this.pagination.rowsPerPage,// 每页大小
            sortBy: this.pagination.sortBy,// 排序字段
            desc: this.pagination.descending// 是否降序
          }
        }).then(resp => { // 这里使用箭头函数
          this.goodsList = resp.data.items;
          this.totalGoods = resp.data.total;
          // 完成赋值后,把加载状态赋值为false
          this.loading = false;
        })
      },
      addGoods() {
        // 修改标记
        this.isEdit = false;
        // 控制弹窗可见:
        this.show = true;
        // 把oldBrand变为null
        this.oldBrand = null;
      },
      editGoods(oldGoods){
        // 修改标记
        this.isEdit = true;
        // 控制弹窗可见:
        this.show = true;
        // 获取要编辑的brand
        this.oldGoods = oldGoods;
      },
      closeWindow(){
        // 重新加载数据
        this.getDataFromServer();
        // 关闭窗口
        this.show = false;
      }
    },
    components:{
      MyGoodsForm
    }
  }
</script>

<style scoped>

</style>

主要的改动点:

  • 页面的v-data-table中的属性绑定修改。items指向goodsList,totalItems指向totalGoods
  • 页面渲染的字段名修改:字段改成商品的SPU字段:id、title,cname(商品分类名称),bname(品牌名称)
  • data属性修改了以下属性:
    • goodsList:当前页商品数据
    • totalGoods:商品总数
    • headers:头信息,需要修改头显示名称
    • oldGoods:准备要修改的商品
  • 加载数据的函数:getDataFromServer,请求的路径进行了修改,另外去除了跟排序相关的查询。SPU查询不排序
  • 新增商品的事件函数:清除了一些数据查询接口,只保留弹窗

查看效果:

在这里插入图片描述
在这里插入图片描述

因为没有编写查询功能,表格一直处于loading状态。

接下来看弹窗:

在这里插入图片描述
在这里插入图片描述

4.3.2.上下架状态按钮

另外,似乎页面少了对上下架商品的过滤,在原始效果图中是有的:

在这里插入图片描述
在这里插入图片描述

这在Vuetify中是一组按钮,我们查看帮助文档:

在这里插入图片描述
在这里插入图片描述

查看实例得到以下信息:

v-btn:一个按钮

v-btn-toggle:按钮组,内部可以有多个按钮,点击切换,有以下属性:

  • multiple:是否支持多选,默认是false
  • value:选中的按钮的值,如果是多选,结果是一个数组;单选,结果是点击的v-btn中的value值,因此按钮组的每个btn都需要指定value属性

改造页面:

首先在data中定义一个属性,记录按钮的值。

代码语言:javascript
复制
filter:{
    saleable: false, // 上架还是下架
    search: '', // 搜索过滤字段
}

这里我们的做法是定义一个filter属性,内部在定义search来关联过滤字段,saleable来关联上下架情况。

这样watch就必须监听filter,而不是只监听search了:

代码语言:javascript
复制
filter: {// 监视搜索字段
  handler() {
    this.getDataFromServer();
  },
  deep:true
}

另外,页面中与search有关的所有字段都需要修改成filter.search:

代码语言:javascript
复制
<!--搜索框,与search属性关联-->
<v-text-field label="输入关键字搜索" v-model.lazy="filter.search" append-icon="search" hide-details/>

然后,在页面中添加按钮组:

代码语言:javascript
复制
 <v-flex xs3>
     状态:
     <v-btn-toggle v-model="filter.saleable">
         <v-btn flat>
             全部
         </v-btn>
         <v-btn flat :value="true">
             上架
         </v-btn>
         <v-btn flat :value="false">
             下架
         </v-btn>
     </v-btn-toggle>
</v-flex>

最后,不要忘了在查询时,将saleable携带上:

代码语言:javascript
复制
getDataFromServer() { // 从服务的加载数的方法。
    // 发起请求
    this.$http.get("/item/spu/page", {
        params: {
            key: this.filter.search, // 搜索条件
            saleable: this.filter.saleable, // 上下架
            page: this.pagination.page,// 当前页
            rows: this.pagination.rowsPerPage,// 每页大小
        }
    }).then(resp => { // 这里使用箭头函数
        this.goodsList = resp.data.items;
        this.totalGoods = resp.data.total;
        // 完成赋值后,把加载状态赋值为false
        this.loading = false;
    })
}

4.4.后台提供接口

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

4.4.1.实体类

SPU

代码语言:javascript
复制
@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详情

代码语言:javascript
复制
@Table(name="tb_spu_detail")
public class SpuDetail {
    @Id
    private Long spuId;// 对应的SPU的id
    private String description;// 商品描述
    private String specTemplate;// 商品特殊规格的名称及可选值模板
    private String specifications;// 商品的全局规格属性
    private String packingList;// 包装清单
    private String afterService;// 售后服务
    // 省略getter和setter
}

4.4.2.controller

先分析:

  • 请求方式:GET
  • 请求路径:/spu/page
  • 请求参数:
    • page:当前页
    • rows:每页大小
    • key:过滤条件
    • saleable:上架或下架
  • 返回结果:商品SPU的分页信息。
    • 要注意,页面展示的是商品分类和品牌名称,而数据库中保存的是id,怎么办? 我们可以新建一个类,继承SPU,并且拓展cname和bname属性,写到ly-item-interface public class SpuBo extends Spu { String cname;// 商品分类名称 String bname;// 品牌名称 // 略 。。 }

编写controller代码:

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

代码语言:javascript
复制
@RestController
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    /**
     * 分页查询SPU
     * @param page
     * @param rows
     * @param key
     * @return
     */
    @GetMapping("/spu/page")
    public ResponseEntity<PageResult<SpuBo>> querySpuByPage(
            @RequestParam(value = "page", defaultValue = "1") Integer page,
            @RequestParam(value = "rows", defaultValue = "5") Integer rows,
            @RequestParam(value = "key", required = false) String key) {
        // 分页查询spu信息
        PageResult<SpuBo> result = this.goodsService.querySpuByPageAndSort(page, rows, key);
        if (result == null || result.getItems().size() == 0) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return ResponseEntity.ok(result);
    }
}

4.4.3.service

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

代码语言:javascript
复制
@Service
public class GoodsService {

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private BrandMapper brandMapper;

    public PageResult<SpuBo> querySpuByPageAndSort(Integer page, Integer rows, Boolean saleable, String key) {
        // 1、查询SPU
        // 分页,最多允许查100条
        PageHelper.startPage(page, Math.min(rows, 100));
        // 创建查询条件
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        // 是否过滤上下架
        if (saleable != null) {
            criteria.orEqualTo("saleable", saleable);
        }
        // 是否模糊查询
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("title", "%" + key + "%");
        }
        Page<Spu> pageInfo = (Page<Spu>) this.spuMapper.selectByExample(example);

        List<SpuBo> list = pageInfo.getResult().stream().map(spu -> {
            // 2、把spu变为 spuBo
            SpuBo spuBo = new SpuBo();
            // 属性拷贝
            BeanUtils.copyProperties(spu, spuBo);

            // 3、查询spu的商品分类名称,要查三级分类
            List<String> names = this.categoryService.queryNameByIds(
                    Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
            // 将分类名称拼接后存入
            spuBo.setCname(StringUtils.join(names, "/"));

            // 4、查询spu的品牌名称
            Brand brand = this.brandMapper.selectByPrimaryKey(spu.getBrandId());
            spuBo.setBname(brand.getName());
            return spuBo;
        }).collect(Collectors.toList());

        return new PageResult<>(pageInfo.getTotal(), list);
    }
}

4.4.4.mapper

代码语言:javascript
复制
public interface SpuMapper extends Mapper<Spu> {
}

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

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

在CategoryService中添加功能:

代码语言:javascript
复制
public List<String> queryNameByIds(List<Long> ids) {
    return this.categoryMapper.selectByIdList(ids).stream().map(Category::getName).collect(Collectors.toList());
}

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

代码语言:javascript
复制
public interface CategoryMapper extends Mapper<Category>, SelectByIdListMapper<Category, Long> { 
    // ...coding
}

4.5.测试

刷新页面,查看效果:

在这里插入图片描述
在这里插入图片描述

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 4.商品查询
    • 4.1.效果预览
      • 4.2.从0开始
        • 4.3.页面实现
          • 4.3.1.页面基本表格
          • 4.3.2.上下架状态按钮
        • 4.4.后台提供接口
          • 4.4.1.实体类
          • 4.4.2.controller
          • 4.4.3.service
          • 4.4.4.mapper
          • 4.4.5.Category中拓展查询名称的功能
        • 4.5.测试
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档