效果图如下:
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));
}
}
}