Download vetcopilot-app/src/utils/validation.ts from WWsCa/VetNet-LCPS: direct link, hf CLI and curl.
- Browser
- Download file 7.25 kB
-
https://huggingface.co/WWsCa/VetNet-LCPS/resolve/main/vetcopilot-app/src/utils/validation.ts
- Command line
-
hf download hf://WWsCa/VetNet-LCPS/vetcopilot-app/src/utils/validation.ts
-
curl -L -o validation.ts https://huggingface.co/WWsCa/VetNet-LCPS/resolve/main/vetcopilot-app/src/utils/validation.ts
7.25 kB
| /** | |
| * 通用表单验证工具 | |
| */ | |
| /** 验证结果:通过返回 null,失败返回错误消息 */ | |
| export type ValidationResult = string | null | |
| /** 字段级错误集合 */ | |
| export type FieldErrors = Record<string, string> | |
| /** 中国手机号格式 */ | |
| const PHONE_REGEX = /^1[3-9]\d{9}$/ | |
| /** 邮箱格式 */ | |
| const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ | |
| /** 动物名称:2-20字符,中英文数字 */ | |
| const ANIMAL_NAME_REGEX = /^[\u4e00-\u9fa5a-zA-Z0-9]{2,20}$/ | |
| /** 主人姓名:2-20字符,中文(允许中间有 · ) */ | |
| const OWNER_NAME_REGEX = /^[\u4e00-\u9fa5·]{2,20}$/ | |
| /** 中文年龄格式:数字 + 岁/月/周/天/年 */ | |
| const AGE_REGEX = /^(\d+(\.\d+)?)\s*(岁|月|周|天|年)$/ | |
| // ============================================================ | |
| // 基础验证 | |
| // ============================================================ | |
| /** 验证手机号 */ | |
| export function validatePhone(value: string): ValidationResult { | |
| if (!value || !value.trim()) return '请输入手机号' | |
| if (!PHONE_REGEX.test(value.trim())) return '手机号格式不正确(11位数字)' | |
| return null | |
| } | |
| /** 验证必填字段 */ | |
| export function validateRequired(value: string, label: string): ValidationResult { | |
| if (!value || !value.trim()) return `请输入${label}` | |
| return null | |
| } | |
| /** 验证正数(体重等) */ | |
| export function validatePositiveNumber(value: string, label: string): ValidationResult { | |
| if (!value || !value.trim()) return null // 可选字段 | |
| const num = Number(value) | |
| if (isNaN(num) || num <= 0) return `${label}必须为正数` | |
| return null | |
| } | |
| /** 验证日期不早于今天(用于下次回访日期等未来日期) */ | |
| export function validateFutureDate(value: string, label: string): ValidationResult { | |
| if (!value) return null // 可选字段 | |
| const date = new Date(value) | |
| const today = new Date() | |
| today.setHours(0, 0, 0, 0) | |
| if (date < today) return `${label}不能早于今天` | |
| return null | |
| } | |
| /** 验证结束日期不早于开始日期 */ | |
| export function validateDateRange(startDate: string, endDate: string, startLabel: string, endLabel: string): ValidationResult { | |
| if (!startDate || !endDate) return null | |
| if (new Date(endDate) < new Date(startDate)) return `${endLabel}不能早于${startLabel}` | |
| return null | |
| } | |
| // ============================================================ | |
| // 新增验证函数 | |
| // ============================================================ | |
| /** 验证邮箱格式 */ | |
| export function validateEmail(value: string): ValidationResult { | |
| if (!value || !value.trim()) return null // 可选字段 | |
| if (!EMAIL_REGEX.test(value.trim())) return '邮箱格式不正确' | |
| return null | |
| } | |
| /** | |
| * 验证年龄 | |
| * 支持格式: | |
| * - 纯数字(视为"岁"):3 → 3岁 | |
| * - 带单位:3岁 / 6个月 / 2周 / 14天 / 1年 | |
| * 范围:0-50岁(月/周/天会自动换算校验) | |
| */ | |
| export function validateAge(value: string): ValidationResult { | |
| if (!value || !value.trim()) return null // 可选字段 | |
| const trimmed = value.trim() | |
| // 尝试匹配中文年龄格式 | |
| const ageMatch = trimmed.match(AGE_REGEX) | |
| if (ageMatch) { | |
| const num = parseFloat(ageMatch[1]) | |
| const unit = ageMatch[3] | |
| switch (unit) { | |
| case '岁': | |
| case '年': | |
| if (num < 0 || num > 50) return '年龄应在 0-50 岁之间' | |
| break | |
| case '月': | |
| if (num < 0 || num > 600) return '年龄应在 0-50 岁之间(600个月)' | |
| break | |
| case '周': | |
| if (num < 0 || num > 2600) return '年龄应在 0-50 岁之间(2600周)' | |
| break | |
| case '天': | |
| if (num < 0 || num > 18250) return '年龄应在 0-50 岁之间(18250天)' | |
| break | |
| } | |
| return null | |
| } | |
| // 纯数字视为"岁" | |
| const pureNum = Number(trimmed) | |
| if (!isNaN(pureNum) && pureNum >= 0 && pureNum <= 50) return null | |
| return '年龄格式不正确(如:3岁 / 6个月 / 2周)' | |
| } | |
| /** 验证体重(正数,0.01-200kg) */ | |
| export function validateWeight(value: string): ValidationResult { | |
| if (!value || !value.trim()) return null // 可选字段 | |
| const num = Number(value) | |
| if (isNaN(num)) return '体重必须为数字' | |
| if (num <= 0) return '体重必须为正数' | |
| if (num < 0.01) return '体重最小为 0.01 kg' | |
| if (num > 200) return '体重不能超过 200 kg' | |
| return null | |
| } | |
| /** | |
| * 验证密码强度 | |
| * - 至少 6 位 | |
| * - 建议包含字母和数字(仅警告,不阻断) | |
| */ | |
| export function validatePassword(value: string): ValidationResult { | |
| if (!value) return '请输入密码' | |
| if (value.length < 6) return '密码至少 6 位' | |
| // 建议包含字母和数字(弱密码仅提示,不阻断提交) | |
| const hasLetter = /[a-zA-Z]/.test(value) | |
| const hasDigit = /\d/.test(value) | |
| if (!hasLetter || !hasDigit) { | |
| return '建议包含字母和数字以增强安全性' | |
| } | |
| return null | |
| } | |
| /** 验证密码强度(严格模式,用于注册等场景) | |
| * - 至少 6 位 | |
| * - 必须包含字母和数字 | |
| */ | |
| export function validatePasswordStrict(value: string): ValidationResult { | |
| if (!value) return '请输入密码' | |
| if (value.length < 6) return '密码至少 6 位' | |
| const hasLetter = /[a-zA-Z]/.test(value) | |
| const hasDigit = /\d/.test(value) | |
| if (!hasLetter || !hasDigit) return '密码必须包含字母和数字' | |
| return null | |
| } | |
| /** 注册/登录密码校验:仅校验最小长度,不阻断弱密码 */ | |
| export function validatePasswordBasic(value: string): ValidationResult { | |
| if (!value) return '请输入密码' | |
| if (value.length < 6) return '密码至少 6 位' | |
| return null | |
| } | |
| /** 验证动物名称(2-20字符,中英文数字) */ | |
| export function validateAnimalName(value: string): ValidationResult { | |
| if (!value || !value.trim()) return '请输入动物名称' | |
| const trimmed = value.trim() | |
| if (trimmed.length < 2) return '动物名称至少 2 个字符' | |
| if (trimmed.length > 20) return '动物名称最多 20 个字符' | |
| if (!ANIMAL_NAME_REGEX.test(trimmed)) return '动物名称仅支持中英文和数字' | |
| return null | |
| } | |
| /** 验证主人姓名(2-20字符,中文,允许中间有 ·) */ | |
| export function validateOwnerName(value: string): ValidationResult { | |
| if (!value || !value.trim()) return '请输入宠主姓名' | |
| const trimmed = value.trim() | |
| if (trimmed.length < 2) return '宠主姓名至少 2 个字符' | |
| if (trimmed.length > 20) return '宠主姓名最多 20 个字符' | |
| if (!OWNER_NAME_REGEX.test(trimmed)) return '宠主姓名仅支持中文' | |
| return null | |
| } | |
| /** 验证最大长度 */ | |
| export function validateMaxLength(value: string, max: number, label: string): ValidationResult { | |
| if (!value) return null | |
| if (value.length > max) return `${label}不能超过 ${max} 个字符` | |
| return null | |
| } | |
| // ============================================================ | |
| // 批量验证 | |
| // ============================================================ | |
| /** 批量验证字段,返回第一个错误或 null */ | |
| export function validateFields(validations: Array<{ field: string; result: ValidationResult }>): FieldErrors | null { | |
| const errors: FieldErrors = {} | |
| for (const { field, result } of validations) { | |
| if (result) errors[field] = result | |
| } | |
| return Object.keys(errors).length > 0 ? errors : null | |
| } | |