Skip to content

useFormValidate

useFormValidate hook 专门用于表单数据验证,基于 vee-validate 构建,提供独立的表单验证功能。

功能特点

  • 独立验证 - 可单独使用的表单验证功能
  • 📝 响应式验证 - 表单数据变化时自动更新验证状态
  • 🛡️ 双重规则支持 - 支持 vee-validate 内置规则和 Yup Schema 两种验证方式
  • 异步验证 - 支持异步验证函数
  • 🔄 状态重置 - 提供验证状态重置功能
  • 🎯 错误详情 - 提供详细的验证错误信息

支持的验证方式

框架支持两种验证方式,可根据项目需求选择:

1. vee-validate 内置规则(字符串格式)

  • 适用场景:简单验证、快速开发
  • 格式:字符串形式,如 'required|email'
  • 优点:语法简洁,上手快速
  • 前提:需要先调用 initFormValidate() 注册规则

2. Yup Schema 验证(对象格式)

  • 适用场景:复杂验证、类型安全、异步验证
  • 格式:Yup 对象形式
  • 优点:类型安全、功能强大、支持复杂逻辑
  • 前提:需要安装 yup 依赖

接口关系

该hook基于 vee-validate 构建,提供简化的验证接口。

typescript
// 参数接口
interface UseValidateFormProps {
  data?: MaybeRef<Record<string, any>> // 要验证的表单数据
  rules?: TypedSchema // 验证规则(支持字符串规则或 Yup Schema)
}

// 返回值接口
interface UseValidateFormReturn {
  validate: () => Promise<ValidationResult> // 验证表单数据
  reset: () => void // 重置验证状态
  submit: Function // 处理表单提交(来自 vee-validate)
}

// 验证结果接口
interface ValidationResult {
  valid: boolean // 是否验证通过
  errors: Record<string, string> // 错误信息对象
}

验证规则初始化

使用 vee-validate 内置规则前,需要先初始化:

js
import { initFormValidate } from '@duxweb/dvha-core'

// 在应用启动时调用,注册所有内置验证规则
initFormValidate()

使用方法

方式一:vee-validate 内置规则

js
import { initFormValidate, useValidateForm } from '@duxweb/dvha-core'
import { ref } from 'vue'

// 初始化内置规则(应用启动时调用一次)
initFormValidate()

const formData = ref({
  name: '',
  email: ''
})

const { validate, reset, submit } = useValidateForm({
  data: formData,
  rules: {
    name: 'required|min:2',
    email: 'required|email'
  }
})

方式二:Yup Schema 验证

js
import { useValidateForm } from '@duxweb/dvha-core'
import { ref } from 'vue'
import * as yup from 'yup'

const formData = ref({
  name: '',
  email: ''
})

const { validate, reset, submit } = useValidateForm({
  data: formData,
  rules: yup.object({
    name: yup.string().required('姓名是必填的').min(2, '姓名至少2个字符'),
    email: yup.string().required('邮箱是必填的').email('请输入有效邮箱')
  })
})

参数说明

参数类型必需说明
dataMaybeRef<Record<string, any>>要验证的表单数据
rulesTypedSchema验证规则(支持字符串规则或 Yup Schema)

返回值

字段类型说明
validate() => Promise<ValidationResult>验证表单数据
reset() => void重置验证状态
submitFunction处理表单提交(来自 vee-validate)

方式一:vee-validate 内置规则示例

基础验证示例

js
import { initFormValidate, useValidateForm } from '@duxweb/dvha-core'
import { ref } from 'vue'

// 应用启动时初始化
initFormValidate()

const formData = ref({
  username: '',
  password: '',
  confirmPassword: '',
  email: '',
  phone: '',
  age: null
})

const { validate, reset } = useValidateForm({
  data: formData,
  rules: {
    username: 'required|min:3|max:20',
    password: 'required|min:6',
    confirmPassword: 'required|confirmed:@password',
    email: 'required|email',
    phone: 'regex:^1[3-9]\\d{9}$',
    age: 'numeric|min_value:1|max_value:120'
  }
})

