后台项目里,新增和编辑弹窗大多离不开 el-form。表单校验看起来简单,真正容易出问题的地方通常是 model、prop、rules 三者没有对齐,或者弹窗复用后遗留了上一次的校验状态。
最小可用的表单结构
el-form 的 :model 指向表单数据;每个 el-form-item 的 prop 必须与数据字段、规则字段一致。下面的 name 和 email 三处都对应同一个字段名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| <script setup lang="ts">
import { reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
interface UserForm {
name: string
email: string
}
const formRef = ref<FormInstance>()
const form = reactive<UserForm>({
name: '',
email: '',
})
const rules: FormRules<UserForm> = {
name: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 20, message: '长度为 2 到 20 个字符', trigger: 'blur' },
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '邮箱格式不正确', trigger: ['blur', 'change'] },
],
}
</script>
<template>
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户名" prop="name">
<el-input v-model="form.name" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="name@example.com" />
</el-form-item>
</el-form>
</template>
|
如果 prop="userName",而 form 写的是 name,规则不会生效。这是排查校验问题时最先该看的地方。
提交前统一校验
不要只依赖输入框失焦时的提示。提交按钮仍然要执行一次 validate,确认所有字段都通过后再调接口。
1
2
3
4
5
6
7
| const submit = async () => {
const valid = await formRef.value?.validate().catch(() => false)
if (!valid) return
await saveUser({ ...form })
}
|
validate 校验失败时会抛出异常,所以这里用 catch 转成 false。通过后再复制一份数据提交,可以避免接口函数直接修改响应式对象。
编辑弹窗要清掉旧提示
一个弹窗既用于新增又用于编辑时,切换数据后经常会看到上一位用户留下的红色提示。可以在弹窗打开后清掉校验状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
| import { nextTick } from 'vue'
const openCreate = async () => {
Object.assign(form, { name: '', email: '' })
await nextTick()
formRef.value?.clearValidate()
}
const openEdit = async (user: UserForm) => {
Object.assign(form, user)
await nextTick()
formRef.value?.clearValidate()
}
|
如果表单初始值始终不变,也可以使用 formRef.value?.resetFields()。它会把数据还原为组件初始化时的值,更适合单纯的“清空新增表单”。
只校验一个字段
分步表单或者发送验证码前,往往只需要校验邮箱或手机号。这时用 validateField,不必校验整张表单。
1
2
3
4
5
6
| const sendCode = async () => {
const valid = await formRef.value?.validateField('email').catch(() => false)
if (!valid) return
await requestEmailCode(form.email)
}
|
自定义规则适合什么场景
确认密码、日期范围、业务编号唯一性等情况,内置规则不够用时可以增加 validator。校验函数只负责给出结果,不要在里面请求大量无关数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| const passwordForm = reactive({
password: '',
confirmPassword: '',
})
const validateConfirmPassword = (
_rule: unknown,
value: string,
callback: (error?: Error) => void,
) => {
if (value !== passwordForm.password) {
callback(new Error('两次输入的密码不一致'))
return
}
callback()
}
|
小结
我现在处理 Element Plus 表单时,先保证字段名完全对齐,再把提交校验、弹窗清理和局部校验补齐。这样新增、编辑、分步操作放在同一个页面里,表单状态也不会越用越乱。