Form 表单
表单容器与校验。由 SrForm 与 SrFormItem 两个组件配合使用。
基础用法
vue
<script setup>
import { ref } from 'vue'
const formRef = ref()
const form = ref({ name: '', role: undefined })
const rules = {
name: [
{ required: true, message: '请输入用户名' },
{ min: 2, max: 12, message: '长度需在 2–12 个字符之间' }
],
role: [{ required: true, message: '请选择角色' }]
}
async function submit() {
const valid = await formRef.value?.validate()
if (valid) {
// 提交逻辑
}
}
</script>
<template>
<SrForm ref="formRef" :model="form" :rules="rules" label-position="top" @submit="submit">
<SrFormItem prop="name" label="用户名" help="2–12 个字符">
<SrInput v-model="form.name" clearable />
</SrFormItem>
<SrFormItem prop="role" label="角色">
<SrSelect v-model="form.role" :options="options" />
</SrFormItem>
<SrButton variant="primary" native-type="submit">提交</SrButton>
</SrForm>
</template>数据由宿主持有
组件不接管表单数据。 model 是宿主传进来的对象,SrFormItem 通过 prop 从里面取值、把错误写回显示。
这样设计的好处是不与任何响应式方案冲突——用 reactive、ref, 还是 Pinia、VueUse 的 useForm,组件都不关心。
代价是必须传 model:prop 是相对 model 的路径,没有 model 就无从取值。
校验规则
| 字段 | 类型 | 说明 |
|---|---|---|
required | boolean | 是否必填 |
min / max | number | 长度或数值范围(视字段类型而定) |
pattern | RegExp | 正则校验 |
validator | (value) => boolean | string | Promise | 自定义校验 |
message | string | 失败提示 |
trigger | 'blur' | 'change' | 触发时机,默认 blur |
ts
const rules = {
email: [
{ required: true, message: '请输入邮箱' },
{ pattern: /^[^@]+@[^@]+\.[^@]+$/, message: '邮箱格式不正确' }
],
password: [
{
validator: (value) => value.length >= 8 || '密码至少 8 位'
}
]
}嵌套路径
prop 支持 a.b 形式,直接对应 model 里的嵌套结构。
vue
<!-- 校验 model.user.profile.city -->
<SrFormItem prop="user.profile.city" label="城市">
<SrInput v-model="form.user.profile.city" />
</SrFormItem>异步校验
validator 可以返回 Promise——「用户名是否已被占用」这类需要请求服务端的场景。
ts
{
validator: async (value) => {
const taken = await checkUsername(value)
return taken ? '该用户名已被占用' : true
}
}异步校验不会自动防抖
每次触发都会发一次请求。若触发时机是 change,输入过程中会连续请求。 建议在 validator 内部自己做防抖,或把触发时机设为 blur。
校验触发时机
默认 blur——失焦才校验,输入过程中不打扰。
若改成 change,请留意:用户刚敲第一个字符就报「长度不足」是很烦人的体验, 通常需要配合「只在已经出过错之后才实时校验」的策略(validate-on-rule-change 之类)。本组件把这个策略留给宿主,因为不同产品的容忍度差别很大。
实例方法
通过 ref 调用:
| 方法 | 说明 |
|---|---|
validate() | 校验全部字段,返回 Promise<boolean> |
validateField(prop) | 校验单个字段 |
resetFields() | 重置为初始值并清空校验状态 |
clearValidate() | 只清空校验状态,不改数据 |
vue
<script setup>
const formRef = ref()
// 提交前校验
async function onSubmit() {
const valid = await formRef.value.validate()
if (!valid) return
// …
}
// 只清错误提示,保留用户已填内容
formRef.value.clearValidate()resetFields 与 clearValidate 的区别值得留意: 前者会把数据也改回去(通常用于「取消编辑」),后者只擦掉红字。 用错的话,「清空校验」按钮会把用户填的内容一起清掉。
布局
labelPosition 控制标签位置:
vue
<SrForm label-position="left" label-width="80px">…</SrForm>
<SrForm label-position="right" label-width="80px">…</SrForm>
<SrForm label-position="top">…</SrForm>top 在窄屏下更稳——长标签不会把输入框挤窄。 表单字段超过 3 个、或标签较长时,top 通常比左右布局更好用。
SrForm Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model | Record<string, unknown> | — | 必填,表单数据对象 |
rules | Record<string, SrFormRule[]> | — | 校验规则,键为字段路径 |
labelPosition | 'left' | 'right' | 'top' | 'right' | 标签位置 |
labelWidth | string | — | 标签宽度(左右布局时生效) |
size | 'sm' | 'md' | 'lg' | 'md' | 统一子控件尺寸 |
disabled | boolean | false | 统一禁用全部子控件 |
SrFormItem Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
prop | string | — | 字段路径,支持 a.b 嵌套 |
label | string | — | 标签文本 |
required | boolean | 自动推断 | 是否必填;不传时从规则里推断(有 required: true 则显示星号) |
rules | SrFormRule[] | — | 字段级规则,与容器规则合并 |
help | string | — | 帮助文本,显示在控件下方 |
error | string | — | 外部错误信息,优先级高于内部校验结果 |
SrFormItem 插槽
| 名称 | 说明 |
|---|---|
default | 表单控件 |
label | 自定义标签 |
无障碍
SrFormItem会把id自动关联到<label>,点标签即可聚焦对应控件- 校验失败时提示文本带
role="alert",读屏软件会即时播报 - 失败字段加
aria-invalid="true",便于辅助技术定位问题 - 必填星号是视觉提示,语义上的「必填」通过
aria-required暴露—— 只靠一个红色星号,读屏用户是感知不到的