Vue 组件应用:仿Element UI中的Form组件 简单版

1,232 阅读1分钟

调用

  • 由Form, FormItem 及实现双向绑定的Input组成
<template>
    <div>
        <h3>Element表单</h3>
        <hr>
        <k-form :model="model" :rules="rules" ref="loginForm">
            <k-form-item label="用户名" prop="username">
                <k-input v-model="model.username" autocomplete="off" placeholder="输入用户名"></k-input>
            </k-form-item>
            <k-form-item label="确认密码" prop="password">
                <k-input type="password" v-model="model.password" autocomplete="off"  placeholder='请输入密码'></k-input>
            </k-form-item>
            <k-form-item>
                <button @click="submitForm('loginForm')">提交</button>
            </k-form-item>
        </k-form>
        {{model}}
    </div>
</template>

<script>
import KForm from "./Form";
import KFormItem from "./FormItem";
import KInput from "./Input";

export default {
    components: {
        KForm,
        KFormItem,
        KInput
    },
    data() {
        return {
            model: { username: "tom", password: "" },
            rules: {
                username: [{ required: true, message: "请输入用户名" }],
                password: [{ required: true, message: "请输入密码" }]
            }
        };
    },
    methods: {
        submitForm(form) {
            this.$refs[form].validate(valid => {
                if (valid) { alert('请求登录!') } else { alert('校验失败!') }
            });
        }
    }
};
</script>

Input

  • 双向绑定:@input、:value
  • 派发校验事件
  • v-bind='$attrs',实现非props属性绑定
<template>
    <div>
        <input :value="value" @input="onInput" v-bind="$attrs">
    </div>
</template>

<script>
export default {
    inheritAttrs: false,
    props: {
        value: {
            type: String,
            default: ''
        },
    },
    methods: {
        onInput(e) {
            this.$emit('input', e.target.value)
            // 派发校验事件
            this.$parent.$emit('validate')
        }
    },
}
</script>

FormItem

  • 为Input预留插槽 slot
  • 能够展示label和校验信息
  • 能够进行校验
<template>
    <div>
        <label v-if="label">{{label}}</label>
        <slot></slot>
        <p v-if="errorMessage">{{errorMessage}}</p>
    </div>
</template>

<script>
import Schema from 'async-validator'

export default {
    inject: ['form'],
    props: {
        label: {
            type: String,
            default: ''
        },
        prop: {
            type: String
        }
    },
    data() {
        return {
            errorMessage: ''
        }
    },
    mounted() {
        this.$on('validate', this.validate)
    },
    methods: {
        validate() {
            const value = this.form.model[this.prop]
            const rules = this.form.rules[this.prop]
            // npm i async-validator -S
            const desc = { [this.prop]: rules }
            const schema = new Schema(desc)
            // return的是校验结果的Promise
            return schema.validate({ [this.prop]: value }, error => {
                if (error) {
                    this.errorMessage = error[0].message
                } else {
                    this.errorMessage = ''
                }
            })
        }
    },
}
</script>

Form

  • 给FormIten预留插槽 slot
  • 设置数据和校验规则
  • 全局校验
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
export default {
  provide() {
    return {
      form: this
    };
  },
  props: {
    model: {
      type: Object,
      required: true
    },
    rules: {
      type: Object
    }
  },
  methods: {
    validate(cb) {
      const tasks = this.$children
        .filter(item => item.prop)
        .map(item => item.validate());

      // 所有任务都通过才算校验通过
      Promise.all(tasks)
        .then(() => cb(true))
        .catch(() => cb(false));
    }
  }
};
</script>