8.品牌新增 qs

参看:
乐优商城(四)品牌管理(前端)
https://blog.csdn.net/lyj2018gyq/article/details/82220560
乐优商城(五)品牌管理(后端)
https://blog.csdn.net/lyj2018gyq/article/details/82223835#二、后端接口实现

1.品牌的新增

昨天我们完成了品牌的查询,接下来就是新增功能。点击新增品牌按钮
在这里插入图片描述
1545222288968

Brand.vue页面有一个提交按钮:
在这里插入图片描述
1545222410267

点击触发addBrand方法:
在这里插入图片描述
1545222464105

把数据模型之的show置为true,而页面中有一个弹窗与show绑定:
在这里插入图片描述
1545222628405

弹窗中有一个表单子组件,并且是一个局部子组件,有页面可以找到该组件:
在这里插入图片描述
1545222756536

1.1.页面实现

1.1.1.重置表单

重置表单相对简单,因为v-form组件已经提供了reset方法,用来清空表单数据。只要我们拿到表单组件对象,就可以调用方法了。

我们可以通过$refs内置对象来获取表单组件。

首先,在表单上定义ref属性:
在这里插入图片描述
1530674011487

然后,在页面查看this.$refs属性:

      reset(){
        // 重置表单
        console.log(this);
      }

查看如下:
在这里插入图片描述
1530674402709

看到this.$refs中只有一个属性,就是myBrandForm

我们在clear中来获取表单对象并调用reset方法:
在这里插入图片描述
1545221936052

要注意的是,这里我们还手动把this.categories清空了,因为我写的级联选择组件并没有跟表单结合起来。需要手动清空。

1.1.2.表单校验

1.1.2.1.校验规则

Vuetify的表单校验,是通过rules属性来指定的:
在这里插入图片描述
1530687657711

校验规则的写法:
在这里插入图片描述
1530688192442

说明:

  • 规则是一个数组
  • 数组中的元素是一个函数,该函数接收表单项的值作为参数,函数返回值两种情况:
    • 返回true,代表成功,
    • 返回错误提示信息,代表失败
1.1.2.2.编写校验

我们有四个字段:

  • name:做非空校验和长度校验,长度必须大于1
  • letter:首字母,校验长度为1,非空。
  • image:图片,不做校验,图片可以为空
  • categories:非空校验,自定义组件已经帮我们完成,不用写了

首先,我们定义规则:
在这里插入图片描述
1545221873038

然后,在页面标签中指定:

<v-text-field v-model="brand.name" label="请输入品牌名称" hint="例如:oppo" :rules="[rules.required, rules.nameLength]"></v-text-field>
<v-text-field v-model="brand.letter" label="请输入品牌首字母" hint="例如:O" :rules="[rules.letter]"></v-text-field>

效果:
在这里插入图片描述
1530690428352

1.1.3.表单提交

在submit方法中添加表单提交的逻辑:

submit() {
    console.log(this.$qs);
    // 表单校验
    if (this.$refs.myBrandForm.validate()) {
        // 定义一个请求参数对象,通过解构表达式来获取brand中的属性{categories letter name image}
        const {categories, letter, ...params} = this.brand; // params:{name, image, cids, letter}
        // 数据库中只要保存分类的id即可,因此我们对categories的值进行处理,只保留id,并转为字符串
        params.cids = categories.map(c => c.id).join(",");
        // 将字母都处理为大写
        params.letter = letter.toUpperCase();
        // 将数据提交到后台
        // this.$http.post('/item/brand', this.$qs.stringify(params))
        this.$http({
            method: this.isEdit ? 'put' : 'post',
            url: '/item/brand',
            data: params
        }).then(() => {
            // 关闭窗口
            this.$emit("close");
            this.$message.success("保存成功!");
        })
            .catch(() => {
            this.$message.error("保存失败!");
        });
    }
}
  1. 通过this.$refs.myBrandForm选中表单,然后调用表单的validate方法,进行表单校验。返回boolean值,true代表校验通过
  2. 通过解构表达式来获取brand中的值,categories需要处理,单独获取。其它的存入params对象中
  3. 品牌和商品分类的中间表只保存两者的id,而brand.categories中保存的是对象数组,里面有id和name属性,因此这里通过数组的map功能转为id数组,然后通过join方法拼接为字符串
  4. 发起请求
  5. 弹窗提示成功还是失败,这里用到的是我们的自定义组件功能message组件:
    在这里插入图片描述
    1526140298249

这个插件把$message对象绑定到了Vue的原型上,因此我们可以通过this.$message来直接调用。

包含以下常用方法:

  • info、error、success、warning等,弹出一个带有提示信息的窗口,色调与为普通(灰)、错误(红色)、成功(绿色)和警告(黄色)。使用方法:this.$message.info(“msg”)
  • confirm:确认框。用法:this.$message.confirm("确认框的提示信息"),返回一个Promise。

