Ver Fonte

基础文件上次

kindring há 1 ano atrás
commit
0dd4683554
4 ficheiros alterados com 596 adições e 0 exclusões
  1. 291 0
      fieldCheck.js
  2. 295 0
      formData.js
  3. 2 0
      index.js
  4. 8 0
      package.json

+ 291 - 0
fieldCheck.js

@@ -0,0 +1,291 @@
+/**
+ * 表单字段验证库
+ * author:kindring
+ * date:2023/10/08
+ */
+
+/**
+ * @typedef {string} errMessage 错误信息
+ */
+
+
+/**
+ * @typedef { Array<string | RegExp> } checkFields 验证字段匹配项
+ */
+/**
+ * @typedef {Object} checkRule 规则对象
+ * @property {string} [type] 类型
+ * @property {number} [min] 最小值
+ * @property {number} [max] 最大值
+ * @property {number} [length] 长度
+ * @property {RegExp} [regex] 正则表达式
+ * @property {errMessage} [message] 错误信息
+ * @property {boolean} [require] 是否必须
+ * @property {number} [minLength] 最小长度
+ * @property {number} [maxLength] 最大长度
+ * @property {validatorFunction} [validator] 自定义验证函数
+ */
+
+/**
+ * @typedef {function} validatorFunction 自定义验证函数
+ * @param {any} value 需要验证的值
+ * @returns {string} 返回错误信息或者 null
+ */
+
+/**
+ * @typedef {Object} ruleItem 验证规则对象
+ * @property {Array<string>} checkFields 需要验证的字段
+ * @property {Array<validatorFunction | checkRule>} rules 验证规则
+ */
+
+/**
+ * @typedef {number} checkCode 验证码
+ * @property {1} code_pass 验证通过
+ * @property {2} code_notPass 验证不通过
+ * @property {3} code_notMatch 未匹配到验证规则
+ */
+
+
+/**
+ * @class FieldCheck
+ * @description 表单字段验证类
+ * @property {Array<ruleItem>} ruleItems 验证规则
+ * @property {function} addRuleItem 添加一条验证规则
+ * @property {function} verify 检查表单是否符合规则
+ * @example
+ * let fieldCheck = new FieldCheck();
+ * fieldCheck.addRuleItem('rule1',['name'],[
+ *    {
+ *    type: 'string',
+ *    minLength: 2,
+ *    maxLength: 10,
+ *    message: '姓名必须为2-10个字符'
+ *    }
+ *    ]);
+ *    fieldCheck.addRuleItem('rule2',['age'],[
+ *    {
+ *    type: 'number',
+ *    min: 18,
+ *    max: 100,
+ *    message: '年龄必须为18-100岁'
+ *    }]);
+ *    let errMsg = fieldCheck.verify({
+ *    name: 'kindring',
+ *    age: 18});
+ *    console.log(errMsg);
+ *    // null
+ *    let errMsg = fieldCheck.verify({
+ *    name: 'kindring',
+ *    age: 17});
+ *    console.log(errMsg);
+ *    // 年龄必须为18-100岁
+ */
+class FieldCheck{
+    // 通过
+    #code_pass = 1;
+    // 未通过
+    #code_notPass = 2;
+
+    // 无法匹配到验证规则
+    #code_notMatch = 3;
+
+    /**
+     * @type {Array< ruleItem >}
+     */
+    #ruleItems = [];
+    constructor() {
+        this.#ruleItems = [];
+    }
+
+    /**
+     * 判断值是否定义
+     * @param v
+     * @returns {boolean}
+     * @private
+     */
+    _isDef (v) {
+        return v !== undefined && v !== null
+    }
+    _toString = Object.prototype.toString;
+    /**
+     * 判断是否为空
+     * @param v
+     * @returns {boolean}
+     */
+    _isEmpty(v){
+        return v === undefined || v === '';
+    }
+    _isRegExp (v) {
+        return this._toString.call(v) === '[object RegExp]'
+    }
+
+
+    /**
+     * 构建验证规则
+     * @param {Array<string | RegExp>} checkFields 需要验证的字段
+     * @param {Array<validatorFunction | checkRule>} ruleArr 验证规则
+     * @returns {ruleItem} 验证规则对象
+     */
+    buildRuleItem( checkFields , ruleArr) {
+        //  检测checkFields是否为数组
+        //  检测ruleArr是否为数组
+        if(!Array.isArray(checkFields) || !Array.isArray(ruleArr)){
+            throw new Error('checkFields or ruleArr is not Array');
+        }
+        //  检测checkFields中的每一项是否为字符串或者正则
+        for(let field of checkFields){
+            if(typeof field !== 'string' && !(field instanceof RegExp)){
+                throw new Error('checkFields item is not string or RegExp');
+            }
+        }
+        //  检测ruleArr中的每一项是否为函数或者对象
+        for(let rule of ruleArr){
+            if(typeof rule !== 'function' && typeof rule !== 'object'){
+                throw new Error('ruleArr item is not function or object');
+            }
+        }
+        /**
+         * @type {ruleItem}
+         */
+        let ruleItem = {
+            checkFields: checkFields,
+            rules: ruleArr
+        }
+        // this.ruleItems = this.ruleItems.push(ruleItem);
+        return ruleItem;
+    }
+
+    /**
+     * 添加一条验证规则
+     * @param { string } ruleName 验证规则名,用于区分
+     * @param { Array<string | RegExp> } checkFields 用于匹配字段的字符或者正则数组
+     * @param { Array<validatorFunction | checkRule> } ruleArr 验证规则
+     * @returns { FieldCheck } 返回当前对象
+     */
+    addRuleItem( ruleName, checkFields , ruleArr) {
+        let ruleItem = this.buildRuleItem(checkFields,ruleArr);
+        this.#ruleItems.push(ruleItem);
+        return this;
+    }
+
+    /**
+     * 获取验证规则
+     * @param { string } field 字段名
+     * @returns { ruleItem } 验证规则
+     */
+    getRuleItem(field){
+        let ruleItem = this.#ruleItems.find(item=>{
+            // 判断是否为正则
+            for (const _matchKey of item.checkFields) {
+                // 判断是否为正则
+                if(_matchKey instanceof RegExp){
+                    // console.log(`使用正则进行匹配,${_matchKey.test(key)}`);
+                    if(_matchKey.test(field)){
+                        // console.log(`通过正则匹配规则成功,${_matchKey.test(key)}`);
+                        return true;
+                    }
+                }else{
+                    // console.log(`比较是否全等,${_matchKey} === ${key} ?${_matchKey === key}`);
+                    if(_matchKey === field){
+                        // console.log(`通过字符${_matchKey}匹配成功`);
+                        return true;
+                    };
+                }
+            }
+            return false;
+        });
+        return ruleItem;
+    }
+
+    /**
+     * 检查字段是否符合规则
+     * @param field 字段名
+     * @param value 字段值
+     * @returns {Array<checkCode | errMessage>} 错误码或错误信息
+     */
+    checkField(field, value){
+        let ruleItem = this.getRuleItem(field);
+        if(!ruleItem || !ruleItem.rules){
+            return [this.#code_notMatch];
+        }
+        // 判断值是否为undefined
+        if(value === undefined){
+            return [this.#code_notPass, '字段值为undefined'];
+        }
+        // 开始匹配规则
+        for(let _rule of ruleItem.rules ){
+            // 判断是否有自定义验证函数
+            if(typeof _rule === 'function'){
+                let  _msg = _rule(value);
+                // console.log(_msg)
+                if(_msg){
+                    return [this.#code_notPass,_msg]
+                }
+            }
+
+            // 判断类型
+            if(_rule.type && typeof value !== _rule.type){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断是否为必填项
+            if(_rule.require && this._isEmpty(value)){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断最小值
+            if(_rule.min && value < _rule.min){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断最大值
+            if(_rule.max && value > _rule.max){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断值是否达到指定长度
+            if(_rule.length && value.length && value.length !== _rule.length){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断最小长度
+            if(_rule.minLength && value.length && value.length < _rule.minLength){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断最大长度
+            if(_rule.maxLength && value.length && value.length > _rule.maxLength){
+                return [this.#code_notPass, _rule.message]
+            }
+
+            // 判断是否符合正则
+            if(_rule.regex && !_rule.regex.test(value)){
+                return [this.#code_notPass, _rule.message]
+            }
+
+        }
+
+        return [this.#code_pass]
+
+    }
+
+    /**
+     * 检查表单是否符合规则
+     * @param { Object } formObject 需要检验的表单项 字段:值
+     * @param [isMustMatch] 是否强制要求匹配规则
+     * @returns { errMessage } 错误码或错误信息
+     */
+    verify(formObject, isMustMatch){
+        for (const _oKey in formObject) {
+            let value = formObject[_oKey];
+            let r = this.checkField(_oKey,value);
+            if(r[0] === this.#code_notPass){
+                return r[1]
+            }else if(isMustMatch && r[0] === this.#code_notMatch){
+                return `字段没有对应匹配项`
+            }
+        }
+    }
+}
+
+export default FieldCheck;

+ 295 - 0
formData.js

@@ -0,0 +1,295 @@
+import FieldCheck from "./fieldCheck";
+import fieldIsAllow from "@/until/fieldIsAllow";
+
+let hasOwnProperty = Object.prototype.hasOwnProperty;
+function hasOwn (obj, key) {
+    return hasOwnProperty.call(obj, key)
+}
+
+/**
+ * @typedef {object} formItemData 表单项数据
+ * @property {string} [val] 表单项值
+ * @property {string} [msg] 表单项错误信息
+ * @property {number} [state] 表单项状态 0 通过 1 通过 2 不通过
+ * @property {string} [showText] 表单项显示文本,用于在某些
+ * @property {string} [label] 表单项显示文本
+ * @property {string} [init] 表单项初始值
+ * @property {Array} [options] 表单项枚举值
+ * @property {string} [depend] 依赖字段, 该项存在将使用依赖字段的option中的checkField字段进行匹配验证规则
+ * @property {string} [reCheckField] 该表单项用于匹配规则的字段
+ * @property {Array} [disables] 禁用项
+ */
+
+
+/**
+ * @class FormItem
+ * @description 表单项
+ * @param {object} object 表单项数据
+ * @param {FieldCheck} [fieldCheck] 字段验证对象
+ * @param {object} [option] 配置项
+ */
+class FormItem {
+    formData;
+    /**
+     * @type {FieldCheck} 字段验证对象
+     */
+    fieldCheck;
+    // 表单状态 默认 0 通过 1 不通过 2
+    formState_default = 0;
+    formState_pass = 1;
+    formState_notPass = 2;
+
+    defaultOption = {
+        isMustMatchRule: false,// 表单字段是否必须匹配到验证规则
+    }
+    option = {};
+    /**
+     *
+     * @param object
+     * @param {FieldCheck} [fieldCheck] 字段验证对象
+     * @param {object} [option] 配置项
+     */
+    constructor(object, fieldCheck, option) {
+        this.fieldCheck = fieldCheck || new FieldCheck();
+        // 合并配置项
+        this.option = Object.assign(this.defaultOption, option);
+        let errMsg;
+        // 拿出其中的每一项来构建对应的表单项
+        for (let key in object) {
+            this[key] = object[key];
+            // 验证表单项是否符合要求,不符合要求则抛出错误
+            errMsg = FormItem.buildFormItem(object, key, object[key], this.fieldCheck, this.option.isMustMatchRule);
+            if (errMsg) {
+                throw new Error(`表单项${key}不符合要求,err:${errMsg}`);
+            }
+        }
+        this.formData = object;
+    }
+    static isObject (obj) {
+        return obj !== null && typeof obj === 'object'
+    }
+
+    /**
+     * 检查表单项是否符合要求
+     * @param { object } object 表单项数据
+     * @param { string } field 字段名
+     * @param { formItemData } formItemData 表单项数据
+     * @param { FieldCheck } fieldCheck 字段验证对象
+     * @param { boolean } isMustMatchRule 表单字段是否必须匹配到验证规则
+     * @returns { string } errMsg 错误信息
+     */
+    static buildFormItem(object, field, formItemData, fieldCheck, isMustMatchRule) {
+        if ( !FormItem.isObject(formItemData) ){
+            return `form item ${field} must be object`;
+        }
+        // 是否需要从验证规则表中查找对应的验证规则
+        let isNeedMatchRule = true;
+
+        // 用于匹配的字段
+        let checkFieldStr = field;
+
+        let disables = formItemData.disables;
+
+        // 设置默认值
+        formItemData.val = formItemData.val || formItemData.init || '';
+        // 设置默认提示词
+        formItemData.msg = formItemData.msg || '';
+        // 设置默认状态
+        formItemData.state = formItemData.state || FormData.formState_default;
+        // 设置默认显示文本
+        formItemData.label = formItemData.label || '';
+
+        // 判断是否有 options 选项有则判断是否有 init 选项,没有则设置第一个为 init
+        if ( formItemData.options  ){
+            if( !formItemData.options.length || !formItemData.options[0] ){
+                return `form item ${field} options must be array and has item`;
+            }
+            if ( !formItemData.init ){
+                formItemData.init = formItemData.options[0].key;
+            }
+
+            // 判断 val 与 init 是否存在于 options 中
+            let hasInit = false;
+            for (let i = 0; i < formItemData.options.length; i++) {
+                let option = formItemData.options[i];
+                if ( option.key === formItemData.init ){
+                    hasInit = true;
+                }
+
+                // 判断该options是否为禁用项
+                if ( disables && disables.indexOf(option.key) !== -1 ){
+                    option.disabled = true;
+                }
+            }
+            if ( !hasInit ){
+                return `form item ${field} init value must be in options`;
+            }
+
+        }
+
+        // 判断是否有 depend 依赖字段 有依赖字段则依据依赖字段中的 option 中的 checkField 字段进行判断
+        if( formItemData.depend && formItemData.reCheckField ){
+            return `form item ${field} has depend and reCheckField, but depend and reCheckField can not exist at the same time`;
+        }
+
+        // 判断是否有 depend 依赖字段 有依赖字段则依据依赖字段中的 option 中的 checkField 字段进行判断
+        if ( formItemData.depend ){
+            // 判断依赖字段是否存在
+            if ( !object[formItemData.depend] ){
+                return `form item ${field} depend field ${formItemData.depend} but the field not exist`;
+            }
+            // 判断依赖字段的 option 是否存在
+            if ( !object[formItemData.depend].options ){
+                return `form item ${field} depend field ${formItemData.depend} has no options`;
+            }
+            // 判断依赖字段的 options 中是否有 checkField 字段
+            let hasCheckField = false;
+            for (let i = 0; i < object[formItemData.depend].options.length; i++) {
+                let option = object[formItemData.depend].options[i];
+                if ( option.checkField ){
+                    hasCheckField = true;
+                    checkFieldStr = option.checkField;
+                    break;
+                }
+            }
+            if ( !hasCheckField ){
+                return `form item ${field} depend field ${formItemData.depend} has no checkField`;
+            }
+
+        }
+
+        // 判断是否有 reCheckField 有则使用该字段的值进行规则验证
+        if ( formItemData.reCheckField ){
+            checkFieldStr = formItemData.reCheckField;
+        }
+
+
+
+        // 判断是否有 rules 规则
+        if(isMustMatchRule){
+            if(fieldCheck.getRuleItem(checkFieldStr)){
+                return `form item ${field} has no rules`;
+            }
+        }
+
+        return '';
+    }
+
+    /**
+     * 初始化表单项数据
+     * @param { formObject } formObject 表单对象
+     */
+    static initFormItemData ( formObject ) {
+        let keys = Object.keys(formObject);
+        for(let i = 0; i < keys.length; i++){
+            let key = keys[i];
+            formObject[key].val = formObject[key].init;
+            formObject[key].msg = '';
+            formObject[key].state = FormData.formState_default;
+            formObject[key].showText = '';
+        }
+    }
+
+
+    /**
+     * 检查表单项是否符合要求
+     * @param {object} form 表单对象
+     * @param isMustMatch 是否必须全部匹配到验证规则
+     * @returns {boolean}
+     */
+    checkForm (form, isMustMatch) {
+        let r = true;
+        let n_checkPass = 0,
+            n_checkTotal = 0;
+        let msg = '';
+        for (const fieldKey in form) {
+            let formItem = form[fieldKey];
+            let depend = form[formItem.depend];
+            let checkField = fieldKey;
+            let tmpInd = -1;
+
+            n_checkTotal++;
+
+            if(formItem.reCheckField){
+                checkField = formItem.reCheckField;
+            }
+
+            // 禁用值判断 array
+            if(formItem.disables){
+                if(formItem.disables.indexOf(formItem.val) !== -1){
+                    formItem.msg = '该项内容不合法';
+                    r = false;
+                }
+            }
+
+            // 枚举值判断
+            if(formItem.options){
+                // 有枚举字段,只判断是否在枚举中
+                // console.log(`检测枚举字段:${checkField},值:${formItem.val}`);
+                tmpInd = formItem.options.findIndex(item=>item.value == formItem.val);
+                if(tmpInd === -1){
+                    console.log(`检测枚举字段:${checkField},值:${formItem.val}不在范围内`);
+                    formItem.msg = '选项不在范围内';
+                    formItem.state = 1;
+                    r = false;
+                }else{
+                    // 判断值是否为禁用项
+                    if(formItem.options[tmpInd].disabled){
+                        formItem.msg = '该选项已经被禁用';
+                        r = false;
+                    }
+                }
+                // 枚举值判断完毕,继续下一个字段
+                n_checkPass++;
+                continue;
+            }
+
+            // 依赖字段判断
+            if(depend){
+                if(depend.options){
+                    // 依赖的对象有枚举类型,检查该枚举类型是否有有检测值
+                    let optionItem = depend.options.find(item=>item.value == depend.val);
+                    if(!optionItem){
+                        depend.msg = '选项不在范围内';
+                        formItem.msg = '该值依赖项输入异常';
+                        r = false;
+                        // continue;
+                    }
+                    if(optionItem.checkField){
+                        // console.log(`采用依赖项的检测字段${optionItem.checkField}`)
+                        checkField = optionItem.checkField;
+                    }
+
+                }else{
+                    r = false;
+                }
+                if(!r)
+                {
+                    depend.msg = '该项依赖项输入异常';
+                    formItem.msg = '该值依赖项输入异常';
+                }
+            }
+
+            // 使用验证规则进行
+            formItem.msg = this.fieldCheck.verify({
+                [checkField]:formItem.val,
+            })
+
+
+            if (formItem.msg) r = false;
+            if(r){
+                n_checkPass++;
+                formItem.state = this.formState_pass
+            }else{
+                formItem.state = this.formState_notPass
+            }
+        }
+
+        msg = `检查表单项通过率:${n_checkPass}/${n_checkTotal}`;
+        console.log(msg);
+        return r;
+    }
+
+}
+
+export default FormItem;

+ 2 - 0
index.js

@@ -0,0 +1,2 @@
+import FieldCheck from "./fieldCheck";
+import FormItem from "./formData";

+ 8 - 0
package.json

@@ -0,0 +1,8 @@
+{
+  "name": "kindFormData",
+  "version": "1.0.0",
+  "description": "表单规则验证工具库",
+  "main": "index.js",
+  "author": "kindring",
+  "license": "MIT"
+}