1.品牌的新增
我们完成了品牌的查询,接下来就是新增功能。点击新增品牌按钮
Brand.vue页面有一个提交按钮:
点击触发addBrand方法:
把数据模型之的show置为true,而页面中有一个弹窗与show绑定:
弹窗中有一个表单子组件,并且是一个局部子组件,有页面可以找到该组件:
1.1.页面实现
1.1.1.重置表单
重置表单相对简单,因为v-form组件已经提供了reset方法,用来清空表单数据。只要我们拿到表单组件对象,就可以调用方法了。
我们可以通过$refs内置对象来获取表单组件。
首先,在表单上定义ref属性:
然后,在页面查看this.$refs属性:
代码语言:javascript复制reset(){
// 重置表单
console.log(this);
}
查看如下:
看到this.$refs中只有一个属性,就是myBrandForm
我们在clear中来获取表单对象并调用reset方法:
要注意的是,这里我们还手动把this.categories清空了,因为我写的级联选择组件并没有跟表单结合起来。需要手动清空。
1.1.2.表单校验
1.1.2.1.校验规则
Vuetify的表单校验,是通过rules属性来指定的:
校验规则的写法:
说明:
- 规则是一个数组
- 数组中的元素是一个函数,该函数接收表单项的值作为参数,函数返回值两种情况:
- 返回true,代表成功,
- 返回错误提示信息,代表失败
1.1.2.2.编写校验
我们有四个字段:
- name:做非空校验和长度校验,长度必须大于1
- letter:首字母,校验长度为1,非空。
- image:图片,不做校验,图片可以为空
- categories:非空校验,自定义组件已经帮我们完成,不用写了
首先,我们定义规则:
然后,在页面标签中指定:
代码语言:html复制<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>
效果:
1.1.3.表单提交
在submit方法中添加表单提交的逻辑:
代码语言:javascript复制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("保存失败!");
});
}
}
- 通过this.$refs.myBrandForm选中表单,然后调用表单的validate方法,进行表单校验。返回boolean值,true代表校验通过
- 通过解构表达式来获取brand中的值,categories需要处理,单独获取。其它的存入params对象中
- 品牌和商品分类的中间表只保存两者的id,而brand.categories中保存的是对象数组,里面有id和name属性,因此这里通过数组的map功能转为id数组,然后通过join方法拼接为字符串
- 发起请求
- 弹窗提示成功还是失败,这里用到的是我们的自定义组件功能message组件:
这个插件把$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
- 返回值:无,只需要响应状态码
代码:
代码语言:java复制 /**
* 新增品牌
* @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
这里要注意,我们不仅要新增品牌,还要维护品牌和商品分类的中间表。
代码语言:java复制/**
* 新增品牌
*
* @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,实现中间表的新增:
代码语言:java复制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);
}
400:请求参数不合法
1.3.解决400
1.3.1.原因分析
我们填写表单并提交,发现报错了。查看控制台的请求详情:
发现请求的数据格式是JSON格式。
解决办法:请查看
https://cloud.tencent.com/developer/article/2439665
图片上传:请查看
https://cloud.tencent.com/developer/article/2439671