vue实现复杂的循环表单

865 阅读2分钟

前言

在我们的日常开发中,表单需求非常常见,可能会碰到一些比较复杂的情况,我们可以参考下面循环表单的实现得到一丝丝启发~

一、先看看效果

bf05c418-3ac6-4a15-b774-2655f6f750da.gif

二、循环表单的实现

其实实现也比较简单,这里以elementUI的form表单为例。利用vue数据响应式原理,双向绑定,引用类型的特性实现。话不多说,直接上码。

<template>
    <div class="content_style">
        <div v-for="form in ruleForm" :key="form.key" class="all_content">
            <el-button class="delete_class_form" type="text" @click="deleteForm(ruleForm, form.key)">删除</el-button>
            <div class="form1_content">
                <el-form
                    ref="ruleForm1"
                    class="form"
                    :model="form.ruleForm1"
                    label-width="100px"
                >
                    <el-form-item label="清单名称" prop="name" :rules="rules.name">
                        <el-input v-model="form.ruleForm1.name"></el-input>
                    </el-form-item>
                    <el-form-item label="清单类型" prop="type">
                        <el-select v-model="form.ruleForm1.type" placeholder="请选择">
                            <el-option
                                v-for="item in options"
                                :key="item.value"
                                :label="item.label"
                                :value="item.value"
                            >
                            </el-option>
                        </el-select>
                    </el-form-item>
                </el-form>
            </div>
            <div>
                <div v-for="(item, j) in form.ruleForm2" :key="item.key" class="form2_content">
                    <el-form class="form" :ref="`ruleForm2-${item.key}`" :model="item" label-width="100px">
                        <el-button class="delete_class_form2" type="text" @click="deleteForm(form.ruleForm2, item.key)">删除</el-button>
                        <el-form-item :label="`活动名称${j+1}`" prop="name" :rules="rules.name">
                            <el-input v-model="item.name"></el-input>
                        </el-form-item>
                        <el-form-item label="活动类型" prop="type">
                            <el-select v-model="item.type" placeholder="请选择">
                                <el-option
                                    v-for="val in options"
                                    :key="val.value"
                                    :label="val.label"
                                    :value="val.value"
                                >
                                </el-option>
                            </el-select>
                        </el-form-item>
                        <el-form-item v-for="(extra, k) in item.extraInfo" :key="extra.key" :label="`备注${k+1}`" :prop="`extraInfo.${k}.value`" :rules="rules.extraInfo">
                            <el-input v-model="extra.value"></el-input>
                            <el-button v-if="item.extraInfo.length !== 1" type="text" icon="el-icon-minus" class="delete_extra_class" @click="deleteForm(item.extraInfo, extra.key)"></el-button>
                            <el-button v-if="k === item.extraInfo.length-1" type="text" icon="el-icon-plus" class="add_extra_class" @click="addForm(item.extraInfo, 'extra')"></el-button>
                        </el-form-item>
                    </el-form>
                </div>
                <el-button plain type="text" icon="el-icon-plus" class="add_form2_class" @click="addForm(form.ruleForm2, 'form2')"></el-button>
            </div>
        </div>
        <el-button plain type="text" icon="el-icon-plus" class="add_form_class" @click="addForm(ruleForm, 'form')"></el-button>
        <div class="button_group">
            <el-button type="primary" class="reset" size="small" @click="reset">重置</el-button>
            <el-button type="primary" class="confirm" size="small" @click="confirm">确定</el-button>
        </div>
    </div>
</template>
export default {
    data() {
        return {
            ruleForm: [this.getDefaultForm(new Date().getTime())],
            options: [
                {value: '1',label: '黄金糕'}, 
                {value: '2',label: '双皮奶'}, 
                {value: '3',label: '蚵仔煎'}, 
                {value: '4',label: '龙须面'}, 
                {value: '5',label: '北京烤鸭'}
            ],
            rules: {
                name: [{required: true, message: '名称必填', trigger: ['change', 'blur']}],
                extraInfo: [{required: true, message: '备注必填', trigger: ['change', 'blur']}],
            }
        }
    },
    methods: {
        getDefaultForm2(baseKey) {
            return {
                name: '', 
                type:'', 
                key: `${baseKey}-form2`, 
                extraInfo: [this.getDefaultExtra(baseKey)]
            }
        },
        getDefaultExtra(baseKey) {
            return {
                value:'', 
                key: `${baseKey}-extra`
            }
        },
        getDefaultForm(baseKey) {
            return {
                ruleForm1: {name: '', type: ''},
                ruleForm2: [this.getDefaultForm2(baseKey)],
                key: `${baseKey}-form1`
            }
        },
        deleteForm(item, key) {
            const index = item.findIndex(v => v.key === key)
            if(index !== -1) {
                item.splice(index, 1)
            }
        },
        addForm(item, type) {
            const baseKey = new Date().getTime()
            const addMethod = {
                extra: this.getDefaultExtra,
                form: this.getDefaultForm,
                form2: this.getDefaultForm2
            }[type]
            item.push(addMethod(baseKey))
        },
        reset() {
            this.ruleForm = [this.getDefaultForm(new Date().getTime())];
        },
        confirm() {
            this.$confirm('确定提交?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                this.$message({
                    type: 'success',
                    message: '提交成功'
                });
            }).catch(() => {});
        }
    }
    
}

需要注意的是因为要涉及到增删改,所以需要给数组的每个对象设置一个key值,而且这个key在同一级数组中还要是唯一的,那么就可以利用时间戳来实现,或者es6新增的symbol来实现,方便后续的增删。而增删又用到了引用类型数据的特点,方法入参传过去的是引用地址,所以后续只要不是深拷贝,针对该参数的改变实际上都是针对同一个引用地址数据的改变。

同时还要注意的一点就是如果需要表单校验生效的话,需要保证每个表单的prop是正确的,不然后续校验就找不到对应的绑定变量了。

三、总结

复杂的循环表单的实现其实也不难,多看多思考就行了哈~ 可以参照demo,github链接:github.com/qiangguangl…

下期预告

针对以上的循环表单,其实涉及到了多个el-form,那么我们最后在提交表单的时候需要对表单都进行校验,但因为是for循环出来的,所以表单具体的ref我们也不知道有多少个,具体怎么校验就是下期的内容啦~

针对循环表单,我们如何全部校验呢?敬请期待~