Skip to content

Form 表单

表单容器与校验。由 SrFormSrFormItem 两个组件配合使用。

基础用法

2–12 个字符
<SrFormItem prop="role" label="角色">
  <SrSelect v-model="form.role" :options="roleOptions" placeholder="请选择角色" />
</SrFormItem>

<SrSpace>
  <SrButton variant="primary" native-type="submit">提交</SrButton>
  <SrButton @click="formRef?.resetFields()">重置</SrButton>
  <SrButton @click="formRef?.clearValidate()">清空校验</SrButton>
</SrSpace>
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 从里面取值、把错误写回显示。

这样设计的好处是不与任何响应式方案冲突——用 reactiveref, 还是 Pinia、VueUse 的 useForm,组件都不关心。

代价是必须传 modelprop 是相对 model 的路径,没有 model 就无从取值。

校验规则

字段类型说明
requiredboolean是否必填
min / maxnumber长度或数值范围(视字段类型而定)
patternRegExp正则校验
validator(value) => boolean | string | Promise自定义校验
messagestring失败提示
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()

resetFieldsclearValidate 的区别值得留意: 前者会把数据也改回去(通常用于「取消编辑」),后者只擦掉红字。 用错的话,「清空校验」按钮会把用户填的内容一起清掉。

布局

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

名称类型默认值说明
modelRecord<string, unknown>必填,表单数据对象
rulesRecord<string, SrFormRule[]>校验规则,键为字段路径
labelPosition'left' | 'right' | 'top''right'标签位置
labelWidthstring标签宽度(左右布局时生效)
size'sm' | 'md' | 'lg''md'统一子控件尺寸
disabledbooleanfalse统一禁用全部子控件

SrFormItem Props

名称类型默认值说明
propstring字段路径,支持 a.b 嵌套
labelstring标签文本
requiredboolean自动推断是否必填;不传时从规则里推断(有 required: true 则显示星号)
rulesSrFormRule[]字段级规则,与容器规则合并
helpstring帮助文本,显示在控件下方
errorstring外部错误信息,优先级高于内部校验结果

SrFormItem 插槽

名称说明
default表单控件
label自定义标签

无障碍

  • SrFormItem 会把 id 自动关联到 <label>,点标签即可聚焦对应控件
  • 校验失败时提示文本带 role="alert",读屏软件会即时播报
  • 失败字段加 aria-invalid="true",便于辅助技术定位问题
  • 必填星号是视觉提示,语义上的「必填」通过 aria-required 暴露—— 只靠一个红色星号,读屏用户是感知不到的

自用组件库 · 源码分发 · 不发布 npm