Files
zcglxt/src/utils/fieldValidator.ts
Claude e48975f9d5 fix: 修复前端登录体验和API调用问题
- 修复路由守卫:未登录时直接跳转,不显示提示信息
- 修复API拦截器:401错误直接跳转,无需确认
- 移除不必要的ElMessageBox确认框
- 优化Token过期处理逻辑
- 修复文件管理API引入路径和URL前缀
- 修复调拨/回收管理API端点不匹配问题
- 修复通知管理API方法不匹配问题
- 统一系统配置API路径为单数形式

影响文件:
- src/router/index.ts
- src/api/request.ts
- src/api/file.ts
- src/api/index.ts

测试状态:
- 前端构建通过
- 所有API路径已验证
- 登录流程测试通过

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 00:26:33 +08:00

262 lines
6.2 KiB
TypeScript

/**
* 动态字段验证器
* 根据字段配置进行表单验证
*/
import type { FieldConfig, ValidationResult, FormData } from '@/types/form'
/**
* 验证单个字段
* @param value 字段值
* @param field 字段配置
* @param allFormData 所有表单数据(用于自定义验证)
* @returns 验证结果
*/
export function validateField(
value: any,
field: FieldConfig,
allFormData: FormData = {}
): ValidationResult {
const errors: string[] = []
// 1. 必填验证
if (field.required) {
if (value === undefined || value === null || value === '') {
errors.push(`${field.label}不能为空`)
return { isValid: false, errors }
}
}
// 如果值为空且非必填,则跳过后续验证
if (!field.required && (value === undefined || value === null || value === '')) {
return { isValid: true, errors: [] }
}
// 2. 根据字段类型进行验证
switch (field.fieldType) {
case 'text':
case 'textarea':
validateText(value, field, errors)
break
case 'number':
validateNumber(value, field, errors)
break
case 'email':
validateEmail(value, field, errors)
break
case 'phone':
validatePhone(value, field, errors)
break
case 'url':
validateUrl(value, field, errors)
break
case 'select':
case 'multiselect':
case 'boolean':
case 'date':
case 'tree':
// 这些类型一般不需要额外验证
break
}
// 3. 自定义正则验证
if (field.validationRules?.pattern && value) {
try {
const regex = new RegExp(field.validationRules.pattern)
if (!regex.test(value)) {
errors.push(field.validationRules.customMessage || `${field.label}格式不正确`)
}
} catch (error) {
console.error('正则表达式验证失败:', error)
}
}
// 4. 自定义验证函数
if (field.validationRules?.custom) {
try {
const result = field.validationRules.custom(value, allFormData)
if (result !== true) {
errors.push(typeof result === 'string' ? result : `${field.label}验证失败`)
}
} catch (error) {
console.error('自定义验证失败:', error)
errors.push(`${field.label}验证出错`)
}
}
return {
isValid: errors.length === 0,
errors
}
}
/**
* 验证所有字段
* @param data 表单数据
* @param fields 字段配置列表
* @returns 字段级错误信息
*/
export function validateFields(
data: FormData,
fields: FieldConfig[]
): Record<string, string[]> {
const errors: Record<string, string[]> = {}
fields.forEach((field) => {
const value = data[field.name]
const result = validateField(value, field, data)
if (!result.isValid) {
errors[field.name] = result.errors
}
})
return errors
}
/**
* 文本类型验证
*/
function validateText(value: any, field: FieldConfig, errors: string[]): void {
if (typeof value !== 'string') {
errors.push(`${field.label}必须是文本`)
return
}
const { min, max } = field.validationRules || {}
if (min && value.length < min) {
errors.push(`${field.label}长度不能少于${min}个字符`)
}
if (max && value.length > max) {
errors.push(`${field.label}长度不能超过${max}个字符`)
}
}
/**
* 数字类型验证
*/
function validateNumber(value: any, field: FieldConfig, errors: string[]): void {
const num = Number(value)
if (isNaN(num)) {
errors.push(`${field.label}必须是数字`)
return
}
const { min, max } = field.validationRules || {}
if (min !== undefined && num < min) {
errors.push(`${field.label}不能小于${min}`)
}
if (max !== undefined && num > max) {
errors.push(`${field.label}不能大于${max}`)
}
}
/**
* 邮箱验证
*/
function validateEmail(value: any, field: FieldConfig, errors: string[]): void {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(value)) {
errors.push(`${field.label}邮箱格式不正确`)
}
}
/**
* 手机号验证
*/
function validatePhone(value: any, field: FieldConfig, errors: string[]): void {
// 中国大陆手机号验证
const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(value)) {
errors.push(`${field.label}手机号格式不正确`)
}
}
/**
* URL验证
*/
function validateUrl(value: any, field: FieldConfig, errors: string[]): void {
try {
new URL(value)
} catch {
errors.push(`${field.label}URL格式不正确`)
}
}
/**
* 创建VeeValidate验证规则
* @param field 字段配置
* @returns VeeValidate规则对象
*/
export function createValidationRule(field: FieldConfig): any {
const rules: any = {}
// 必填规则
if (field.required) {
rules.required = true
}
// 根据字段类型添加规则
switch (field.fieldType) {
case 'text':
case 'textarea':
if (field.validationRules?.min) {
rules.min = field.validationRules.min
}
if (field.validationRules?.max) {
rules.max = field.validationRules.max
}
if (field.validationRules?.pattern) {
rules.regex = new RegExp(field.validationRules.pattern)
}
break
case 'number':
if (field.validationRules?.min !== undefined) {
rules.min_value = field.validationRules.min
}
if (field.validationRules?.max !== undefined) {
rules.max_value = field.validationRules.max
}
break
case 'email':
rules.email = true
break
case 'phone':
rules.regex = /^1[3-9]\d{9}$/
break
case 'url':
rules.url = true
break
}
return rules
}
/**
* 获取字段错误消息
* @param field 字段配置
* @param errorType 错误类型
* @returns 错误消息
*/
export function getFieldErrorMessage(field: FieldConfig, errorType: string): string {
const messages: Record<string, string> = {
required: `${field.label}不能为空`,
min: `${field.label}长度/值不能小于${field.validationRules?.min}`,
max: `${field.label}长度/值不能大于${field.validationRules?.max}`,
email: `${field.label}邮箱格式不正确`,
url: `${field.label}URL格式不正确`,
regex: `${field.label}格式不正确`
}
return messages[errorType] || `${field.label}验证失败`
}