// utils/validators.ts // 轻量类型声明,避免对特定 UI 库的硬依赖 export interface FormItemRule { required?: boolean message?: string trigger?: 'blur' | 'change' pattern?: RegExp validator?: ( rule: FormItemRule, value: any, callback: (error?: Error) => void, ) => void } type I18nT = (key: string, ...args: any[]) => string export const Patterns = { // 至少 8-20 位,包含大小写字母、数字、特殊字符中的至少三类 password: /^(?:(?=.*[a-z])(?=.*[A-Z])(?=.*\d)|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+=-])|(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*()_+=-])|(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+=-]))[\w!@#$%^&*()+=-]{8,20}$/, email: /^[\w.+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, idNumber: /^[A-Z0-9]{6,18}$/i, mobile: /^\d{6,15}$/, postcode: /^[a-z0-9]{1,15}$/i, // 允许英文地址常见符号:空格- , . / # ' address: /^[A-Z0-9\s\-.,/#']+$/i, // 允许中文、字母、数字、空格、连字符、常见中文分隔符与中点 addressCn: /^[\u4E00-\u9FA5A-Z0-9\s\-·,。、《》()()#]+$/i, } // ✅ 通用规则封装 export const Validators = { required: (msg: string, trigger: 'blur' | 'change' = 'blur'): FormItemRule => ({ required: true, message: msg, trigger, }), pattern: (regex: RegExp, msg: string, trigger: 'blur' | 'change' = 'blur'): FormItemRule => ({ pattern: regex, message: msg, trigger, }), custom: ( validator: (rule: FormItemRule, value: any, callback: (error?: Error) => void) => void, trigger: 'blur' | 'change' = 'blur', ): FormItemRule => ({ validator, trigger, }), } // ✅ 自定义校验 export function validateAge18( rule: FormItemRule, value: string, callback: (error?: Error) => void, t: I18nT, ) { if (!value) return callback(new Error(t('card.vaildate.v5'))) const today = new Date() const birthDate = new Date(value) let age = today.getFullYear() - birthDate.getFullYear() const month = today.getMonth() - birthDate.getMonth() if (month < 0 || (month === 0 && today.getDate() < birthDate.getDate())) age-- age < 18 ? callback(new Error(t('card.New.n3'))) : callback() } export function validateAddress( rule: FormItemRule, value: string, callback: (error?: Error) => void, t: I18nT, ) { const val = String(value ?? '').trim() if (val.length < 2 || val.length > 40 || !Patterns.address.test(val)) { callback(new Error(t('card.New.n1'))) } else { callback() } } export function validateName( rule: FormItemRule, value: string, callback: (error?: Error) => void, ) { const val = String(value ?? '').trim() const regex = /^[A-Z\s'-]+$/i if (!val) { callback(new Error('Name is required')) return } if (!regex.test(val)) { callback(new Error('Invalid name format')) return } if (val.length < 2 || val.length > 23) { callback(new Error('Name length must be 2-23 characters')) return } // 连续空格或标点的简单规避 if (/\s{2,}/.test(val)) { callback(new Error('Name contains consecutive spaces')) return } callback() } // 复杂密码校验(独立导出,便于在表单中直接复用) export function validatePassword( rule: FormItemRule, value: string, callback: (error?: Error) => void, t?: I18nT, ) { const val = String(value ?? '') if (!val) return callback(new Error(t ? t('vaildate.password.empty') : 'Password is required')) if (!Patterns.password.test(val)) { return callback( new Error( t ? t('vaildate.password.format') : 'Password must be 8-20 chars and include 3 of: upper, lower, digit, symbol', ), ) } callback() } export function validatePostcode( rule: FormItemRule, value: string, callback: (error?: Error) => void, t?: I18nT, ) { const val = String(value ?? '').trim() if (!val) return callback(new Error(t ? t('card.vaildate.v8') : 'Postcode is required')) if (!Patterns.postcode.test(val)) { return callback(new Error(t ? t('card.New.n2') : 'Invalid postcode')) } callback() } export function validateAddressCn( rule: FormItemRule, value: string, callback: (error?: Error) => void, t?: I18nT, ) { const val = String(value ?? '').trim() if (!val) return callback(new Error(t ? t('card.vaildate.v27') : 'Address is required')) if (!Patterns.addressCn.test(val)) { return callback(new Error(t ? t('card.vaildate.v27') : 'Invalid address characters')) } callback() }