gtht-miniapp-sdk
Version:
gtht-miniapp-sdk 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库
270 lines (269 loc) • 8.62 kB
JavaScript
import { chainGet, isBoolean, isEmptyValue, isFunction, isNumber, isString, toArray, } from '../../utils';
import getUrlRegexp from './getUrlRegexp';
function getMessage(message) {
return isFunction(message) ? message() : message;
}
function handleRange(len, type, rule) {
if (isNumber(rule.len)) {
return len !== rule.len ? `${type}.len` : true;
}
else if (isNumber(rule.min) && isNumber(rule.max)) {
return len < rule.min || len > rule.max ? `${type}.range` : true;
}
else if (isNumber(rule.min)) {
return len < rule.min ? `${type}.min` : true;
}
else if (isNumber(rule.max)) {
return len > rule.max ? `${type}.max` : true;
}
else {
return true;
}
}
const typeStrategies = {
string(value, rule) {
if (isString(value)) {
return handleRange(value.length, 'string', rule);
}
else {
return false;
}
},
number(value, rule) {
if (isNumber(value)) {
return handleRange(value, 'number', rule);
}
else {
return false;
}
},
integer(value, rule) {
if (Number.isInteger(value)) {
return handleRange(value, 'number', rule);
}
else {
return false;
}
},
float(value, rule) {
if (Number.isFinite(value) && value % 1 !== 0) {
return handleRange(value, 'number', rule);
}
else {
return false;
}
},
boolean(value) {
return isBoolean(value);
},
function(value) {
return isFunction(value);
},
regexp(value) {
if (isString(value)) {
try {
new RegExp(value);
return true;
}
catch {
return false;
}
}
else {
return value instanceof RegExp;
}
},
array(value, rule) {
if (Array.isArray(value)) {
return handleRange(value.length, 'number', rule);
}
else {
return false;
}
},
object(value) {
return value && typeof value === 'object' && !Array.isArray(value);
},
enum(value, rule) {
return rule.enum?.includes(value) ? true : 'enum';
},
date(value) {
return value instanceof Date;
},
url(value) {
return isString(value) && getUrlRegexp().test(value);
},
hex(value) {
return isString(value) && /^#(?:[0-9A-F]{6}|[0-9A-F]{3})$/i.test(value);
},
email(value) {
return (isString(value) &&
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value));
},
};
export class Validator {
validateMessages = undefined;
setValidateMessages(validateMessages) {
this.validateMessages = validateMessages;
}
getValidTriggerRules(rules, trigger) {
let validRules = rules;
if (trigger) {
const validTrigger = toArray(trigger);
validRules = rules.filter(Boolean).filter((rule) => {
let { trigger: ruleTrigger } = rule;
if (!ruleTrigger) {
return true;
}
ruleTrigger = toArray(ruleTrigger);
return validTrigger.some((name) => ruleTrigger.includes(name));
});
}
return validRules;
}
validate(rules, options = {}) {
const { validateFirst, value } = options;
return new Promise((resolve, reject) => {
if (validateFirst) {
Promise.all(rules.map((rule) => {
return this.validateRule(value, rule);
}))
.then(() => {
resolve();
})
.catch(({ error, rule }) => {
reject([this.replaceSymbol(error, rule, options)]);
});
}
else {
Promise.allSettled(rules.map((rule) => {
return this.validateRule(value, rule);
})).then((values) => {
const rejected = values.filter(({ status }) => {
return status === 'rejected';
});
if (rejected.length === 0) {
resolve();
}
else {
reject(rejected.map((result) => {
const { error, rule } = result.reason;
return this.replaceSymbol(error, rule, options);
}));
}
});
}
});
}
validateRule(value, rule) {
if (rule.transform) {
value = rule.transform(value);
}
return new Promise((resolve, reject) => {
const handleReject = (error) => {
reject({
error,
rule,
});
};
// empty
const isEmpty = isEmptyValue(value, rule.whitespace);
if (isEmpty && !rule.validator) {
if (rule.required) {
handleReject(getMessage(rule.message) ||
chainGet(this.validateMessages, 'required'));
}
else {
resolve();
}
return;
}
// validator
if (rule.validator) {
const result = rule.validator(value, rule);
if (result instanceof Promise) {
result
.then(() => {
resolve();
})
.catch((error) => {
handleReject(error);
});
}
else if (result === true) {
resolve();
}
else if (isString(result)) {
handleReject(result);
}
else {
handleReject(getMessage(rule.message));
}
return;
}
// pattern
if (rule.pattern instanceof RegExp) {
const result = rule.pattern.test(String(value));
if (result) {
resolve();
}
else {
handleReject(getMessage(rule.message) ||
chainGet(this.validateMessages, 'pattern.mismatch'));
}
return;
}
// internal type
let ruleType = rule.type;
if (!ruleType &&
(isNumber(rule.min) || isNumber(rule.max) || isNumber(rule.len))) {
ruleType = 'string';
}
if (ruleType && Object.keys(typeStrategies).includes(ruleType)) {
this.validateInternal(ruleType, value, rule)
.then(() => {
resolve();
})
.catch((err) => {
handleReject(getMessage(rule.message) || err);
});
return;
}
resolve();
});
}
validateInternal(type, value, rule) {
return new Promise((resolve, reject) => {
const result = typeStrategies[type]?.(value, rule);
if (result === true) {
resolve();
}
else {
reject(chainGet(this.validateMessages, isString(result) ? result : `types.${type}`));
}
});
}
replaceSymbol(string, rule, options = {}) {
if (string instanceof Error) {
string = string.message;
}
if (!isString(string)) {
string = String(string);
}
const label = isString(options.label) ? options.label : String(options.name);
const matches = {
'${min}': rule.min,
'${max}': rule.max,
'${len}': rule.len,
'${enum}': rule.enum,
'${label}': label,
'${value}': options.value,
'${type}': rule.type,
'${pattern}': rule.pattern?.toString(),
};
const regexp = /\$\{(?:min|max|len|enum|label|value|type|pattern)\}/g;
return string.replace(regexp, (m) => {
return matches[m] ?? '';
});
}
}