第 24 章:表单与校验
学习目标
- 写 async-validator 规则
- 嵌套 / 数组 / 异步校验
- 自己写校验规则
- 避坑:trigger 时机
一、最简表单
vue
<template>
<el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" />
</el-form-item>
<el-button @click="submit">提交</el-button>
</el-form>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
const formRef = ref<FormInstance>()
const form = reactive({ username: '', password: '' })
const rules: FormRules = {
username: [{ required: true, message: '必填', trigger: 'blur' }],
password: [
{ required: true, message: '必填', trigger: 'blur' },
{ min: 6, message: '至少 6 位', trigger: 'blur' },
],
}
const submit = async () => {
await formRef.value?.validate()
await api.save(form)
}
</script>⚠️ 坑 1:rules 必须用
FormRules类型,否则 TS 推断为any。
二、内置校验规则
| 规则 | 说明 |
|---|---|
required | 必填 |
min / max | 长度 / 数值 |
pattern | 正则 |
type: 'email' | 邮箱 |
type: 'url' | URL |
validator | 自定义函数 |
三、自定义校验
ts
const validatePass2 = (_: any, value: string, cb: (err?: Error) => void) => {
if (value !== form.password) cb(new Error('两次密码不一致'))
else cb()
}
const rules: FormRules = {
password2: [{ validator: validatePass2, trigger: 'blur' }],
}四、异步校验
ts
const validateUsername = async (_: any, value: string, cb: (err?: Error) => void) => {
try {
const res = await api.checkUsername(value)
if (res.exists) cb(new Error('用户名已被占用'))
else cb()
} catch (e) {
cb()
}
}五、嵌套字段
ts
const form = reactive({
user: { name: '', email: '' },
})
const rules: FormRules = {
'user.name': [{ required: true }],
'user.email': [{ type: 'email' }],
}⚠️ 坑 2:嵌套字段必须用字符串路径,不能传
user.name字段路径对象。
六、数组字段
vue
<el-form-item
v-for="(item, i) in form.items"
:key="i"
:prop="`items.${i}.name`"
:rules="{ required: true, message: '必填' }"
>
<el-input v-model="item.name" />
</el-form-item>七、整体重置
ts
formRef.value?.resetFields() // 还原到初始值
formRef.value?.clearValidate() // 只清除校验⚠️ 坑 3:
resetFields只能重置到form初始值,如果 form 是 reactive 后续被改,reset 会回到那个被改的"初始"。
八、控台校验
ts
const submit = async () => {
try {
await formRef.value?.validate()
// 校验通过
} catch {
ElMessage.error('请检查表单')
}
}九、实战:注册表单
ts
const rules: FormRules = {
username: [
{ required: true, message: '必填' },
{ pattern: /^[a-z][a-z0-9_]{2,15}$/, message: '字母数字下划线,3-16 位' },
],
email: [
{ required: true, message: '必填' },
{ type: 'email', message: '格式错误' },
],
password: [
{ required: true },
{ validator: (rule, value, cb) => {
if (!/[A-Z]/.test(value)) cb(new Error('需含大写'))
else cb()
} },
],
}十、本章小结
| 能力 | 关键 |
|---|---|
rules | :rules + prop 对应 |
validator | (rule, value, cb) |
| 嵌套 | 'a.b.c' 字符串路径 |
| 数组 | items.${i}.name |
| 重置 | resetFields / clearValidate |
动手练习
- 注册表单:用户名 / 邮箱 / 密码,带正则
- 密码确认:两次密码一致性校验
- 异步校验:用户名查重
下一章:第 25 章:权限控制 →