Some checks failed
CI Check / check (push) Has been cancelled
- 创建统一UI三层架构系统(基础层、颜色层、组合层) - 拆分Admin页面为独立Tab组件(MonitorTab、UsersTab、DepartmentsTab等) - 提取公共逻辑到Hooks(useToast、useDataFetch、useUserOperations等) - 提取工具函数(validators、formatters、api等) - 创建统一弹窗组件(Modal)和useModal Hook - 添加错误边界和统一状态组件 - 完善类型定义和可访问性支持 - 更新文档(优化记录、优化计划、更新日志)
171 lines
4.0 KiB
TypeScript
171 lines
4.0 KiB
TypeScript
/**
|
||
* 表单验证工具函数
|
||
* 统一管理各种表单验证逻辑
|
||
*/
|
||
|
||
/**
|
||
* 验证结果接口
|
||
*/
|
||
export interface ValidationResult {
|
||
valid: boolean
|
||
error?: string
|
||
}
|
||
|
||
/**
|
||
* 验证字符串是否非空(去除空白字符后)
|
||
* @param value - 要验证的值
|
||
* @param fieldName - 字段名称,用于错误消息
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateNotEmpty(value: string, fieldName: string = '字段'): ValidationResult {
|
||
const trimmed = value.trim()
|
||
if (!trimmed) {
|
||
return {
|
||
valid: false,
|
||
error: `${fieldName}不能为空`,
|
||
}
|
||
}
|
||
return { valid: true }
|
||
}
|
||
|
||
/**
|
||
* 验证部门名称
|
||
* @param name - 部门名称
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateDepartmentName(name: string): ValidationResult {
|
||
return validateNotEmpty(name, '部门名称')
|
||
}
|
||
|
||
/**
|
||
* 验证用户邮箱
|
||
* @param email - 邮箱地址
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateEmail(email: string): ValidationResult {
|
||
if (!email) {
|
||
return {
|
||
valid: false,
|
||
error: '邮箱不能为空',
|
||
}
|
||
}
|
||
|
||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||
if (!emailPattern.test(email)) {
|
||
return {
|
||
valid: false,
|
||
error: '请输入有效的邮箱地址',
|
||
}
|
||
}
|
||
|
||
return { valid: true }
|
||
}
|
||
|
||
/**
|
||
* 验证字段键名(必须符合标识符规则)
|
||
* @param key - 字段键名
|
||
* @param index - 字段索引(可选,用于错误消息)
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateFieldKey(key: string, index?: number): ValidationResult {
|
||
const trimmed = key.trim()
|
||
|
||
if (!trimmed) {
|
||
return {
|
||
valid: false,
|
||
error: index !== undefined ? `字段 ${index + 1} 的键名不能为空` : '字段键名不能为空',
|
||
}
|
||
}
|
||
|
||
// 键名必须以字母开头,只能包含字母、数字和下划线
|
||
const keyPattern = /^[a-zA-Z][a-zA-Z0-9_]*$/
|
||
if (!keyPattern.test(trimmed)) {
|
||
return {
|
||
valid: false,
|
||
error:
|
||
index !== undefined
|
||
? `字段 ${index + 1} 的键名需以字母开头,并仅包含字母、数字或下划线`
|
||
: '字段键名需以字母开头,并仅包含字母、数字或下划线',
|
||
}
|
||
}
|
||
|
||
return { valid: true }
|
||
}
|
||
|
||
/**
|
||
* 验证字段键名是否唯一
|
||
* @param keys - 所有字段键名数组
|
||
* @param currentIndex - 当前字段索引(可选)
|
||
* @returns 验证结果,如果发现重复则返回第一个重复的索引
|
||
*/
|
||
export function validateUniqueKeys(keys: string[], currentIndex?: number): ValidationResult {
|
||
const seenKeys = new Set<string>()
|
||
|
||
for (let i = 0; i < keys.length; i++) {
|
||
const trimmedKey = keys[i].trim()
|
||
|
||
if (!trimmedKey) {
|
||
continue // 空键名由 validateFieldKey 处理
|
||
}
|
||
|
||
if (seenKeys.has(trimmedKey)) {
|
||
return {
|
||
valid: false,
|
||
error: `字段键名 "${trimmedKey}" 已重复,请重新命名`,
|
||
}
|
||
}
|
||
|
||
seenKeys.add(trimmedKey)
|
||
}
|
||
|
||
return { valid: true }
|
||
}
|
||
|
||
/**
|
||
* 验证模板名称
|
||
* @param name - 模板名称
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateTemplateName(name: string): ValidationResult {
|
||
return validateNotEmpty(name, '模板名称')
|
||
}
|
||
|
||
/**
|
||
* 验证密码(可选)
|
||
* @param password - 密码
|
||
* @param minLength - 最小长度,默认 0(不验证长度)
|
||
* @returns 验证结果
|
||
*/
|
||
export function validatePassword(password: string, minLength: number = 0): ValidationResult {
|
||
if (!password) {
|
||
return {
|
||
valid: false,
|
||
error: '密码不能为空',
|
||
}
|
||
}
|
||
|
||
if (minLength > 0 && password.length < minLength) {
|
||
return {
|
||
valid: false,
|
||
error: `密码长度至少为 ${minLength} 个字符`,
|
||
}
|
||
}
|
||
|
||
return { valid: true }
|
||
}
|
||
|
||
/**
|
||
* 组合多个验证结果
|
||
* @param results - 验证结果数组
|
||
* @returns 第一个失败的验证结果,或成功结果
|
||
*/
|
||
export function combineValidationResults(...results: ValidationResult[]): ValidationResult {
|
||
for (const result of results) {
|
||
if (!result.valid) {
|
||
return result
|
||
}
|
||
}
|
||
return { valid: true }
|
||
}
|
||
|