封装思路:

1、复制表单模板:

1)统计自己项目都有哪些表单项,然后从Element UI组件库中将自己所需表单项的模板复制下来。与此同时建立两个组件,测试组件Test.vue和封装组件Test.vue。
2)在父组件 Test.vue 中,定义一个组件状态(数据)formData对象,这个对象包含了自己所需的所有表单项。比如:表单的样式、所有类型的表单、按钮(提交、重置)、校验规则。

2、一个个改造(文本、下拉、单选、复选等):

1)在子组件中对每个表单项进行改造。
2)在父组件中,在foemData对象中,定义一个属性,用来代表所有类型的表单,这个属性是一个数组类型,数组中是一个个对象,每一个对象代表一个类型的表单。每个对象中通过添加type属性,来判断在子组件中渲染哪个类型的表单。
3)在子组件中,获取到formDate对象中代表所有表单类型的这个属性,然后进行v-for动态渲染表单的每一项数据。

3、按钮渲染

1)在父组件中,在foemData对象中,定义一个属性,用来代表所有类型的按钮,这个属性是一个数组类型,数组中是一个个对象,每一个对象代表一种按钮。每个对象需要传入action属性和call方法。分别用来代表按钮的功能以及表单验证成功后所执行的回调。
2)在子组件中,获取到formDate对象中代表所有类型的按钮的这个属性,然后进行v-for动态渲染表单的每一项数据。

4、默认值的设定(修改表单的时候)

1)通过watch监听父组件传过来的formData对象,在监听的回调函数中调用提前在methods中定义好的方法,在这个方法里面主要做的就是对子组件中的每个表单项的默认值进行设置。

5、rules规则的处理

1)规则只需要将组件库中的模板规则通过父组件传到子组件中即可,子组件可直接使用。

1.Test.vue测试组件

<template>
    <div style="margin: 30px">
    <Form :data="formData"></Form>
    </div>
</template>

