乐优商城(九)商品管理

目录

三、商品新增

3.1 效果预览

3.2 新增商品页基本框架

3.2.1 Steppers、步骤线

3.2.2 页面实现

3.2.3 步骤线切换按钮

3.3 商品基本信息

3.3.1 在data中定义goods属性

3.3.2 商品分类选择框

3.3.3 品牌选择(下拉选择框)

3.3.4 其他字段


三、商品新增

此页面单独新建一个组件MyGoodsForm,并且在MyGoods中进行引用:

  // 导入自定义的表单组件
  import MyGoodsForm from './MyGoodsForm'
<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-btn color="primary" @click="addGoods">新增商品</v-btn>

addGoods方法中,设置对话框的show属性为true:

        addGoods(){
          //修改标记
          this.isEdit = false;
          //控制弹窗可见
          this.show = true;
          //把oldGoods变为null
          this.oldGoods = null;
        },

 

3.1 效果预览

这个表单比较复杂,因为商品的信息比较多,分成了4个部分来填写:

  • 基本信息

  • 商品描述信息

  • 规格参数信息

  • SKU信息

3.2 新增商品页基本框架

3.2.1 Steppers、步骤线

预览效果图中,分四个步骤显示商品表单的组件,叫做stepper,看下文档:

其基本结构如图:

一个步骤线(v-stepper)总的分为两部分:

  • v-stepper-header:代表步骤的头部进度条,只能有一个

    • v-stepper-step:代表进度条的每一个步骤,可以有多个

  • v-stepper-items:代表当前步骤下的内容组,只能有一个,内部有stepper-content

    • v-stepper-content:代表每一步骤的页面内容,可以有多个

v-stepper

  • value:其值是当前所在的步骤索引,可以用来控制步骤切换

  • dark:是否使用黑暗色调,默认false

  • non-linear:是否启用非线性步骤,用户不用按顺序切换,而是可以调到任意步骤,默认false

  • vertical:是否垂直显示步骤线,默认是false,即水平显示

v-stepper-header的属性:

v-stepper-step的属性

  • color:颜色

  • complete:当前步骤是否已经完成,布尔值

  • editable:是否可编辑任意步骤(非线性步骤)

  • step:步骤索引

v-stepper-items

v-stepper-content

  • step:步骤索引,需要与v-stepper-step中的对应

3.2.2 页面实现

首先我们在data中定义一个变量,记录当前的步骤数:

data() {
    return {
        step: 1, // 当前的步骤数,默认为1
    }
},

然后在模板页面中引入步骤线:

<v-stepper v-model="step">
    <v-stepper-header>
      <v-stepper-step :complete="step > 1" step="1">基本信息</v-stepper-step>
      <v-divider/>
      <v-stepper-step :complete="step > 2" step="2">商品描述</v-stepper-step>
      <v-divider/>
      <v-stepper-step :complete="step > 3" step="3">规格参数</v-stepper-step>
      <v-divider/>
      <v-stepper-step step="4">SKU属性</v-stepper-step>
    </v-stepper-header>
    <v-stepper-items>
      <v-stepper-content step="1">
        基本信息
      </v-stepper-content>
      <v-stepper-content step="2">
        商品描述
      </v-stepper-content>
      <v-stepper-content step="3">
        规格参数
      </v-stepper-content>
      <v-stepper-content step="4">
        SKU属性
      </v-stepper-content>
    </v-stepper-items>
  </v-stepper>

效果:

3.2.3 步骤线切换按钮

分析

方法:通过修改step的值来进行切换。

因此,我们需要定义两个按钮,点击后修改step的值,让步骤前进或后退。

那么这两个按钮放哪里?

如果放在MyGoodsForm内,当表单内容过多时,按钮会被挤压到屏幕最下方,不够友好。最好是能够悬停状态。

所以,按钮必须放到MyGoods组件中,也就是父组件。

父组件的对话框是一个card,card组件提供了一个滚动效果,scrollable,如果为true,card的内容滚动时,其头部和底部是可以静止的。

现在card的头部是弹框的标题,card的中间就是表单内容。如果我们把按钮放到底部,就可以实现悬停效果。

添加按钮

对父组件中的对话框进行改造:

页面效果:

添加点击事件

现在这两个按钮点击后没有任何反应。需要绑定点击事件,来修改MyGoodsForm中的step的值。也就是说,父组件要修改子组件的属性状态。要使用props属性。

先在父组件定义一个step属性:

然后在点击事件中修改它:

在点击下一步时要对所填内容进行有效性验证

页面绑定事件:

然后把step属性传递给子组件:

子组件中接收属性:

最终效果:

3.3 商品基本信息

商品基本信息,主要是一些纯文本比较简单的SPU属性,例如:

商品分类、商品品牌、商品标题、商品卖点(子标题),包装清单,售后服务

接下来,逐步添加这些表单项。

3.3.1 在data中定义goods属性

