效果图以下:javascript
//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>复制代码
//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>复制代码
//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>
复制代码
//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> 复制代码
//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 = "";
}
}
);
}
}
复制代码
//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));
}
}
复制代码
//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("登陆失败");
}
});
}
}
复制代码
优化KFromItem内不是input的状况css
<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));
}
}
}
复制代码