vue动态生成表单元素
前幾天接了一個(gè)需求,需要?jiǎng)討B(tài)生成一個(gè)表單數(shù)據(jù),然后提交,提交完數(shù)據(jù)后。通過編輯按鈕進(jìn)入時(shí),需要進(jìn)行數(shù)據(jù)回填。
一、頁面展示:
I. 沒生成表單前的狀態(tài)
Vue-UEedit
UEedit
II. 單機(jī)生成表單生成表單
III. 根據(jù)選擇方式展示不同的表單元素
IV. 如果從編輯頁進(jìn)入該頁面有數(shù)據(jù)的話,進(jìn)行數(shù)據(jù)回填
樣式同第三點(diǎn)相似,這里不再說明
二、思路:
請輸入標(biāo)題,請選擇類型 為父組件;請選擇方式 為子組件;根據(jù)請選擇方式出來的內(nèi)容為孫子組件, 單選和下拉下面的生成參數(shù)是從孫組件
三、難點(diǎn):
動(dòng)態(tài)生成數(shù)據(jù)、數(shù)據(jù)多層傳遞(四層數(shù)據(jù)向下傳遞+四層數(shù)據(jù)向上傳遞)、數(shù)據(jù)格式轉(zhuǎn)換、數(shù)據(jù)清空、
數(shù)據(jù)關(guān)聯(lián)、數(shù)據(jù)解耦、空白表單數(shù)據(jù)添加、 含原始表單數(shù)據(jù)添加、表單數(shù)據(jù)刪除、非響應(yīng)式數(shù)據(jù)處理、
合并表單數(shù)據(jù)(判空+去重+重新排序)、深層數(shù)據(jù)傳遞監(jiān)聽
四、結(jié)構(gòu)上分析:
(1)數(shù)據(jù)類型: 兩層數(shù)據(jù),三層數(shù)據(jù),四層數(shù)據(jù)
二層數(shù)據(jù):
新增
層級1 --> 層級2–>層級1(整合數(shù)據(jù))–>提交
編輯
回填 層級1 --> 層級2
修改+新增 層級1 --> 層級2–>層級1 --> 提交
三層數(shù)據(jù):
新增
層級1 --> 層級2–>層級3–>層級2–>層級1(整合數(shù)據(jù))–>提交
編輯
回填 層級1 --> 層級2–>層級3
修改+新增 層級1 --> 層級2–>層級3–>層級2–>層級1(整合數(shù)據(jù))–>提交
四層數(shù)據(jù):
新增
層級1 --> 層級2–>層級3–>層級4–>層級3層級2–>層級1(整合數(shù)據(jù))–>提交
編輯
回填 層級1 --> 層級2–>層級3–>層級4
修改+新增 層級1 --> 層級2–>層級3–>層級4–>層級3–>層級2–>層級1(整合數(shù)據(jù))–>提交
(2)生成類型:
普通文本輸入框、數(shù)字輸入框、下拉框、
關(guān)聯(lián)值類型1:文本輸入框+文本輸入框、
關(guān)聯(lián)值類型2:文本輸入框+單選框
(3)關(guān)鍵值傳遞: 新增/編輯來回?cái)?shù)據(jù)格式化轉(zhuǎn)換:
例如:
提交時(shí)候分享參數(shù):
// 格式化URL動(dòng)態(tài)添加數(shù)據(jù)格式formatURL(obj) {let url = "";const tempArr = [];const arr = Object.keys(obj);let leng = 0;arr.map(item => {if (item.slice(-1) * 1 > leng) {leng = item.slice(-1) * 1;}});for (let i = 1; i <= leng; i += 1) {const obj1key = arr.filter(item => item.slice(-1) * 1 === i);const obj1 = {};obj1key.map(item => {obj1[item] = obj[item];});tempArr.push(obj1);}tempArr.forEach(v => {Object.keys(v).map(key => {url += `${key}=${v[key]}&`;});});url = url.substring(0, url.length - 1);return `${this.data.link}?${url}`;}回填時(shí)解析分享參數(shù):
// 解析返回的分享參數(shù)formatDEcodeURL(URL) {const urlsrc = URL;const arr1 = [];const arr2 = [];const obj = {};urlsrc.split("&").map(v => {if (v.substring(0, 4) === "link") {arr1.push(v);}if (v.substring(0, 4) === "type") {arr2.push(v);}});arr1.forEach(v => {arr2.forEach(k => {if (v.charAt(4) === k.charAt(4)) {obj[`link${v.charAt(4)}`] = v.substring(6);obj[`type${k.charAt(4)}`] =k.substring(6) === "1" ? "必填" : "非必填";} else {obj[`link${v.charAt(4)}`] = v.substring(6);obj[`type${k.charAt(4)}`] =k.substring(6) === "1" ? "必填" : "非必填";}});});this.todoObj = obj;const max = Math.max(arr1.length, arr2.length);for (let i = 0; i < max; i += 1) {this.addShareLink();}},(4)監(jiān)聽第二三層數(shù)據(jù)變化,實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)改變:
例如:
watch: {// 新增頁面 監(jiān)測父組件傳入值變化secdown: {handler(val) {this.changeChoose(val);},deep: true,immediate: true},// 編輯頁面 監(jiān)測子組件變化,來刷新子組件其余值chooseTypes: {handler(val) {this.changeTypes(val);},deep: true,immediate: true}},五、代碼分析:
動(dòng)態(tài)生成數(shù)據(jù)父組件講解
HTML
<divv-for="item in createFormArray":key="item.id"><el-row:gutter="24"style="margin-top:10px;"><el-col :span="3"><div class="item-title">輸入項(xiàng){{ item.id }}:</div></el-col><el-col :span="3"><el-inputv-model="createFormObj[item.value]"placeholder="請輸入標(biāo)題"/></el-col><el-col :span="3"><el-selectv-model="createFormObj[item.kind]"placeholder="請選擇類型"><el-optionv-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, //輸入需要生成表單的個(gè)數(shù)createFormObj: {}, //存放每一個(gè)生成表單對象createFormArray: [], //生成表單所有生成對象的數(shù)組choose: [ //請選擇類型選擇器里面的選擇值{value: 1,label: "必填"},{value: 2,label: "非必填"}],}},createForm() {for (; this.createIndex <= this.countPage; this.createIndex += 1) {//造數(shù)據(jù),給每一項(xiàng)添加上 id,value,kind, type方便我們后面綁定數(shù)據(jù)使用(綁定的數(shù)據(jù)我們給后面加上索引區(qū)分)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-selectv-model="chooseTypes"placeholder="請選擇方式"@change="storeType"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"/></el-select></el-col><div><!-- 傳入 項(xiàng)數(shù) 和 選擇的方式 --><InputItem:child = "secdown":showitem = "dynamical" //從兒子組件將“選擇的方式” 傳給孫子組件:showindex="+chooseTypes" //從兒子組件將“項(xiàng)數(shù)” 傳給孫子組件@lastchild="getChild"/> //為了獲取孫子組件數(shù)據(jù),綁定函數(shù)傳遞過去</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: "數(shù)字輸入"},{value: 7,label: "Hidden"}],childrenMess: []};},watch: {secdown: {handler(val) {this.changeChoose(val);},deep: true,immediate: true}},methods: {getChild(val) { // 接受孫子組件傳遞過來的數(shù)據(jù),并將數(shù)據(jù)傳給父組件this.$emit("receive", { ...this.childrenMess, ...val });},storeType(val) { // 每天選擇時(shí),接受孫子組件傳遞過來的數(shù)據(jù),并將數(shù)據(jù)傳給父組件this.childrenMess = { id: this.dynamical, value: val };this.$emit("receive", { ...this.childrenMess });},changeChoose(val) {this.chooseTypes = val.type;}} }; </script>InputItem孫子組件講解
HTM層:
<template><div class="data-manage-container"><div v-show="showindex === 1"><el-col :span="3"><el-inputv-model="generated_data.input_title"placeholder="請輸入默認(rèn)值"@change="getTextOne(showindex,$event)"/></el-col><el-col :span="3">最大長度:<el-input-numberv-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-selectv-model="generated_data.formvalue"placeholder="請輸入默認(rèn)值"><el-optionv-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"><divclass="item-title"@click = "formAddParam"> <i class="el-icon-circle-plus"/></div></el-col></el-row><el-rowv-for="(todo,index) in FormTodoParams":key="todo.id"><el-row:gutter="20"style="padding-left:200px;padding-top:10px;"><el-col :span="1"><divclass="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;">參數(shù):</el-col><el-col:span="3"style="margin-left: -38px;"><el-inputv-model.trim="formObj[todo.value]"placeholder="輸入內(nèi)容"size="mini"clearable@change="getParamsFour(showindex,formObj)"/></el-col><el-col:span="3"style="margin-left: 10px;margin-top:10px;"><el-radio-groupv-model="generated_data.defaltRadio"size="small"@change="getSelectFour(showindex,$event)"><el-radio:label="formObj[todo.value]">選擇為默認(rèn)值</el-radio></el-radio-group></el-col></el-row></el-row></div></div><div v-show="showindex === 6"><el-col :span="3"><el-inputv-model="generated_data.selectData"placeholder="請輸入默認(rèn)值"@change="getTextSix(showindex,$event)"/></el-col><el-col :span="3">最小值:<el-input-numberv-model="generated_data.selectData_min":min="0"size="small"label="最小值"@change="getMinSix(showindex,$event)"/></el-col><el-col :span="3">最大值:<el-input-numberv-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-inputv-model="generated_data.selectnomalData"placeholder="請輸入默認(rèn)值"@change="getMaxSeven(showindex,$event)"/></el-col></div></div> </template>HTML這里主要是根據(jù)不同的選擇方式顯示不同的表單內(nèi)容,
JS層
<script> export default {name: "InputItem",components: {},props: {showindex: {type: Number,default: 0},showitem: {type: Number,default: 0},child: {type: Object,default: () => ({})}},data() {return {indexNormal: 0,formObj: {},selectValue: [{value: 1,label: "必填"},{value: 0,label: "非必填"}],generaData: {inputTitle: "",numLength: 0,formvalue: "",selectData: 0,selectData_min: 0,selectData_max: 0,selectnomalData: "",defaltRadio: "",value: 0},formIndex: 0,FormTodoParams: [],typeFour: {choose: "",chooseObj: {}},typeFive: {choose: "",chooseObj: {}}};},watch: {// 新增頁面 監(jiān)測父組件傳入值變化child: {handler(val) {this.watchChoose(val);},deep: true,immediate: true},// 編輯頁面 監(jiān)測子組件變化,來刷新子組件其余值generaData: {handler(val) {this.watchGeneraData(val);},deep: true,immediate: true}},mounted() {},methods: {// 編輯時(shí)有數(shù)據(jù)觸發(fā) 數(shù)據(jù)回填watchChoose(val) {this.generaData.inputTitle = val.text;this.generaData.numLength = val.length;this.generaData.selectData = +val.num_default;this.generaData.selectData_min = +val.min;this.generaData.selectData_max = +val.max;this.generaData.formvalue = val.is_required;this.generaData.selectnomalData = val.novel;this.generaData.defaltRadio = val.default;this.generaData.id = val.id;this.generaData.type = this.showindex;if (val.type_value && val.type_value.length) {this.indexNormal = val.type_value.length;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];}}},watchGeneraData(val) {return val;},// 沒有 子組件時(shí)刷新頁面getInputBox(index) {if (index === 1 || index === 6 || index === 7) {this.watchGeneraData(this.generaData);this.integrationData(index);}},// 當(dāng)選擇"單選"或者"下拉"時(shí)生成表單元素formAddParam() {this.formIndex += 1;if (this.indexNormal > 0) {this.FormTodoParams.push({id: this.formIndex + this.indexNormal,value: `value${this.formIndex + this.indexNormal}`});} else {this.FormTodoParams.push({id: this.formIndex,value: `value${this.formIndex}`});}},formRemoveParam(index) {this.FormTodoParams.splice(index, 1);},// 整合并獲取輸入數(shù)據(jù)integrationData(index) {switch (index) {case 1:this.$emit("lastchild", {...this.generaData,type: this.showindex,id: this.showitem});break;case 4:this.$emit("lastchild", {...this.generaData,...this.typeFour,type: this.showindex,id: this.showitem});break;case 5:this.$emit("lastchild", {...this.generaData,...this.typeFive,type: this.showindex,id: this.showitem});break;case 6:this.$emit("lastchild", {...this.generaData,type: this.showindex,id: this.showitem});break;case 7:this.$emit("lastchild", {...this.generaData,type: this.showindex,id: this.showitem});break;default:break;}},// 下拉框處理getSelectFour(index, val) {if (index === 4) {this.typeFour.choose = val;this.integrationData(index);} else {this.typeFive.choose = val;this.integrationData(index);}},// 下拉框處理getParamsFour(index, val) {if (index === 4) {this.typeFour.chooseObj = val;this.integrationData(index);} else {this.typeFive.chooseObj = val;this.integrationData(index);}}} }; </script>這里代碼并不是全部代碼,只是抽出部分進(jìn)行講解,父組件有1300行左右的代碼,主要是細(xì)節(jié)處理所有并沒有貼出來。
總結(jié)
以上是生活随笔為你收集整理的vue动态生成表单元素的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吸引发烧友的视听Linux发行版
- 下一篇: 最佳实践———Jenkins对离线和插件