import Schema from 'async-validator'; import { IParam } from '../interface'; import { isEmpty, isExistAndNotEmpty } from './util'; /** * 校验对象 * * @export * @class VerifyUtil */ export class VerifyUtil { /** * 错误提示信息 * * @static * @type {string} * @memberof VerifyUtil */ public static errorInfo = ''; /** * 值比较 * * @static * @param {*} value * @param {*} value2 * @returns {number} * @memberof VerifyUtil */ public static compare(value: any, value2: any): number { let result: any; if ( typeof value === 'boolean' && typeof value2 === 'boolean' ) { result = this.compareBoolean(value, value2); } else if ( value != null && value !== '' && value2 != null && value2 !== '' && !isNaN(value.toString()) && !isNaN(value2.toString()) ) { result = this.compareNumber(parseFloat(value), parseFloat(value2)); } else if ( this.isParseDate(value) && this.isParseDate(value2) ) { result = this.compareDate( new Date(value).getTime(), new Date(value2).getTime() ); } else if ( value && value2 && typeof value === 'string' && typeof value2 === 'string' ) { result = this.compareString(value, value2); } return result; } /** * 字符串比较 * * @static * @param {*} value * @param {*} value2 * @returns {number} * @memberof VerifyUtil */ public static compareString(value: string, value2: string): number { return value.localeCompare(value2); } /** * boolean 值比较 * * @static * @param {*} value * @param {*} value2 * @returns {number} * @memberof VerifyUtil */ public static compareBoolean(value: any, value2: any): number { if (value === value2) { return 0; } else { return -1; } } /** * 时间值比较(毫秒数) * * @static * @param {number} value * @param {number} value2 * @returns {number} * @memberof VerifyUtil */ public static compareDate(value: number, value2: number): number { if (value > value2) { return 1; } else if (value < value2) { return -1; } else { return 0; } } /** * 是否是时间 * * @static * @param {string} value * @returns {boolean} * @memberof VerifyUtil */ public static isParseDate(value: string): boolean { if (!isNaN(Date.parse(value))) { return true; } return false; } /** * 数值比较 * * @static * @param {number} value * @param {number} value2 * @returns {number} * @memberof VerifyUtil */ public static compareNumber(value: number, value2: number): number { if (isNaN(value)) { value = 0; } if (isNaN(value2)) { value2 = 0; } if (value > value2) { return 1; } else if (value < value2) { return -1; } else { return 0; } } /** * 文本包含 * * @static * @param {*} value * @param {*} value2 * @returns {boolean} * @memberof VerifyUtil */ public static contains(value: any, value2: string): boolean { if (value && value2) { // 定义一数组 let arr = []; arr = value2.split(','); // 定义正则表达式的连接符 const S = String.fromCharCode(2); const reg = new RegExp(S + value + S); return reg.test(S + arr.join(S) + S); } return false; } /** * 比较值 * * @static * @param {*} value * @param {*} op * @param {*} value2 * @returns {boolean} * @memberof VerifyUtil */ public static testCond(value: any, op: any, value2: any): boolean { // 等于操作 if (Object.is(op, 'EQ')) { return value == value2; } // 不等于操作 if (Object.is(op, 'NOTEQ')) { return value != value2; } if ( typeof value !== 'boolean' && typeof value2 !== 'boolean' ) { // 大于操作 if (Object.is(op, 'GT')) { const result: number = this.compare(value, value2); if (result !== undefined && result > 0) { return true; } else { return false; } } // 大于等于操作 if (Object.is(op, 'GTANDEQ')) { const result: number = this.compare(value, value2); if (result !== undefined && result >= 0) { return true; } else { return false; } } // 小于操作 if (Object.is(op, 'LT')) { const result: number = this.compare(value, value2); if (result !== undefined && result < 0) { return true; } else { return false; } } // 小于等于操作 if (Object.is(op, 'LTANDEQ')) { const result: number = this.compare(value, value2); if (result !== undefined && result <= 0) { return true; } else { return false; } } } if ((typeof value2 === 'string')) { // 值包含在给定的范围中 if (Object.is(op, 'IN')) { return this.contains(value, value2); } // 值不包含在给定的范围中 if (Object.is(op, 'NOTIN')) { return !this.contains(value, value2); } } if ( value && (typeof value === 'string') && value2 != null && value2 !== '' ) { // 文本包含 if (Object.is(op, 'LIKE')) { return (value.toUpperCase().indexOf(value2.toString().toUpperCase()) !== -1); } // 文本左包含 if (Object.is(op, 'LEFTLIKE')) { return (value.toUpperCase().indexOf(value2.toString().toUpperCase()) === 0); } // 文本右包含 if (Object.is(op, 'RIGHTLIKE')) { const nPos = value.toUpperCase().indexOf(value2.toString().toUpperCase()); if (nPos === -1) { return false; } return nPos + value2.toString().length === value.length; } } // 不为空判断操作 if (Object.is(op, 'ISNOTNULL')) { return value != null && value !== ''; } // 为空判断操作 if (Object.is(op, 'ISNULL')) { return value == null || value === ''; } // 空判断 if (Object.is(op, 'TESTNULL')) { } // 自定义包含 if (Object.is(op, 'USERLIKE')) { } return false; } /** * 检查属性常规条件 * * @static * @param {*} value 属性值 * @param {string} op 检测条件 * @param {*} value2 预定义值 * @param {string} errorInfo 错误信息 * @param {string} paramType 参数类型 * @param {*} form 表单对象 * @param {boolean} primaryModel 是否必须条件 * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldSimpleRule( value: any, op: string, value2: any, errorInfo: string, paramType: string, form: any, primaryModel: boolean ): boolean { if (Object.is(paramType, 'CURTIME')) { value2 = `${new Date()}`; } if (Object.is(paramType, 'ENTITYFIELD')) { value2 = value2 ? value2.toLowerCase() : ''; const _value2Field = form[value2] ? form[value2] : value2; value2 = _value2Field; } if (isEmpty(errorInfo)) { errorInfo = App.ts("app.util.errorvalue","内容必须符合值规则"); } this.errorInfo = errorInfo; const result = this.testCond(value, op, value2); if (!result) { if (primaryModel) { // throw new Error(this.errorInfo); } } return !result; } /** * 检查属性字符长度规则 * * @static * @param {*} viewValue * @param {any} minLength * @param {boolean} indexOfMin * @param {any} maxLength * @param {boolean} indexOfMax * @param {string} errorInfo * @param {boolean} primaryModel * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldStringLengthRule( viewValue: string, minLength: any, indexOfMin: boolean, maxLength: any, indexOfMax: boolean, errorInfo: string, primaryModel: boolean ): boolean { if (isEmpty(errorInfo)) { this.errorInfo = App.ts("app.util.errorlength","内容长度必须符合范围规则"); } else { this.errorInfo = errorInfo; } const _isEmpty = isEmpty(viewValue); if (_isEmpty) { if (primaryModel) { throw new Error('值为空'); } this.errorInfo = App.ts("app.util.errorempty","值为空"); return true; } const viewValueLength: number = viewValue.length; // 小于等于 if (minLength !== null) { if (indexOfMin) { if (viewValueLength < minLength) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } else { if (viewValueLength <= minLength) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } } // 大于等于 if (maxLength !== null) { if (indexOfMax) { if (viewValueLength > maxLength) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } else { if (viewValueLength >= maxLength) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } } this.errorInfo = ''; return false; } /** * 检查属性值正则式规则 * * @static * @param {string} viewValue 属性值 * @param {*} strReg 验证正则 * @param {string} errorInfo 错误信息 * @param {boolean} primaryModel 是否关键条件 * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldRegExRule( viewValue: string, strReg: any, errorInfo: string, primaryModel: boolean ): boolean { if (isEmpty(errorInfo)) { this.errorInfo = App.ts("app.util.errorregular","值必须符合正则规则"); } else { this.errorInfo = errorInfo; } const _isEmpty = isEmpty(viewValue); if (_isEmpty) { if (primaryModel) { throw new Error('值为空'); } this.errorInfo = App.ts("app.util.errorempty","值为空"); return true; } const regExp = new RegExp(strReg); if (!regExp.test(viewValue)) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } this.errorInfo = ''; return false; } /** * 检查脚本值规则 * * @static * @param {string} value 属性值 * @param {*} data 数据对象 * @param {*} scriptCode 脚本内容 * @param {string} errorInfo 错误信息 * @param {boolean} primaryModel 是否关键条件 * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldScriptRule( value: string, data: any, scriptCode: any, errorInfo: string, primaryModel: boolean ): { isPast: boolean; infoMessage: string } { if (isEmpty(errorInfo)) { this.errorInfo = App.ts("app.util.errorscriptcode","值必须符合脚本规则"); } else { this.errorInfo = errorInfo; } // 脚本准备参数 const source = data; let selfError = ''; let resultBoolean = true; // 脚本回调 const callback = (error: any) => { resultBoolean = false; // 脚本回调多个错误信息 if (error?.length > 0) { error.forEach((item: any) => { if (item?.message) { selfError += item.message; } }); // 脚本回调单个错误信息 } else if (error?.message) { selfError = error.message; } }; try { // 避免脚本内变量冲突 const runScript = () => eval(scriptCode); runScript(); } catch (error) { console.error(error); } this.errorInfo = ''; if (!resultBoolean && primaryModel) { throw new Error(this.errorInfo); } return { isPast: resultBoolean, infoMessage: selfError || errorInfo }; } /** * 检查属性值范围规则 * * @static * @param {string} viewValue 属性值 * @param {*} minNumber 最小数值 * @param {boolean} indexOfMin 是否包含最小数值 * @param {*} maxNumber 最大数值 * @param {boolean} indexOfMax 是否包含最大数值 * @param {string} errorInfo 错误信息 * @param {boolean} primaryModel 是否关键条件 * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldValueRangeRule( viewValue: string, minNumber: any, indexOfMin: boolean, maxNumber: any, indexOfMax: boolean, errorInfo: string, primaryModel: boolean ): boolean { if (isEmpty(errorInfo)) { this.errorInfo = App.ts("app.util.errorvaluerange","值必须符合值范围规则"); } else { this.errorInfo = errorInfo; } const _isEmpty = isEmpty(viewValue); if (_isEmpty) { if (primaryModel) { throw new Error('值为空'); } this.errorInfo = App.ts("app.util.errorempty","值为空"); return true; } const valueFormat = this.checkFieldRegExRule( viewValue, /^-?\d*\.?\d+$/, '', primaryModel ); if (valueFormat) { return true; } else { this.errorInfo = errorInfo; } const data = Number.parseFloat(viewValue); // 小于等于 if (minNumber !== null) { if (indexOfMin) { if (data < minNumber) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } else { if (data <= minNumber) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } } // //大于等于 if (maxNumber != null) { if (indexOfMax) { if (data > maxNumber) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } else { if (data >= maxNumber) { if (primaryModel) { throw new Error(this.errorInfo); } return true; } } } this.errorInfo = ''; return false; } /** * 检查属性值系统值范围规则 暂时支持正则表达式 * * @static * @param {string} viewValue 属性值 * @param {*} strReg 正则 * @param {string} errorInfo 错误信息 * @param {boolean} primaryModel 是否关键条件 * @returns {boolean} * @memberof VerifyUtil */ public static checkFieldSysValueRule( viewValue: string, strReg: any, errorInfo: string, primaryModel: boolean ): boolean { return this.checkFieldRegExRule(viewValue, strReg, errorInfo, primaryModel); } /** * 遍历数据并进行逻辑判断,支持&&和||,支持短路 * * @param {any[]} array 数组 * @param {Function} callback 回调函数 * @param {string} [operateTag='AND'] 与或操作标识,支持AND、OR * @param {boolean} [isReverse=false] 是否取反 * @returns {boolean} * @memberof VerifyUtil */ public static logicForEach( array: any[], callback: (item: any, index: number) => boolean, operateTag = 'AND', isReverse = false ): boolean { if (!(array?.length > 0)) { return false; } let result: boolean = operateTag == 'AND'; for (let i = 0, len = array.length; i < len; i++) { const temp = callback(array[i], i); if (operateTag == 'AND') { if (!temp) { result = false; break; } } else if (operateTag == 'OR') { if (temp) { result = true; break; } } } return isReverse ? !result : result; } /** * 校验属性值规则 * * @param {string} name 校验属性值所在字段的名称 * @param {*} data 数据对象 * @param {*} condition 规则条件 * @returns {{ isPast: boolean, infoMessage: string }} * @memberof VerifyUtil */ public static verifyDeRules( name: string, data: any, condition: any ): { isPast: boolean; infoMessage: string } { const flag = { isPast: true, infoMessage: condition.ruleInfo }; if (condition.condType == 'GROUP') { const childRules = condition.conditions; if (childRules?.length > 0) { flag.isPast = this.logicForEach( childRules, (item: any) => { const { isPast, infoMessage } = this.verifyDeRules( name, data, item ); // 每次都把分组的结果信息改为该条件的信息,短路后是最后一个条件的信息 flag.infoMessage = infoMessage; return isPast; }, condition.condOp, !!condition.notMode ); // 分组结果为false时,如果是AND分组且取反,或是OR分组未取反,提示分组信息 if ( !flag.isPast && ((condition.condOp == 'AND' && condition.notMode) || (condition.condOp == 'OR' && !condition.notMode)) ) { flag.infoMessage = condition.ruleInfo; } } } else { try { // 常规规则 if (condition.condType == 'SIMPLE') { flag.isPast = !this.checkFieldSimpleRule( data[name], condition.condOp, condition.paramValue, condition.ruleInfo, condition.paramType, data, condition.keyCond ); // 数值范围 } else if (condition.condType == 'VALUERANGE2') { flag.isPast = !this.checkFieldValueRangeRule( data[name], condition.minValue, condition.includeMinValue, condition.maxValue, condition.includeMaxValue, condition.ruleInfo, condition.keyCond ); // 正则式 } else if (condition.condType == 'REGEX') { flag.isPast = !this.checkFieldRegExRule( data[name], condition.regExCode, condition.ruleInfo, condition.keyCond ); // 长度 } else if (condition.condType == 'STRINGLENGTH') { flag.isPast = !this.checkFieldStringLengthRule( data[name], condition.minValue, condition.includeMinValue, condition.maxValue, condition.includeMaxValue, condition.ruleInfo, condition.keyCond ); // 系统值规则 } else if ( condition.condType == 'SYSVALUERULE' && condition.sysValueRule ) { const { ruleType, regExCode, scriptCode, ruleInfo } = condition.sysValueRule; flag.infoMessage = condition.ruleInfo || ruleInfo; if (ruleType == 'REG') { flag.isPast = !this.checkFieldRegExRule( data[name], regExCode, flag.infoMessage, condition.keyCond ); } else if (ruleType == 'SCRIPT') { const { isPast, infoMessage } = this.checkFieldScriptRule( data[name], data, scriptCode, flag.infoMessage, condition.keyCond ); flag.isPast = isPast; flag.infoMessage = infoMessage || flag.infoMessage; } } } catch (error) { flag.isPast = false; } // 取反 flag.isPast = condition.notMode ? !flag.isPast : flag.isPast; } return flag; } /** * 构建校验条件 * * @param {*} model 模型数据 * * @memberof VerifyUtil */ public static buildVerConditions(model: any): IParam[] { if (!model) { return []; } const isNumber: boolean = model?.editorType === 'NUMBER' ? true : false; const rules: Array = []; // 构建值的type,最大值,最小值,最大长度,最小长度 if (isNumber) { if (model.maxValue) { rules.push({ type: 'number', max: model.maxValue, message: `${App.ts("app.util.maxvalue","内容最大值必须为")}${model.maxValue}`, }); } if (model.minValue) { rules.push({ type: 'number', min: model.minValue, message: `${App.ts("app.util.minvalue","内容最小值必须为")}${model.minValue}`, }); } } else { if (model.maxLength) { rules.push({ validator: (rule: any, value: any, callback: any) => { const length = isExistAndNotEmpty(value) ? value.length : 0; if (length > model.maxLength) { return Promise.reject( `${App.ts("app.util.maxlength","内容最大长度必须为")}${model.maxLength},${App.ts("app.util.currentlength","当前长度为")}${value.length}` ); } return Promise.resolve(); }, }); } if (model.minLength) { rules.push({ type: 'string', min: model.minLength, message: `${App.ts("app.util.maxlength","内容最小长度必须为")}${model.minLength}`, }); } } // 构建正则 if ( model.getPSSysValueRule && model.getPSSysValueRule.ruleType && Object.is(model.getPSSysValueRule.ruleType, 'REG') ) { rules.push({ type: isNumber ? 'number' : 'string', pattern: model.getPSSysValueRule.regExCode, message: `${model.getPSSysValueRule.ruleInfo}`, }); } // 精度 if (model.precision) { const validateData = (rule: any, value: any, callback: Function) => { let errorMessage; if (value && value.toString()) { const tempValue: string = value.toString(); if (tempValue.indexOf('.') !== -1) { const len: number = tempValue.substring( tempValue.indexOf('.') + 1 ).length; if (len !== model.precision) { errorMessage = `${App.ts("app.util.valueprecision","内容精度必须为")}${model.precision}`; } } else { if (model.precision !== 0) { errorMessage = `${App.ts("app.util.valueprecision","内容精度必须为")}${model.precision}`; } } } if (errorMessage) { return Promise.reject(errorMessage); } return Promise.resolve(); }; rules.push(validateData); } return rules; } /** * 校验项规则 * * @static * @param {string} property * @param {*} data * @param {*} rules * @return {*} * @memberof VerifyUtil */ public static validateItem(property: string, data: any, rules: any) { // 1.获取数值和规则 const rule = rules[property]; // 2.创建校验规则 const schema = new Schema({ [property]: rule }); // 校验返回Promise return schema.validate(data); } }