Vue.js+ElementUI+vant生成動態表單配置


前言

我司最近在搭建一款后台管理系統,使用的是Vue全家桶配合Element-ui,遇到一個問題,需要處理很多的表單,所以想到的解決方案是通過后台配置生成動態表單,這對於我來說也算是新的挑戰,涉及的功能有動態表單渲染和驗證,那么一起來學習一下我是如何實現的吧!

本文僅僅代表筆者自己的思路,如果您有更好的實現方式,可以在下方留下您寶貴的建議。筆者將十分感謝

開發准備

需要儲備的知識點

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

本項目是基於vue-cli2.0搭建的腳手架,在這里默認大家搭建好了,誰贊成,誰反對!

33473706d98e35c79ec12ba0e6cbfd9b.gif

靜態表單數據准備

后台返回的數據是這樣的,這里我們拿一個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方法。

作為靚仔的我,肯定很貼心的為大家准備了官網鏈接

47d751670c6d38357427ad88717822ef.png

官網鏈接

這里就不過多講解,官網比較權威,本篇博客的重點是動態表單。

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-option
                  v-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>

24rACn.png

現在的話,表單已經全部渲染完畢了,也實現了雙向綁定,現在需要做的是如何實現動態表單驗證。

官網解釋:Form 組件提供了表單驗證的功能,只需要通過 rules 屬性傳入約定的驗證規則,並將 Form-Item 的 prop 屬性設置為需校驗的字段名即可,

  • prop字段
  • rules
  • model

在這里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-option
                  v-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端和手機端是配套使用的,所以手機端我們也實現動態表單的功能,

廢話不多說上號!

923684000d453fd8b6335cb7e9719bc2.jpg

還是拿Test.vue組件來舉例子,對於移動端的話,首先需要安裝vant依賴,默認大家已經安裝好了。

vant官網

動態表單渲染

由於手機端沒有下拉框這個組件,而是使用Picker選擇器來代替,那么就需要思考一個問題,多個picker的話怎么實現一一對應呢?該怎么處理?

cc7d9a4a60f4d47d12a7cdd93179cfc0.jpg

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);
        }
    }
})

最終實現效果

25Mqw8.png

可以看到最終所有的數據都實現了雙向綁定,提交到后台的數據就是表單里面的數據,也可以全部獲取到,最后需要實現的就是表單的驗證的功能。

動態表單驗證

對於輸入框和文本域的驗證比較簡單,只需要添加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,是否可以使用單獨的組件進行處理,這些都是有待需要考慮的問題。

結尾

如果覺得本篇博客對您有幫助的話,記得給作者三連,點贊👍👍👍,關注,收藏,您的支持就是我寫作路上最大的動力,我們下篇文章見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM