312 lines
6.8 KiB
TypeScript
312 lines
6.8 KiB
TypeScript
/**
|
|
* 表单验证组合式函数
|
|
*/
|
|
|
|
import { ref, reactive, computed } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
|
|
/**
|
|
* 使用表单验证
|
|
*/
|
|
export const useFormValidation = <T extends Record<string, any>>(
|
|
initialData: T,
|
|
rules: FormRules
|
|
) => {
|
|
const formRef = ref<FormInstance>()
|
|
const formData = reactive<T>({ ...initialData })
|
|
const errors = ref<Record<string, string>>({})
|
|
const isValidating = ref(false)
|
|
|
|
/**
|
|
* 验证整个表单
|
|
*/
|
|
const validateForm = async (): Promise<boolean> => {
|
|
if (!formRef.value) return false
|
|
|
|
try {
|
|
isValidating.value = true
|
|
await formRef.value.validate()
|
|
errors.value = {}
|
|
return true
|
|
} catch (error) {
|
|
console.error('表单验证失败:', error)
|
|
return false
|
|
} finally {
|
|
isValidating.value = false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证指定字段
|
|
*/
|
|
const validateField = async (field: keyof T): Promise<boolean> => {
|
|
if (!formRef.value) return false
|
|
|
|
try {
|
|
await formRef.value.validateField(field as string)
|
|
delete errors.value[field as string]
|
|
return true
|
|
} catch (error) {
|
|
errors.value[field as string] = error as string
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 清除验证结果
|
|
*/
|
|
const clearValidation = (fields?: (keyof T)[]) => {
|
|
if (!formRef.value) return
|
|
|
|
if (fields) {
|
|
formRef.value.clearValidate(fields as string[])
|
|
fields.forEach(field => {
|
|
delete errors.value[field as string]
|
|
})
|
|
} else {
|
|
formRef.value.clearValidate()
|
|
errors.value = {}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重置表单
|
|
*/
|
|
const resetForm = () => {
|
|
if (!formRef.value) return
|
|
|
|
formRef.value.resetFields()
|
|
Object.assign(formData, initialData)
|
|
errors.value = {}
|
|
}
|
|
|
|
/**
|
|
* 设置字段错误
|
|
*/
|
|
const setFieldError = (field: keyof T, message: string) => {
|
|
errors.value[field as string] = message
|
|
}
|
|
|
|
/**
|
|
* 清除字段错误
|
|
*/
|
|
const clearFieldError = (field: keyof T) => {
|
|
delete errors.value[field as string]
|
|
}
|
|
|
|
/**
|
|
* 获取字段错误
|
|
*/
|
|
const getFieldError = (field: keyof T) => {
|
|
return errors.value[field as string]
|
|
}
|
|
|
|
/**
|
|
* 检查表单是否有错误
|
|
*/
|
|
const hasErrors = computed(() => {
|
|
return Object.keys(errors.value).length > 0
|
|
})
|
|
|
|
/**
|
|
* 检查表单是否有效
|
|
*/
|
|
const isValid = computed(() => {
|
|
return !hasErrors.value && !isValidating.value
|
|
})
|
|
|
|
return {
|
|
formRef,
|
|
formData,
|
|
errors: computed(() => errors.value),
|
|
isValidating: computed(() => isValidating.value),
|
|
hasErrors,
|
|
isValid,
|
|
validateForm,
|
|
validateField,
|
|
clearValidation,
|
|
resetForm,
|
|
setFieldError,
|
|
clearFieldError,
|
|
getFieldError
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 常用验证规则
|
|
*/
|
|
export const validationRules = {
|
|
/**
|
|
* 必填验证
|
|
*/
|
|
required: (message = '此字段为必填项') => ({
|
|
required: true,
|
|
message,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 邮箱验证
|
|
*/
|
|
email: (message = '请输入正确的邮箱格式') => ({
|
|
type: 'email' as const,
|
|
message,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 手机号验证
|
|
*/
|
|
phone: (message = '请输入正确的手机号格式') => ({
|
|
pattern: /^1[3-9]\d{9}$/,
|
|
message,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 长度验证
|
|
*/
|
|
length: (min: number, max: number, message?: string) => ({
|
|
min,
|
|
max,
|
|
message: message || `长度必须在${min}-${max}位之间`,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 最小长度验证
|
|
*/
|
|
minLength: (min: number, message?: string) => ({
|
|
min,
|
|
message: message || `长度不能少于${min}位`,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 最大长度验证
|
|
*/
|
|
maxLength: (max: number, message?: string) => ({
|
|
max,
|
|
message: message || `长度不能超过${max}位`,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 正则验证
|
|
*/
|
|
pattern: (pattern: RegExp, message: string) => ({
|
|
pattern,
|
|
message,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 自定义验证
|
|
*/
|
|
custom: (validator: (rule: any, value: any, callback: any) => void) => ({
|
|
validator,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 账号验证(字母数字下划线)
|
|
*/
|
|
account: (message = '账号只能包含字母、数字和下划线,长度4-20位') => ({
|
|
pattern: /^[a-zA-Z0-9_]{4,20}$/,
|
|
message,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 密码验证
|
|
*/
|
|
password: (min = 6, max = 20, message?: string) => ({
|
|
min,
|
|
max,
|
|
message: message || `密码长度必须在${min}-${max}位之间`,
|
|
trigger: 'blur'
|
|
}),
|
|
|
|
/**
|
|
* 确认密码验证
|
|
*/
|
|
confirmPassword: (passwordField: string, message = '两次输入的密码不一致') => ({
|
|
validator: (rule: any, value: any, callback: any) => {
|
|
if (!value) {
|
|
callback(new Error('请再次输入密码'))
|
|
return
|
|
}
|
|
// 这里需要访问表单数据,在实际使用时需要传入表单数据
|
|
callback()
|
|
},
|
|
trigger: 'blur'
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 使用实时验证
|
|
*/
|
|
export const useRealtimeValidation = <T extends Record<string, any>>(
|
|
formData: T,
|
|
rules: FormRules
|
|
) => {
|
|
const errors = ref<Record<string, string>>({})
|
|
const validFields = ref<Set<string>>(new Set())
|
|
|
|
/**
|
|
* 验证单个字段
|
|
*/
|
|
const validateField = async (field: keyof T, value: any) => {
|
|
const fieldRules = rules[field as string]
|
|
if (!fieldRules) return true
|
|
|
|
try {
|
|
// 这里简化处理,实际应该使用async-validator
|
|
const ruleArray = Array.isArray(fieldRules) ? fieldRules : [fieldRules]
|
|
|
|
for (const rule of ruleArray) {
|
|
if (rule.required && (!value || value === '')) {
|
|
throw new Error(rule.message || '此字段为必填项')
|
|
}
|
|
|
|
if (rule.min && value && value.length < rule.min) {
|
|
throw new Error(rule.message || `长度不能少于${rule.min}位`)
|
|
}
|
|
|
|
if (rule.max && value && value.length > rule.max) {
|
|
throw new Error(rule.message || `长度不能超过${rule.max}位`)
|
|
}
|
|
|
|
if (rule.pattern && value && !rule.pattern.test(value)) {
|
|
throw new Error(rule.message || '格式不正确')
|
|
}
|
|
}
|
|
|
|
delete errors.value[field as string]
|
|
validFields.value.add(field as string)
|
|
return true
|
|
} catch (error: any) {
|
|
errors.value[field as string] = error.message
|
|
validFields.value.delete(field as string)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查所有字段是否有效
|
|
*/
|
|
const isAllValid = computed(() => {
|
|
const requiredFields = Object.keys(rules)
|
|
return requiredFields.every(field => validFields.value.has(field)) &&
|
|
Object.keys(errors.value).length === 0
|
|
})
|
|
|
|
return {
|
|
errors: computed(() => errors.value),
|
|
validFields: computed(() => validFields.value),
|
|
isAllValid,
|
|
validateField
|
|
}
|
|
}
|