<script>
    import Form from "./Form";

    export default {
        name: "Test",
        components:{Form},
        data(){
            return {
                formData:{
                    width:'100px',
                    items:[
                        {
                            type:'Input',
                            label:'活动名称',
                            prop:'name',
                            width:'240px',
                            placeholder:'请填写活动名称',
                            default:'请'
                        },
                        {
                            type:'Select',
                            label:'活动区域',
                            prop:'region',
                            placeholder:'请选择活动区域',
                            default:'beijing',
                            options:[
                                {
                                    label:'上海',
                                    value:'shanghai'
                                },
                                {
                                    label:'北京',
                                    value:'beijing'
                                }
                            ]
                        },
                        {
                            type:'Switch',
                            label:'即时配送',
                            prop:'delivery',
                            default: true
                        },
                        {
                            type:'Checkbox',
                            label:'活动性质',
                            prop:'type',
                            default:'1',
                            options:[
                                {
                                    label:'线下主题活动',
                                    value:'1'
                                },
                                {
                                    label:'单纯品牌曝光',
                                    value:'2'
                                }
                            ]
                        },
                        {
                            type:'Radio',
                            label:'特殊资源',
                            prop:'resource',
                            default:'b',
                            options:[
                                {
                                    label:'线上品牌商赞助',
                                    value:'a'
                                },
                                {
                                    label:'线下场地免费',
                                    value:'b'
                                }
                            ]
                        },
                        {
                            type:'Textarea',
                            label:'活动形式',
                            prop:'desc',
                            placeholder:'请填写活动形式',
                            default: '活动'
                            //span:5
                        },
                        {
                            type:'Date',
                            label:'活动日期',
                            prop:'date1',
                            placeholder:'请选择活动日期',
                            default: '2022-6-1'

                        },
                        {
                            type:'Time',
                            label:'活动时间',
                            prop:'date2',
                            placeholder:'请选择活动时间',
                            default: '2022-6-2 12:00:01'

                        },
                        {
                            type:'Datetime',
                            label:'活动日期时间',
                            prop:'date3',
                            placeholder:'请选择活动日期时间',
                            default: '2022-6-1 12:00:03'

                        }
                    ],
                    buttons:[
                        {
                            label:'确定',
                            type:'primary',
                            action:'submit',
                            call:(data)=>{
                                console.log(data)
                            }
                        },
                        {
                            label:'重置',
                            type:'primary',
                            action:'reset',
                            call:()=>{
                                console.log('reset')
                            }
                        },
                        {
                            label:'其他',
                            type:'primary',
                            call:(data)=>{
                                console.log(data)
                            }
                        }
                    ],
                    rules:{
                        name: [
                            { required: true, message: '请输入活动名称', trigger: 'blur' },
                            { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
                        ],
                        region: [
                            { required: true, message: '请选择活动区域', trigger: 'change' }
                        ],
                        date1: [
                            { type: 'date', required: true, message: '请选择日期', trigger: 'change' }
                        ],
                        date2: [
                            { type: 'date', required: true, message: '请选择时间', trigger: 'change' }
                        ],
                        type: [
                            { type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }
                        ],
                        resource: [
                            { required: true, message: '请选择活动资源', trigger: 'change' }
                        ],
                        desc: [
                            { required: true, message: '请填写活动形式', trigger: 'blur' }
                        ]
                    }
                }
            }
        }
    }
</script>

2.封装的表单组件form.vue

<template>
    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" :label-width="data.width" >


        <el-form-item :label="item.label" :prop="item.prop" v-for="(item,k) in data.items" :key="k" v-show="!item.hide">

            <template v-if="item.type=='slot'">
                <slot :name="item.slot_name"></slot>
            </template>

            <el-input v-model="ruleForm[item.prop]" :placeholder="item.placeholder" v-if="item.type=='Input'"
                      :style="{width: item.width}" :readonly="item.readonly" :show-password="item.password" v-show="!item.hide"></el-input>

            <el-select v-model="ruleForm[item.prop]" :placeholder="item.placeholder" v-if="item.type=='Select'">
                <el-option :label="o.label" :value="o.value" v-for="(o,i) in item.options" :key="i"></el-option>
            </el-select>

            <el-switch v-model="ruleForm[item.prop]" v-if="item.type=='Switch'"></el-switch>


            <el-checkbox-group v-model="ruleForm[item.prop]" v-if="item.type=='Checkbox'">
                <el-checkbox :label="o.value" v-for="(o,k) in item.options" :key="k" >{{o.label}}</el-checkbox>
            </el-checkbox-group>

            <el-radio-group v-model="ruleForm[item.prop]" v-if="item.type=='Radio'">
                <el-radio :label="o.value" v-for="(o,k) in item.options" :key="k" >{{o.label}}</el-radio>
            </el-radio-group>

            <el-col :span="item.span || 10">
                <el-input type="textarea" v-model="ruleForm[item.prop]" :placeholder="item.placeholder" v-if="item.type=='Textarea'"></el-input>
            </el-col>

            <el-col :span="item.span || 5">
                <el-date-picker type="date" :placeholder="item.placeholder" v-model="ruleForm[item.prop]" style="width: 100%;" v-if="item.type=='Date'"></el-date-picker>
            </el-col>
            <el-col :span="item.span || 5">
                <el-time-picker :placeholder="item.placeholder" v-model="ruleForm[item.prop]" style="width: 100%;" v-if="item.type=='Time'"></el-time-picker>
            </el-col>
            <el-col :span="item.span || 5">
                <el-date-picker type="datetime" :placeholder="item.placeholder" v-model="ruleForm[item.prop]" style="width: 100%;" v-if="item.type=='Datetime'"></el-date-picker>
            </el-col>


        </el-form-item>

        <el-form-item>
            <el-button :type="b.type" @click="callSelf('ruleForm',b.action,b.call)" v-for="(b,k) in data.buttons" :key="k">{{b.label}}</el-button>
        </el-form-item>
    </el-form>
</template>

<script>
    export default {
        name: "Form",
        data() {
            return {
                ruleForm: {
                },
                rules: {
                },
                watchKes:[]
            };
        },
        props:{
            data:Object
        },
        methods: {
            callSelf(formName,action,callback){
                if(action=='submit'){
                    this.submitForm(formName,callback)
                }else if(action=='reset'){
                    this.resetForm()
                    callback && callback()
                }else{
                    callback && callback()
                }
            },
            submitForm(formName,callback) {
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        console.log('submit!');
                        callback && callback(this.ruleForm)
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },
            setForm(row){
                for (let key in row) {
                    this.ruleForm[key]=row[key]
                }
            },
            resetForm() {
                this.$refs.ruleForm.resetFields();
            },
            init(data){
                let form={}
                let box=[]
                data.items.forEach((item)=>{
                    switch (item.type) {
                        case 'Checkbox':
                            if(item.default){
                                if(Array.isArray(item.default)){
                                    box = box.concat(item.default)
                                }else{
                                    box.push(item.default)
                                }
                            }
                            form[item.prop]=box
                            break

                        case 'Date':
                        case 'Datetime':
                        case 'Time':
                            if(item.default){
                                form[item.prop]=new Date(item.default)
                            }

                            break
                        default:
                            form[item.prop]=item.default
                            break
                    }

                })

                this.ruleForm = form

                this.rules =  this.data.rules
            },
        },
        watch:{
            data:{
                handler(newVal){
                    //console.log(newVal)
                    this.init(newVal)
                },
                immediate:true,
                deep:true
            }
        },
        created() {
           // this.init()
        }
    }
</script>

<style scoped>

</style>