Vue生成动态表单(input,radio,checkbox,select),并动态添加验证规则

本文介绍如何根据后台配置动态生成包含不同组件(如单行文本、文本域、单选、多选、下拉选择)的表单,并实现相应的验证功能。示例代码展示了如何使用Element UI库创建表单,以及处理不同类型的表单元素。同时,文章提到了在处理复选框时遇到的问题及其临时解决方案,并提供了手机号验证的回调函数示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义一个form

根据后台配置不同属性,渲染不同的组件。这里写的比较糙,有兴趣的小伙伴可以自行改进。

 <el-form :model="letterForm" ref="letterForm" label-width="100px" :rules="formValidateRules">

            <div v-for="(formItem,index) in formItemList" :key="formItem.id" class="form_item_content">
                <!--单行文本-->
                <el-form-item v-if="formItem.dataType == ('varchar' || 'char')"
                              :label="formItem.fieldText"
                              :prop="formItem.fieldName">
                    <el-input v-model="letterForm[formItem.fieldName]"
                              :placeholder=`请输入${formItem.fieldText}`></el-input>
                </el-form-item>

                <!--文本域-->
                <el-form-item v-if="formItem.dataType == 'textarea'" :label="formItem.fieldText"
                              :prop="formItem.fieldName">
                    <el-input
                            type="textarea"
                            :rows="2"
                            :placeholder=`请输入${formItem.fieldText}`
                            v-model="letterForm[formItem.fieldName]">
                    </el-input>
                </el-form-item>

                <!--单选-->
                <el-form-item v-else-if="formItem.dataType == 'radio'" :label="formItem.fieldText"
                              :prop="formItem.fieldName">
                    <el-radio v-for="(radioItem,radioIndex) in formItem.defaultValue"
                              :key="radioIndex"
                              v-model="letterForm[formItem.fieldName]"
                              :label="radioItem">
                        {{radioItem}}
                    </el-radio>
                </el-form-item>

                <!--多选-->
                <el-form-item v-else-if="formItem.dataType == 'checkbox'" :label="formItem.fieldText"
                              :prop="formItem.fieldName">
                    <el-checkbox-group v-model="checkedModel['list'+formItem.ext05]">
                        <el-checkbox v-for="(checkItem,checkIndex) in formItem.defaultValue"
                                     :key="checkIndex"
                                     :label="checkItem">
                            {{checkItem}}
                        </el-checkbox>
                    </el-checkbox-group>
                </el-form-item>

                <!--下拉选择-->
                <el-form-item v-else-if="formItem.dataType == 'select'" :label="formItem.fieldText"
                              :prop="formItem.fieldName">
                    <el-select v-model="letterForm[formItem.fieldName]" placeholder="请选择">
                        <el-option
                                v-for="(selectItem,selectIndex) in formItem.defaultValue"
                                :key="selectIndex"
                                :label="selectItem"
                                :value="selectItem">
                        </el-option>
                    </el-select>
                </el-form-item>
            </div>

            <el-form-item>
                <el-button type="primary" @click="submitForm('letterForm')">提交</el-button>
                <el-button @click="resetForm('letterForm')">重置</el-button>
            </el-form-item>
        </el-form>

请求数据,渲染表单,添加对应的验证

