基于 Element Plus 封装的 JSON 配置表单组件
基于 Element Plus 封装的 JSON 配置表单组件
不想多写一行代码了!!!懒,真的是进化的原动力啊。为了在各种XX表单的复制粘贴中解放出来,我写下了这篇文章
在现代前端开发中,表单是一个常见的功能,它们通常需要灵活性和可配置性。基于 Vue 3 和 Element Plus 库,我们可以创建一个动态表单组件,该组件能够通过 JSON 对象来配置表单的结构和行为。这种方式可以大大简化表单的创建过程,提高开发效率。
开始之前
在开始之前,确保你的项目已经安装了 Vue 3 和 Element Plus。如果还没有安装,可以通过以下命令进行安装:
npm install vue@next
npm install element-plus
好的,让我们根据需求来一步步构建和应用这个组件。
需求分析
在构建动态表单组件之前,我们需要明确我们的需求:
- 动态性:能够根据传入的 JSON 配置动态生成表单项。
- 灵活性:支持不同类型的表单控件,如输入框、选择器、开关等。
- 校验:支持表单验证规则,确保用户输入的数据有效性。
- 可维护性:通过 JSON 配置管理表单项,易于维护和扩展。
为什么要封装表单组件
在大型应用中,表单可能在不同的地方以不同的结构和规则出现。如果我们手动创建每个表单,会导致大量重复的代码,且每次变更都需要手动更新每个表单。通过封装一个动态表单组件,我们可以:
- 减少重复代码,提高开发效率。
- 通过配置来管理表单,使得更新变得简单快速。
- 提高代码的可读性和可维护性。
实现动态表单组件
第一步:定义组件接口
首先,我们定义 TypeScript 接口来描述表单项和验证规则:
// formTypes.ts
export interface FormField {
type: string;
label: string;
model: string;
options?: Array<{ label: string; value: any }>;
placeholder?: string;
rules?: Array<FormRule>;
}
export interface FormRule {
required?: boolean;
message: string;
trigger: string | string[];
validator?: (rule: FormRule, value: string, callback: Function) => void;
}
export interface FormConfig {
fields: Array<FormField>;
}
第二步:构建动态表单组件
使用 <script setup lang="ts">
语法糖来构建我们的 DynamicForm.vue
组件:
<template>
<el-form :model="formModel" :rules="formRules" ref="dynamicForm">
<!-- 动态生成表单项 -->
<el-row :gutter="20">
<el-col :span="8" v-for="field in formConfig.fields" :key="field.model">
<el-form-item :label="field.label" :prop="field.model">
<component
:is="field.type"
v-model="formModel[field.model]"
:placeholder="field.placeholder"
v-bind="field.options ? { options: field.options } : {}"
></component>
</el-form-item>
</el-col>
</el-row>
<!-- 表单操作按钮 -->
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElButton, ElRow, ElCol } from 'element-plus';
import type { FormConfig } from './formTypes';
const props = defineProps({
config: {
type: Object as () => FormConfig,
required: true
}
});
const formModel = ref({});
const formRules = ref({});
const dynamicForm = ref(null);
// 初始化表单模型和规则
for (const field of props.config.fields) {
formModel.value[field.model] = '';
formRules.value[field.model] = field.rules || [];
}
// 提交表单
const submitForm = () => {
dynamicForm.value.validate((valid) => {
if (valid) {
// 表单验证成功,处理数据
} else {
// 表单验证失败,提示用户
}
});
};
// 重置表单
const resetForm = () => {
dynamicForm.value.resetFields();
};
</script>
第三步:在父组件中使用动态表单组件
现在我们可以在父组件中定义表单的配置,并传递给 DynamicForm
组件:
<template>
<dynamic-form :config="formConfig" />
</template>
<script setup lang="ts">
import DynamicForm from './DynamicForm.vue';
import type { FormConfig } from './formTypes';
const formConfig: FormConfig = {
fields: [
{
type: 'el-input',
label: '姓名',
model: 'name',
placeholder: '请输入姓名',
rules: [
{ required: true, message: '姓名不能为空', trigger: 'blur' }
]
},
// 其他表单项配置...
]
};
</script>
添加成功和失败回调
为了让动态表单组件更加灵活,我们可以向 DynamicForm.vue
组件添加一个回调函数的入参,以便在表单提交并通过验证后执行。同时,我们可以提供一个默认的处理函数,以便在表单验证失败时执行。
我们需要修改 DynamicForm.vue
组件,以接收一个额外的回调函数作为参数,并在表单验证成功后调用它。
<template>
<!-- ...省略其它模板代码... -->
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</template>
<script setup lang="ts">
// ...省略其它导入和定义...
const props = defineProps({
config: {
type: Object as () => FormConfig,
required: true
},
onSubmitSuccess: Function as PropType<(formData: any) => void>,
onSubmitFail: Function as PropType<(errors: any) => void>
});
const formModel = ref({});
const formRules = ref({});
const dynamicForm = ref(null);
// ...省略初始化代码...
// 提交表单
const submitForm = () => {
dynamicForm.value.validate((valid: boolean, invalidFields: any) => {
if (valid) {
props.onSubmitSuccess?.(formModel.value);
} else {
props.onSubmitFail
? props.onSubmitFail(invalidFields)
: defaultSubmitFail(invalidFields);
}
});
};
// 默认的表单验证失败处理函数
const defaultSubmitFail = (errors: any) => {
console.error('表单验证失败:', errors);
// 这里可以添加一些默认的错误处理,比如弹出通知或消息提示用户
};
// 重置表单
const resetForm = () => {
dynamicForm.value.resetFields();
};
</script>
在父组件中传递回调函数
在父组件中,我们可以定义提交成功和失败时的处理函数,并将它们传递给 DynamicForm
组件。
<template>
<dynamic-form
:config="formConfig"
:on-submit-success="handleSuccess"
:on-submit-fail="handleFail"
/>
</template>
<script setup lang="ts">
import DynamicForm from './DynamicForm.vue';
import type { FormConfig } from './formTypes';
const formConfig: FormConfig = {
// ...表单配置...
};
// 表单提交成功的回调函数
const handleSuccess = (formData: any) => {
console.log('表单提交成功, 表单数据:', formData);
// 这里可以执行提交到服务器的操作
};
// 表单提交失败的回调函数
const handleFail = (errors: any) => {
console.error('表单提交失败, 错误信息:', errors);
// 这里可以执行一些错误处理,比如显示错误信息
};
</script>
这样,父组件就能够控制表单提交成功和失败后的行为了。如果父组件没有提供 onSubmitSuccess
或 onSubmitFail
函数,动态表单组件将使用默认的处理函数。这提供了灵活性,同时确保了即使没有自定义处理函数,组件也能够正常工作。
异步校验支持
异步校验是现代表单不可或缺的一部分,尤其在需要与后端服务交互来验证输入的有效性时。为了支持异步校验,我们可以在表单项配置中添加一个异步校验函数。
{
// ...其他配置项
asyncValidator: (rule, value) => {
return new Promise((resolve, reject) => {
// 这里可以调用API进行异步校验
someApiCheck(value).then(response => {
if (response.isValid) {
resolve();
} else {
reject('该值不可用');
}
});
});
},
}
表单状态管理
表单可能有多种状态,如加载、禁用、提交中。我们可以在组件中添加一个formState
对象来管理这些状态,并允许父组件通过属性来控制这些状态。
<template>
<el-form :disabled="formState.disabled">
<!-- 表单项 -->
</el-form>
</template>
<script setup>
// ...省略其他代码
const props = defineProps({
// ...其他属性
formState: {
type: Object,
default: () => ({ disabled: false, loading: false })
}
});
</script>
表单项依赖关系
在一些场景下,某些表单项的显示可能依赖于其他项的值。我们可以在每个表单项的配置中添加一个showIf
函数,该函数根据当前表单的值决定该项是否显示。
{
// ...其他配置项
showIf: (formData) => {
return formData.someOtherField === 'certainValue';
},
}
表单布局自定义
为了支持更灵活的布局配置,我们可以使用插槽(slot)来允许父组件控制表单项的布局。
<template>
<el-form>
<slot name="custom-layout">
<!-- 默认布局 -->
</slot>
</el-form>
</template>
表单数据初始化和变更
表单数据的初始化和动态变更可以通过v-model
或props
来实现。我们可以监听这些属性的变化来更新表单的状态。
<script setup>
// ...省略其他代码
const props = defineProps({
// ...其他属性
modelValue: {
type: Object,
default: () => ({})
}
});
watch(() => props.modelValue, (newValue) => {
formModel.value = newValue;
}, { deep: true });
</script>
表单提交的进一步抽象
我们可以将表单提交逻辑进一步抽象出来,定义一个submitHandler
方法,以支持不同的提交行为。
const submitHandler = async () => {
try {
await dynamicForm.value.validate();
await props.onSubmit(formModel.value);
} catch (errors) {
props.onSubmitFail ? props.onSubmitFail(errors) : defaultSubmitFail(errors);
}
};
完整代码
<template>
<el-form
:model="formModel"
:rules="formRules"
ref="dynamicForm"
:disabled="formState.disabled"
@submit.prevent="handleSubmit"
>
<el-row :gutter="20">
<el-col :span="12" v-for="field in formConfig.fields" :key="field.model" v-if="showField(field)">
<el-form-item :label="field.label" :prop="field.model" :rules="field.rules">
<component
:is="getFieldComponent(field)"
v-model="formModel[field.model]"
v-bind="field.props"
v-on="field.listeners"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { ref, watch, defineProps, PropType, reactive, toRefs } from 'vue';
import { ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElButton, ElRow, ElCol } from 'element-plus';
import type { FormField, FormRule, FormConfig } from './formTypes';
// 注册 Element Plus 组件
const components = {
ElInput,
ElSelect,
ElOption,
// ...其他你需要支持的 Element Plus 组件
};
// 定义组件的 props
const props = defineProps({
formConfig: {
type: Object as PropType<FormConfig>,
required: true
},
formState: {
type: Object as PropType<{
disabled: boolean;
loading: boolean;
}>,
default: () => ({ disabled: false, loading: false })
}
});
// 表单模型和规则
const formModel = reactive({});
const formRules = reactive({});
// 初始化表单模型和规则
props.formConfig.fields.forEach((field) => {
formModel[field.model] = field.defaultValue || '';
if (field.rules) {
formRules[field.model] = field.rules;
}
});
// 表单引用
const dynamicForm = ref<typeof ElForm | null>(null);
// 显示逻辑
const showField = (field: FormField) => {
return !field.showIf || field.showIf(formModel);
};
// 获取对应的表单项组件
const getFieldComponent = (field: FormField) => {
return components[field.type] || field.type;
};
// 提交表单
const submitForm = () => {
dynamicForm.value?.validate((valid: boolean) => {
if (valid) {
props.formConfig.onSubmit?.(formModel);
} else {
props.formConfig.onSubmitFail?.('表单验证失败');
}
});
};
// 重置表单
const resetForm = () => {
dynamicForm.value?.resetFields();
};
// 表单提交处理
const handleSubmit = () => {
submitForm();
};
// 监听表单状态变化
watch(() => props.formState, (newState) => {
if (dynamicForm.value) {
dynamicForm.value.loading = newState.loading;
dynamicForm.value.disabled = newState.disabled;
}
}, { deep: true });
</script>
总结
通过上述步骤,我们成功地实现了一个动态表单组件,它可以根据 JSON 配置来生成表单,并包括了数据绑定和验证。这种方法简化了表单的创建流程,提高了开发效率,并增强了代码的可维护性。
转载自:https://juejin.cn/post/7352527589247254538