项目介绍
由于公司目前做的是一个后台管理系统、大部分页面是基于表单+表格的形式作为展现。所以在为了不重复做工的情况下、封装了一个模板页、一个动态表单、一个动态表格。
下面主要介绍一下,如何封装一个动态表单。
- 动态配置
- 插件扩展
- 校验规则 动态配置,我们想要实现的表单应该是一种类似JSON的一串参数,根据传入的参数表单会进行自动的配置展现。那接下来我们来看看一下如何做到传入即展示。 首先应该把拆分为两个部分。form文件主要作为
form.vue文件
<template>
<div class="eppForm">
<nb-form v-bind="config" @submit.native.prevent>
<nb-row :gutter="config.gutter">
<template v-for="(item, index) in config.formItem">
<nb-col :key="index" :span="config.span">
<nb-form-item :label="item.label" :prop="item.model">
<eppFormComponent
:value="config.formData[item.model]"
:componentConfig="item"
@update="handleUpdateFormComponent"
>
</eppFormComponent>
</nb-form-item>
</nb-col>
</template>
<nb-form-item v-if="!!config.btn.length">
<nb-button
v-for="(button, index) in config.btn"
:key="index"
:type="button.type"
:size="button.size"
@click="handleClick(button)"
>{{ button.name }}</nb-button
>
</nb-form-item>
</nb-row>
</nb-form>
</div>
</template>
<script>
import eppFormComponent from './epp-form-component';
export default {
name: 'eppForm',
props: {
formConfig: {
type: Object,
required: true,
default: () => {},
},
},
components: { eppFormComponent },
data() {
return {
defaultConfig: {
ref: 'eppForm',
labelPosition: 'right', // 默认为右对齐
inline: true, // 默认为横向展示
size: 'small',
labelWidth: '80px', // 默认label为80
buttonShow: true, // 设置查询按钮默认存在
btn: [], // 表单按钮
gutter: 0, // 属性来指定每一栏之间的间隔,默认间隔为 0
span: 8, // 默认每行配置为8的行宽
},
};
},
computed: {
// 为props增加默认配置
config() {
return Object.assign(this.defaultConfig, this.formConfig);
},
},
methods: {
handleClick(btn) {
btn.onClick && btn.onClick();
},
handleUpdateFormComponent({ key, value }) {
this.formConfig.formData[key] = value;
this.formConfig.onUpdateData
&& this.formConfig.onUpdateData(...arguments);
},
getFormData() {
return this.$props.formConfig.formData || {};
},
validate(callback) {
const { ref } = this.config;
this.$refs[ref].validate(callback);
},
resetForm() {
const { ref } = this.config;
this.$refs[ref].resetFields();
return this.config.formData;
},
},
};
</script>
<style scoped>
/* /deep/ .nb-form--inline .nb-form-item{
width:100%;
}
/deep/.nb-form--inline .nb-form-item__content {
width:calc(100% - 80px);
} */
</style>
formComponent.vue文件
<template>
<component
v-bind="componentConfig"
:is="componentType(componentConfig.componentType)"
v-model="controlModel"
>
<!-- 判断是否有对应的children、并且是否有对应的options -->
<template
v-if="componentConfig.children && componentConfig.children.options"
>
<component
v-for="(op, index) in componentConfig.children.options"
:label="op.label || op[componentConfig.children.label]"
:value="op.value || op[componentConfig.children.value]"
:is="componentType(componentConfig.children.type || 'option')"
:key="index"
></component>
</template>
</component>
</template>
<script>
export default {
name: 'eppFormComponent',
props: {
componentConfig: {
type: Object,
required: true,
default: () => {},
},
value: [Array, String, Number, Object, Boolean, Date],
},
data() {
return {
controlModel: this.value,
};
},
watch: {
value(newValue) {
this.controlModel = newValue;
},
controlModel(newValue) {
const key = this.$props.componentConfig.model;
this.$emit('update', { key, value: newValue });
},
},
computed: {
formData() {
console.log(this.$parent.$props);
},
},
methods: {
componentType(type) {
return `nb-${type}`;
},
},
};
</script>