这里的checkbox 渲染后赋值有问题,所以用来这种写法。本来想直接把对应的组件对象的默认值赋值成一个空数组作为checkbox group的value绑定(

js:
this.letterForm[formItem.fieldName] = []

页面:
<el-checkbox-group v-model="letterForm[formItem.fieldName]">
                        <el-checkbox v-for="(checkItem,checkIndex) in formItem.defaultValue"
                                     :key="checkIndex"
                                     :label="checkItem">
                            {{checkItem}}
                        </el-checkbox>
                    </el-checkbox-group>

),当时选中后没有效果,先使用目前这种比较low的方法来实现。有大佬知道知道其中的问题欢迎评论区留言。

 //手机号验证回调
    const checkMobile = (rule, value, callback) => {
        const pattern = /^((0\d{2,3}-\d{7,8})|(1[3576849]\d{9}))$/
        if (value !== '') {
            if (!pattern.test(value)) {
                callback(new Error('请输入正确的电话'))
            } else {
                callback()
            }
        }
    }
     const $vue = new Vue({
        el: "#main",
        data: {
            formItemList: [],//表单字段集合
            formValidateRules: {},//表单验证对象
            letterForm: {},//表单model
            checkedModel: {
                list1: [],
                list2: [],
                list3: [],
                list4: [],
            }
        },
        mounted() {
            this.getFormList();
        },
        methods: {

            //获取表单字段集合
            getFormList() {
                const _this = this
                $api.get(`getFieldByFormId?formId=${formId}`)
                    .then(res => {
                        _this.initLabel(res.data)
                        _this.initValidateRules(res.data)

                        _this.$nextTick(() => {
                            _this.formItemList = res.data
                        })
                        console.log(_this.formItemList);
                    })
                    .catch(err => {
                        console.log(err);
                    })
            },

            //初始化表单验证对象
            initValidateRules(formItemList) {
                formItemList.forEach(formItem => {
                    // this.formData[formItem.fieldName] = ''
                    let itemRule = []
                    //如果该字段是必填字段,那么给表单验证规则对象中新增一个对应属性
                    if (!formItem.isMust) {
                        //非空验证
                        itemRule.push({required: true, message: `请输入${formItem.fieldText}`, trigger: 'blur'})
                        //字符最大长度验证
                        itemRule.push({
                            max: formItem.maxLength,
                            message: `长度不能超过${formItem.maxLength}个字符`,
                            trigger: 'blur'
                        })

                        //如果字段包含email并且需要验证,新增邮箱验证
                        if (formItem.fieldName.indexOf("email") !== -1) {
                            itemRule.push({type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']})
                        }

                        //如果字段包含telephone或者mobile并且需要验证,新增手机号验证
                        if (formItem.fieldName.indexOf("telephone") !== -1 || formItem.fieldName.indexOf("mobile") !== -1) {
                            itemRule.push({required: true, validator: checkMobile, trigger: ['blur', 'change']})
                        }
                    }
                    //将验证规则push到表单验证对象
                    this.formValidateRules[formItem.fieldName] = itemRule
                })
                console.log("formValidateRules:", this.formValidateRules);
                console.log("letterForm:", this.letterForm);
            },

            //初始化表单数据
            initLabel(formItemList) {
                let checkIndex = 0
                formItemList.forEach(formItem => {
                    if (formItem.dataType === 'radio') {
                        formItem.defaultValue = formItem.defaultValue.split(',')
                    } else if (formItem.dataType === 'checkbox') {
                        formItem.defaultValue = formItem.defaultValue.split(',')
                        formItem.ext05 = ++checkIndex
                    } else if (formItem.dataType === 'select') {
                        formItem.defaultValue = formItem.defaultValue.split(',')
                    }
                })
            },

            //写信提交
            submitForm(formName) {
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        //TODO 请求接口
                        console.log('submit');
                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },

            //重置表单
            resetForm(formName) {
                this.$refs[formName].resetFields();
            },
        }

    });

效果图

在这里插入图片描述

### 实现 Vue.js 动态表单Vue.js 中实现动态生成表单的功能涉及多个方面,包括但不限于定义表单结构、处理表单状态以及响应用户交互。 #### 定义表单结构 为了构建灵活的动态表单,在代码层面首先要考虑的是如何表示每一个表单。可以采用一种通用的数据模型来描述不同的输入控件及其属性: ```typescript export type FormItemType = 'input' | 'select' | 'checkbox' | 'radio'; export interface FormItem { type: FormItemType; payload: any; next: (current: FormItem, ancestors: FormItem[]) => FormItem | null; parent: FormItem | null; } ``` 这段 TypeScript 代码展示了 `FormItem` 接口的设计[^4],其中包含了用于区分具体控件种类的枚举类型 `FormItemType` 及其他必要的配置。 #### 创建基础 HTML 结构引入 Vue.js 对于前端页面而言,合理的布局是成功的一半。因此建议先搭建好静态模板框架,确保正确加载了 Vue 库文件。这一步骤为后续逻辑编写提供了坚实的环境支撑[^2]。 #### 编写核心业务逻辑 当一切准备就绪之后,则需专注于开发主要的应用程序行为——即依据预设规则即时调整显示哪些字段给最终使用者填写。这里的关键在于巧妙运用 Vue 提供的各种特性,像计算属性、侦听器或是自定义指令等工具,从而达到预期效果。 考虑到某些场景下可能需要频繁切换不同版本之间的视图而不丢失已有的数据填充情况,此时 `<keep-alive>` 组件便派上了用场;它可以有效地保存内部子组件的状态信息直至再次激活为止[^5]。 #### 使用第三方库简化复杂度 如果目本身较为庞大或者时间紧迫的话,不妨借助一些成熟的开源解决方案来加速进度。例如提到过的 **vue-dynamic-form-component** 就是一个不错的选择,它不仅支持常规的对象映射方式,还特别针对深层次嵌套结构进行了优化处理[^1]。 ### 示例代码片段 下面给出一段简单的例子用来说明上述概念的实际操作方法: ```html <div id="app"> <form> <!-- 循环渲染表单 --> <div v-for="(item,index) in items" :key="index"> <component :is="getComponent(item.type)" v-model="item.payload.value"></component> </div> <!-- 添加新条目按钮 --> <button @click.prevent="addItem">Add Item</button> <!-- 提交整个表单 --> <button type="submit">Submit</button> </form> </div> <script src="https://cdn.jsdelivr.net/npm/vue@next"></script> <script> const app = Vue.createApp({ data() { return { items: [ {type:'input',payload:{label:'',value:''}}, ... ] } }, methods:{ getComponent(type){ switch(type){ case 'input':return InputField; case 'select':return SelectBox; default:return DefaultElement; } }, addItem(){ this.items.push({/* 新增 */}); } } }); // 注册全局组件 app.component('InputField',{...}) .component('SelectBox',{...}) .mount('#app'); </script> ``` 此段脚本演示了怎样利用循环机制配合动态组件技术快速组装出可变长度且样式各异的信息采集区域,同时也体现了基于事件驱动模式下的新增动作触发流程。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值