实现Element通用基础表单组件

636 阅读1分钟

前言

实现一个跟Element-UI类似的表单组件,让我们可以了解开发通用组件的流程。 效果:Element表单

通用表单组件

  • Index
    <template>
        <el-form :model="model" :reules="rules" ref="loginForm">
            <el-form-item prop="username">
                <el-input t ype="text" v-model="model.username" plachedor="请输入用户名"></el-input>
            </el-form-item>
            <el-form-item prop="password">
                <el-input type="password" v-model="model.password" plachedor="请输入密码"></el-input>
            </el-form-item>
            <el-form-item>
                <button @click="login">登录</button>
            </el-form-item>
        </el-form>
    </template>
    <script>
        import elForm from 'el-form'
        import elFormItem from 'el-form-item'
        import elInput from 'el-input.vue'
        export default{
            components:{
                elForm,
                ekFormItem,
                elInput
            },
            data(){
                return{
                    model:{
                        username:"",
                        password:""
                    },
                    rules:{
                        username:[{required:true,message:"必须输入用户名"}],
                        password:[{required:true,message:"必须输入密码"}]
                    }
                }
            },
            methods:{
                login(){
                    this.$refs.loginForm.validate(isValid=>{
                           if(isValid){
                               alert("登录成功")
                           }else{
                               alert("登录失败")
                           }
                    })
                }
            }
        }
    </script>
  • el-form 指定数据,校验规则
    <template>
        <div>
            <slot></slot>
        </div>
    </template>
    <script>
        export default{
            provide(){
                return{
                    form:this //provide进行隔代传参。把整体内容进行传参,方便管理。因为传入的是响应式对象,所以接收方也是响应式的。
                }
            },
            props:{
                model:{
                    type:Object,
                    required:true
                },
                rules:Object
            },
            methods:{
                //cb是整体表单提交的回调函数
                validate(cb){
                    //所有子组件进行校验,过滤掉不需要校验的值
                    const tasks = this.$children.filter(item=>item.prop)
                        .map(item=>item.validate())
                    //tasks返回的是Promise数组,使用Promise.all进行并发处理
                    Promise.all(tasks).then(()=>{
                        cb(true)
                    }).catch(()=>{
                        cb(false)
                    })
                        
                }
            }
        }
    </script>
  • el-form-item
    <template>
        <div>
            <lable v-if="label">{{label}}</label>
            <slot></slot>
            <p v-if="error">{{error}}</p>
        </div>
    </template>
    <script>
        import Validator from 'async-validator' //使用async-validator库进行校验 npm install async-validator
        export default{
            inject:['form'],
            props:{
                label:{
                    type:String,
                    default:""
                },
                prop:{
                    type:String//prop的作用是获取到具体哪一项的内容数据
                }
            },
            data(){
                return{
                    error:""
                }
            },
            mounted(){
                //监听校验
                this.$on("validate",()=>{
                    this.validate()
                })
            },
            methods:{
                validate(){
                    const value = this.form.model[this.prop]//当前项校验规则
                    const rules = this.form.rules[this.prop]//当前项值
                    
                    const validator = new Validator({[this.prop]:rules})//创建一个校验器实例
                    //校验,返回Promise
                    return validator.validate({[this.prop]:value},errors=>{
                        if(errors){
                            this.error = errors[0].message
                        }else{
                            this.error = ""
                        }
                    })
                    
                }
            }
        }
    </script>
  • el-input
   <template>
       <div>
           <input :type="type" :value="value" @input="onInput" v-bind="$attrs">
       </div>
   </template>
   
   <script>
       export default{
           props:{
               type:{
                   type:String,
                   default:"text"
               },
               value:{
                   type:String,
                   default:""
               }
           },
           methos:{
               onInput(e){
                   this.$emit("input",e.target.value)
                   this.$parent.$emit("validate") //事件的派发者与监听者必须是同一角色。因为父组件的内容是使用slot进行内容分发,所以要使用$parent指向父级。$parent指FormItem
               }
           }
       }
   </script>

弹窗组件

弹窗这类组件的特点是它们在当前vue实例之外独立存在,通常挂载于body;它们是通过js动态创建的,不需要在任何组件中声明。