// 验证表单
async function handleValidate() {
  const result = await validate()
  if (result.valid) {
    console.log('验证通过')
  }
  else {
    console.log('验证失败:', result.errors)
  }
}

常用内置规则

js
const rules = {
  // 必填验证
  name: 'required',

  // 长度验证
  title: 'required|min:2|max:50',

  // 邮箱验证
  email: 'required|email',

  // 数值验证
  age: 'required|numeric|min_value:1|max_value:120',
  price: 'required|decimal:2|min_value:0',

  // 正则验证
  phone: 'required|regex:^1[3-9]\\d{9}$',

  // 确认验证(密码确认)
  password: 'required|min:6',
  confirmPassword: 'required|confirmed:@password',

  // URL验证
  website: 'url',

  // 日期验证
  birthday: 'required|date_format:YYYY-MM-DD',

  // 选择验证
  status: 'required|one_of:active,inactive,pending'
}

方式二:Yup Schema 验证示例

基础验证示例

js
import { useValidateForm } from '@duxweb/dvha-core'
import { ref } from 'vue'
import * as yup from 'yup'

const formData = ref({
  username: '',
  password: '',
  confirmPassword: '',
  email: '',
  phone: '',
  age: null
})

const validationSchema = yup.object({
  username: yup.string()
    .required('用户名是必填的')
    .min(3, '用户名至少3个字符')
    .max(20, '用户名最多20个字符'),
  password: yup.string()
    .required('密码是必填的')
    .min(6, '密码至少6个字符'),
  confirmPassword: yup.string()
    .required('请确认密码')
    .oneOf([yup.ref('password')], '两次密码不一致'),
  email: yup.string()
    .required('邮箱是必填的')
    .email('请输入有效邮箱'),
  phone: yup.string()
    .matches(/^1[3-9]\d{9}$/, '请输入有效手机号'),
  age: yup.number()
    .required('年龄是必填的')
    .positive('年龄必须是正数')
    .integer('年龄必须是整数')
    .max(120, '年龄不能超过120岁')
})

const { validate, reset } = useValidateForm({
  data: formData,
  rules: validationSchema
})

异步验证示例

js
import { useValidateForm } from '@duxweb/dvha-core'
import * as yup from 'yup'

// 自定义异步验证函数
async function checkEmailExists(email) {
  const response = await fetch(`/api/check-email?email=${email}`)
  const data = await response.json()
  return !data.exists
}

async function checkUsernameExists(username) {
  const response = await fetch(`/api/check-username?username=${username}`)
  const data = await response.json()
  return !data.exists
}

const formData = ref({
  email: '',
  username: ''
})

const { validate } = useValidateForm({
  data: formData,
  rules: yup.object({
    email: yup.string()
      .email('请输入有效邮箱')
      .required('邮箱是必填的')
      .test('email-exists', '邮箱已被使用', checkEmailExists),
    username: yup.string()
      .required('用户名是必填的')
      .min(3, '用户名至少3个字符')
      .test('username-exists', '用户名已被使用', checkUsernameExists)
  })
})

// 执行异步验证
async function handleAsyncValidate() {
  try {
    const result = await validate()
    if (result.valid) {
      console.log('异步验证通过')
    }
    else {
      console.log('异步验证失败:', result.errors)
    }
  }
  catch (error) {
    console.error('验证过程出错:', error)
  }
}

复杂验证示例

js
import * as yup from 'yup'

