| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // 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()
- }
|