@truenewx/tnxvue3
Version:
互联网技术解决方案:Vue3扩展支持
368 lines (359 loc) • 17.9 kB
text/typescript
/**
* 校验规则转换器,将服务端元数据中的校验规则转换为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;
}
}