【Vue项目 + 自写java后端】尚品汇(七)后台项目 ElementUI 表单验证 + 三级联动

2023-07-10,,

ElementUI 表单验证

1 标准验证规则

Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。

      <el-form :rules="rules" :model="tmForm">
<el-form-item prop="tmname" label="品牌名称" label-width="100px">
<el-input autocomplete="off" v-model="tmForm.tmname" style="width: 400px"></el-input>
</el-form-item> <el-form-item prop="logourl" label="品牌LOGO" label-width="100px">
<el-upload
class="upload-demo"
action="/api/admin/product/fileUpload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
<img v-if="tmForm.logourl" :src="tmForm.logourl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过10MB</div>
</el-upload> </el-form-item>
</el-form>

这里出现输入正确也一直有提示的情况,原因是因为el-form表单中的model表单绑定对象使用的是v-model而不是:model

v-model:表示value属性

:model:表示子组件有个prop叫model

      // 表单验证规则
rules: {
tmname: [
{required: true, message: '请输入品牌名', trigger: 'blur'},
{min: 3, max: 5, message: '长度在3到5个字符', trigger: 'blur'}
],
logourl: [
{required: true, message: '请上传品牌LoGo'}
]
}

required:是否为必填项,true则会在form-item前出现红色*

message:验证失败时出现的提示信息

trigger: 验证的时机

blur:失去聚焦时验证

change:改动时验证

在函数回调中进行表单验证
    addOrUpdateTradeMark() {
this.$refs.ruleForm.validate(async (valid) => {
if (valid) {
await this.$API.tradeMark.reqAddOrUpdateTradeMark(this.tmForm)
this.$message({
message: this.tmForm.id ? `品牌 ${this.tmForm.tmname} 修改成功` : `品牌 ${this.tmForm.tmname} 添加成功`,
type: 'success'
})
this.hiddenDialogForm()
await this.getPageList(this.page)
} else {
return false;
}
});
}

注意async 和 await的位置,应该放在最近的箭头函数处

自定义验证规则

  data() {
var checkTmname = (rule, value, callback) => {
if(value.length >= 2 && value.length <= 5) {
callback()
} else {
callback(new Error('品牌名称应在2-5位字符'))
}
}
return {
...
// 表单验证规则
rules: {
tmname: [
{required: true, message: '请输入品牌名称', trigger: 'blur'},
// {min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur'}
{validator: checkTmname, trigger: 'blur'}
],
logourl: [
{required: true, message: '请上传品牌LOGO'}
]
}
}
},

前端

1 静态组件

1.1 注册三级联动全局组件

/src/main.js

import CategorySelect from "@/components/CategorySelect";
Vue.component(CategorySelect.name, CategorySelect)

三级联动组件 /src/components/CategorySelect/index.vue

<template>
<div>
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="一级分类">
<el-select placeholder="请选择">
<el-option label="区域一" value=""></el-option>
<el-option label="区域二" value=""></el-option>
</el-select>
</el-form-item> <el-form-item label="二级分类">
<el-select placeholder="请选择">
<el-option label="区域一" value=""></el-option>
<el-option label="区域二" value=""></el-option>
</el-select>
</el-form-item> <el-form-item label="三级分类">
<el-select placeholder="请选择">
<el-option label="区域一" value=""></el-option>
<el-option label="区域二" value=""></el-option>
</el-select>
</el-form-item> <el-form-item>
<el-button type="primary" @click="">查询</el-button>
</el-form-item>
</el-form> </div>
</template> <script>
export default {
name: "CategorySelect"
}
</script> <style scoped> </style>

在/src/views/product/attr中引入全局组件

<template>
<div>
<el-card style="margin-bottom: 20px"> <category-select/> </el-card>
<el-card style="margin-top: 20px"> </el-card>
</div>
</template> <script>
export default {
name: "index"
}
</script> <style scoped> </style>

2 编写请求接口

