Form表单校验封装

2,164 阅读2分钟

2 form表单校验

  1. 整体思路: form.vue组件包含rulesfieldListformItem.vue组件包含filename,labelinput.vue包含 value,将input.vue组件中的input,blur, foucs事件emitformItem.vue组件中,form.vue中的rules.name对象与filename相对应,所有的校验都在formItem中判断。

​ 2.Form组件

  • Form组件作为最外层容器,需要将rules规则和fieldList带入,在created生命周期中获取所有子实例FromItem

    rules:
          {
            username: [
              {
                require: true,
                message: '用户名不能为空',
                trigger: 'blur'
              },
            ],
            email: [
              {
                require: true,
                message: '邮箱不能为空',
                trigger: 'blur'
              },
              {
                type: 'email',
                message: '邮箱格式不正确',
                trigger: 'blur'
              }
            ]
          },
          filedList: {
            name: '',
            email: ''
          }
    
  • 组件基本结构,slot

    <template>
      <div class="form">
        <slot></slot>
      </div>
    </template>
    
  1. FormItem组件

    <template>
      <div class="form-item">
        <label v-if="label" :class="{'label-red': isRequire}">{{ label }}</label>
        <slot></slot>
        <p v-show="errTip" style="color: red">{{ errTip }}</p>
      </div>
    </template>
    
    • 需要监听input组件的实时变化,blur失去焦点时需要校验,focus需要将错误的提示信息去掉,
    • 需要获取Form组件的data值,获取相对应的规则this.parentRules = this.$parent.rules[this.filename] || [];
  2. input组件

    <template>
      <input
        type="text"
        :value="currentValue"
        @input="handleInput"
        @blur="handleBlur"
        @focus="handleFocus"
      >
    </template>
    
    • 结合语法糖v-model,将数据通过$emit上发,this.$parent.$emit('onFoucsInput') // 失去焦点时校验
  3. 完整的组件代码

    • input
    <template>
      <input
        type="text"
        :value="currentValue"
        @input="handleChange"
        @blur="handleBlur"
        @focus="handleFocus"
      >
    </template>
    
    <script>
    export default {
      name: 'Input',
      props: {
        value: {
          type: String | Number
        }
      },
      data() {
        return {
          currentValue: ''
        }
      },
      watch: {
        value(newval) {
          this.currentValue = newval
        }
      },
      mounted() {
        
      },
      methods: {
        handleChange(e) {
          const value = e.target.value;
          this.$emit('input', value)
          this.$parent.$emit('onChangeInput', value) // 结合$on$emit
        },
        handleBlur() {
          this.$parent.$emit('onBlurInput', this.currentValue) // 失去焦点时校验
        },
        handleFocus() {
          this.$parent.$emit('onFoucsInput') // 失去焦点时校验
        }
      }
    }
    </script>
    
    <style>
    
    </style>
    
    • FormItem组件
    <template>
      <div class="form-item">
        <label v-if="label" :class="{'label-red': isRequire}">{{ label }}</label>
        <slot></slot>
        <p v-show="errTip" style="color: red">{{ errTip }}</p>
      </div>
    </template>
    
    <script>
    import {checkType} from '../checkType.js'
    export default {
      name: 'formItem',
      props: {
        filename: [String], // 对应哪个规则
        label: [String]
      },
      data() {
        return {
          parentRules: [],
          checking: '',
          errTip: '',
          currentVal: '', // 当前值
          isRequire: false, // 必传
        }
      },
      created() {
      },
      mounted() {
        // 获取父级元素的rules,对应自己的校验规则
        // console.log(this.$parent.rules[this.filename], this.filename)
        this.parentRules = this.$parent.rules[this.filename] || [];
        this.isRequire = this.parentRules.some(rule => rule.require);
        this.init();
      },
      methods: {
        init() {
          this.$on('onChangeInput', this.handleChange)
          this.$on('onBlurInput', this.handleBlur)
          this.$on('onFoucsInput', this.onFoucsInput) // 清除错误状态
        },
        // 过滤带有trigger的rules数据,如果没有,则不用验证,有则需要验证
        filterRules(trigger) {
          return this.parentRules.filter((item) => {
            return item.trigger && item.trigger.indexOf(trigger) > -1;
          })
        },
        // 处理过滤数据,封装校验规则
        handleVaticad(trigger, callback=function(){}) {
          const rules = this.filterRules(trigger);
          if(!rules && !rules.length) {
            return 'error'
          }
          // 判断规则
          for(let i = 0; i<rules.length; i++) {
            if(this.checking === 'error') {
              break
            }
            this.handleCheckAllType(rules[i])
          }
          return this.checking
        },
        // 统一的判断规则,非空,和校验
        handleCheckAllType(obj) {
          if((obj.require && !this.currentVal) || (obj.type && !checkType.check(this.currentVal, obj.type))) {
            this.errTip = obj.message;
            this.checking = 'error';
            return false;
          }
          // if(obj.type && !checkType.check(this.currentVal, obj.type)){
          //   this.errTip = obj.message;
          //   this.checking = 'error';
          //   return false;
          // }
          this.checking = 'success';
        },
        handleChange(val) {
          // console.log({val})
        },
        handleBlur(val) {
          this.currentVal = val;
          this.handleVaticad('blur')
        },
        onFoucsInput() {
          this.errTip = '';
          this.checking = '';
        }
      }
    }
    </script>
    
    <style scoped>
      .label-red {
        position: relative;
      }
      .label-red::after {
        content: '*';
        color: red;
        position: absolute;
        left: -9px;
        top: 0;
        font-size: 14px;
      }
    </style>
    
  4. form组件

    <template>
      <div class="form">
        <slot></slot>
      </div>
    </template>
    
    <script>
    export default {
      name: 'formx',
      props: {
        rules: [Object], // 规则数组
        fieldList: [Object], // 包含的input的model值
      },
      created() {
        // 由于组件是从内到外渲染的,所以子组件渲染好之后,可在created中获取所有子组件的实例
        this.arrList = this.$children
      },
      data() {
        return {
          arrList: []
        }
      },
      methods: {
        // 需要有一个点击校验所有的规则,做一个回调功能的promise
        handleFormItemRules() {
          return new Promise((resolve) => {
            let checking = 'success';
            this.arrList.forEach((item) => {
              item.handleVaticad('blur')
              if(item.checking === 'error') {
                checking = item.checking;
              }
            })
            resolve(checking)
          })
        }
      }
    }
    </script>
    
    <style>
    
    </style>
    
  5. checkType文件

    export const checkType = (function () {
      const rules = {
        isNumber: /^[0-9]*$/,
        email: /^([a-zA-Z0-9]+[-|_|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[-|_|.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/
      }
      return {
        check(str, type) {
          return rules[type].test(str)
        },
        addRules(rule, type) {
          rules[type] = rule;
        }
      }
    })()
    
  6. 最后效果图