| var pattern = { | 
|     email: /^\S+?@\S+?\.\S+?$/, | 
|     idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, | 
|     url: new RegExp( | 
|         "^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", | 
|         'i') | 
| }; | 
|   | 
| const FORMAT_MAPPING = { | 
|     "int": 'integer', | 
|     "bool": 'boolean', | 
|     "double": 'number', | 
|     "long": 'number', | 
|     "password": 'string' | 
|     // "fileurls": 'array' | 
| } | 
|   | 
| function formatMessage(args, resources = '') { | 
|     var defaultMessage = ['label'] | 
|     defaultMessage.forEach((item) => { | 
|         if (args[item] === undefined) { | 
|             args[item] = '' | 
|         } | 
|     }) | 
|   | 
|     let str = resources | 
|     for (let key in args) { | 
|         let reg = new RegExp('{' + key + '}') | 
|         str = str.replace(reg, args[key]) | 
|     } | 
|     return str | 
| } | 
|   | 
| function isEmptyValue(value, type) { | 
|     if (value === undefined || value === null) { | 
|         return true; | 
|     } | 
|   | 
|     if (typeof value === 'string' && !value) { | 
|         return true; | 
|     } | 
|   | 
|     if (Array.isArray(value) && !value.length) { | 
|         return true; | 
|     } | 
|   | 
|     if (type === 'object' && !Object.keys(value).length) { | 
|         return true; | 
|     } | 
|   | 
|     return false; | 
| } | 
|   | 
| const types = { | 
|     integer(value) { | 
|         return types.number(value) && parseInt(value, 10) === value; | 
|     }, | 
|     string(value) { | 
|         return typeof value === 'string'; | 
|     }, | 
|     number(value) { | 
|         if (isNaN(value)) { | 
|             return false; | 
|         } | 
|         return typeof value === 'number'; | 
|     }, | 
|     "boolean": function(value) { | 
|         return typeof value === 'boolean'; | 
|     }, | 
|     "float": function(value) { | 
|         return types.number(value) && !types.integer(value); | 
|     }, | 
|     array(value) { | 
|         return Array.isArray(value); | 
|     }, | 
|     object(value) { | 
|         return typeof value === 'object' && !types.array(value); | 
|     }, | 
|     date(value) { | 
|         return value instanceof Date; | 
|     }, | 
|     timestamp(value) { | 
|         if (!this.integer(value) || Math.abs(value).toString().length > 16) { | 
|             return false | 
|         } | 
|         return true; | 
|     }, | 
|     file(value) { | 
|         return typeof value.url === 'string'; | 
|     }, | 
|     email(value) { | 
|         return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; | 
|     }, | 
|     url(value) { | 
|         return typeof value === 'string' && !!value.match(pattern.url); | 
|     }, | 
|     pattern(reg, value) { | 
|         try { | 
|             return new RegExp(reg).test(value); | 
|         } catch (e) { | 
|             return false; | 
|         } | 
|     }, | 
|     method(value) { | 
|         return typeof value === 'function'; | 
|     }, | 
|     idcard(value) { | 
|         return typeof value === 'string' && !!value.match(pattern.idcard); | 
|     }, | 
|     'url-https'(value) { | 
|         return this.url(value) && value.startsWith('https://'); | 
|     }, | 
|     'url-scheme'(value) { | 
|         return value.startsWith('://'); | 
|     }, | 
|     'url-web'(value) { | 
|         return false; | 
|     } | 
| } | 
|   | 
| class RuleValidator { | 
|   | 
|     constructor(message) { | 
|         this._message = message | 
|     } | 
|   | 
|     async validateRule(fieldKey, fieldValue, value, data, allData) { | 
|         var result = null | 
|   | 
|         let rules = fieldValue.rules | 
|   | 
|         let hasRequired = rules.findIndex((item) => { | 
|             return item.required | 
|         }) | 
|         if (hasRequired < 0) { | 
|             if (value === null || value === undefined) { | 
|                 return result | 
|             } | 
|             if (typeof value === 'string' && !value.length) { | 
|                 return result | 
|             } | 
|         } | 
|   | 
|         var message = this._message | 
|   | 
|         if (rules === undefined) { | 
|             return message['default'] | 
|         } | 
|   | 
|         for (var i = 0; i < rules.length; i++) { | 
|             let rule = rules[i] | 
|             let vt = this._getValidateType(rule) | 
|   | 
|             Object.assign(rule, { | 
|                 label: fieldValue.label || `["${fieldKey}"]` | 
|             }) | 
|   | 
|             if (RuleValidatorHelper[vt]) { | 
|                 result = RuleValidatorHelper[vt](rule, value, message) | 
|                 if (result != null) { | 
|                     break | 
|                 } | 
|             } | 
|   | 
|             if (rule.validateExpr) { | 
|                 let now = Date.now() | 
|                 let resultExpr = rule.validateExpr(value, allData, now) | 
|                 if (resultExpr === false) { | 
|                     result = this._getMessage(rule, rule.errorMessage || this._message['default']) | 
|                     break | 
|                 } | 
|             } | 
|   | 
|             if (rule.validateFunction) { | 
|                 result = await this.validateFunction(rule, value, data, allData, vt) | 
|                 if (result !== null) { | 
|                     break | 
|                 } | 
|             } | 
|         } | 
|   | 
|         if (result !== null) { | 
|             result = message.TAG + result | 
|         } | 
|   | 
|         return result | 
|     } | 
|   | 
|     async validateFunction(rule, value, data, allData, vt) { | 
|         let result = null | 
|         try { | 
|             let callbackMessage = null | 
|             const res = await rule.validateFunction(rule, value, allData || data, (message) => { | 
|                 callbackMessage = message | 
|             }) | 
|             if (callbackMessage || (typeof res === 'string' && res) || res === false) { | 
|                 result = this._getMessage(rule, callbackMessage || res, vt) | 
|             } | 
|         } catch (e) { | 
|             result = this._getMessage(rule, e.message, vt) | 
|         } | 
|         return result | 
|     } | 
|   | 
|     _getMessage(rule, message, vt) { | 
|         return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) | 
|     } | 
|   | 
|     _getValidateType(rule) { | 
|         var result = '' | 
|         if (rule.required) { | 
|             result = 'required' | 
|         } else if (rule.format) { | 
|             result = 'format' | 
|         } else if (rule.arrayType) { | 
|             result = 'arrayTypeFormat' | 
|         } else if (rule.range) { | 
|             result = 'range' | 
|         } else if (rule.maximum !== undefined || rule.minimum !== undefined) { | 
|             result = 'rangeNumber' | 
|         } else if (rule.maxLength !== undefined || rule.minLength !== undefined) { | 
|             result = 'rangeLength' | 
|         } else if (rule.pattern) { | 
|             result = 'pattern' | 
|         } else if (rule.validateFunction) { | 
|             result = 'validateFunction' | 
|         } | 
|         return result | 
|     } | 
| } | 
|   | 
| const RuleValidatorHelper = { | 
|     required(rule, value, message) { | 
|         if (rule.required && isEmptyValue(value, rule.format || typeof value)) { | 
|             return formatMessage(rule, rule.errorMessage || message.required); | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     range(rule, value, message) { | 
|         const { | 
|             range, | 
|             errorMessage | 
|         } = rule; | 
|   | 
|         let list = new Array(range.length); | 
|         for (let i = 0; i < range.length; i++) { | 
|             const item = range[i]; | 
|             if (types.object(item) && item.value !== undefined) { | 
|                 list[i] = item.value; | 
|             } else { | 
|                 list[i] = item; | 
|             } | 
|         } | 
|   | 
|         let result = false | 
|         if (Array.isArray(value)) { | 
|             result = (new Set(value.concat(list)).size === list.length); | 
|         } else { | 
|             if (list.indexOf(value) > -1) { | 
|                 result = true; | 
|             } | 
|         } | 
|   | 
|         if (!result) { | 
|             return formatMessage(rule, errorMessage || message['enum']); | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     rangeNumber(rule, value, message) { | 
|         if (!types.number(value)) { | 
|             return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); | 
|         } | 
|   | 
|         let { | 
|             minimum, | 
|             maximum, | 
|             exclusiveMinimum, | 
|             exclusiveMaximum | 
|         } = rule; | 
|         let min = exclusiveMinimum ? value <= minimum : value < minimum; | 
|         let max = exclusiveMaximum ? value >= maximum : value > maximum; | 
|   | 
|         if (minimum !== undefined && min) { | 
|             return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? | 
|                 'exclusiveMinimum' : 'minimum' | 
|             ]) | 
|         } else if (maximum !== undefined && max) { | 
|             return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? | 
|                 'exclusiveMaximum' : 'maximum' | 
|             ]) | 
|         } else if (minimum !== undefined && maximum !== undefined && (min || max)) { | 
|             return formatMessage(rule, rule.errorMessage || message['number'].range) | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     rangeLength(rule, value, message) { | 
|         if (!types.string(value) && !types.array(value)) { | 
|             return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); | 
|         } | 
|   | 
|         let min = rule.minLength; | 
|         let max = rule.maxLength; | 
|         let val = value.length; | 
|   | 
|         if (min !== undefined && val < min) { | 
|             return formatMessage(rule, rule.errorMessage || message['length'].minLength) | 
|         } else if (max !== undefined && val > max) { | 
|             return formatMessage(rule, rule.errorMessage || message['length'].maxLength) | 
|         } else if (min !== undefined && max !== undefined && (val < min || val > max)) { | 
|             return formatMessage(rule, rule.errorMessage || message['length'].range) | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     pattern(rule, value, message) { | 
|         if (!types['pattern'](rule.pattern, value)) { | 
|             return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     format(rule, value, message) { | 
|         var customTypes = Object.keys(types); | 
|         var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); | 
|   | 
|         if (customTypes.indexOf(format) > -1) { | 
|             if (!types[format](value)) { | 
|                 return formatMessage(rule, rule.errorMessage || message.typeError); | 
|             } | 
|         } | 
|   | 
|         return null | 
|     }, | 
|   | 
|     arrayTypeFormat(rule, value, message) { | 
|         if (!Array.isArray(value)) { | 
|             return formatMessage(rule, rule.errorMessage || message.typeError); | 
|         } | 
|   | 
|         for (let i = 0; i < value.length; i++) { | 
|             const element = value[i]; | 
|             let formatResult = this.format(rule, element, message) | 
|             if (formatResult !== null) { | 
|                 return formatResult | 
|             } | 
|         } | 
|   | 
|         return null | 
|     } | 
| } | 
|   | 
| class SchemaValidator extends RuleValidator { | 
|   | 
|     constructor(schema, options) { | 
|         super(SchemaValidator.message); | 
|   | 
|         this._schema = schema | 
|         this._options = options || null | 
|     } | 
|   | 
|     updateSchema(schema) { | 
|         this._schema = schema | 
|     } | 
|   | 
|     async validate(data, allData) { | 
|         let result = this._checkFieldInSchema(data) | 
|         if (!result) { | 
|             result = await this.invokeValidate(data, false, allData) | 
|         } | 
|         return result.length ? result[0] : null | 
|     } | 
|   | 
|     async validateAll(data, allData) { | 
|         let result = this._checkFieldInSchema(data) | 
|         if (!result) { | 
|             result = await this.invokeValidate(data, true, allData) | 
|         } | 
|         return result | 
|     } | 
|   | 
|     async validateUpdate(data, allData) { | 
|         let result = this._checkFieldInSchema(data) | 
|         if (!result) { | 
|             result = await this.invokeValidateUpdate(data, false, allData) | 
|         } | 
|         return result.length ? result[0] : null | 
|     } | 
|   | 
|     async invokeValidate(data, all, allData) { | 
|         let result = [] | 
|         let schema = this._schema | 
|         for (let key in schema) { | 
|             let value = schema[key] | 
|             let errorMessage = await this.validateRule(key, value, data[key], data, allData) | 
|             if (errorMessage != null) { | 
|                 result.push({ | 
|                     key, | 
|                     errorMessage | 
|                 }) | 
|                 if (!all) break | 
|             } | 
|         } | 
|         return result | 
|     } | 
|   | 
|     async invokeValidateUpdate(data, all, allData) { | 
|         let result = [] | 
|         for (let key in data) { | 
|             let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) | 
|             if (errorMessage != null) { | 
|                 result.push({ | 
|                     key, | 
|                     errorMessage | 
|                 }) | 
|                 if (!all) break | 
|             } | 
|         } | 
|         return result | 
|     } | 
|   | 
|     _checkFieldInSchema(data) { | 
|         var keys = Object.keys(data) | 
|         var keys2 = Object.keys(this._schema) | 
|         if (new Set(keys.concat(keys2)).size === keys2.length) { | 
|             return '' | 
|         } | 
|   | 
|         var noExistFields = keys.filter((key) => { | 
|             return keys2.indexOf(key) < 0; | 
|         }) | 
|         var errorMessage = formatMessage({ | 
|             field: JSON.stringify(noExistFields) | 
|         }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) | 
|         return [{ | 
|             key: 'invalid', | 
|             errorMessage | 
|         }] | 
|     } | 
| } | 
|   | 
| function Message() { | 
|     return { | 
|         TAG: "", | 
|         default: '验证错误', | 
|         defaultInvalid: '提交的字段{field}在数据库中并不存在', | 
|         validateFunction: '验证无效', | 
|         required: '{label}必填', | 
|         'enum': '{label}超出范围', | 
|         timestamp: '{label}格式无效', | 
|         whitespace: '{label}不能为空', | 
|         typeError: '{label}类型无效', | 
|         date: { | 
|             format: '{label}日期{value}格式无效', | 
|             parse: '{label}日期无法解析,{value}无效', | 
|             invalid: '{label}日期{value}无效' | 
|         }, | 
|         length: { | 
|             minLength: '{label}长度不能少于{minLength}', | 
|             maxLength: '{label}长度不能超过{maxLength}', | 
|             range: '{label}必须介于{minLength}和{maxLength}之间' | 
|         }, | 
|         number: { | 
|             minimum: '{label}不能小于{minimum}', | 
|             maximum: '{label}不能大于{maximum}', | 
|             exclusiveMinimum: '{label}不能小于等于{minimum}', | 
|             exclusiveMaximum: '{label}不能大于等于{maximum}', | 
|             range: '{label}必须介于{minimum}and{maximum}之间' | 
|         }, | 
|         pattern: { | 
|             mismatch: '{label}格式不匹配' | 
|         } | 
|     }; | 
| } | 
|   | 
|   | 
| SchemaValidator.message = new Message(); | 
|   | 
| export default SchemaValidator |