1.2.后台实现新增

1.2.1.controller

还是一样,先分析四个内容:

  • 请求方式:POST
  • 请求路径:/brand
  • 请求参数:brand对象,外加商品分类的id数组cids
  • 返回值:无,只需要响应状态码

代码:

    /**
     * 新增品牌
     * @param brand
     * @param cids
     */
    @PostMapping
    public ResponseEntity<Void> saveBrand(Brand brand, @RequestParam("cids") List<Long> cids){
        this.brandService.saveBrand(brand, cids);
        return ResponseEntity.status(HttpStatus.CREATED).build();
    }

1.2.2.Service

这里要注意,我们不仅要新增品牌,还要维护品牌和商品分类的中间表。

    /**
     * 新增品牌
     *
     * @param brand
     * @param cids
     */
    @Transactional
    public void saveBrand(Brand brand, List<Long> cids) {

        // 先新增brand
        this.brandMapper.insertSelective(brand);

        // 在新增中间表
        cids.forEach(cid -> {
            this.brandMapper.insertCategoryAndBrand(cid, brand.getId());
        });
    }

这里调用了brandMapper中的一个自定义方法,来实现中间表的数据新增

1.2.3.Mapper

通用Mapper只能处理单表,也就是Brand的数据,因此我们手动编写一个方法及sql,实现中间表的新增:

public interface BrandMapper extends Mapper<Brand> {

    /**
     * 新增商品分类和品牌中间表数据
     * @param cid 商品分类id
     * @param bid 品牌id
     * @return
     */
    @Insert("INSERT INTO tb_category_brand(category_id, brand_id) VALUES (#{cid},#{bid})")
    int insertBrandAndCategory(@Param("cid") Long cid, @Param("bid") Long bid);
}

1.2.4.测试

在这里插入图片描述
1532827997361

400:请求参数不合法

1.3.解决400

1.3.1.原因分析

我们填写表单并提交,发现报错了。查看控制台的请求详情:
在这里插入图片描述
1530696121642

发现请求的数据格式是JSON格式。

原因分析:

axios处理请求体的原则会根据请求数据的格式来定:

  • 如果请求体是对象:会转为json发送

  • 如果请求体是String:会作为普通表单请求发送,但需要我们自己保证String的格式是键值对。

    如:name=jack&age=12

1.3.2.QS工具

QS是一个第三方库,我们可以用npm install qs --save来安装。不过我们在项目中已经集成了,大家无需安装:
在这里插入图片描述
1530696509189

这个工具的名字:QS,即Query String,请求参数字符串。

什么是请求参数字符串?例如: name=jack&age=21

QS工具可以便捷的实现 JS的Object与QueryString的转换。

在我们的项目中,将QS注入到了Vue的原型对象中,我们可以通过this.$qs来获取这个工具:
在这里插入图片描述
1539821449329

我们将this.$qs对象打印到控制台:

created(){
    console.log(this.$qs);
}

发现其中有3个方法:
在这里插入图片描述
1532850873556

这里我们要使用的方法是stringify,它可以把Object转为QueryString。

测试一下,使用浏览器工具,把qs对象保存为一个临时变量temp1,然后调用stringify方法:
在这里插入图片描述
1526182230872

成功将person对象变成了 name=zhangsan&age=30的字符串了

1.3.3.解决问题

修改页面,对参数处理后发送:
在这里插入图片描述
1545223244002

然后再次发起请求,发现请求成功:
在这里插入图片描述
1530698685973

1.4.新增完成后关闭窗口

我们发现有一个问题:新增不管成功还是失败,窗口都一致在这里,不会关闭。

这样很不友好,我们希望如果新增失败,窗口保持;但是新增成功,窗口关闭才对。

因此,我们需要在新增的ajax请求完成以后,关闭窗口

但问题在于,控制窗口是否显示的标记在父组件:MyBrand.vue中。子组件如何才能操作父组件的属性?或者告诉父组件该关闭窗口了?

之前我们讲过一个父子组件的通信,有印象吗?

  • 第一步:在父组件中定义一个函数,用来关闭窗口,不过之前已经定义过了。父组件在使用子组件时,绑定事件,关联到这个函数:Brand.vue
<!--对话框的内容,表单-->
<v-card-text class="px-5" style="height:400px">
    <brand-form @close="closeWindow" :oldBrand="oldBrand" :isEdit="isEdit"/>
</v-card-text>
  • 第二步,子组件通过this.$emit调用父组件的函数:BrandForm.vue
    在这里插入图片描述
    1545223341933

测试一下,保存成功:
在这里插入图片描述
1530713222558

我们优化一下,关闭的同时重新加载数据:

closeWindow(){
    // 关闭窗口
    this.show = false;
    // 重新加载数据
    this.getDataFromServer();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值