planFieldValidation.js 5.22 KB
/**
 * 动态验证系统
 *
 * @description 提供可配置的字段验证功能,支持同步/异步验证
 * @module utils/planFieldValidation
 * @author Claude Code
 * @created 2026-02-14
 */

/**
 * 验证结果类型
 * @typedef {Object} ValidationResult
 * @property {boolean} valid - 是否通过
 * @property {string} [error] - 错误信息
 */

/**
 * 内置验证规则
 */
export const VALIDATION_RULES = {
  REQUIRED: 'required',
  MIN: 'min',
  MAX: 'max',
  RANGE: 'range',
  PATTERN: 'pattern',
  CUSTOM: 'custom'
}

/**
 * 执行字段验证
 *
 * @param {*} value - 待验证的值
 * @param {Object} rules - 验证规则配置
 * @param {Object} context - 验证上下文(包含 formData 等)
 * @returns {ValidationResult} 验证结果
 *
 * @example
 * // 必填验证
 * validateField(null, { required: true })
 * // => { valid: false, error: '该字段为必填' }
 *
 * // 最小长度验证
 * validateField('ab', { min: 3 })
 * // => { valid: false, error: '至少需要3个字符' }
 *
 * // 自定义验证
 * validateField(25, { custom: (value) => value >= 18 })
 * // => { valid: false, error: '年龄必须满18岁' }
 */
export function validateField(value, rules = {}, context = {}) {
  // 必填检查
  if (rules.required) {
    // 函数形式的 required - 执行自定义验证
    if (typeof rules.required === 'function') {
      const result = rules.required(value, context)
      if (!result) {
        return {
          valid: false,
          error: rules.requiredMessage || '该字段为必填'
        }
      }
    } else if (!isNotEmpty(value)) {
      // 布尔值形式的 required - 检查是否为空
      return {
        valid: false,
        error: rules.requiredMessage || '该字段为必填'
      }
    }
  }

  // 最小长度
  if (rules.min !== undefined && value && value.length < rules.min) {
    return {
      valid: false,
      error: rules.minMessage || `至少需要${rules.min}个字符`
    }
  }

  // 最大长度
  if (rules.max !== undefined && value && value.length > rules.max) {
    return {
      valid: false,
      error: rules.maxMessage || `最多${rules.max}个字符`
    }
  }

  // 数值范围
  if (rules.range) {
    const numValue = parseFloat(value)
    if (Number.isNaN(numValue)) {
      return {
        valid: false,
        error: '请输入有效数字'
      }
    }
    const [min, max] = rules.range
    if ((min !== undefined && numValue < min) || (max !== undefined && numValue > max)) {
      return {
        valid: false,
        error: rules.rangeMessage || `请输入${min || 0}-${max || '∞'}之间的数值`
      }
    }
  }

  // 正则表达式
  if (rules.pattern && !new RegExp(rules.pattern).test(value)) {
    return {
      valid: false,
      error: rules.patternMessage || '格式不正确'
    }
  }

  // 自定义验证函数
  if (rules.custom && typeof rules.custom === 'function') {
    const result = rules.custom(value, context)
    if (!result) {
      return {
        valid: false,
        error: rules.customMessage || '验证失败'
      }
    }
  }

  // 全部通过
  return { valid: true }
}

/**
 * 批量验证表单数据
 *
 * @param {Object} formData - 表单数据
 * @param {Object} fieldDefinitions - 字段定义
 * @returns {Object} 验证结果 { valid: boolean, errors: Object }
 *
 * @example
 * const result = validateForm(formData, fieldDefinitions)
 * if (result.valid) {
 *   // 提交
 * } else {
 *   // 显示错误
 *   console.log(result.errors)
 * }
 */
export function validateForm(formData, fieldDefinitions) {
  const errors = {}

  for (const [key, value] of Object.entries(formData)) {
    // 跳过空值(如果非必填)
    if (value === null || value === undefined || value === '') {
      const definition = fieldDefinitions[key]
      if (definition?.validation) {
        const rules = definition.validation

        // 检查是否真正的必填:先调用 required 规则判断
        const isRequired = rules.required ? typeof rules.required === 'function' ? rules.required(value) : true : false

        // 如果是必填字段且值为空,验证失败
        if (isRequired && !isNotEmpty(value)) {
          errors[key] = '该字段为必填'
        }
      }
      continue
    }

    // 验证非空值
    const definition = fieldDefinitions[key]
    if (definition?.validation) {
      const rules = definition.validation

      // 将 required 规则放在第一位,确保它被优先检查
      const orderedRules = {}
      if (rules.required) {
        orderedRules.required = rules.required
      }
      for (const ruleKey in rules) {
        if (ruleKey !== 'required') {
          orderedRules[ruleKey] = rules[ruleKey]
        }
      }

      const result = validateField(value, orderedRules, { formData, ...formData })
      if (!result.valid) {
        errors[key] = result.error
      }
    }
  }

  return {
    valid: Object.keys(errors).length === 0,
    errors
  }
}

/**
 * 检查值是否非空
 *
 * @param {*} value - 待检查的值
 * @returns {boolean} 是否非空
 */
export function isNotEmpty(value) {
  if (value === null || value === undefined) return false
  if (typeof value === 'string' && value.trim() === '') return false
  if (Array.isArray(value) && value.length === 0) return false
  return true
}