Bootstrap

vue+elementui 表单验证封装

el-form-item.js

function getPropByPath(obj, path, strict) {
  let tempObj = obj
  path = path.replace(/\[(\w+)\]/g, '.$1')
  path = path.replace(/^\./, '')

  let keyArr = path.split('.')
  let i = 0
  for (let len = keyArr.length; i < len - 1; ++i) {
    if (!tempObj && !strict) break
    let key = keyArr[i]
    if (key in tempObj) {
      tempObj = tempObj[key]
    } else {
      if (strict) {
        throw new Error('please transfer a valid prop path to form item!')
      }
      break
    }
  }
  return {
    o: tempObj,
    k: keyArr[i],
    v: tempObj ? tempObj[keyArr[i]] : null
  }
}

export default {
  props: {
    verify: {
      type: Array,
      default() {
        return []
      }
    }
  },
  methods: {
    getRules() {
      const result = this.getVerifyRules() || []

      if (result.length) {
        return result
      }

      let formRules = this.form.rules
      let selfRules = this.rules
      let requiredRule = this.required !== undefined
        ? { required: !!this.required }
        : []
      formRules = formRules ? getPropByPath(formRules, this.prop ||
        '').o[this.prop || ''] : []

      return [].concat(selfRules || formRules || []).concat(requiredRule)
    },
    getVerifyRules() {
      if (!this.verify.length) {
        return null
      }

      const result = []

      let label = this.label

      if (label[label.length - 1] === ':' || label[label.length - 1] === ':') {
        label = label.slice(0, -1)
      }

      this.verify.forEach(val => {
        if (val.length === 0) {
          return
        }
        const rule = factory(val, label).ruleGenerator()

        rule && result.push(rule)
      }, this)

      return result
    }
  }
}


/**
 * NotNull // 不能为空
 * Number // 必须为数字
 * Int // 必须为整数
 * PositiveInt //正整数
 * DateTime=yyyy-MM-dd hh:mm:ss // 必须是日期时间,yyyy-mm-dd hh:mm:ss
 * Email // 必须是Email
 * ZipCode // 必须是邮政编码
 * CnTel // 必须是固定电话(010-55095236,0335-6229586)
 * CnPhone // 必须是手机号码(15011245852)
 * IDCardNo // 必须身份证号码
 * Length=3 // 长度等于3
 * Regex=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} // 必须符合正则表达式
 * PositiveNumber  !/^\d+(\.\d{0,2})?$/   //必须非负的数字
 * TwoDecimalPlacesumber  !/^\d+(\.\d{0,2})?$/   //正数,且小数点后面最多两位
 * LetterNumber /^[a-zA-Z\d]+$/ 只能输入字母大小写和数字
 * BackCardNumber  /^([1-9]{1})(\d{14}|\d{18})$/ 请正确输入银行卡号
 * MaxDigit 判断任意字符最多多少位   写法MaxDigit89    最大89位
 * Chinese  // 必须中文
 * UniqueidCards   //判断重复的身份证
 * TwoDecimal  !/^0\.[1-9]{1,2}|0\.0[1-9]$/   // 折扣
 * */
class VerifyRule {
  constructor(label, message) {
    this.label = label
    this.message = message
  }

  ruleGenerator() {
    return null
  }
  validatorGeneratorIdcard(idCards, message) {

    return (rule, value, callback) => {
      if (!value || value === '') {
        callback()
        return
      } 
      const idCard = idCards?idCards:[];
      const isValid = idCard.some((item)=>{
          return value == item               
      })

      if (isValid) {
        callback(new Error(message))
      } else {
        callback()
      }  
    }
  }

  validatorGenerator(regex, message) {
    return (rule, value, callback) => {
      if (!value || value === '') {
        callback()
        return
      }

      const isValid = regex.test(value)

      if (!isValid) {
        callback(new Error(message))
      } else {
        callback()
      }
    }
  }
}

class NotNullRule extends VerifyRule {
  constructor(label, message) {
    message = message || '请输入' + label
    super(label, message)
  }

  ruleGenerator() {
    return {
      required: true,
      message: this.message,
      trigger: ['blur', 'change']
    }
  }
}

class RegexStaticRule extends VerifyRule {
  constructor(label, message, regExp) {
    super(label, message)
    this.regExp = regExp
  }

