UNPKG

@truenewx/tnxvue3

Version:

互联网技术解决方案:Vue3扩展支持

368 lines (359 loc) 17.9 kB
/** * 校验规则转换器,将服务端元数据中的校验规则转换为validator组件的规则。 * validator组件详见:https://github.com/validatorjs/validator.js */ import validator from 'validator'; import {Validator} from '../../../tnxcore/src/tnxcore.ts'; import {ApiModelPropertyMeta, ApiModelMeta} from '../../../tnxcore/src/api/meta.ts'; export type ValidateResultType = 'error' | 'warning' | 'success'; export type ValidateResult = { result: boolean; message?: string; type?: ValidateResultType; } export type ValidatorRule = { name?: string; required?: boolean; message?: string; type?: ValidateResultType; trigger?: 'blur' | 'change'; validator?: (fieldValue: string) => ValidateResult; } export default class TnxTdValidator extends Validator { getRule(validationName: string, validationValue: any, fieldMeta?: ApiModelPropertyMeta): ValidatorRule | undefined { let rule: ValidatorRule; let fieldCaption = ''; // 据目前观察,字段格式校验的错误消息均显示在字段旁,无需显示字段名称,未来如果出现不在字段旁显示的场景,再考虑扩展 // if (fieldMeta && fieldMeta.caption) { // fieldCaption = fieldMeta.caption; // } switch (validationName) { case 'required': case 'notNull': case 'notEmpty': case 'notBlank': if (validationValue === true) { rule = { required: true, validator(fieldValue: string): ValidateResult { if (validationValue) { let blank = fieldValue === undefined || fieldValue === null; if (!blank && typeof fieldValue === 'string') { blank = fieldValue.trim().length === 0; } if (blank) { let message = this.getErrorMessage(validationName, fieldCaption); return {result: false, message, type: 'error'}; } } return {result: true}; } } } break; case 'minLength': rule = { validator(fieldValue: string): ValidateResult { if (typeof validationValue === 'number' && typeof fieldValue === 'string') { let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length; let fieldLength = fieldValue.length + enterLength; if (fieldLength < validationValue) { let message = this.getErrorMessage(validationName, fieldCaption, validationValue, validationValue - fieldLength); return {result: false, message, type: 'error'}; } } return {result: true}; } }; break; case 'maxLength': rule = { validator(fieldValue: string): ValidateResult { if (typeof validationValue === 'number' && typeof fieldValue === 'string') { let enterLength = fieldValue.indexOf('\n') < 0 ? 0 : (fieldValue.match(/\n/g) as any).length; let fieldLength = fieldValue.length + enterLength; if (fieldLength > validationValue) { let message = this.getErrorMessage(validationName, fieldCaption, validationValue, fieldLength - validationValue); return {result: false, message, type: 'error'}; } } return {result: true}; } }; break; case 'number': if (validationValue === true) { rule = { validator(fieldValue: string): ValidateResult { if (fieldValue && typeof fieldValue === 'string') { if (!validator.isNumeric(fieldValue, {no_symbols: true})) { let message = this.getErrorMessage(validationName, fieldCaption); return {result: false, message, type: 'error'}; } } return {result: true}; } }; } break; case 'notContainsHtmlChars': if (validationValue === true) { rule = { validator(fieldValue: string): ValidateResult { if (typeof fieldValue === 'string') { let limitedValues = ['<', '>', "'", '"', '\\']; for (let i = 0; i < limitedValues.length; i++) { if (fieldValue.indexOf(limitedValues[i]) >= 0) { let s = limitedValues.join(' '); let message = this.getErrorMessage('notContains', fieldCaption, s); return {result: false, message, type: 'error'}; } } } return {result: true}; } }; } break; case 'notContainsIllegalFilenameChars': if (validationValue === true) { rule = { validator(fieldValue: string): ValidateResult { if (typeof fieldValue === 'string') { let limitedValues = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']; for (let i = 0; i < limitedValues.length; i++) { if (fieldValue.indexOf(limitedValues[i]) >= 0) { let s = limitedValues.join(' '); let message = this.getErrorMessage('notContains', fieldCaption, s); return {result: false, message, type: 'error'}; } } } return {result: true}; } }; } break; case 'notContains': if (validationValue) { rule = { validator(fieldValue: string): ValidateResult { if (typeof fieldValue === 'string') { let limitedValues = (validationValue as string).split(' '); for (let i = 0; i < limitedValues.length; i++) { if (validator.contains(fieldValue, limitedValues[i])) { let message = this.getErrorMessage('notContains', fieldCaption, validationValue); return {result: false, message, type: 'error'}; } } } return {result: true}; } }; } break; case 'rejectHtmlTags': if (validationValue === true) { rule = { validator(fieldValue: string): ValidateResult { if (fieldValue) { if (/<[a-z]+[ ]*[/]?[ ]*>/gi.test(fieldValue)) { let message = this.getErrorMessage(validationName, fieldCaption); return {result: false, message, type: 'error'}; } } return {result: true}; } }; } break; case 'allowedHtmlTags': if (validationValue) { rule = { validator(fieldValue: string): ValidateResult { if (typeof fieldValue === 'string') { let tags = (validationValue as string).toLowerCase().split(','); if (tags.length) { let value = fieldValue.toLowerCase(); let leftIndex = value.indexOf('<'); let rightIndex = leftIndex >= 0 ? value.indexOf('>', leftIndex) : -1; while (leftIndex >= 0 && rightIndex >= 0) { let sub = value.substring(leftIndex + 1, rightIndex); let spaceIndex = sub.indexOf(' '); let tag = spaceIndex >= 0 ? sub.substring(0, spaceIndex) : sub; if (tag.startsWith('/')) { tag = tag.substring(1); } if (!tags.contains(tag.toLowerCase())) { let message = this.getErrorMessage(validationName, fieldCaption, tags.join(', ')); return {result: false, message, type: 'error'}; } leftIndex = value.indexOf('<', rightIndex); rightIndex = leftIndex >= 0 ? value.indexOf('>', leftIndex) : -1; } } } return {result: true}; } }; } break; case 'forbiddenHtmlTags': if (validationValue) { rule = { validator(fieldValue: string): ValidateResult { if (fieldValue) { let tags = (validationValue as string).toLowerCase().split(','); if (tags.length) { let value = fieldValue.toLowerCase(); for (let tag of tags) { if (value.includes('<' + tag + '>') || value.includes('<' + tag + ' ')) { let message = this.getErrorMessage(validationName, fieldCaption, tags.join(', ')); return {result: false, message, type: 'error'}; } } } } return {result: true}; } }; } break; case 'email': if (validationValue === true) { rule = { validator(fieldValue: string): ValidateResult { if (typeof fieldValue === 'string' && fieldValue) { if (!validator.isEmail(fieldValue)) { let message = this.getErrorMessage(validationName, fieldCaption); return {result: false, message, type: 'error'}; } } return {result: true}; } } } break; case 'cellphone': case 'idCardNo': case 'url': case 'opposableUrl': rule = { validator(fieldValue: string): ValidateResult { if (validationValue) { let message = this.validateRegExp(validationName, fieldValue, fieldCaption); if (message) { return {result: false, message, type: 'error'}; } } return {result: true}; } }; break; case 'regex': rule = { validator(fieldValue: string): ValidateResult { if (fieldValue) { let pattern = (validationValue as any).pattern; if (!pattern.startsWith('^')) { pattern = '^' + pattern; } if (!pattern.endsWith('$')) { pattern += '$'; } let regexp = new RegExp(pattern, 'gi'); if (!regexp.test(fieldValue)) { let message = (validationValue as any).message; if (message) { message = fieldCaption + message; } else { message = this.getErrorMessage('regex', fieldCaption, ''); } return {result: false, message, type: 'error'}; } } return {result: true}; } } break; case 'custom': if (typeof validationValue === 'function') { rule = { validator(fieldValue: string): ValidateResult { let message = validationValue(fieldValue); if (message) { return {result: false, message, type: 'error'}; } return {result: true}; } } } break; } if (rule) { rule.name = validationName; let metaType = 'text'; if (fieldMeta && fieldMeta.type) { metaType = fieldMeta.type.toLowerCase(); } let optional = metaType === 'option' || metaType === 'datetime' || metaType === 'date' || metaType === 'time'; rule.trigger = optional ? 'change' : 'blur'; } return rule; } getRules(meta: ApiModelMeta): Record<string, ValidatorRule[]> { let rules: Record<string, ValidatorRule[]> = {}; Object.keys(meta).forEach(fieldName => { let fieldMeta = meta[fieldName]; if (fieldMeta.validation) { let fieldRules: ValidatorRule[] = []; Object.keys(fieldMeta.validation).forEach(validationName => { let validationValue = fieldMeta.validation[validationName]; let rule = this.getRule(validationName, validationValue, fieldMeta); if (rule) { fieldRules.push(rule); } }); // 将可能包含的引用字段路径中的.替换为__,以符合规则名称规范 let ruleName = fieldName.replace('.', '__'); rules[ruleName] = fieldRules; } }); return rules; } /** * 用指定规则集校验指定模型中的所有字符串值字段 * @return 校验结果错误消息集映射,key-字段名,value-错误消息集,校验通过的不包含在内 */ validate(rules: Record<string, ValidatorRule[]>, model: Record<string, any>): Record<string, string[]> { let result: Record<string, string[]> = {}; if (rules && model) { Object.keys(rules).forEach(fieldName => { const fieldRules = rules[fieldName]; if (Array.isArray(fieldRules) && fieldRules.length) { const messages: string[] = []; const fieldValue = model[fieldName]; if (typeof fieldValue === 'string') { for (let rule of fieldRules) { if (typeof rule.validator === 'function') { const r = rule.validator(fieldValue); if (r && r.result !== true && r.message) { messages.push(r.message); } } } if (messages.length > 0) { result[fieldName] = messages; } } } }); } return result; } }