/src/api/product/attr.js

import request from "@/utils/request";

export const reqCategoryInfo = (id) => {
return request({
url: `/admin/product/attrInfoList/${id}`,
method: 'get'
})
}

CategorySelect组件加载完成时获取一级分类的属性信息

/src/components/CategorySelect/index.vue

  mounted() {
this.getCategoryInfo()
},
methods: {
async getCategoryInfo() {
let result = await this.$API.attr.reqCategoryInfo(-1)
if (result.code === 200) {
this.list1 = result.data
}
},

-1代表没有父组件,即一级分类

使用el-select生成三级分类选项

    <el-form :inline="true" class="demo-form-inline" :model="categoryForm">
<el-form-item label="一级分类">
<el-select placeholder="请选择"
@change="list1Handle()"
v-model="categoryForm.category1Id">
<el-option :label="c1.categoryName"
:value="c1.id"
v-for="(c1, index) in list1"
:key="c1.id"
></el-option>
</el-select>
</el-form-item> <el-form-item label="二级分类">
<el-select placeholder="请选择"
@change="list2Handle()"
v-model="categoryForm.category2Id">
<el-option :label="c2.categoryName"
:value="c2.id"
v-for="(c2, index) in list2"
:key="c2.id"
></el-option>
</el-select>
</el-form-item> <el-form-item label="三级分类">
<el-select placeholder="请选择" v-model="categoryForm.category3Id">
<el-option :label="list3 ? c3.categoryName : ''"
:value="c3.id"
v-for="(c3, index) in list3"
:key="c3.id"
></el-option>
</el-select>
</el-form-item> <el-form-item>
<el-button type="primary" @click="">查询</el-button>
</el-form-item>
</el-form>

-model:表单双向绑定的数据对象

:model:双向绑定的数据对象的

@change: 选项发生改变时触发函数

:value='':该项会与上面:model的值进行双向绑定

:key:循环的区别值,不能够省略,否则会出现意想不到的错误,估计是ElementUI提供的组件内部使用了

绑定的函数:

<script>
export default {
name: "CategorySelect",
data() {
return {
list1: [],
list2: [],
list3: [],
categoryForm: {
category1Id: "",
category2Id: "",
category3Id: "",
}
}
},
mounted() {
this.getCategoryInfo()
},
methods: {
async getCategoryInfo() {
let result = await this.$API.attr.reqCategoryInfo(-1)
if (result.code === 200) {
this.list1 = result.data
}
},
async list1Handle() {
this.categoryForm.category2Id = ''
this.categoryForm.category3Id = ''
const {category1Id} = this.categoryForm
let result = await this.$API.attr.reqCategoryInfo(category1Id)
if (result.code === 200) {
this.list2 = result.data
}
},
async list2Handle() {
this.categoryForm.category3Id = ''
const {category2Id} = this.categoryForm
let result = await this.$API.attr.reqCategoryInfo(category2Id)
if (result.code === 200) {
this.list3 = result.data
}
}
}
}
</script>

需要注意的一个点是在一级分类的value改变的时候应该将二三级分类的categoryform.categoryxid置空,否则会出现一个很严重的bug

比如二级分类选定电子书刊,三级分类选择一个电子书,如果这时候没有置空而将二级分类变为其他的选项的话,三级分类的label默认仍为电子书刊的电子书,这是由于二级分类双向绑定的对象的值是上一次残留的结果

后端

这部分后端相对来说简单一些,首先用xpath爬取了一点点数据,放入数据库后利用mybatis逆向工程生成entity、xml以及mapper接口。

insert into t_categoryName(pid, categoryName) values(-1, '图书、音像、电子书刊');
insert into t_categoryName(pid, categoryName) values(-1, '手机')
insert into t_categoryName(pid, categoryName) values(-1, '家用电器');
insert into t_categoryName(pid, categoryName) values(-1, '数码');
insert into t_categoryName(pid, categoryName) values(-1, '家居家装');
insert into t_categoryName(pid, categoryName) values(-1, '电脑办公');
insert into t_categoryName(pid, categoryName) values(-1, '厨具');
insert into t_categoryName(pid, categoryName) values(-1, '个护化妆');
insert into t_categoryName(pid, categoryName) values(-1, '服饰内衣');
insert into t_categoryName(pid, categoryName) values(-1, '钟表');
insert into t_categoryName(pid, categoryName) values(-1, '鞋靴');
insert into t_categoryName(pid, categoryName) values(-1, '母婴');
insert into t_categoryName(pid, categoryName) values(-1, '礼品箱包');
insert into t_categoryName(pid, categoryName) values(-1, '礼品箱包');
insert into t_categoryName(pid, categoryName) values(-1, '食品饮料、保健食品');
insert into t_categoryName(pid, categoryName) values(-1, '珠宝');
insert into t_categoryName(pid, categoryName) values(-1, '运动健康') insert into t_categoryName(pid, categoryName) values(1, '5');
insert into t_categoryName(pid, categoryName) values(1, '电子书刊');
insert into t_categoryName(pid, categoryName) values(1, '音像');
insert into t_categoryName(pid, categoryName) values(1, '英文原版');
insert into t_categoryName(pid, categoryName) values(1, '文艺');
insert into t_categoryName(pid, categoryName) values(1, '少儿');
insert into t_categoryName(pid, categoryName) values(1, '人文社科');
insert into t_categoryName(pid, categoryName) values(1, '经管励志');
insert into t_categoryName(pid, categoryName) values(1, '生活');
insert into t_categoryName(pid, categoryName) values(1, '科技');
insert into t_categoryName(pid, categoryName) values(1, '教育');
insert into t_categoryName(pid, categoryName) values(1, '港台图书');
insert into t_categoryName(pid, categoryName) values(1, '其他'); insert into t_categoryName(pid, categoryName) values(19, '电子书');
insert into t_categoryName(pid, categoryName) values(19, '网络原创')
insert into t_categoryName(pid, categoryName) values(19, '数字杂志');
insert into t_categoryName(pid, categoryName) values(19, '多媒体图书');
insert into t_categoryName(pid, categoryName) values(19, '小说');
insert into t_categoryName(pid, categoryName) values(19, '杂志'); insert into t_categoryName(pid, categoryName) values(20, '音乐');
insert into t_categoryName(pid, categoryName) values(20, '影视教育');
insert into t_categoryName(pid, categoryName) values(20, '音像');

记录逆向工程的一个坑点,如果数据库字段默认不使用下划线方式的话,转换成的实体类的字段全是小写的,而使用之后会将下划线变为驼峰。但是这个记得好像是能在配置文件设置的,懒得查了。。

如:category_name -> categoryName

CategoryHandler.java

@RequestMapping("/admin/product/attrInfoList")
@RestController
public class CategoryHandler {
@Autowired
CategoryService categoryService; @RequestMapping(value = "/{categoryId}", method = RequestMethod.GET)
public ResultEntity<Object> getAttrInfoList(@PathVariable("categoryId") Integer pid) {
List<Category> cateGoryList = categoryService.getCateGoryByPid(pid); return ResultEntity.successWithData(cateGoryList);
}
}

CategoryServiceImpl.java

@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Override
public List<Category> getCateGoryByPid(int pid) {
CategoryExample categoryExample = new CategoryExample();
categoryExample.createCriteria().andPidEqualTo(pid);
List<Category> categories = categoryMapper.selectByExample(categoryExample); return categories;
}
}

【Vue项目 + 自写java后端】尚品汇(七)后台项目 ElementUI 表单验证 + 三级联动的相关教程结束。

《【Vue项目 + 自写java后端】尚品汇(七)后台项目 ElementUI 表单验证 + 三级联动.doc》

下载本文的Word格式文档,以方便收藏与打印。