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>
This commit is contained in:
261
src/utils/fieldValidator.ts
Normal file
261
src/utils/fieldValidator.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* 动态字段验证器
|
||||
* 根据字段配置进行表单验证
|
||||
*/
|
||||
|
||||
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}验证失败`
|
||||
}
|
||||
Reference in New Issue
Block a user