效果圖以下: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));
}
}
}
複製代碼