视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
vue构建动态表单的方法示例
2020-11-27 22:07:12 责编:小采
文档


概述

后台管理系统里面有非常多的表单需求,我们希望能够通过写一个json格式的数据,通过vue的循环动态地去渲染动态表单。并且能够在外部得到渲染出来的表单的数据,可以对表单进行重置操作。我结合element ui的控件的下拉框,输入框,时间选择控件和vue-treeselect,做了一个动态表单。

v-model的理解

先简单讲一下vue-model是怎么玩的。其实vue-model相当于给表单元素传递一个value,外部监听input事件。所以我们自己封装表单组件的时候也是可以传递一个value值,监听input事件获取输入的值。

<input type="text" v-model="something">
<!--等价于-->
<input type="text"
 v-bind:value="something"
 v-on:input="something = $event.target.value">

封装表单组件

组件最重要的开发思想就是设计好输入输出。这里就以下拉框组件为例吧。使用的是element ui的下拉框,进行一个简单封装。
输入:name:每个表单的数据标识,如区域编码输入框,父元素应该传递areaCode过来。

value: 表单选择/输入的值,从父元素获取后赋值给currentValue,通过监听父元素的值实现同步变
化。

options:下拉框要渲染的选项值,一般是个对象数组。

输出:onInputEvent,emit一个input事件,让父元素能够感知组件的数据变化。

也就是可以在组件使用的地方监听input事件

<template>
 <el-form-item :label="label">
 <el-select v-model="currentValue" @input="onInputEvent">
 <el-option
 v-for="item in options"
 :key="item.value"
 :label="item.label"
 :value="item.value">
 </el-option>
 </el-select>
 </el-form-item>
</template>

<script>
 import formMixins from '../../../mixins/form-model'
 export default {
 name: "SelectList",
 props: ['name', 'label', 'value','options'],
 mixins: [formMixins],
 data() {
 return {
 currentValue: this.value
 }
 },
 methods: {
 onInputEvent(value) {
 this.$emit('input', this.name, value);
 }
 },
 watch: {
 value(val) {
 this.currentValue= val;
 }
 }
 }
</script>

一点封装

由于每个表单组件都是监听父元素的value值变化,数据变化时都是触发onInputEvent并执行this.$emit('input'),所以我们可以把这部分内容抽取出来放在mixins里面。

form-model.js

export default {
 props: ['name', 'value'],

 data () {
 return {
 currentValue: this.value
 };
 },
 methods: {
 onInputEvent(value) {
 this.$emit('input', this.name, value);
 },
 reset() {
 this.currentValue = "";
 }
 },
 watch: {
 value (val) {
 this.currentValue = val;
 }
 }
};

然后我们的下拉框组件就可以少写一些共用的代码,直接用 mixins: [formMixins]

<template>
 <el-form-item :label="label">
 <el-select v-model="currentValue" @input="onInputEvent">
 <el-option
 v-for="item in options"
 :key="item.value"
 :label="item.label"
 :value="item.value">
 </el-option>
 </el-select>
 </el-form-item>
</template>

<script>
 import formMixins from '../../../mixins/form-model'
 export default {
 name: "SelectList",
 props: ['name', 'label', 'value', 'options'],
 mixins: [formMixins],
 data() {
 return {
 currentValue: this.value
 }
 }
 }
</script>

动态生成表单

这里主要是根据配置的数据,循环生成表单组件。默认提供提交和重置按钮,如果不需要可以通过slot传递其他操作按钮。这里的要点主要有:

监听表单组件的数据变化:

每个表单组件都有一个name标识它的业务含义,绑定的数据也是formData[field.name],@input事件传递updateForm,在updateForm里面更新this.formData[name],保证了this.formData里面的数据是和表单组件选择/填写的内容一致。

重置时改变表单组件的数据:

因为组件内部会监听父元素的value,所以这里只要清空this.formData的值,组件内部的数据也会跟着清空。

 <template>
 <div>
 <el-form :inline="true" ref="form" :model="formData" class="demo-form-inline">
 <el-col :span="field.cols" v-for="(field, index) in config.fieldsConfig" v-bind:key="index">
 <component :key="index"
 :is="field.fieldType"
 :label="field.label"
 :value="formData[field.name]"
 :multiple="field.multiple"
 @input="updateForm"
 v-bind="field"
 :options="field.options"
 :ref="field.name"
 >
 </component>
 </el-col>
 <slot name="buttons">
 <el-button type="primary" @click="submit" size="small">{{onSubmitText}}</el-button>
 <el-button type="default" @click="reset" size="small">{{onResetText}}</el-button>
 </slot>
 </el-form>
 </div>
