1. 程式人生 > >vue動態生成表單元素

vue動態生成表單元素

前幾天接了一個需求,需要動態生成一個表單資料,然後提交,提交完資料後。通過編輯按鈕進入時,需要進行資料回填。


  1. 沒生成表單前的狀態

在這裡插入圖片描述


  1. 單機生成表單生成表單
    在這裡插入圖片描述

  1. 根據選擇方式展示不同的表單元素

在這裡插入圖片描述


  1. 如果從編輯頁進入該頁面有資料的話,進行資料回填

樣式同第三點相似,這裡不再說明


思路:

請輸入標題請選擇型別 為父元件;請選擇方式 為子元件;根據請選擇方式出來的內容為孫子元件


難點:

動態生成資料,資料多層傳遞(三層資料向下傳遞+三層資料向上傳遞),資料格式轉換,資料清空


第一步:

動態生成資料父元件講解

HTML

           <div
              v-for="item in createFormArray"
              :key="item.id">
              <el-row
                :gutter="24"
                style="margin-top:10px;">
                <el-col :span="3">
                  <div class="item-title">輸入項{{ item.id }}:</div>
                </el-col>
                <el-col :span="3">
                  <el-input
                    v-model="createFormObj[item.value]"
                    placeholder="請輸入標題"/>
                </el-col>
                <el-col :span="3">
                  <el-select
                    v-model="createFormObj[item.kind]"
                    placeholder="請選擇型別">
                    <el-option
                      v-for="(item,index) in choose"
                      :key="index"
                      :label="item.label"
                      :value="item.value"/>
                  </el-select>
                </el-col>
                
                <!-- 嵌入的第二層,請選擇方式元件-->
                
                <DynamicData
                  :dynamical = "item.id"
                  :secdown = "item.indexDA"
                  @receive= "receive"/>
              </el-row>
            </div>

JS

import DynamicData from "./dynamic_data"; //引入選擇方式元件
export default {
components: {
    VueEditor,
    DynamicData
  },
data() {
    return {
          createIndex:1,      //生成表單的索引
          countPage: 0,       //輸入需要生成表單的個數
          createFormObj: {},     //存放每一個生成表單物件
          createFormArray: [],   //生成表單所有生成物件的陣列
          choose: [         //請選擇型別選擇器裡面的選擇值
		      {
		          value: 1,
		          label: "必填"
		        },
		        {
		          value: 2,
		          label: "非必填"
		        }
			],
          
          
    }
    },
    createForm() {
      for (; this.createIndex <= this.countPage; this.createIndex += 1) {
      
       //造資料,給每一項新增上 id,value,kind, type方便我們後面繫結資料使用(繫結的資料我們給後面加上索引區分)
       
        this.createFormArray.push({    
          id: this.createIndex,
          value: `link${this.createIndex}`,
          kind: `kind${this.createIndex}`,
          type: `type${this.createIndex}`
        });
      }
    }
}



第二步:

DynamicData兒子元件講解

HTML

<template>
  <div class="data-manage-container">
    <el-col :span="3">
      <el-select
        v-model="chooseTypes"
        placeholder="請選擇方式"
        @change="storeType">
        <el-option
          v-for="item in options"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        />
      </el-select>
    </el-col>
    <div>
      <!-- 傳入 項數 和 選擇的方式 -->
      <InputItem
        :child = "secdown"
        :showitem = "dynamical"        //從兒子元件將“選擇的方式” 傳給孫子元件
        :showindex="+chooseTypes" //從兒子元件將“項數” 傳給孫子元件
        @lastchild="getChild"/>         //為了獲取孫子元件資料,繫結函式傳遞過去
    </div>
  </div>
</template>

JS

<script>
import InputItem from "./show_input_item";  //引入孫子元件

