1.首先是最普通,也是大家最先想到的方法,直接封装:
代码语言:javascript复制<template>
<el-form
:inline="true"
:model="value"
label-position="right"
:label-width="formConfig.labelWidth"
:rules="rules"
size='mini'
>
<slot name="formItem" />
<el-form-item
v-for="(item,index) in formConfig.formItemList"
:key="index"
:label="item.label"
:prop="item.prop"
>
<el-input
v-if="item.type=='input'"
v-model="value[item.prop]"
:disabled="item.disabled"
:clearable="true"
:placeholder="item.placeholder"
></el-input>
<el-select
:clearable="true"
v-else-if="item.type=='select'"
v-model="value[item.prop]"
:disabled="item.disabled"
:placeholder="item.placeholder"
>
<el-option
v-for="(optItem,index) in item.optList"
:key="index"
:label="optItem.label"
:value="optItem.value"
></el-option>
</el-select>
<el-date-picker
:value-format="item.dateFormate"
v-else
v-model="value[item.prop]"
:type="item.type"
:disabled="item.disabled"
:clearable="true"
:placeholder="item.label"
></el-date-picker>
</el-form-item>
<div class="searchBtn">
<el-button-group>
<el-button
v-for="(item, index) in formConfig.operate"
:key="index"
size="small"
:type="item.type"
:icon="item.icon"
@click="item.handleClick"
>{{item.name}}
</el-button>
</el-button-group>
<slot name="operate"></slot>
</div>
</el-form>
</template>
<script>
export default {
components: {},
props: {
formConfig: {
type: Object,
required: true
},
value: {
type: Object,
required: true
},
rules: {
type: Object
}
},
computed: {},
methods: {
setDefaultValue() {
const formData = { ...this.value };
// 设置默认值
this.formConfig.formItemList.forEach(({ key, value }) => {
if (formData[key] === undefined || formData[key] === null) {
formData[key] = value;
}
});
this.$emit("input", formData);
}
},
mounted() {
this.setDefaultValue();
}
};
</script>
第一种方法简单明了,基本能够处理解决大多数的表单问题,并且与slot的完美组合,已经可以达到我们的需求要求。
2.实现表单动态渲染、可视化配置的方法,动态表单又可以分为两种方法:
首先,需要配置 el-form :
代码语言:javascript复制<template>
<el-form
class="dynamic-form"
:inline="formConfig.inline"
:model="value"
:label-position="formConfig.labelPosition"
:label-width="formConfig.labelWidth"
:size='formConfig.size'
:status-icon="formConfig.statusIcon">
<slot/>
</el-form>
</template>
<script>
export default {
props: {
formConfig: {
type: Object,
required: true
},
value: {
type: Object,
required: true
}
},
methods: {
setDefaultValue() {
const formData = { ...this.value };
// 设置默认值
this.formConfig.formItemList.forEach(({ key, value }) => {
if (formData[key] === undefined || formData[key] === null) {
formData[key] = value
}
});
this.$emit('input', formData)
}
},
mounted() {
this.setDefaultValue()
},
}
</script>
第二步,开始渲染 form-item :
- 第一种,利用 vue 内置的 component 组件,写起来可能像这样:
<el-form-item>
<component :is="`el-${item.type}`" />
</el-form-item>
- 第二种,使用 v-if 逐个判断:
<el-form-item>
<el-input v-if="item.type === 'input'" />
<span v-else>未知控件类型</span>
</el-form-item>
考虑到每种表单控件的处理逻辑千差万别,这里本博主先介绍第一种(为了防止有人照搬,这里本博主只做简单介绍,不懂得私信):
代码语言:javascript复制<template>
<el-form-item :label="label">
<el-select v-model="currentValue" @input="onInputEvent" size="small">
<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 '~/components/Form/iForm/form-model'
export default {
name: "SelectList",
props: ['name', 'label', 'value', 'options'],
mixins: [formMixins],
data() {
return {
currentValue: this.value
}
}
}
</script>
由于每个表单组件都是监听父元素的value值变化,数据变化时都是触发onInputEvent并执行this.$emit(‘input’),所以我们可以把这部分内容抽取出来放在mixins里面。
代码语言:javascript复制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;
}
}
};
动态生成表单
这里主要是根据配置的数据,循环生成表单组件。默认提供提交和重置按钮,如果不需要可以通过slot传递其他操作按钮。这里的要点主要有: 监听表单组件的数据变化: 每个表单组件都有一个name标识它的业务含义,绑定的数据也是formData[field.name],@input事件传递updateForm,在updateForm里面更新this.formData[name],保证了this.formData里面的数据是和表单组件选择/填写的内容一致。 重置时改变表单组件的数据: 因为组件内部会监听父元素的value,所以这里只要清空this.formData的值,组件内部的数据也会跟着清空。
代码语言:javascript复制<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>
动态加载的第二种方法不多介绍,只提醒一句,注意 v-model 的绑定问题, 组件内不能直接修改props 。
温馨提示:
你需要知道 v-model 的工作原理 :
代码语言:javascript复制<input v-model="something">
这不过是以下示例的语法糖:
代码语言:html复制<input
:value="something"
@input="something = $event.target.value">