</template>
<script>
 import SelectList from './basicComponent/SelectList'
 import TextInput from './basicComponent/TextInput'
 import TimeSelector from './basicComponent/TimeSelector'
 import SelectTree from './basicComponent/SelectTree'
 import StaffSelectPopedit from './businessComponent/StaffSelectPopedit'
 export default {
 name: "FormGenerator",
 components: { SelectList, TextInput, TimeSelector, SelectTree, StaffSelectPopedit},
 props: ["config", "value"],
 data() {
 return {
 formData: this.value,
 onSubmitText: this.config.buttons.onSubmitText || '提交',
 onResetText: this.config.buttons.onResetText || '重置'
 }
 },
 methods: {
 updateForm(fieldName, value) {
 this.formData[fieldName] = value;
 },
 submit() {
 this.$emit("submit");
 },
 reset() {
 for(var name in this.formData) {
 if(typeof this.formData === "String") {
 this.formData[name] = "";
 } else {
 this.formData[name] = null;
 }
 }
 }
 }
 }
</script>

业务使用的地方

像下拉框的选择数据,这些应该是后台渲染的,所以我们暂时用setTimeout模拟一下。感觉这里this.config.fieldsConfig[4].options写的不太优雅,依赖于配置数据的顺序肯定不是啥好事情。求大神指点。

<template>
 <div>
 <form-generator :config="config"
 @submit="getFormData"
 v-model="formData"
 >
 </form-generator>
 </div>
</template>
<script>
 import FormGenerator from '../components/form/FormGenerator'
 export default {
 name: "FormGeneratorDemo",
 components: { FormGenerator },
 created () {
 this.queryOrderType();
 this.queryAreaTree();
 },
 data() {
 return {
 formData: {
 orderCode: "",
 orderType: "",
 beginTime: "",
 endTime: "",
 area: [],
 staff:""
 },
 config: {
 fieldsConfig: [
 {
 name: 'orderCode',
 label: '定单编码',
 fieldType: 'TextInput',
 cols: 8
 },
 {
 name: 'orderType',
 label: '定单类型',
 fieldType: 'SelectList',
 options: [],
 cols: 8
 },
 {
 name: 'beginTime',
 label: '开始时间',
 fieldType: 'TimeSelector',
 cols: 8
 },
 {
 name: 'endTime',
 label: '结束时间',
 fieldType: 'TimeSelector',
 cols: 8
 },
 {
 name: 'area',
 label: '区域',
 fieldType: 'selectTree',
 options: [],
 multiple: true,
 cols: 8
 },
 {
 name: 'staff',
 label: '人员选择',
 fieldType: 'StaffSelectPopedit',
 cols: 8
 }
 ],
 buttons: {
 onSubmitText: '提交',
 onResetText: '重置'
 }
 }
 }
 },
 methods: {
 getFormData() {
 console.log(this.formData);
 },
 queryOrderType() {
 setTimeout(() => {
 this.config.fieldsConfig[1].options = [
 { label: 'select1', value: 'key1'},
 { label: 'select2', value: 'key2'},
 { label: 'select3', value: 'key3'}
 ];
 }, 100)
 },
 queryAreaTree() {
 this.config.fieldsConfig[4].options = [
 {
 id: 'a',
 label: 'a',
 children: [{
 id: 'aa',
 label: 'AA',
 }, {
 id: 'ab',
 label: 'AB',
 }],
 }, {
 id: 'b',
 label: 'B',
 }, {
 id: 'c',
 label: 'C',
 }
 ]
 }
 }
 }
</script>

大概就是这样的思路,我们希望我们只要写上面那样子的配置数据就可以动态生成各种这样的表单组件,不用写一大堆重复代码。如果有更好的解决办法,欢迎和我联系。另外,代码路径https://github.com/supportlss/vue-dynamic-form

下载本文
显示全文
专题