vue動態生成表單元素
阿新 • • 發佈:2018-11-26
前幾天接了一個需求,需要動態生成一個表單資料,然後提交,提交完資料後。通過編輯按鈕進入時,需要進行資料回填。
- 沒生成表單前的狀態
- 單機生成表單生成表單
- 根據選擇方式展示不同的表單元素
- 如果從編輯頁進入該頁面有資料的話,進行資料回填
樣式同第三點相似,這裡不再說明
思路:
請輸入標題,請選擇型別 為父元件;請選擇方式 為子元件;根據請選擇方式出來的內容為孫子元件
難點:
動態生成資料,資料多層傳遞(三層資料向下傳遞+三層資料向上傳遞),資料格式轉換,資料清空
第一步:
動態生成資料父元件講解
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行左右的程式碼,主要是細節處理所有並沒有貼出來。