ph-utils
Version:
js 开发工具集,前后端都可以使用(commonjs和es module)
234 lines (233 loc) • 7.62 kB
JavaScript
/**
* 数据验证器
*/
// 默认的错误提示信息
const defaultMsgs = {
mobile: "请输入正确的手机号",
same: "两次输入不一致",
required: "%s为必填字段",
};
const defaultMsg = "请输入正确的数据";
// 一些常用的验证正则
const ruleRegexs = {
/** 验证跟其余数据相等的正则,一般用于验证再次输入密码 */
same: /^same:(.+)$/i,
/** 验证手机号的正则表达式 */
mobile: /^1[345678]\d{9}$/,
/** 非空验证的正则表达式 */
required: /^\S{1}.*/,
};
// 规则比对函数
const ruleFns = {
/** 验证相等 */
same(val1, val2) {
return val2 === val1;
},
/** 正则匹配 */
pattern(regex, val) {
if (val == null) {
return false;
}
return regex.test(String(val));
},
};
class ValidateError extends Error {
constructor(detail, key, message) {
super(message);
this.name = "ValidateError";
this.key = key;
this.detail = detail;
}
}
/**
* 数据验证器
*/
class Validator {
/**
* 构造数据验证转换器
*
* See {@link https://gitee.com/towardly/ph/wikis/utils/validator|Validator文档}.
*
* @param schemas 配置验证转换规则
*
* @example
*
* const validator = new Validator([
* { key: 'mobile', rules: ['required', 'mobile'] },
* { key: 'code': rules: /^\d{6}$/, message: '请输入正确的验证码' },
* { key: 'confirmPassword', rules: ['required', 'same:password'] }
* ])
* // 验证某一个字段
* validator.validateKey().then(res => {})
*/
constructor(schemas) {
this.rules = {};
this.addSchemas(schemas);
}
addSchemas(schemas) {
for (let schema of schemas) {
this.rules[schema.key] = this._parseSchemaRules(schema);
}
}
addSchema(schema) {
this.rules[schema.key] = this._parseSchemaRules(schema);
}
/**
* 进行数据验证
* @param data 待验证的数据
* @param all 是否全部验证, false - 只要验证错误一个则停止验证
* @returns
*/
async validate(data, all = false) {
return new Promise((resolve, reject) => {
const detail = {};
let currentKey = undefined;
let currentMessage = undefined;
for (let key in this.rules) {
let errMsg = this._validateRule(this.rules[key], data[key], data);
if (errMsg !== "") {
errMsg = errMsg.replace("%s", key);
detail[key] = errMsg;
currentKey = key;
currentMessage = errMsg;
if (all)
break;
}
}
if (currentKey == null) {
resolve(true);
}
else {
reject(new ValidateError(detail, currentKey, currentMessage));
}
});
}
/**
* 只验证指定 key 的数据格式
* @param key 指定待验证的 key
* @param value 待验证的数据
* @param data 原始数据,当验证确认密码时需要使用
*/
async validateKey(key, value, data) {
return new Promise((resolve, reject) => {
let keyRules = this.rules[key];
if (keyRules == null) {
resolve({ key, value });
return;
}
let errMsg = this._validateRule(keyRules, value, data);
if (errMsg !== "") {
errMsg = errMsg.replace("%s", key);
reject(new ValidateError({ [key]: errMsg }, key, errMsg));
}
else {
resolve({ key, value });
}
});
}
_validateRule(rules, value, data) {
let errMsg = "";
for (let rule of rules) {
// 如果数据为空,则判断是否是必填
if (rule.rule === "required") {
if (value == null || !ruleFns.pattern(ruleRegexs.required, value)) {
errMsg = rule.message;
}
}
else if (typeof rule.rule === "function") {
if (!rule.rule(value)) {
errMsg = rule.message;
}
}
else if (rule.sameKey != null) {
if (data != null) {
if (!ruleFns.same(value, data[rule.sameKey])) {
errMsg = rule.message;
}
}
}
else {
if (!ruleFns.pattern(rule.rule, value)) {
errMsg = rule.message;
}
}
if (errMsg !== "") {
break;
}
}
return errMsg;
}
_parseSchemaRules(schema) {
// 解析规则
let rules = [];
let rule = schema.rules;
if (schema.required === true) {
rules.push(...this._parseStringRule("required", schema.message));
}
if (rule != null) {
if (typeof rule === "string") {
rules = rules.concat(this._parseStringRule(rule, schema.message));
}
else if (rule instanceof Array) {
for (let ruleItem of rule) {
if (typeof ruleItem === "string") {
rules.push(...this._parseStringRule(ruleItem, schema.message));
}
else if (ruleItem instanceof RegExp ||
typeof ruleItem === "function") {
rules.push({
rule: ruleItem,
message: schema.message || defaultMsg,
});
}
else {
if (typeof ruleItem.rule === "string") {
rules.push(...this._parseStringRule(ruleItem.rule, ruleItem.message));
}
else {
rules.push({
rule: ruleItem.rule,
message: ruleItem.message || defaultMsg,
});
}
}
}
}
else {
rules.push({ rule, message: defaultMsg });
}
}
return rules;
}
_parseStringRule(rule, ruleErrMsg) {
let rules = [];
let trule = rule.split("|");
for (let r of trule) {
let message = ruleErrMsg;
let rrule = null;
let sameKey;
if (rule === "required") {
rrule = "required";
message = message || ruleErrMsg || defaultMsgs.required;
}
else if (ruleRegexs.same.test(r)) {
let m = r.match(ruleRegexs.same);
if (m != null) {
rrule = ruleRegexs.same;
let m = r.match(ruleRegexs.same);
if (m != null) {
sameKey = m[1];
}
message = message || defaultMsgs["same"];
}
}
else if (Object.hasOwn(ruleRegexs, r)) {
rrule = ruleRegexs[r];
message = message || defaultMsgs[r];
}
rules.push({ rule: rrule, message: message, sameKey });
}
return rules;
}
}
export default Validator;