data() {
    return {
        goods:{
            categories:{}, // 商品3级分类数组信息
            brandId: 0,// 品牌id信息
            title: '',// 标题
            subTitle: '',// 子标题
            spuDetail: {
                packingList: '',// 包装列表
                afterService: '',// 售后服务
            },
        }
    }

注意,这里我们在goods中定义了spuDetail属性,然后把包装列表和售后服务作为它的属性,这样符合数据库的结构。

3.3.2 商品分类选择框

商品分类选框之前已经做过了。是级联选框。直接拿来用:

<v-cascader
        url="/item/category/list"
        required
        showAllLevels
        v-model="goods.categories"
        label="请选择商品分类"/>

和以前使用有一些区别:

  • 一个商品只能有一个分类,所以这里去掉了multiple属性

  • 商品SPU中要保存3级商品分类,因此我们这里需要选择showAllLevels属性,显示所有3级分类

效果:

查看goods的属性,三级类目都在:

3.3.3 品牌选择(下拉选择框)

品牌不分级别,使用普通下拉选框即可。查看官方文档的下拉选框说明:

组件名:v-select

比较重要的一些属性:

  • item-text:选项中用来展示的字段名,默认是text

  • item-value:选项中用来作为value值的字段名,默认是value

  • items:待选项的对象数组

  • label:提示文本

  • multiple:是否支持多选,默认是false

其它次要属性:

  • autocomplete:是否根据用户输入的文本进行搜索过滤(自动),默认false

  • chips:是否以小纸片方式显示用户选中的项,默认false

  • clearable:是否添加清空选项图标,默认是false

  • color:颜色

  • dense:是否压缩选择框高度,默认false

  • editable:是否可编辑,默认false

  • hide-details:是否隐藏错误提示,默认false

  • hide-selected:是否在菜单中隐藏已选择的项

  • hint:提示文本

  • 其它基本与v-text-filed组件类似,不再一一列举

页面实现

备选项items需要去后台查询,而且必须是在用户选择商品分类后去查询。

先定义一个属性,保存品牌的待选项信息:

<!--品牌-->
<v-select
      :items="brandOptions"
      item-text="name"
      item-value="id"
      label="所属品牌"
      v-model="goods.brandId"
      required
      autocomplete
      clearable
      dense chips
      />

然后编写一个watch,监控goods.categories的变化:

watch: {
    'goods.categories': {
        deep: true,
            handler(val) {
            // 判断商品分类是否存在,存在才查询
            if (val && val.length > 0) {
                // 根据分类查询品牌
                this.$http.get("/item/brand/cid/" + this.goods.categories[2].id)
                    .then(({data}) => {
                    this.brandOptions = data;
                })
            }
        }
    }
}

我们的品牌对象包含以下字段:id、name、letter、image。显然item-text应该对应name,item-value应该对应id

因此添加一个选框,指定item-text和item-value

<!--品牌-->
<v-select
      :items="brandOptions"
      item-text="name"
      item-value="id"
      label="所属品牌"
      v-model="goods.brandId"
      required
      autocomplete
      clearable
      dense chips
      />

后台接口

  • Controller
    /**
     * 根据category的id查询品牌信息
     * @param cid
     * @return
     */
    @GetMapping("cid/{cid}")
    public ResponseEntity<List<Brand>> queryBrandByCategoryId(@PathVariable("cid") Long cid){
        List<Brand> list = this.brandService.queryBrandByCategoryId(cid);
        if (list == null){
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        return ResponseEntity.ok(list);
    }
  • Mapper
    /**
     * 根据category id查询brand,左连接
     * @param cid
     * @return
     */
    @Select("SELECT b.* FROM tb_brand b LEFT JOIN tb_category_brand cb ON b.id=cb.brand_id WHERE cb.category_id=#{cid}")
    List<Brand> queryBrandByCategoryId(Long cid);
  • Service

接口

    /**
     * 根据category id查询brand
     * @param cid
     * @return
     */
    List<Brand> queryBrandByCategoryId(Long cid);

实现类

    /**
     * 根据category id查询brand
     * @param cid
     * @return
     */
    @Override
    public List<Brand> queryBrandByCategoryId(Long cid) {

        return this.brandMapper.queryBrandByCategoryId(cid);
    }

测试

3.3.4 其他字段

标题等字段都是普通文本,直接使用v-text-field即可:

<v-text-field label="商品标题" v-model="goods.title" :counter="200" required />
<v-text-field label="商品卖点" v-model="goods.subTitle" :counter="200"/>
<v-text-field label="包装清单" v-model="goods.spuDetail.packingList" :counter="1000" multi-line :rows="3"/>
<v-text-field label="售后服务" v-model="goods.spuDetail.afterService" :counter="1000" multi-line :rows="3"/>

一些新的属性:

  • counter:计数器,记录当前用户输入的文本字数

  • rows:文本域的行数

  • multi-line:把单行文本变成文本域

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值