1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > Vue.js+ElementUI+vant生成动态表单配置

Vue.js+ElementUI+vant生成动态表单配置

时间:2019-11-04 04:03:27

相关推荐

Vue.js+ElementUI+vant生成动态表单配置

前言

我司最近在搭建一款后台管理系统,使用的是Vue全家桶配合Element-ui,遇到一个问题,需要处理很多的表单,所以想到的解决方案是通过后台配置生成动态表单,这对于我来说也算是新的挑战,涉及的功能有动态表单渲染和验证,那么一起来学习一下我是如何实现的吧!

本文仅仅代表笔者自己的思路,如果您有更好的实现方式,可以在下方留下您宝贵的建议。笔者将十分感谢

开发准备

需要储备的知识点

了解Element ui表单了解Vue中的$set(target,key,value)方法了解vant中的表单组件

本项目是基于vue-cli2.0搭建的脚手架,在这里默认大家搭建好了,谁赞成,谁反对!

静态表单数据准备

后台返回的数据是这样的,这里我们拿一个json数据举例

{"showName": "姓名",// 名称"showValue": null, //值"htmlElements": "输入框", // 表单类型"fieldLength": 99,// 字段长度"requiredOrNot": 1,//是否必填}

然后类型的话大概有以下几种

输入框文本域日历控件下拉框单选框复选框

我们为每一种类型生成一种组件,Test.vue组件里面

data(){return{fieldArray:[],// 表单字段集合fieldObj:{},sex:[{// 性别name:'男',value:"male"},{name:"女",value:"female"}],hobbies:[ // 爱好{name:"吃饭",value:"吃饭"},{name:"玩游戏",value:"玩游戏"},{name:"打豆豆",value:"打豆豆"},],job:[{// 职业name:"医生",value:"doctor"},{name:"老师",value:"teacher"},{name:"司机",value:"driver"}]}}

这里准备多种日历控件是因为后续手机端使用vant组件的时候需要用到

由于vue中的数据是双向绑定的,所以只有在data里面的数据是可以实现双向绑定的,重新向data里面添加的数据无法达到双向绑定的效果,官网为我们提供了一个set方法。

作为靓仔的我,肯定很贴心的为大家准备了官网链接

官网链接

这里就不过多讲解,官网比较权威,本篇博客的重点是动态表单。

Vue.set(target,propertyName/index,value)

参数

{Object | Array} target{string | number} propertyName/index{any} value

返回值:设置的值。

用法

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

Element-ui表单元素

官网链接

动态表单渲染

这里使用axios请求本地json数据,static/json/form.json

{"data":[{"showName": "姓名","showValue": null,"htmlElements": "输入框","fieldLength": 10,"requiredOrNot": 1,"desc":"请输入姓名"},{"showName": "描述","showValue": null,"htmlElements": "文本域","fieldLength": 99,"requiredOrNot": 1,"desc":"请输入描述"},{"showName": "爱好","showValue": null,"htmlElements": "复选框","requiredOrNot": 1,"desc":"请选择爱好"},{"showName": "性别","showValue": null,"htmlElements": "单选框","requiredOrNot": 1},{"showName": "出生日期","showValue": null,"htmlElements": "日历控件","requiredOrNot": 1,"desc":"请选择出生日期"},{"showName": "结婚时间","showValue": null,"htmlElements": "日历控件","requiredOrNot": 1,"desc":"请选择结婚时间"},{"showName": "职业","showValue": null,"htmlElements": "下拉框","requiredOrNot": 1,"desc":"请选择职业"}]}

Test.vue文件

<template><div><h2>测试动态表单</h2><el-form :model="fieldObj" ref="ruleForm" label-width="180px" class="demo-ruleForm"><template v-for="(item,index) of fieldArray"><template v-if="item.htmlElements==='输入框'"><el-form-item :label="item.showName"><el-input v-model="fieldObj[item.showName]" :max="item.fieldLength" :placeholder="item.desc" show-word-limit></el-input></el-form-item></template><template v-if="item.htmlElements==='文本域'"><el-form-item :label="item.showName"><el-input type="textarea" rows="4" :placeholder="item.desc" v-model="fieldObj[item.showName]" :maxlength="item.fieldLength" show-word-limit></el-input></el-form-item></template><template v-if="item.htmlElements==='日历控件'"><el-form-item :prop="item.showName" :label="item.showName"><el-date-picker v-model="fieldObj[item.showName]" :name="item.showName" type="date"format="yyyy-MM-dd" value-format="yyyy-MM-dd":placeholder="item.desc"></el-date-picker></el-form-item></template><template v-if="item.htmlElements==='下拉框'"><el-form-item :label="item.showName"><el-select v-model="fieldObj[item.showName]" :placeholder="item.describe"><el-optionv-for="items in job":key="items.name":label="items.name":value="items.value"></el-option></el-select></el-form-item></template><template v-if="item.htmlElements==='单选框'"><el-form-item :label="item.showName"><template v-for="(child,index) in sex"><el-radio v-model="fieldObj[item.showName]" :label="child.value">{{child.name}}</el-radio></template></el-form-item></template><template v-if="item.htmlElements==='复选框'"><el-form-item :label="item.showName"><el-checkbox-group v-model="fieldObj[item.showName]"><template v-for="(child,index) of hobbies"><el-checkbox :label="child.name"></el-checkbox></template></el-checkbox-group></el-form-item></template></template></el-form></div></template><script>import axios from 'axios'export default {name: "Test",data(){return{fieldArray:[],// 表单字段集合fieldObj:{},sex:[{ // 性别name:'男',value:"male"},{name:"女",value:"female"}],hobbies:[ // 爱好{name:"吃饭",value:"吃饭"},{name:"玩游戏",value:"玩游戏"},{name:"打豆豆",value:"打豆豆"},],job:[{ // 职业name:"医生",value:"doctor"},{name:"老师",value:"teacher"},{name:"司机",value:"driver"}]}},mounted(){this.getFieldData();},methods:{getFieldData(){ // 获取动态表单数据axios.get("../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})}}}</script><style scoped></style>

现在的话,表单已经全部渲染完毕了,也实现了双向绑定,现在需要做的是如何实现动态表单验证。

官网解释:Form 组件提供了表单验证的功能,只需要通过rules属性传入约定的验证规则,并将 Form-Item 的prop属性设置为需校验的字段名即可,

prop字段rulesmodel

在这里rules设置为动态的,而不是放在data里面提前写好,这里需要知道每一种类型的触发形式

输入框/文本域 trigger: ‘blur’单选框/复选框/日历控件/下拉框 trigger: ‘change’

动态表单验证

对于表单中的每一种验证形式都了解之后,Test.vue里面的文件就变成了

<template><div><h2>测试动态表单</h2><el-form :model="fieldObj" ref="ruleForm" label-width="180px" class="demo-ruleForm"><template v-for="(item,index) of fieldArray"><template v-if="item.htmlElements==='输入框'"><el-form-item :label="item.showName" :prop="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请输入'+item.showName, trigger: 'blur' }]:[]"><el-input v-model="fieldObj[item.showName]" :max="item.fieldLength":placeholder="item.desc" show-word-limit ></el-input></el-form-item></template><template v-if="item.htmlElements==='文本域'"><el-form-item :label="item.showName" :prop="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请输入'+item.showName, trigger: 'blur' }]:[]"><el-input type="textarea" rows="4" :placeholder="item.desc" v-model="fieldObj[item.showName]" :maxlength="item.fieldLength" show-word-limit></el-input></el-form-item></template><template v-if="item.htmlElements==='日历控件'"><el-form-item :prop="item.showName" :label="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请选择'+item.showName, trigger: 'change' }]:[]"><el-date-picker v-model="fieldObj[item.showName]" :name="item.showName" type="date"format="yyyy-MM-dd" value-format="yyyy-MM-dd":placeholder="item.desc"></el-date-picker></el-form-item></template><template v-if="item.htmlElements==='下拉框'"><el-form-item :label="item.showName" :prop="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请选择'+item.showName, trigger: 'change' }]:[]"><el-select v-model="fieldObj[item.showName]" :placeholder="item.describe"><el-optionv-for="items in job":key="items.name":label="items.name":value="items.value"></el-option></el-select></el-form-item></template><template v-if="item.htmlElements==='单选框'"><el-form-item :label="item.showName" :prop="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请选择'+item.showName, trigger: 'change' }]:[]"><template v-for="(child,index) in sex"><el-radio v-model="fieldObj[item.showName]" :label="child.value">{{child.name}}</el-radio></template></el-form-item></template><template v-if="item.htmlElements==='复选框'"><el-form-item :label="item.showName" :prop="item.showName" :rules="item.requiredOrNot==1?[{ required: true, message: '请选择'+item.showName, trigger: 'change' }]:[]"><el-checkbox-group v-model="fieldObj[item.showName]"><template v-for="(child,index) of hobbies"><el-checkbox :label="child.name"></el-checkbox></template></el-checkbox-group></el-form-item></template></template><div class="text-align"><el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button><el-button @click="resetForm('ruleForm')">重置</el-button></div></el-form></div></template><script>import axios from 'axios'export default {name: "Test",data(){return{fieldArray:[],// 表单字段集合fieldObj:{},sex:[{// 性别name:'男',value:"male"},{name:"女",value:"female"}],hobbies:[ // 爱好{name:"吃饭",value:"吃饭"},{name:"玩游戏",value:"玩游戏"},{name:"打豆豆",value:"打豆豆"},],job:[{// 职业name:"医生",value:"doctor"},{name:"老师",value:"teacher"},{name:"司机",value:"driver"}]}},mounted(){this.getFieldData();},methods:{getFieldData(){// 获取动态表单数据axios.get("../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})},submitForm(formName){// 提交验证this.$refs[formName].validate((valid) => {if (valid) {console.log('提交数据');} else {return false;}});},resetForm(formName) {// 重置表单this.$refs[formName].resetFields();}}}</script><style scoped></style>

新增的内容有:

el-form-item新增了:prop=“item.showName”el-form-item新增了:rules=“item.requiredOrNot==1?[{ required: true, message: ‘请选择’+item.showName, trigger: ‘change’ }]:[]”el-form-item新增了:rules=“item.requiredOrNot==1?[{ required: true, message: ‘请输入’+item.showName, trigger: ‘blur’ }]:[]”methods里面新增了验证方法和重置表单的方法

vant动态表单验证

由于pc端和手机端是配套使用的,所以手机端我们也实现动态表单的功能,

废话不多说上号!

还是拿Test.vue组件来举例子,对于移动端的话,首先需要安装vant依赖,默认大家已经安装好了。

vant官网

动态表单渲染

由于手机端没有下拉框这个组件,而是使用Picker选择器来代替,那么就需要思考一个问题,多个picker的话怎么实现一一对应呢?该怎么处理?

form.json里面新增一个对象-城市,之前的代码还是可以复用

{"showName": "城市","showValue": null,"htmlElements": "下拉框","requiredOrNot": 1,"desc":"请选择职业"}

这样一来就有多个下拉框,从而来解决多个picker的问题。

Test.vue的代码

html代码区

<template><div><h2 class="title">测试vant动态表单</h2><van-form @submit="submitClaim"><template v-for="(item,index) of fieldArray"><template v-if="item.htmlElements==='输入框'"><van-field :maxlength="item.fieldLength" show-word-limit v-model="fieldObj[item.showName]" :name="item.showName" :label="item.showName"/></template><template v-if="item.htmlElements==='文本域'"><van-field rows="2" autosize :label="item.showName" :name="item.showName" type="textarea" v-model="fieldObj[item.showName]" :maxlength="item.fieldLength" show-word-limit/></template><template v-if="item.htmlElements==='日历控件'"><van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]" /></template><template v-if="item.htmlElements==='复选框'"><van-field :name="item.showName" :label="item.showName"><template #input><van-checkbox-group v-model="fieldObj[item.showName]" direction="horizontal"><template v-for="(child,index) of hobbies"><van-checkbox :name="child.value">{{child.name}}</van-checkbox></template></van-checkbox-group></template></van-field></template><template v-if="item.htmlElements==='单选框'"><van-field :name="item.showName" :label="item.showName"><template #input><van-radio-group v-model="fieldObj[item.showName]" direction="horizontal"><template v-for="(child,index) of sex"><van-radio :name="child.value">{{child.name}}</van-radio></template></van-radio-group></template></van-field></template><template v-if="item.htmlElements==='下拉框'"><van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]"/></template></template></van-form></div></template>

JavaScript代码区

import axios from 'axios'export default {name: "Test",data(){return{fieldArray:[],// 表单字段集合fieldObj:{},sex:[{// 性别name:'男',value:"male"},{name:"女",value:"female"}],hobbies:[ // 爱好{name:"吃饭",value:"吃饭"},{name:"玩游戏",value:"玩游戏"},{name:"打豆豆",value:"打豆豆"},],}},mounted(){this.getFieldArray();},methods:{getFieldArray(){// 获取本地动态表单配置json数据axios.get("../../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})},submitClaim(taskInfo){}}}

现在的话基本实现了输入框,文本域,单选框,复选框的值双向绑定,下一步是解决多个日历框和下拉框的取值一一对应。

日历框和弹出层都是通过v-model来控制显示和隐藏,所以只需知道日历框和弹出层的个数,然后循环遍历处理就可以了,getFieldArray方法重新处理

axios.get("../../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else if(item.htmlElements==='日历控件'){this.$set(this.dateObj,item.showName,false); // 日历控件全部先隐藏this.$set(this.fieldObj,item.showName,item.showValue);}else if(item.htmlElements=='下拉框'){this.$set(this.fieldObj,item.showName,item.showValue);this.$set(this.dropDownObj,item.showName,false); // 弹出层全部先隐藏}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})

data数据里面新增 dateObj对象

dateObj:{},// 控制日期的显示隐藏

处理日历控件

页面html新增相关内容

<van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]" @click="dateObj[item.showName]=true"/><template v-for="(item,key,index) of dateObj"><van-calendar v-model="dateObj[key]" @confirm="(date)=>onConfirmTime(date,item,key)"/></template>

methods新增方法

onConfirmTime(date,item,key){// 日历控件this.fieldObj[key]=this.formatDate(date);this.dateObj[key]=false;},formatDate(date) {// 格式化日期let year=date.getFullYear();let month=date.getMonth()+1;let day=date.getDate();if(month<10){month='0'+month;}if(day<10){day='0'+day;}return `${year}-${month}-${day}`;},

使用v-model绑定提前写好的日期对象dateObj,然后遍历对象来控制每一个日历控件的显示隐藏。

绑定对应的key值,这样就可以达到获取每一个日历对象点击之后对应的值。

处理下拉框

data里面新增dropDownObj对象,dropDownTempObj对象,dropDownMap map对象

dropDownObj:{},// 控制下拉框的显示隐藏dropDownTempObj:{},// 下拉框对象,用于picker里面的值dropDownMap:new Map(),

mounted生命周期里面新增

this.dropDownMap.set("职业",["医生","老师","司机"]);this.dropDownMap.set("城市",["北京","上海","广州","深圳"])

页面新增html内容

<van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]" @click="dropDownObj[item.showName]=true"/><template v-for="(item,key,index) of dropDownObj"><van-popup v-model="dropDownObj[key]" position="bottom" :style="{width: '100%'}"><van-picker show-toolbar @confirm="(value)=>onConfirmDropdown(value,key)" @cancel="dropDownObj[key]=false" :columns="handleData(dropDownTempObj[key])"/></van-popup></template>

methods新增相关方法

onConfirmDropdown(value,key){// 下拉框选中数据this.dropDownObj[key]=false;this.fieldObj[key]=value;},handleData(key){// 下拉框获取每一个配置项return this.dropDownMap.get(key);},

getFieldArray方法重写

axios.get("../../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else if(item.htmlElements==='日历控件'){this.$set(this.dateObj,item.showName,false); // 日历控件全部先隐藏this.$set(this.fieldObj,item.showName,item.showValue);}else if(item.htmlElements=='下拉框'){this.$set(this.fieldObj,item.showName,item.showValue);this.$set(this.dropDownObj,item.showName,false); // 弹出层全部先隐藏this.$set(this.dropDownTempObj,item.showName,item.showName);}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})

最终实现效果

可以看到最终所有的数据都实现了双向绑定,提交到后台的数据就是表单里面的数据,也可以全部获取到,最后需要实现的就是表单的验证的功能。

动态表单验证

对于输入框和文本域的验证比较简单,只需要添加required和rules验证规则就可以

输入框和文本域

<van-field:required="item.requiredOrNot==1?true:false":maxlength="item.fieldLength" show-word-limit v-model="fieldObj[item.showName]" :name="item.showName" :label="item.showName" :rules="[{ required: true, message: '请填写'+item.showName }]"/>

这样一来就基本实现了输入框和文本域的验证,至于其它的form表单类型的验证笔者还在研究当中

vant动态表单处理全部代码

html代码片段

<van-form @submit="submitClaim"><template v-for="(item,index) of fieldArray"><template v-if="item.htmlElements==='输入框'"><van-field :maxlength="item.fieldLength" show-word-limit v-model="fieldObj[item.showName]" :name="item.showName" :label="item.showName"/></template><template v-if="item.htmlElements==='文本域'"><van-field rows="2" autosize :label="item.showName" :name="item.showName" type="textarea" v-model="fieldObj[item.showName]" :maxlength="item.fieldLength" show-word-limit/></template><template v-if="item.htmlElements==='日历控件'"><van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]" @click="dateObj[item.showName]=true"/></template><template v-if="item.htmlElements==='复选框'"><van-field :name="item.showName" :label="item.showName"><template #input><van-checkbox-group v-model="fieldObj[item.showName]" direction="horizontal"><template v-for="(child,index) of hobbies"><van-checkbox :name="child.value">{{child.name}}</van-checkbox></template></van-checkbox-group></template></van-field></template><template v-if="item.htmlElements==='单选框'"><van-field :name="item.showName" :label="item.showName"><template #input><van-radio-group v-model="fieldObj[item.showName]" direction="horizontal"><template v-for="(child,index) of sex"><van-radio :name="child.value">{{child.name}}</van-radio></template></van-radio-group></template></van-field></template><template v-if="item.htmlElements==='下拉框'"><van-field :name="item.showName" is-link :label="item.showName" :readonly="true" v-model="fieldObj[item.showName]" @click="dropDownObj[item.showName]=true"/></template></template><van-button type="info" round native-type="submit" :style="{width:'100%',marginTop:'15px'}">提交</van-button></van-form><template v-for="(item,key,index) of dateObj"><van-calendar v-model="dateObj[key]" @confirm="(date)=>onConfirmTime(date,item,key)"/></template><template v-for="(item,key,index) of dropDownObj"><van-popup v-model="dropDownObj[key]" position="bottom" :style="{width: '100%'}"><van-picker show-toolbar @confirm="(value)=>onConfirmDropdown(value,key)" @cancel="dropDownObj[key]=false" :columns="handleData(dropDownTempObj[key])"/></van-popup></template>

JavaScript代码片段

import axios from 'axios'export default {name: "Test",data(){return{fieldArray:[],// 表单字段集合fieldObj:{},sex:[{// 性别name:'男',value:"male"},{name:"女",value:"female"}],hobbies:[ // 爱好{name:"吃饭",value:"吃饭"},{name:"玩游戏",value:"玩游戏"},{name:"打豆豆",value:"打豆豆"},],dateObj:{// 控制日期的显示隐藏},dropDownObj:{// 控制下拉框的显示隐藏},dropDownTempObj:{// 下拉框对象,用于picker里面的值},dropDownMap:new Map(),}},mounted(){this.getFieldArray();this.dropDownMap.set("职业",["医生","老师","司机"]);this.dropDownMap.set("城市",["北京","上海","广州","深圳"])},methods:{getFieldArray(){// 获取本地动态表单配置json数据axios.get("../../static/json/form.json").then(data=>{let response=data.data.data;this.fieldArray=response;for(let i=0;i<response.length;i++){let item=response[i];if(item.htmlElements==='复选框'){this.$set(this.fieldObj,item.showName,[]);}else if(item.htmlElements==='日历控件'){this.$set(this.dateObj,item.showName,false); // 日历控件全部先隐藏this.$set(this.fieldObj,item.showName,item.showValue);}else if(item.htmlElements=='下拉框'){this.$set(this.fieldObj,item.showName,item.showValue);this.$set(this.dropDownObj,item.showName,false); // 弹出层全部先隐藏this.$set(this.dropDownTempObj,item.showName,item.showName);}else {this.$set(this.fieldObj,item.showName,item.showValue);}}})},onConfirmTime(date,item,key){// 日历控件this.fieldObj[key]=this.formatDate(date);this.dateObj[key]=false;},onConfirmDropdown(value,key){// 下拉框选中数据this.dropDownObj[key]=false;this.fieldObj[key]=value;},handleData(key){// 下拉框获取每一个配置项return this.dropDownMap.get(key);},formatDate(date) {// 格式化日期let year=date.getFullYear();let month=date.getMonth()+1;let day=date.getDate();if(month<10){month='0'+month;}if(day<10){day='0'+day;}return `${year}-${month}-${day}`;},submitClaim(taskInfo){console.log(taskInfo);}}}

总结

整体来说动态表单的处理综合难度不算很大,需要的是如何对数据进行处理,当然还有不足之处是没有做到对文件上传的处理,代码的优化程度没有做好,v-for里面写了很多v-if,是否可以使用单独的组件进行处理,这些都是有待需要考虑的问题。

结尾

如果觉得本篇博客对您有帮助的话,记得给作者三连,点赞👍👍👍,关注,收藏,您的支持就是我写作路上最大的动力,我们下篇文章见。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。