const complexSchema = yup.object({
  // 条件验证
  hasJob: yup.boolean(),
  company: yup.string().when('hasJob', {
    is: true,
    then: schema => schema.required('请填写公司名称'),
    otherwise: schema => schema.notRequired()
  }),

  // 数组验证
  tags: yup.array()
    .of(yup.string().required())
    .min(1, '至少选择一个标签')
    .max(5, '最多选择5个标签'),

  // 对象验证
  address: yup.object({
    province: yup.string().required('请选择省份'),
    city: yup.string().required('请选择城市'),
    detail: yup.string().required('请填写详细地址')
  }),

  // 日期验证
  birthday: yup.date()
    .required('请选择生日')
    .max(new Date(), '生日不能是未来日期'),

  // 文件验证
  avatar: yup.mixed()
    .test('fileSize', '文件大小不能超过2MB', (value) => {
      return !value || value.size <= 2 * 1024 * 1024
    })
    .test('fileType', '只支持图片格式', (value) => {
      return !value || ['image/jpeg', 'image/png', 'image/gif'].includes(value.type)
    })
})

对比两种验证方式

特性vee-validate 内置规则Yup Schema 验证
语法字符串格式对象链式调用
类型安全
异步验证
条件验证
复杂逻辑
学习成本中等
性能较好良好
包大小较大
自定义有限高度可定制

选择建议

  • 简单表单:推荐使用 vee-validate 内置规则,语法简洁,开发快速
  • 复杂表单:推荐使用 Yup Schema,功能强大,类型安全
  • 异步验证:必须使用 Yup Schema
  • 条件验证:推荐使用 Yup Schema
  • TypeScript 项目:推荐使用 Yup Schema,有更好的类型支持

与组件集成示例

vue
<script setup lang="ts">
import { initFormValidate, useValidateForm } from '@duxweb/dvha-core'
import { computed, ref } from 'vue'
import * as yup from 'yup'

// 初始化内置规则
initFormValidate()

const formData = ref({
  name: '',
  email: '',
  validationType: 'builtin' // 'builtin' | 'yup'
})

// 内置规则验证
const builtinRules = {
  name: 'required|min:2',
  email: 'required|email'
}

// Yup Schema 验证
const yupSchema = yup.object({
  name: yup.string()
    .required('姓名是必填的')
    .min(2, '姓名至少2个字符'),
  email: yup.string()
    .required('邮箱是必填的')
    .email('请输入有效邮箱')
})

// 根据选择动态切换验证方式
const currentRules = computed(() => {
  return formData.value.validationType === 'builtin' ? builtinRules : yupSchema
})

const { validate, reset } = useValidateForm({
  data: formData,
  rules: currentRules
})

const errors = ref({})
const isValidating = ref(false)

async function handleValidate() {
  isValidating.value = true
  try {
    const result = await validate()
    errors.value = result.valid ? {} : result.errors
    return result.valid
  }
  finally {
    isValidating.value = false
  }
}

async function handleSubmit() {
  const isValid = await handleValidate()
  if (isValid) {
    console.log('表单提交:', formData.value)
  }
}

function handleReset() {
  formData.value = {
    name: '',
    email: '',
    validationType: formData.value.validationType
  }
  errors.value = {}
  reset()
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <label>验证方式:</label>
      <select v-model="formData.validationType">
        <option value="builtin">
          vee-validate 内置规则
        </option>
        <option value="yup">
          Yup Schema 验证
        </option>
      </select>
    </div>

    <div>
      <label>姓名:</label>
      <input
        v-model="formData.name"
        type="text"
        :class="{ error: errors.name }"
      >
      <span v-if="errors.name" class="error-text">{{ errors.name }}</span>
    </div>

    <div>
      <label>邮箱:</label>
      <input
        v-model="formData.email"
        type="email"
        :class="{ error: errors.email }"
      >
      <span v-if="errors.email" class="error-text">{{ errors.email }}</span>
    </div>

    <div>
      <button
        type="submit"
        :disabled="isValidating"
      >
        {{ isValidating ? '验证中...' : '提交' }}
      </button>
      <button type="button" @click="handleReset">
        重置
      </button>
    </div>
  </form>
</template>

<style>
.error {
  border-color: red;
}
.error-text {
  color: red;
  font-size: 12px;
}
</style>