IMS系统表单校验完全指南

表单校验是保障数据质量的重要环节,需要在前端和后端同时进行校验。本文详细介绍 IMS 系统的表单校验架构与实现。

校验规则定义

系统支持多种校验规则类型:

/* 校验规则类型 */
type ValidationType =
    | 'required'      // 必填
    | 'email'        // 邮箱格式
    | 'phone'        // 手机号
    | 'url'           // URL格式
    | 'number'       // 数字
    | 'integer'      // 整数
    | 'min'           // 最小值
    | 'max'           // 最大值
    | 'minLength'    // 最小长度
    | 'maxLength'    // 最大长度
    | 'pattern'      // 正则表达式
    | 'range'        // 范围
    | 'enum'         // 枚举值
    | 'custom'       // 自定义校验;

/* 校验规则定义 */
interface ValidationRule {
    type: ValidationType;
    message: string;
    value?: any;
    trigger?: 'blur' | 'change' | 'focus';
    validator?: function;
}

/* 字段配置 */
interface FieldConfig {
    key: string;
    label: string;
    rules: ValidationRule[];
}

前端校验实现

前端使用异步校验,提供即时反馈:

/* 前端校验器 */
class FrontendValidator {
    validateField(value: any, rules: ValidationRule[]): Promise<string[]> {
        return new Promise(async (resolve) => {
            const errors: string[] = [];

            for (const rule of rules) {
                const error = await this.validateRule(value, rule);
                if (error) {
                    errors.push(error);
                }
            }

            resolve(errors);
        });
    }

    async validateRule(value: any, rule: ValidationRule): Promise<string | null> {
        /* 必填校验 */
        if (rule.type === 'required') {
            if (value === null || value === undefined || value === '') {
                return rule.message;
            }
        }

        /* 邮箱校验 */
        if (rule.type === 'email' && value) {
            const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            if (!emailPattern.test(value)) {
                return rule.message;
            }
        }

        /* 手机号校验 */
        if (rule.type === 'phone' && value) {
            const phonePattern = /^1[3-9]\d{9}$/;
            if (!phonePattern.test(value)) {
                return rule.message;
            }
        }

        /* 最小值校验 */
        if (rule.type === 'min' && value !== '') {
            if (Number(value) < rule.value) {
                return rule.message;
            }
        }

        /* 长度校验 */
        if (rule.type === 'minLength' && value) {
            if (value.length < rule.value) {
                return rule.message;
            }
        }

        /* 自定义校验 */
        if (rule.type === 'custom' && rule.validator) {
            const valid = await rule.validator(value);
            if (!valid) {
                return rule.message;
            }
        }

        return null;
    }
}

后端校验实现

后端校验确保数据安全,支持服务端独立校验:

/* 后端校验器 */
class BackendValidator {
    validate(data: Record<string, any>, schema: Record<string, ValidationRule[]>): void {
        const errors: Record<string, string[]> = {};

        for (const [field, rules] of Object.entries(schema)) {
            const value = data[field];
            const fieldErrors: string[] = [];

            for (const rule of rules) {
                const error = this.validateRule(value, rule);
                if (error) {
                    fieldErrors.push(error);
                }
            }

            if (fieldErrors.length > 0) {
                errors[field] = fieldErrors;
            }
        }

        /* 抛出校验错误 */
        if (Object.keys(errors).length > 0) {
            throw new ValidationException(errors);
        }
    }

    validateRule(value: any, rule: ValidationRule): string | null {
        /* 必填校验(严格模式) */
        if (rule.type === 'required') {
            if (value === null || value === undefined) {
                return rule.message;
            }
            if (typeof value === 'string' && value.trim() === '') {
                return rule.message;
            }
        }

        /* 跳过空值的其他校验 */
        if (value === null || value === undefined || value === '') {
            return null;
        }

        /* 正则校验 */
        if (rule.type === 'pattern' && rule.value) {
            if (!new RegExp(rule.value).test(value)) {
                return rule.message;
            }
        }

        /* 枚举校验 */
        if (rule.type === 'enum' && rule.value) {
            if (!rule.value.includes(value)) {
                return rule.message;
            }
        }

        return null;
    }
}

异步校验

支持服务端异步校验,如用户名唯一性检查:

/* 异步校验服务 */
class AsyncValidator {
    constructor(private api: ApiService) {}

    /* 用户名唯一性校验 */
    async checkUsernameUnique(username: string, excludeId?: string): Promise<boolean> {
        const params = { username };
        if (excludeId) {
            params['excludeId'] = excludeId;
        }

        const response = await this.api.get('/api/check/username', params);
        return response.data.unique;
    }

    /* 邮箱唯一性校验 */
    async checkEmailUnique(email: string, excludeId?: string): Promise<boolean> {
        const params = { email };
        if (excludeId) {
            params['excludeId'] = excludeId;
        }

        const response = await this.api.get('/api/check/email', params);
        return response.data.unique;
    }

    /* 异步校验整合到前端校验器 */
    async validateAsync(value: any, validators: Function[]): Promise<string[]> {
        const errors: string[] = [];

        for (const validator of validators) {
            const result = await validator(value);
            if (result !== true) {
                errors.push(result);
            }
        }

        return errors;
    }
}

校验提示优化

提供友好的错误提示体验:

/* 错误消息管理器 */
class ErrorMessageManager {
    private messages = {
        required: '此字段为必填项',
        email: '请输入有效的邮箱地址',
        phone: '请输入有效的手机号码',
        number: '请输入有效的数字',
        min: (min) => `数值不能小于 ${min}`,
        max: (max) => `数值不能大于 ${max}`,
        minLength: (len) => `至少需要输入 ${len} 个字符`,
        maxLength: (len) => `最多只能输入 ${len} 个字符`,
        pattern: '格式不正确',
        unique: '该值已存在,请更换'
    };

    getMessage(rule: ValidationRule): string {
        /* 优先使用自定义消息 */
        if (rule.message) {
            return rule.message;
        }

        /* 使用默认消息模板 */
        const template = this.messages[rule.type];
        if (typeof template === 'function') {
            return template(rule.value);
        }

        return template || '校验失败';
    }
}

校验设计原则

  • 前端校验重在即时反馈,提升用户体验
  • 后端校验是最后防线,不可或缺
  • 异步校验注意防抖,避免频繁请求
  • 统一错误提示风格,保持一致性

总结

  • 前后端双重校验,保障数据安全
  • 支持多种内置校验规则类型
  • 自定义校验灵活扩展
  • 异步校验用于唯一性检查