vue-表单验证

涉及的技术: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、使用




接口说明

参数名 参数说明 类型 可选值 默认值
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-表单验证)