最近一直在做后台管理项目,自然少不了表单校验。说说我在表单校验上遇到的一些难点。
- 在父组件中校验子组件的信息;
- form表单嵌套表格的校验信息;
- 表单金额的校验规则(产品要求输入最多8位整数,4位小数,最高位不能位0);
首先我们来看父组件如何去校验子组件中的信息
同一组件内的校验,都好办,难的是跨组件校验。
我们来看有这样一个场景:有一个编辑和查看的模块,默认展示查看模块,通过点击父组件的‘编辑’按钮显示编辑模块,编辑后,点击父组件的‘保存’按钮显示查看模块。所以这里点保存时需要去校验子组件,校验成功才能调保存接口。
这里我们主要看父组件,和edit组件,read组件只是展示信息的,没有需要讲的地方。
<!-- 父组件 -->
<template>
<div class="father">
<div class="father-btn">
<el-button v-if="!isEdit" @click="isEdit=true" type="primary">编辑</el-button>
<el-button v-if="isEdit" @click="isEdit=false" disabled>取消</el-button>
<el-button v-if="isEdit" type="primary" @click="saveEdit">保存</el-button>
</div>
<div class="father-content">
<edit v-if="isEdit" ref="edit" />
<read v-else />
</div>
</div>
</template>
<!-- 编辑子组件 -->
<template>
<el-form :model="numberValidateForm" ref="numberValidateForm" label-width="100px" class="demo-ruleForm">
<el-form-item
label="年龄"
prop="age"
:rules="[
{ required: true, message: '年龄不能为空'},
{ type: 'number', message: '年龄必须为数字值'}
]"
>
<el-input type="age" v-model.number="numberValidateForm.age" autocomplete="off"></el-input>
</el-form-item>
</el-form>
</template>
// 父组件
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Read './read.vue';
import Edit './edit.vue';
@Component({
components: {
Edit,
Read,
},
})
export default class Father extends Vue {
private isEdit: boolean = false;
private saveEdit() {
const edit = this.$refs.edit as any;
edit.numberValidateForm.validate((valid) => {
if (valid) {
// 校验通过拿到子组件的值,通过调用子组件的getData方法
const editData = edit.getData();
console.log(editData);
// 调用保存接口
// ...
this.isEdit = false;
} else {
// 校验失败
return false;
}
});
}
}
</script>
// edit.vue
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class Edit extends Vue {
private numberValidateForm: any = {
age: '',
};
private getData() {
return numberValidateForm;
}
}
</script>
Form表单中嵌套表格的这种校验又怎么做呢
<template>
<el-form :model="formData" :rules="rules">
<el-table
:data="formData.tableList"
:show-header="false"
style="width: 100%">
<el-table-column>
<template slot-scope="scope">
<el-form-item
:prop="'tableList.' + scope.$index + '.dateTime'"
:rules='rules.dateTime'>
<el-date-picker
v-model="scope.row.dateTime"
size="mini"
type="date"
value-format="timestamp"
placeholder="请选择日期">
</el-date-picker>
</el-form-item>
</template>
</el-table-column>
<el-table-column>
<template slot-scope="scope">
<el-form-item
:prop="'tableList.' + scope.$index + '.num'"
:rules='rules.num'>
<el-input
type="text"
size="mini"
v-model="scope.row.num">
</el-input>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component
export default class Edit extends Vue {
private formData: any = {
tableList: [
{
dateTime: '',
num: '',
},
{
dateTime: '',
num: '',
},
],
};
get rules() {
const checkDateTime = (rule: any, value: any, callback: any) => {
if (value == null || value === '') {
callback(new Error('请选择日期'));
} else {
callback();
}
};
const checkNum = (rule: any, value: any, callback: any) => {
if (value == null || value === '') {
callback(new Error('请输入'));
} else if (!new RegExp(/(^[1-9]([0-9]{1,7})$|^[1-9]$)/).test(value)) {
callback(new Error('请输入1-8位数字'));
} else {
callback();
}
};
const obj = {
dateTime: [
{ validator: checkDateTime, trigger: ['change', 'blur'] },
],
num: [
{ validator: checkNum, trigger: 'blur' },
],
};
return obj;
}
}
</script>
嵌套table的校验有两个注意点:
- 注意 prop 的绑定方式同一般的不同;
- el-form-item 上除了绑定 prop 还要绑定 rules;
金额和纯数字的校验
<template>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px">
<el-form-item label="数量" prop="amount">
<el-input
type="text"
:maxlength="8"
onkeypress="return event.keyCode>=48&&event.keyCode<=57"
@keyup.native="checkAmount(ruleForm.amount)"
v-model="ruleForm.amount">
</el-input>
</el-form-item>
<el-form-item label="金额" prop="price">
<el-input
type="text"
@keyup.native="checkPrice(ruleForm.price)"
v-model="ruleForm.price">
</el-input>
</el-form-item>
</el-form>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
const reg = /(^[1-9]([0-9]{1,7})$|^[0-9]$)/;
const priceReg = /(^[1-9]([0-9]{0,7})$|^[0-9]$|^[0-9](\.[0-9]{0,4})$|^[1-9]([0-9]{0,7})\.[0-9]([0-9]{0,3})$|^[1-9]([0-9]{0,7})\.$)/;
@Component
export default class EllipsisComponent extends Vue {
private ruleForm: any = {
amount: '',
price: '',
};
get rules() {
const checkAmount = (rule: any, value: any, callback: any) => {
if (value == null || value === '') {
callback(new Error('请输入'));
} else if (!reg.test(value)) {
callback(new Error('请输入1-8位数字'));
} else {
callback();
}
};
const checkPrice = (rule: any, value: any, callback: any) => {
if (value == null || value === '') {
callback(new Error('请输入'));
} else if (!priceReg.test(value)) {
callback(new Error('最多输入8位整数,4位小数'));
} else {
callback();
}
};
const obj = {
amount: [
{ validator: checkAmount, trigger: 'blur' },
],
price: [
{ validator: checkPrice, trigger: 'blur' },
],
};
return obj;
}
private checkAmount(value: any) {
if (value != null && value.length > 0) {
if (!reg.test(value)) {
if (/[^\d.]+/.test(value)) { // 匹配中间是否插入了字母,等其他字符
this.ruleForm.amount = value.replace(/[^\d.]+/, '');
return;
}
if (/^([0-9]\d{8,}(\.\d*)*)$/.test(value)) { // 匹配是否超过8位
this.ruleForm.amount = value.substring(0, 8);
return;
}
if (/^[0]+/.test(value)) { // 最高位是否为0
this.ruleForm.amount = value.replace(/^[0]+/, '');
return;
}
}
}
}
private checkPrice(value: any) {
if (!priceReg.test(value)) {
if (/^(\.)/.test(value)) { // 匹配第一个字符是否为 .
this.ruleForm.price = value.substring(value.lastIndexOf('.') + 1, value.length);
return;
}
if (/[^\d.]+/.test(value)) { // 匹配中间是否插入了字母,等其他字符
this.ruleForm.price = value.replace(/[^\d.]+/, '');
return;
}
if (/([0-9]\d*)(\.\d*){2,}/.test(value)) { // 匹配是否有多个 . --恶意输入
this.ruleForm.price = '';
return;
}
if (/^([1-9]\d{8}(\.\d*))$/.test(value)) { // 匹配小数点前是否为7位
this.ruleForm.price = value.replace(value.charAt(value.lastIndexOf('.') - 1), '');
return;
}
if (/^([0-9]\d{9,}(\.\d*)*)$/.test(value)) { // 匹配小数点前是否超过8位-恶意输入
this.ruleForm.price = '';
return;
}
if (/^([0-9]\d{0,7}(\.\d{5,}))$/.test(value)) { // 小数点后是否超过了3位-恶意输入
this.ruleForm.price = value.substring(0, value.lastIndexOf('.') + 5);
return;
}
if (/^0\d*(\.\d{0,4})?$/.test(value)) { // 匹配第一位是否是0开始
this.ruleForm.price = value.replace(/^([0][0]*)/, '');
return;
}
this.ruleForm.price = value.substring(0, (value.length - 1));
}
}
}
</script>