Vue-elementUI-From组件实现谁校验?

1,190 阅读2分钟


效果图如下:


1、先创建一个input并添加$emit

//input.vue

<template>
  <!-- v-bind="$attrs" 的作用是直接使用父级传过来的属性 -->
  <input :type="type" @input="onInput" :value="value" v-bind="$attrs" />
</template> 
<script>
export default {
  inheritAttrs: false,
  props: {
    type: {
      type: String,
      default: "text"
    },
    value: {
      type: String,
      default: ""
    }
  },
  methods: {
    onInput(e) {
      this.$emit("input", e.target.value);
      //通知校验
      this.$parent.$emit("validate");
    }
  }
};
</script>

2、FromItem校验

2.1获取input中$emit

//FromItem.vue
<div class="from-item">
    <label class="label" v-if="label">{{label}}</label>
    <slot></slot>
    <!-- 需要input触发校验 -->
    <div class="error" v-if="error">{{error}}</div>
  </div>
</template>
<script>
import Schema from "async-validator";
export default {
  inject: ["form"],
  componentName: "k-form-item",
  props: {
    label: {
      type: String,
      default: ""
    },
    prop: {
      type: String
    }
  },
  data() {
    return {
      error: ""
    };
  },
  mounted() {
   //对应input中$emit
    this.$on("validate", () => {
      this.validate();
    });
  },
  methods: {
    validate() {
      //执行校验
      );
    }
  }
};
</script>
<style scoped>
.from-item {
  margin: 10px;
  display: flex;
  justify-content: center;
}
.label {
  padding-right: 10px;
  min-width: 90px;
  text-align: right;
}
.label::after {
  content: ":";
}
.error {
  padding-left: 20px;
  color: #f00;
}
</style>

2.2 通过From的provide获取校验规则

//From.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>
<script>
export default {
  provide() {
    return {
      form: this
    };
  },
  props: {
    model: {
      type: Object,
      required: true
    },
    rules: {
      type: Object
    }
  },
};
</script>

2.3 组件拼接index.vue

//index.vue

<template>
  <div>
    <KForm :model="model" :rules="rules" ref="loginForm">
      <KFromItem label="用户名" prop="username">
        <!--子集可通过 v-bind="$attrs" 获取placeholder="请输入用户名" 此类信息  -->
          <k-input v-model="model.username" placeholder="请输入用户名"></k-input>
      </KFromItem>
      <KFromItem label="密码" prop="password">
        <k-input v-model="model.password" type="password" placeholder="请输入密码"></k-input>
      </KFromItem>
      <KFromItem>
        <button @click="onLogin">登录</button>
      </KFromItem>
    </KForm>
    {{model}}
  </div>
</template>
<script>
import KInput from "./KInput.vue";
import KFromItem from "./KFromItem";
import KForm from "./KForm";
export default {
  data() {
    return {
      model: { username: "wxy", password: "" },
      rules: {
        username: { required: true, message: "必填项" },
        password: { required: true, message: "必填项" }
      }
    };
  },
  components: {
    KInput,
    KFromItem,
    KForm,
    Notice
  },
};
</script>

2.4 FromItem获取校验使用import Schema from "async-validator"校验

//FromItem.vue

methods: {
    validate() {
      //获取校验规则和当前值
      //此处使用form需要inject到form
      //this.prop需要提前声明
      const rules = this.form.rules[this.prop];
      const value = this.form.model[this.prop];

      //创建Schema实例
      const schema = new Schema({
        [this.prop]: rules
      });

      //使用该实例执行校验
      return schema.validate(
        {
          [this.prop]: value
        },
        errors => {
          if (errors) {
            this.error = errors[0].message;
          } else {
            this.error = "";
          }
        }
      );
    }
  }

3、外部调用添加From整体校验

3.1 添加From整体校验

//From.vue 
methods: {
    validate(cb) {
      // 调用所有formitem的validate,只要一个失败就失败
      // 结果是Promise数组
      const task = this.$children
        .filter(item => !!item.prop)
        .map(item => item.validate());
      console.log("TCL: validate -> task", task);

      // 判断所有结果
      Promise.all(task)
        .then(() => cb(true))
        .catch(() => cb(false));
    }
  }

3.2 调用From中的校验

//index.vue

<KFromItem>
        <button @click="onLogin">登录</button>
</KFromItem>

 methods: {
    onLogin() {
      this.$refs.loginForm.validate(isValid => {
        console.log("TCL: onLogin -> isValid", isValid);
        if (isValid) {
          alert("请求登录");
        } else {
          alert("登录失败");
        }
      });
    }
  }

4、优化input派发事件

优化KFromItem内不是input的情况

 <KFromItem label="密码" prop="password">
	<div>
      		  <k-input v-model="model.password" type="password" placeholder="请输入密码"></k-input>
	<div>
</KFromItem>


methods: {
    onInput(e) {
      this.$emit("input", e.target.value);
      //this.$parent.$emit("validate");
      this.dispatch("k-form-item", "validate");
    },
 dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;
      //如果父级存在        name存在或者name不等于传入的componentName都进入循环

      while (parent && (!name || name !== componentName)) {
        //新的父级元素
        parent = parent.$parent;
        if (parent) {
          //存在父级,就把新的父级的名字就重新赋值
          name = parent.$options.componentName;
        }
      }
      //结束循环条件 parent 不存在   &&  name不存在&&name==compontName
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
}