export default {
  name: "DynamicData",
  components: {
    InputItem
  },
  props: {
    dynamical: {
      type: Number,
      default: 0
    },
    types: {
      type: Function,
      default() {}
    },
    secdown: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      chooseTypes: "", 
      options: [     //選擇的型別
        {
          value: 1,
          label: "文字輸入"
        },
        {
          value: 2,
          label: "電話號碼"
        },
        {
          value: 3,
          label: "檔案上傳"
        },
        {
          value: 4,
          label: "下拉框選擇"
        },
        {
          value: 5,
          label: "單選框"
        },
        {
          value: 6,
          label: "數字輸入"
        },
        {
          value: 7,
          label: "Hidden"
        }
      ],
      childrenMess: []
    };
  },
  watch: {
    secdown: {
      handler(val) {
        this.changeChoose(val);
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    getChild(val) {    // 接受孫子元件傳遞過來的資料,並將資料傳給父元件
      this.$emit("receive", { ...this.childrenMess, ...val });
    },
    storeType(val) {     // 每天選擇時,接受孫子元件傳遞過來的資料,並將資料傳給父元件
      this.childrenMess = { id: this.dynamical, value: val };
      this.$emit("receive", { ...this.childrenMess });
    },
    changeChoose(val) {
      this.chooseTypes = val.type;
    }
  }
};
</script>

第三步:

InputItem孫子元件講解

HTML

<template>
  <div class="data-manage-container">
    <div v-show="showindex === 1">
      <el-col :span="3">
        <el-input
          v-model="generated_data.input_title"
          placeholder="請輸入預設值"
          @change="getTextOne(showindex,$event)"/>
      </el-col>
      <el-col :span="3">
        最大長度:<el-input-number
          v-model="generated_data.numLength"
          :min="1"
          size="small"
          label="描述文字"
          @change="getNumberOne(showindex,$event)"/>
      </el-col>
    </div>

    <div v-show="showindex === 4 || showindex === 5">
      <div style="visibility:hidden;">
        <el-select
          v-model="generated_data.formvalue"
          placeholder="請輸入預設值">
          <el-option
            v-for="item in selectValue"
            :key="item.value"
            :label="item.label"
            :value="item.value"/>
        </el-select>
      </div>
      <div class="reduceparams">
        <el-row
          :gutter="10"
          style="padding-left:200px;">
          <el-col :span="5">
            <div
              class="item-title"
              @click = "formAddParam"> <i class="el-icon-circle-plus"/></div>
          </el-col>
        </el-row>
        <el-row
          v-for="(todo,index) in FormTodoParams"
          :key="todo.id">
          <el-row
            :gutter="20"
            style="padding-left:200px;padding-top:10px;">
            <el-col :span="1">
              <div
                class="item-title"
                style="padding-top:10px;"
                @click = "formRemoveParam(index)"> <i class="el-icon-remove"/></div>
            </el-col>
            <el-col
              :span="1"
              style="margin-top:10px;">
              引數:
            </el-col>
            <el-col
              :span="3"
              style="margin-left: -38px;">
              <el-input
                v-model.trim="formObj[todo.value]"
                placeholder="輸入內容"
                size="mini"
                clearable
                @change="getParamsFour(showindex,formObj)"/>
            </el-col>
            <el-col
              :span="3"
              style="margin-left: 10px;
              margin-top:10px;">
              <el-radio-group
                v-model="generated_data.defaltRadio"
                size="small"
                @change="getSelectFour(showindex,$event)">
                <el-radio
                  :label="formObj[todo.value]">選擇為預設值</el-radio>
              </el-radio-group>
            </el-col>
          </el-row>
        </el-row>
      </div>
    </div>

    <div v-show="showindex === 6">
      <el-col :span="3">
        <el-input
          v-model="generated_data.selectData"
          placeholder="請輸入預設值"
          @change="getTextSix(showindex,$event)"/>
      </el-col>
      <el-col :span="3">
        最小值:<el-input-number
          v-model="generated_data.selectData_min"
          :min="0"
          size="small"
          label="最小值"
          @change="getMinSix(showindex,$event)"/>
      </el-col>
      <el-col :span="3">
        最大值:<el-input-number
          v-model="generated_data.selectData_max"
          :min="0"
          size="small"
          label="最大值"
          @change="getMaxSix(showindex,$event)"/>
      </el-col>
    </div>
    <div v-show="showindex === 7">
      <el-col :span="3">
        <el-input
          v-model="generated_data.selectnomalData"
          placeholder="請輸入預設值"
          @change="getMaxSeven(showindex,$event)"/>
      </el-col>
    </div>
  </div>
</template>

HTML這裡主要是根據不同的選擇方式顯示不同的表單內容,

JS

<script>
export default {
  name: "InputItem",
  components: {},
  props: {
    showindex: {
      type: Number,
      default: 0
    },
    showitem: {
      type: Number,
      default: 0
    },
    child: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      formObj: {},

      chooseState: "",
      selectValue: [
        {
          value: 1,
          label: "必填"
        },
        {
          value: 2,
          label: "非必填"
        }
      ],
      generated_data: {
        input_title: "",
        numLength: 0,
        formvalue: "",
        selectData: "",
        selectData_min: "",
        selectData_max: "",
        selectnomalData: "",
        defaltRadio: ""
      },
      formIndex: 0,
      FormTodoParams: [],
      typeOne: {
        text: "",
        length: 0
      },
      typeFour: {
        choose: "",
        chooseObj: 0
      },
      typeFive: {
        choose: "",
        chooseObj: 0
      },
      typeSix: {
        text: "",
        number1: 0,
        number2: 0
      },
      typeSeven: {
        text: ""
      }
    };
  },
  watch: {    //使用孫子元件監聽兒子元件的資料變化,進行相應處理
    child: {
      handler(val) {
        this.watchChoose(val);
      },
      deep: true,
      immediate: true
    }
  },
  mounted() {},
  methods: {
    watchChoose(val) {    //孫子元件進行資料回填
      this.generated_data.input_title = val.default;
      this.generated_data.numLength = val.max_length;
      this.generated_data.selectData = val.default;
      this.generated_data.selectData_min = val.min;
      this.generated_data.selectData_max = val.max;
      this.generated_data.selectnomalData = val.default;
      this.generated_data.defaltRadio = val.default;

      if (val.type_value && val.type_value.length > 0) {
        val.type_value.forEach((v, i) => {
          this.FormTodoParams.push({
            id: i + 1,
            value: `value${i + 1}`
          });
        });
        for (let i = 1; i <= val.type_value.length; i += 1) {
          this.formObj[`value${i}`] = val.type_value[i - 1];
        }
      }

      // formObj[todo.id]
    },
    formAddParam() {     //當選擇方式為4或者5時,需要有新增按鈕,單機生成輸入框
      this.formIndex += 1;
      this.FormTodoParams.push({
        id: this.formIndex,
        value: `value${this.formIndex}`
      });
    },
    formRemoveParam(index) {         // 刪除生成 ,當選擇方式為4或者5時 新增的輸入框
      this.FormTodoParams.splice(index, 1);
    },
                                      // 整合並獲取輸入資料  將不同情況的資料從孫子元件傳遞給兒子元件
    integrationData(index) {
      switch (index) {
        case 1:
          this.$emit("lastchild", this.typeOne);
          break;
        case 4:
          this.$emit("lastchild", this.typeFour);
          break;
        case 5:
          this.$emit("lastchild", this.typeFive);
          break;
        case 6:
          this.$emit("lastchild", this.typeSix);
          break;
        case 7:
          this.$emit("lastchild", this.typeSeven);
          break;
        default:
          break;
      }
    },
    getTextOne(index, val) {  //選擇方式為1
      this.typeOne.text = val;
      this.integrationData(index);
    },
    getNumberOne(index, val) {  //選擇方式為1
      this.typeOne.length = val;
      this.integrationData(index);
    },
    getSelectFour(index, val) {  //選擇方式為4
      if (index === 4) {
        this.typeFour.choose = val;
        this.integrationData(index);
      } else {
        this.typeFive.choose = val;
        this.integrationData(index);
      }
    },
    getParamsFour(index, val) {  //選擇方式為4或者5
      if (index === 4) {
        this.typeFour.chooseObj = val;
        this.integrationData(index);
      } else {
        this.typeFive.chooseObj = val;
        this.integrationData(index);
      }
    },
    getTextSix(index, val) {  //選擇方式為6
      this.typeSix.text = val;
      this.integrationData(index);
    },
    getMinSix(index, val) {  //選擇方式為6
      this.typeSix.number1 = val;
      this.integrationData(index);
    },
    getMaxSix(index, val) {  //選擇方式為6
      this.typeSix.number2 = val;
      this.integrationData(index);
    },
    getMaxSeven(index, val) {   //選擇方式為7
      this.typeSeven.text = val;
      this.integrationData(index);
    }
  }
};
</script>

這裡程式碼並不是全部程式碼,只是抽出部分進行講解,父元件有1300行左右的程式碼,主要是細節處理所有並沒有貼出來。