UNPKG

amis

Version:

一种MIS页面生成工具

383 lines (361 loc) 12.8 kB
import {createObject} from './helper'; import {filter} from './tpl'; import {isPureVariable, resolveVariableAndFilter} from './tpl-builtin'; const isExisty = (value: any) => value !== null && value !== undefined; const isEmpty = (value: any) => value === ''; const makeRegexp = (reg: string | RegExp) => { if (reg instanceof RegExp) { return reg; } else if (/^(?:matchRegexp\:)?\/(.+)\/([gimuy]*)$/.test(reg)) { return new RegExp(RegExp.$1, RegExp.$2 || ''); } else if (typeof reg === 'string') { return new RegExp(reg); } return /^$/; }; export interface ValidateFn { ( values: {[propsName: string]: any}, value: any, arg1?: any, arg2?: any, arg3?: any ): boolean; } export const validations: { [propsName: string]: ValidateFn; } = { isRequired: function (values, value: any) { return ( value !== undefined && value !== '' && value !== null && (!Array.isArray(value) || !!value.length) ); }, isExisty: function (values, value) { return isExisty(value); }, matchRegexp: function (values, value, regexp) { return !isExisty(value) || isEmpty(value) || makeRegexp(regexp).test(value); }, isUndefined: function (values, value) { return value === undefined; }, isEmptyString: function (values, value) { return isEmpty(value); }, isEmail: function (values, value) { return validations.matchRegexp( values, value, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i ); }, isUrl: function (values, value) { return validations.matchRegexp( values, value, /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i ); }, isTrue: function (values, value) { return value === true; }, isFalse: function (values, value) { return value === false; }, isNumeric: function (values, value) { if (typeof value === 'number') { return true; } return validations.matchRegexp(values, value, /^[-+]?(?:\d*[.])?\d+$/); }, isAlpha: function (values, value) { return validations.matchRegexp(values, value, /^[A-Z]+$/i); }, isAlphanumeric: function (values, value) { return validations.matchRegexp(values, value, /^[0-9A-Z]+$/i); }, isInt: function (values, value) { return validations.matchRegexp(values, value, /^(?:[-+]?(?:0|[1-9]\d*))$/); }, isFloat: function (values, value) { return validations.matchRegexp( values, value, /^(?:[-+]?(?:\d+))?(?:\.\d*)?(?:[eE][\+\-]?(?:\d+))?$/ ); }, isWords: function (values, value) { return validations.matchRegexp(values, value, /^[A-Z\s]+$/i); }, isSpecialWords: function (values, value) { return validations.matchRegexp(values, value, /^[A-Z\s\u00C0-\u017F]+$/i); }, isLength: function (values, value, length) { return !isExisty(value) || isEmpty(value) || value.length === length; }, equals: function (values, value, eql) { return !isExisty(value) || isEmpty(value) || value == eql; }, equalsField: function (values, value, field) { return value == values[field]; }, maxLength: function (values, value, length) { // 此方法应该判断文本长度,如果传入数据为number,导致 maxLength 和 maximum 表现一致了,默认转成string if (typeof value === 'number') { value = String(value); } return !isExisty(value) || value.length <= length; }, minLength: function (values, value, length) { return !isExisty(value) || isEmpty(value) || value.length >= length; }, isUrlPath: function (values, value, regexp) { return !isExisty(value) || isEmpty(value) || /^[a-z0-9_\\-]+$/i.test(value); }, maximum: function (values, value, maximum) { return ( !isExisty(value) || isEmpty(value) || (parseFloat(value) || 0) <= (parseFloat(maximum) || 0) ); }, lt: function (values, value, maximum) { return ( !isExisty(value) || isEmpty(value) || (parseFloat(value) || 0) < (parseFloat(maximum) || 0) ); }, minimum: function (values, value, minimum) { return ( !isExisty(value) || isEmpty(value) || (parseFloat(value) || 0) >= (parseFloat(minimum) || 0) ); }, gt: function (values, value, minimum) { return ( !isExisty(value) || isEmpty(value) || (parseFloat(value) || 0) > (parseFloat(minimum) || 0) ); }, isJson: function (values, value, minimum) { if (isExisty(value) && !isEmpty(value) && typeof value === 'string') { try { JSON.parse(value); } catch (e) { return false; } } return true; }, isPhoneNumber: function (values, value) { return ( !isExisty(value) || isEmpty(value) || /^[1]([3-9])[0-9]{9}$/.test(value) ); }, isTelNumber: function (values, value) { return ( !isExisty(value) || isEmpty(value) || /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/.test(value) ); }, isZipcode: function (values, value) { return ( !isExisty(value) || isEmpty(value) || /^[1-9]{1}(\d+){5}$/.test(value) ); }, isId: function (values, value) { return ( !isExisty(value) || isEmpty(value) || /(^[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]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/.test( value ) ); }, notEmptyString: function (values, value) { return !isExisty(value) || !(String(value) && String(value).trim() === ''); }, matchRegexp1: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp2: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp3: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp4: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp5: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp6: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp7: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp8: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); }, matchRegexp9: function (values, value, regexp) { return validations.matchRegexp(values, value, regexp); } }; export function addRule( ruleName: string, fn: ValidateFn, message: string = '' ) { validations[ruleName] = fn; validateMessages[ruleName] = message; } export const validateMessages: { [propName: string]: string; } = { isEmail: 'validate.isEmail', isRequired: 'validate.isRequired', isUrl: 'validate.isUrl', isInt: 'validate.isInt', isAlpha: 'validate.isAlpha', isNumeric: 'validate.isNumeric', isAlphanumeric: 'validate.isAlphanumeric', isFloat: 'validate.isFloat', isWords: 'validate.isWords', isUrlPath: 'validate.isUrlPath', matchRegexp: 'validate.matchRegexp', minLength: 'validate.minLength', maxLength: 'validate.maxLength', maximum: 'validate.maximum', lt: 'validate.lt', minimum: 'validate.minimum', gt: 'validate.gt', isJson: 'validate.isJson', isLength: 'validate.isLength', notEmptyString: 'validate.notEmptyString', equalsField: 'validate.equalsField', equals: 'validate.equals', isPhoneNumber: 'validate.isPhoneNumber', isTelNumber: 'validate.isTelNumber', isZipcode: 'validate.isZipcode', isId: 'validate.isId' }; export function validate( value: any, values: {[propName: string]: any}, rules: {[propName: string]: any}, messages?: {[propName: string]: string}, __ = (str: string) => str ): Array<string> { const errors: Array<string> = []; rules && Object.keys(rules).forEach(ruleName => { if (!rules[ruleName] && rules[ruleName] !== 0) { return; } else if (typeof validations[ruleName] !== 'function') { throw new Error('Validation `' + ruleName + '` not exists!'); } const fn = validations[ruleName]; const args = (Array.isArray(rules[ruleName]) ? rules[ruleName] : [rules[ruleName]] ).map((item: any) => { if (typeof item === 'string' && isPureVariable(item)) { return resolveVariableAndFilter(item, values, '|raw'); } return item; }); if (!fn(values, value, ...args)) { errors.push( filter( __((messages && messages[ruleName]) || validateMessages[ruleName]), { ...[''].concat(args) } ) ); } }); return errors; } export function validateObject( values: {[propName: string]: any}, rules: {[propName: string]: any}, messages?: {[propName: string]: string}, __ = (str: string) => str ) { const ret: { [propName: string]: string[]; } = {}; Object.keys(rules).forEach(key => { const msgs = validate( values[key], values, rules[key] === true ? { isRequired: true } : rules[key], messages, __ ); if (msgs.length) { ret[key] = msgs; } }); return ret; } const splitValidations = function (str: string): Array<string> { let i = 0; const placeholder: {[propName: string]: string} = {}; return str .replace(/matchRegexp\d*\s*\:\s*\/.*?\/[igm]*/g, raw => { placeholder[`__${i}`] = raw; return `__${i++}`; }) .split(/,(?![^{\[]*[}\]])/g) .map(str => (/^__\d+$/.test(str) ? placeholder[str] : str.trim())); }; export function str2rules( validations: string | {[propName: string]: any} ): {[propName: string]: any} { if (typeof validations === 'string') { return validations ? splitValidations(validations).reduce(function ( validations: {[propName: string]: any}, validation ) { const idx = validation.indexOf(':'); let validateMethod = validation; let args = []; if (~idx) { validateMethod = validation.substring(0, idx); args = /^matchRegexp/.test(validateMethod) ? [validation.substring(idx + 1).trim()] : validation .substring(idx + 1) .split(',') .map(function (arg) { try { return JSON.parse(arg); } catch (e) { return arg; } }); } validations[validateMethod] = args.length ? args : true; return validations; }, {}) : {}; } return validations || {}; }