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

二郎神楊戩發表於2021-06-15

前言

我司最近在搭建一款後臺管理系統,使用的是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,是否可以使用單獨的元件進行處理,這些都是有待需要考慮的問題。

結尾

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

相關文章