  ruleGenerator() {
    const validator = super.validatorGenerator(this.regExp, this.message)

    return {
      validator: validator,
      trigger: ['blur', 'change']
    }
  }
}
// 仅用于劳务稿酬提示身份证是否重复
class RegexStaticRuleIDCard extends VerifyRule {
  constructor(label, message, nuiIDCard) {
    super(label, message)
    this.nuiIDCard = nuiIDCard 
  }

  ruleGenerator() {
    const validator = super.validatorGeneratorIdcard(this.nuiIDCard,this.message);
    
    // const validator = (rule, value, callback) => { 
    //   if (window.filterUniqueidCards) {
    //     window.filterUniqueidCards.filter((item)=>{
    //       if(value == item){
            
    //         callback(this.message);
            
    //       }
    //     })
    //   }else {
    //     callback();
    //   }
    // };

    return {
      validator: validator,
      trigger: ['blur', 'change']
    }
  }
}

class RegexRule extends VerifyRule {
  constructor(label, message, exp) {
    super(label, message)
    this.exp = exp
  }

  ruleGenerator() {
    let regExp
    try {
      regExp = new RegExp(this.exp)
    } catch (e) {
      return null
    }

    const validator = super.validatorGenerator(regExp, this.message)

    return {
      validator: validator,
      trigger: ['blur', 'change']
    }
  }
}

class LengthRule extends VerifyRule {
  constructor(label, message, size) {
    super(label, message)
    this.min = size.min
    this.max = size.max
  }

  ruleGenerator() {
    return {
      min: this.min,
      max: this.max,
      message: this.message,
      trigger: ['blur', 'change']
    }
  }
}

class DateTimeRule extends VerifyRule {
  constructor(label, message, dateFormat) {
    super(label, message)
    this.dateFormat = dateFormat
  }

  ruleGenerator() {
    let regExp
    let exp = '^' + this.dateFormat + '$'

    exp = exp.replace('yyyy', '[0-9]{4}')
    exp = exp.replace('yy', '[0-9]{2}')
    exp = exp.replace('MM', '[0-9]{2}')
    exp = exp.replace('dd', '[0-9]{2}')
    exp = exp.replace('hh', '[0-9]{2}')
    exp = exp.replace('mm', '[0-9]{2}')
    exp = exp.replace('ss', '[0-9]{2}')

    try {
      regExp = new RegExp(exp)
    } catch (e) {
      return null
    }

    const validator = super.validatorGenerator(regExp, this.message)

    return {
      validator: validator,
      trigger: ['blur', 'change']
    }
  }
}

function parseParam(param) {
  let result = {
    value: '',
    message: null
  }

  if (param.indexOf('|') === -1) {
    result.value = param
    return result
  }

  const array = param.split('|')

  result.value = array[0]
  result.message = array[1]

  return result
}

function parseRegexParam(param) {
  let result = {
    value: '',
    message: null
  }

  if (param.indexOf('$|') === -1) {
    result.value = param
    return result
  }

  const array = param.split('$|')

  result.value = array[0] + '$'
  result.message = array[1]

  return result
}

function factory(type, label) {
  let param

  if (type.startsWith('NotNull')) {
    param = parseParam(type)
    return new NotNullRule(label, param.message)
  }

  if (type.startsWith('Number')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '必须是数字', /(^-?\d+\.?\d+$)|(^-?\d+$)/)
  }

  if (type.startsWith('Int')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '必须是整数', new RegExp('^-?\\d+$'))
  }

  if (type.startsWith('PositiveInt')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '必须是正整数', /^[1-9]\d*$/)
  }

  if (type.startsWith('Chinese')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '必须是汉字', /^[\u4e00-\u9fa5]*$/)
  }

  if (type.startsWith('Email')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||  '请输入正确格式的email',
      /^([-a-zA-Z0-9_.])+@(([-a-zA-Z0-9])+\.)+([a-zA-Z0-9]{2,4})+$/)
  }

  if (type.startsWith('ZipCode')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||   '请输入正确的邮政编码', /^[1-9][0-9]{5}$/)
  }

  if (type.startsWith('CnTel')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||  '请输入正确的固定电话',
      /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/)
  }

  if (type.startsWith('PositiveNumber')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '为非负的正数',/^[0-9]+$/)
  }

  if (type.startsWith('TwoDecimalPlacesumber')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||  '最多千万级大于0的数字,保留两位小数',
    /^[1-9]\d{0,7}(\.\d{1,2})?$|^0(\.\d{1,2})?$/) 
  }

  if (type.startsWith('numberlacesumber')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||  '千万级以内的数字',
    /^-?[0-9]\d{0,7}(\.\d{1,2})?$|^0(\.\d{1,2})?$/) 
  }
  
  if (type.startsWith('UniqueidCards')) {
    param = parseParam(type) 
    return new RegexStaticRuleIDCard(label, param.message ||  '当前身份证号与其他的重复',window.filterUniqueidCards)
  }

  if (type.startsWith('UniqueinvoiceNos')) {
    param = parseParam(type) 
    return new RegexStaticRuleIDCard(label, param.message ||  '当前发票号码与其他的重复',window.filterUniqueinvoiceNos)
  }

  if (type.startsWith('UniqueUserNos')) {
    param = parseParam(type) 
    return new RegexStaticRuleIDCard(label, param.message ||  '当前领款人与其他的重复',window.filterUniqueinvoiceNos)
  }

  if (type.startsWith('MaxNumberNegativeLength11')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||  '最多千万级数字,保留两位小数',
    /^[-]{0,1}[1-9]\d{0,7}(\.\d{1,2})?$|^[-]{0,1}0(\.\d{1,2})?$/)
  }

  if (type.startsWith('MaxDigit')) {
    const ltParams = type.split('MaxDigit')
    param = parseParam(ltParams[1])
    let myReg = new RegExp(`^[0-9\u4e00-\u9fffa-zA-Z.]{1,${ltParams[1]}}$`)
    return new RegexStaticRule(label, param.message || label + `最多${ltParams[1]}位`,myReg)
  }


  if (type.startsWith('LetterNumber')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '只能输入大小字母和数字',
    /^[a-zA-Z\d]+$/)
  }

  if (type.startsWith('BackCardNumber')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || '请输入正确的',
    /^([1-9]{1})(\d{14}|\d{18})$/)
  }

  if (type.startsWith('MaxNumberLength40')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '最多40位',
    /^.{0,40}$/)
  }
  if (type.startsWith('MaxNumberLength30')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '最多30位',
    /^.{0,30}$/)
  }
  if (type.startsWith('MaxNumberLength10')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '最多10位',
    /^.{0,10}$/)
  }
  if (type.startsWith('MaxNumberLength15')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || label + '最多15位',
    /^.{0,15}$/)
  }

  if (type.startsWith('CnPhone')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message || '请输入正确手机号码', /^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$/)
  }

  if (type.startsWith('IDCardNo')) {
    param = parseParam(type)
    return new RegexStaticRule(label, param.message ||    '请输入正确身份证号码', /(^\d{15}$)|(^\d{17}([0-9]|X)$)/)
  }

  if (type.startsWith('Regex=')) {
    const exp = type.split('=')
    param = parseRegexParam(exp[1])
    return new RegexRule(label, param.message || label + '必须符合正则表达式', param.value)
  }

  if (type.startsWith('Length=')) {
    const eqParams = type.split('=')
    param = parseParam(eqParams[1])
    return new LengthRule(label, param.message || label + '长度必须等于' + param.value, {
      min: parseInt(param.value),
      max: parseInt(param.value)
    })
  }

  if (type.startsWith('Length>')) {
    const gtParams = type.split('>')
    param = parseParam(gtParams[1])
    return new LengthRule(label, param.message || label + '长度必须大于' + param.value, {
      min: parseInt(param.value) + 1,
      max: null
    })
  }

  if (type.startsWith('Length<')) {
    const ltParams = type.split('<')
    param = parseParam(ltParams[1])
    let myReg = new RegExp(`^(.|\r|\n|\s){1,${ltParams[1]}}$`) 
    return new RegexStaticRule(label, param.message || label + `最多${ltParams[1]}位`,myReg)
  }

  if (type.startsWith('DateTime=')) {
    const dateFormat = type.split('=')
    param = parseParam(dateFormat[1])
    return new DateTimeRule(label, param.message || label + '必须是日期时间,' +
      param.value, param.value)
  }

  if (type.startsWith('TwoDecimal')) {
    param = parseParam(type)   
    return new RegexStaticRule(label, param.message || label + '取值在0-1中间,最多2位小数', /(^[0-1]{1}([.]([0-9]){1,2})?$)/)
  }

  return null
}

index.js

import ElFormItemMixin from './el-form-item.js'

const mixins = {
  install: Vue => {
    // 对 el-form-item 验证增强
    Vue.component('ElFormItem').mixin(ElFormItemMixin)
  }
}

// 导出组件
export default mixins

main.js

import MixinELEMENT from './mixins/index.js'
Vue.use(MixinELEMENT)

目录结构
在这里插入图片描述

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;