vue动态生成表单元素

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24147051/article/details/84104056

前几天接了一个需求,需要动态生成一个表单数据,然后提交,提交完数据后。通过编辑按钮进入时,需要进行数据回填。


  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行左右的代码,主要是细节处理所有并没有贴出来。

猜你喜欢

转载自blog.csdn.net/qq_24147051/article/details/84104056