美文网首页vue
vue-表单验证

vue-表单验证

作者: 青青草原灰太狼 | 来源:发表于2018-12-29 15:12 被阅读634次

    涉及的技术:mixin,directive

    1、创建验证规则文件 validator.js

    export default{
        message: {
            required: "这是必填字段",
            email: "请输入有效的电子邮件地址",
            url: "请输入有效的网址",
            date: "请输入有效的日期",
            dateISO: "请输入有效的日期 (YYYY-MM-DD)",
            dateYM: "请输入正确的日期(YYYY-MM)",
            datetime: "请输入正确的日期(YYYY-MM-DD HH:mm:ss)",
            number: "请输入有效的数字",
            digits: "只能输入整数",
            maxlength: "最多可以输入 {0} 个字符",
            minlength: "最少要输入 {0} 个字符",
            rangelength: "请输入长度在 {0} 到 {1} 之间的字符串",
            range: "请输入范围在 {0} 到 {1} 之间的数值",
            percent: "请输入范围在 {0} 到 {1} 之间的数值",
            max: "请输入不大于 {0} 的数值",
            min: "请输入不小于 {0} 的数值",
            decimal: "请精确到小数点后 {0} 位",
            mindecimal: "请至少精确到小数点后 {0} 位",
            maxdecimal: "请最多精确到小数点后 {0} 位",
            rangedecimal: "请精确到小数点后 {0} 至 {1} 位",
            IDCard: "请输入合法的身份证号码",
            phone: "请输入合法的手机号码",
            password: "密码必须符合以下要求:长度为8~16位,至少包含一个大写字母、一个小写字母、一个数字、以及一个特殊符号",
            email2: "请输入有效的电子邮件地址",
            number_0: "请输入非零的有效数字",
            digits_0: "请输入非零的整数",
            English_0: "请输入姓名拼音(小写)",
            space: "不能输入空格"
        },
    
        methods: {
            //必填
            required: function (value, element, param) {
                return value.length > 0;
            },
            //邮箱
            email: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(value);
            },
            //网址
            url: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-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,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value);
            },
            //日期
            date: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return !/Invalid|NaN/.test(new Date(value).toString());
            },
            //日期(ISO)
            dateISO: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value);
            },
            dateYM: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^\d{4}[\/\-]?(0[1-9]|1[012])$/.test(value);
            },
            datetime: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s([01][0-9]|2[0-4]):([0-5][0-9]):([0-5][0-9])$/.test(value);
            },
            //有效的数字
            number: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^-?\d+(?:\.\d+)?$/.test(value);
            },
            //数字
            digits: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                return /^-?\d+$/.test(value);
            },
            //字符串至少n个字符
            minlength: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return value.length >= param[0];
            },
            //字符串最多n个字符
            maxlength: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return value.length <= param[0];
            },
            //字符串长度的范围
            rangelength: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return (value.length >= param[0] && value.length <= param[1]);
            },
            //数字大于n
            min: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return value >= param[0];
            },
            //数字小于n
            max: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return value <= param[0];
            },
            //数字范围n-m
            range: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                return (value * 1 >= param[0] * 1 && value * 1 <= param[1] * 1);
            },
            //百分数
            percent: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                if (value.substr(value.length - 1) === "%") {
                    value = value.substr(0, value.length - 1);
                }
                return (value >= param[0] && value <= param[1]);
            },
            //小数位数n位
            decimal: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = new RegExp("^-?\\d+(.\\d{" + param[0] + "," + param[0] + "})$");
                return rex.test(value);
            },
            //小数位数至少n位
            mindecimal: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = new RegExp("^-?\\d+(.\\d{" + param[0] + ",})$");
                return rex.test(value);
            },
            //小数位数最多n位
            maxdecimal: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = new RegExp("^-?\\d+(.\\d{1," + param[0] + "})?$");
                return rex.test(value);
            },
            //小数位数范围n-m位
            rangedecimal: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = new RegExp("^-?\\d+(.\\d{" + param[0] + "," + param[1] + "})$");
                return rex.test(value);
            },
            //身份证号码
            IDCard: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X)$)/;
                return rex.test(value);
            },
            //手机号码
            phone: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = /^1[345789]\d{9}$/;
                return rex.test(value);
            },
            //手机号码(前面可能含有86)
            phone86: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = /^(86)?1[345789]\d{9}$/;
                return rex.test(value);
            },
            //密码
            password: function (value, element, param) {
                if (value == null || this.trim(value) == "") return true;
                var rex = /^(?=.*\d+)(?=.*[a-z]+)(?=.*[A-Z]+)(?=.*[^A-Za-z0-9\s]+)\S{8,16}$/;
                return rex.test(value);
            },
            //邮箱
            email2: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                var rex = /^(([a-zA-Z0-9])*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}){1,2}$/;
                return rex.test(value);
            },
            //有效的数字(非0)
            number_0: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                if (this.trim(value) == "0") return false;
                return /^-?\d+(?:\.\d+)?$/.test(value);
            },
            //数字(非0)
            digits_0: function (value, element) {
                if (value == null || this.trim(value) == "") return true;
                if (this.trim(value) == "0") return false;
                return /^-?\d+$/.test(value);
            },
            //英文字母(小写)
            English_0: function (value, element) {
                if (value == null) return true;
                var rex = /^[a-z]{1,100}]?$/;
                return rex.test(value);
            },
            //非空格
            space: function (value, element) {
                if (value == null) return true;
                var rex = /^\S+$/;
                return rex.test(value);
            },
            trim(value) {
                return value.replace(/(^\s*)|(\s*$)/g, "");
            }
        }
    }
    

    2、创建存储验证信息的类 validateMsg.js

    import {pluginInstance} from "./plugin";
    
    class ErrorBag {
        constructor(id) {
            this.id = id;
            this.errors = [];
            this.fileds = [];
        }
    
        setErrors(name, msg,el) {
            let errorIndex = this.errors.findIndex(x => x.name === name);
            if (errorIndex > -1 && errorIndex < this.errors.length) {
                this.errors[errorIndex].msg = msg;
            } else {
                let error = {
                    name: name,
                    msg: msg,
                };
                this.errors.push(error);
            }
            if (this.fileds.indexOf(name) < 0) this.fileds.push(name);
        }
    
        deleteErrors(name) {
            let index = this.errors.findIndex(item => item.name === name);
            if (index > -1 && index < this.errors.length) {
                this.errors.splice(index, 1);
            }
            index = this.fileds.findIndex(item => item === name);
            if (index > -1 && index < this.fileds.length) {
                this.fileds.splice(index, 1);
            }
        }
    
        clear() {
            this.errors = [];
            this.fileds = [];
        }
    
        get(name) {
            let error = this.errors.filter(item => item.name === name);
            return error && error.length?error[0].msg : null;
        }
    
        has(name) {
            if(this.errors){
                let error = this.errors.filter(item => item.name === name);
                return error && error.length;
            }
            return false;
        }
    
        all(){
            return this.errors;
        }
        hasAny(){
            return this.errors.length
        }
    }
    
    /*
    * 验证信息
    * */
    export class validateMsg {
        constructor(id) {
            this.id=id;
            this.errors = new ErrorBag(id);
            this._validateRules = {};
            this._validateMsgs = {};
            this._validatePositions = {};
        }
    
        setErrors(name, msg,position) {
            this.errors.setErrors(name, msg,position);
        }
    
        removeErrorMsg(name) {
            this.errors.deleteErrors(name);
        }
        clearMsg(id) {
            if(this.id===id){
                this.errors.clear();
            }
        }
    
        checkAll(checklist,options){
            return pluginInstance.checkAll(checklist,options,this);
        }
        clear(){
            this.errors.clear();
        }
    }
    
    

    3、创建组件基础类(即mixin)mixin.js

    const isBuiltInComponent = (vnode) => {
        if (!vnode) {
            return false;
        }
        const tag = vnode.componentOptions.tag;
        return /^(keep-alive|transition|transition-group)$/.test(tag);
    };
    import {validateMsg} from './validateMsg.js'
    export default {
            beforeCreate() {
                if (isBuiltInComponent(this.$vnode)) {
                    return;
                }
                if (!this.$validator) {
                    this.$validator = new validateMsg(this._uid);
                }
                if (this.$validator) {
                    const Vue = this.$options._base;
                    //定义errors为响应式属性
                    Vue.util.defineReactive(this.$validator, 'errors', this.$validator.errors);
                }
                if (!this.$options.computed) {
                    this.$options.computed = {};
                }
                this.$options.computed['errors'] = function errorBagGetter() {
                    return this.$validator.errors;
                };
    
            },
            //清空所有验证信息
            beforeDestroy() {
              if(this.$validator){
                let id=this._uid;
                this.$validator.clearMsg(id);
              }
            }
        }
    

    4、创建对外调用接口

    /**
     * 验证规则
     */
    import validator from './validator.js'
    
    import mixin from './mixin.js'
    
    const isObject = (obj) => obj !== null && obj && typeof obj === 'object' && !Array.isArray(obj);
    const isString = (obj) => obj !== null && obj && typeof obj === 'string';
    
    let Vue = null;
    /*
    * valedateUtil 实例
    * */
    export let pluginInstance;
    
    class validateUtil {
      static install(_vue, options) {
        if (Vue && _vue === Vue) {
          return;
        }
        Vue = _vue;
        pluginInstance = new validateUtil();
        Vue.mixin(mixin)
        Vue.directive('validate', {
          //指令第一次绑定到元素时调用
          bind: function (el, binding, vnode, oldnode) {
            pluginInstance.bindfunc(el, binding, vnode, oldnode)
          },
        })
      }
    
      static get instance() {
        return pluginInstance;
      }
    
      bindfunc(el, binding, vnode, oldnode) {
        let vm = vnode.context;
        let validate = this.createVM(vm);
        this.addValidateRules(el, binding, vnode, oldnode,validate);
        let vaType = el.getAttribute("validate-type");
        //v-validate:name.change='rules'
        if (!vaType && binding.modifiers){
          let types=Object.keys(binding.modifiers)
          types&&types.length&&(vaType=types[0]);
        }
        switch (vaType) {
          case "change":
            this.change(el, binding, vnode, oldnode, validate);
            break;
          case "input":
            this.oninput(el, binding, vnode, oldnode,validate);
            break;
          default:
            // this.change(el, binding, vnode, oldnode, validate);
            break;
        }
      }
    
      //将验证规则放置 _validateRules
      addValidateRules(el, binding, vnode, oldnode,validate) {
        let name = el.getAttribute("validate-name");
        let dataRules = el.getAttribute("data-rules");
        //v-validate:name='rules'
        if (!name && binding.arg) name = binding.arg;
        if (!dataRules && binding.value) dataRules = binding.value;
        let rules, ruleName, ruleMap ={},msgMap={};
        if (isObject(dataRules)) {
          ruleMap = dataRules.rules;
          msgMap= dataRules.msg
        } else if (isString(dataRules)) {
          rules = dataRules.split(" ");
          for (let item of rules) {
            if(!item) continue;
            let ruleArr = item.split("|");
            let ruleParams = [];
            for (let i = 0; i < ruleArr.length; i++) {
              if (i === 0) {
                ruleName = ruleArr[i];
              }
              else {
                try {
                  ruleParams.push(ruleArr[i] * 1);
                } catch (e) {
                  console.error(e);
                }
              }
            }
            ruleMap[ruleName]=ruleParams;
            let itemtip=el.getAttribute(`validate-tips-${ruleName}`);
            itemtip&&itemtip.length&&(msgMap[ruleName]=itemtip);
          }
        }
        validate._validateRules[name] = ruleMap;
        validate._validateMsgs[name] = msgMap;
        //存储每个验证元素(貌似没啥用了)
        validate._validatePositions[name]=el;
      }
    
      change(el, binding, vnode, oldnode, validate) {
        el.addEventListener('change', () => {
          this.check(el, binding, validate);
        })
      }
    
      oninput(el, binding, vnode, oldnode,validate) {
        el.addEventListener('input', () => {
          this.check(el, binding, validate);
        })
      }
    
      check(el, binding, validate) {
        let name = el.getAttribute("validate-name");
        if (!name && binding.arg) name = binding.arg;
        let rules = validate._validateRules[name],msgMap=validate._validateMsgs[name];
        let checkobj={
          name:name,
          rules:rules,
          msg:msgMap,
          value:el.value,
          el:el,
        }
        this.checkItem(checkobj,validate);
      }
      checkItem(checkobj,validate){
        let name=checkobj.name,rules=checkobj.rules,msgMap=checkobj.msg,value=checkobj.value,el=checkobj.el;
        let ruleName, ruleParams,ruleResult = true;
        !rules&&(rules=validate._validateRules[name]);
        !msgMap&&(msgMap=validate._validateMsgs[name]);
        !el&&(el=validate._validatePositions[name]);
        for (let key of Object.keys(rules)) {
          ruleName = key;
          ruleParams = rules[key];
          if (ruleName in validator.methods && ruleName in validator.message) {
            let _result = validator.methods[ruleName](value, el, ruleParams);
            ruleResult = _result;
            if (!_result) {
              let msg=validator.message[ruleName];
              if(msgMap&&msgMap[ruleName]){
                msg=msgMap[ruleName];
              }
              let errmsg = this.msgFormat(msg, ruleParams);
              validate.setErrors(name, errmsg,el);
              break;
            }
          }
        }
        //全部验证通过,清空错误信息
        if (ruleResult) {
          validate.removeErrorMsg(name);
        }
        return ruleResult;
      }
      msgFormat(msg, param) {
        if (param !== undefined && param.constructor === Array) {
          param.forEach(function (value, index) {
            msg = msg.replace(new RegExp("\\{" + index + "\\}", "g"), function () {
              return value;
            });
          });
        }
        return msg;
      }
    
      // 挂在vue实例上面$validate
      createVM(vm) {
        if (!vm.$validator) {
          let validate = new validateMsg();
          vm.$validator = validate;
        }
        return vm.$validator;
      }
    
      checkAll(ckecklist,options,validate){
        let result=true;
        if(!ckecklist||!ckecklist.length){
          ckecklist=[];
          for(let key of Object.keys(validate._validateRules)){
            let checkItem={
              name:key,
              rules:validate._validateRules[key],
              msg:validate._validateMsgs[key],
              value:validate._validatePositions[key].value,
              el:validate._validatePositions[key],
            };
            ckecklist.push(checkItem);
          }
        }
        if(ckecklist&&ckecklist.length){
          for(let checkitem of ckecklist){
            //是否仅验证加了 v-validate 的元素
            if(options&&options.strict){
              if(!validate._validatePositions[checkitem.name])
                continue;
            }
            let itemresult=this.checkItem(checkitem,validate);
            result&&(result=itemresult);
          }
        }
        return result;
      }
    }
    
    export default validateUtil;
    

    5、使用

    5.1、引入组件

    import validate from './src/plugin'
    Vue.use(validate);
    

    5.2、使用

    <template>
      <div class="login-main">
        <div class="login-form">
          <div class="login-content">
            <div class="input_item">
              <label for="email">test1:</label>
              <input class="mt-input" name="testinput" type="text" id="email" v-bind:class="{'mt-valdate__error':errors.has('test4')}"   v-validate data-rules= "required range|1|100"  validate-name="test4" validate-type="change" validate-tips-required="请输入"  validate-tips-range="范围{0}到{1}">
              <span v-if="errors.has('test4')">{{errors.get('test4').msg}}</span>
            </div>
            <div class="input_item">
              <label for="email">test2:</label>
              <input class="mt-input" v-bind:class="{'mt-valdate__error':errors.has('test3')}"  type="text" id="email1"   v-validate:test3.change="validateTest"  >
              <span v-if="errors.has('test3')" >{{errors.get('test3').msg}}</span>
            </div>
            <div class="input_item">
              <label for="email">test3:</label>
              <input class="mt-input"  v-bind:class="{'mt-valdate__error':errors.has('test2')}" type="text" id="email2"   v-validate data-rules= "required"  validate-name="test2">
              <span v-if="errors.has('test2')" >{{errors.get('test2').msg}}</span>
      </div>
      <div class="input_item">
        <label for="email">test4:</label>
        <input class="mt-input" v-bind:class="{'mt-valdate__error':errors.has('test1')}"  type="text" id="email3"    v-validate:test1.input data-rules= "required min|100" >
        <span v-if="errors.has('test1')" >{{errors.get('test1').msg}}</span>
      </div>
      <input type="submit" class="login-btn" value="验证"  @click="check();">
      <input type="submit" class="login-btn" value="清除"  @click="clearAll();">
      </div>
      </div>
      </div>
    </template>
    <script>
      export default {
        name: 'login',
        data () {
          return {
            email: '',
            validateTest: {
              rules:{
                required:true,
                rangelength: [6,9]
              },
              msg:{
                required:"请输入",
                rangelength:'长度不超过{0}到{1}'
              }
            },
            password: '',
            validatePassword: [
              {required: '', msg: ""},
              {limit: this.limit, msg: ""}
            ]
          }
        },
        methods: {
          check: function () {
            let ret=this.$validator.checkAll();
            console.log(ret);
          },
          clearAll:function(){
            this.$validator.clear();
          }
        }
      }
    </script>
    
    

    接口说明

    参数名 参数说明 类型 可选值 默认值
    v-validate 验证自定义命令 - - -
    data-rules 验证规则 String/object 具体格式见:data-rules说明 -
    validate-name 验证组件名称 String 必填 -
    validate-type 验证类型 String change/input/空 -

    简写说明:v-validate:[验证名称].[验证方式]=[验证规则]

    参数名 参数说明 类型 可选值 默认值
    验证名称 同上述validate-name - - -
    验证方式 同上述validate-type - - -
    验证规则 同上述data-rules String/object 必填 -

    data-rules说明:string/object

    String:不同的验证规则之间用空格分割,验证的条件值用| 分割,例如:required range|1|100
    Object

    {
        //验证规则
        rules: {
            required: true,
            rangelength: [6, 9]
         },
         //提示信息
        msg: {
            required: "请输入信息",
            rangelength: "长度范围{0}到{1}"
        }
    }
    

    errors 说明

    方法名 参数说明 返回值
    errors.has('验证名称') 该验证是否通过 true/false
    errors.get('验证名称') 获取验证错误信息 String
    errors.hasAny() 是否存在验证失败信息 true/false
    errors.all() 所有验证失败的信息 数组

    vue的核心是数据驱动,所以该插件没有添加任何提示样式,可根据自身需求设置验证提示组件,添加消息提示组件也很方便的
    github地址:https://github.com/zh-huan/validate

    相关文章

      网友评论

        本文标题:vue-表单验证

        本文链接:https://www.haomeiwen.com/subject/jioulqtx.html