1. 从“手写表单”到“动态生成”的转变
如果你做过中后台项目,尤其是那些需要大量表单配置的管理系统,一定对“手写表单”的繁琐深有体会。一个稍微复杂点的表单,动辄几十个字段,每个字段都要写<el-form-item>、<el-input>,还要处理校验规则、联动逻辑、布局样式。更头疼的是,当产品经理拿着新需求过来,要求某个字段在某些条件下隐藏,或者某个下拉框的选项需要根据另一个字段的值动态变化时,你就要去翻找那一大坨模板代码,小心翼翼地修改,生怕牵一发而动全身。这种开发模式,不仅效率低下,维护成本也极高。
我接手过一个数据报表配置平台的项目,核心功能就是让运营人员能自己配置数据查询和展示表单。最初我们也是硬编码,结果就是每次新增一个查询维度,前端都要发版,后端也要跟着改接口,整个团队疲于奔命。直到我们引入了表单动态生成的方案,局面才彻底扭转。运营在可视化界面上拖拖拽拽,就能生成一个功能完整的查询表单,前端代码几乎不用动,后端也只需要提供一个通用的数据提交接口。这种前后端解耦、配置即开发的模式,极大地提升了开发效率和系统的灵活性。
在Vue 3的生态中,要实现这种动态表单,form-create-designer是一个绕不开的利器。它不是一个独立的UI组件库,而是一个基于form-create的表单设计器。简单来说,form-create负责在运行时根据JSON配置渲染出真实的表单,而form-create-designer则提供了一个可视化的拖拽界面,让你能像搭积木一样生成这份JSON配置。对于需要快速构建表单配置后台、低代码平台或者任何需要动态表单能力的项目,它都能让你事半功倍。
2. 核心概念拆解:form-create 与 form-create-designer 的关系
在深入使用之前,我们必须先理清几个核心概念和它们之间的关系,这是避免后续混乱的关键。
2.1 form-create:表单的“渲染引擎”
你可以把form-create理解为一个强大的“表单渲染引擎”。它的核心输入是一份遵循特定规则的JSON配置(我们称之为“规则”rule),输出则是一个完全可交互的Vue表单组件。这份JSON配置描述了表单的完整结构:有哪些字段(比如输入框、选择器、日期选择器)、每个字段的验证规则、字段之间的依赖和联动关系、以及整个表单的布局。
它的工作模式是声明式的。你不需要在模板里写<input v-model=”…”>,只需要告诉form-create:“这里需要一个文本输入框,绑定到username字段,并且不能为空。” 它就会在运行时为你生成对应的DOM和逻辑。这对于需要根据后端数据动态生成不同表单的场景(比如不同业务类型对应不同的录入表单)是终极解决方案。
2.2 form-create-designer:表单的“可视化工厂”
而form-create-designer,则是生产那份给form-create使用的JSON配置的“可视化工厂”。它本身是一个独立的Vue组件,提供了一个所见即所得的设计界面。
在这个界面里,左侧通常是一个丰富的组件库(基础输入、选择器、布局组件等),中间是模拟真实表单的预览画布,右侧是当前选中组件的属性配置面板。你可以通过拖拽左侧的组件到画布上来添加字段,然后在右侧面板调整这个字段的标签、字段名、默认值、校验规则、是否禁用等所有属性。你做的每一个操作,都会实时转化为一份内部的JSON规则。设计完成后,你可以通过调用设计器的方法,将这份规则JSON.stringify()后保存到数据库或文件中。当你的应用需要展示这个表单时,只需从数据库读出这份JSON,交给form-create去渲染即可。
2.3 工作流全景图
理解它们的关系,就理解了整个动态表单方案的工作流:
- 设计阶段(开发/运营人员操作):在集成了
form-create-designer的Vue 3管理后台中,通过拖拽配置,生成表单规则JSON。 - 存储阶段:将生成的规则JSON保存至后端数据库。通常,每条规则对应一个表单模板,并有一个唯一的模板ID。
- 运行阶段(最终用户操作):在最终用户使用的页面(可能是同一个应用的不同路由,也可能是另一个独立应用)中,根据模板ID从后端获取规则JSON。
- 渲染阶段:在Vue 3页面中,使用
form-create组件,并将获取到的规则JSON作为rule属性传入,form-create会将其渲染为完整的表单。 - 交互与提交:用户在渲染出的表单中填写数据,数据被双向绑定。提交时,获取
form-create实例中的数据对象,发送给后端API。
整个过程中,前端业务代码不需要为每个表单单独编写模板,实现了真正的动态化。form-create-designer是生成配置的工具,form-create是消费配置的工具,二者相辅相成。
3. 在Vue 3项目中集成 form-create-designer
理论清晰了,我们开始动手。在Vue 3项目中集成form-create-designer,我推荐使用Vite作为构建工具,整个过程非常顺畅。
3.1 环境准备与安装
首先,确保你有一个基于Vite的Vue 3项目。如果没有,可以用以下命令快速创建一个:
npm create vue@latest my-form-designer-project # 按照提示选择项目配置,这里建议添加 TypeScript 和 Vue Router 以应对更复杂的场景。 cd my-form-designer-project npm install接下来,安装核心依赖。这里需要注意版本兼容性。form-create和form-create-designer都有针对Vue 3的专门版本。
npm install @form-create/designer@next npm install @form-create/element-ui@next这里解释一下:
@form-create/designer@next:这是Vue 3版本的表单设计器。@form-create/element-ui@next:这是form-create的Vue 3渲染器,基于Element Plus UI库。它提供了将JSON规则渲染成Element Plus表单组件的能力。如果你使用Ant Design Vue或其他UI库,需要安装对应的渲染器包,但form-create-designer的设计界面本身是基于Element Plus的,所以通常搭配Element Plus使用最省心。
同时,你需要安装Element Plus本身:
npm install element-plus3.2 全局引入与组件注册
安装完成后,我们需要在项目中全局引入这些库。我习惯在main.ts(或main.js)中完成这项工作,这样在任何组件中都可以直接使用。
// main.ts import { createApp } from 'vue' import App from './App.vue' // 1. 引入 Element Plus 及其样式 import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // 2. 引入 form-create 的 Element Plus 适配器 import formCreate from '@form-create/element-ui' // 3. 引入 form-create-designer 及其样式 import FcDesigner from '@form-create/designer' import '@form-create/designer/dist/index.css' const app = createApp(App) // 4. 全局使用 app.use(ElementPlus) app.use(formCreate) // 注册 form-create app.use(FcDesigner) // 注册 designer app.mount('#app')这样,我们就完成了基础的集成。form-create和form-create-designer都被注册为全局组件,可以在模板中直接使用<fc-designer>标签。
3.3 创建第一个设计器页面
现在,我们来创建一个简单的设计器页面。在src/views或src/components目录下新建一个FormDesigner.vue组件。
<template> <div class="designer-container"> <fc-designer ref="designerRef" /> </div> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue'; // 获取设计器实例的引用,用于调用其方法 const designerRef = ref(); onMounted(() => { // 组件挂载后,可以操作设计器实例 console.log(designerRef.value); }); </script> <style scoped> .designer-container { height: calc(100vh - 60px); /* 根据你的布局调整高度 */ width: 100%; } </style>将这个组件添加到你的路由中,访问对应的路由,你应该就能看到一个完整的表单设计器界面了!左侧是组件列表,中间是画布,右侧是属性配置栏。你可以尝试拖拽一个“输入框”到画布,然后在右侧修改它的标签和字段名,初步感受一下它的能力。
4. 设计器的核心功能与实战配置
一个光秃秃的设计器只能自己玩,要融入真实项目,我们必须掌握如何与它交互:如何设置初始值、如何获取配置结果、如何自定义组件和菜单。
4.1 初始化与数据管理:加载和保存规则
设计器的核心数据就是“规则”(rule)和“选项”(option)。rule是表单结构的描述数组,option是表单的整体配置(如提交按钮文案、标签宽度等)。
加载已有规则:当你编辑一个已存在的表单模板时,需要从后端获取之前保存的JSON,并赋值给设计器。
<template> <fc-designer ref="designerRef" :value="formRule" /> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue'; import type { Rule } from '@form-create/element-ui'; const designerRef = ref(); const formRule = ref<Rule[]>([]); // 用于绑定设计器的值 // 模拟从后端API获取已有的表单规则 const loadFormTemplate = async (templateId: string) => { const response = await fetch(`/api/form-template/${templateId}`); const data = await response.json(); // 假设后端返回的数据结构为 { rule: [...], option: {...} } formRule.value = data.rule; // 如果需要设置option,可以通过设计器实例方法 // designerRef.value?.setOption(data.option); }; onMounted(() => { // 假设通过路由参数获取模板ID const templateId = '123'; loadFormTemplate(templateId); }); </script>保存当前规则:设计完成后,你需要获取当前的配置并保存。
<script setup lang="ts"> import { ref } from 'vue'; import type { Rule, Api } from '@form-create/element-ui'; const designerRef = ref<Api>(); const handleSave = () => { if (!designerRef.value) return; // 1. 获取当前设计器的规则 const currentRule: Rule[] = designerRef.value.getRule(); // 2. 获取当前设计器的全局选项 const currentOption = designerRef.value.getOption(); const templateData = { rule: currentRule, option: currentOption, // 还可以保存其他元信息,如模板名称、创建时间等 name: '我的表单模板', }; // 3. 调用后端API保存 fetch('/api/form-template', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(templateData), }).then(() => { console.log('保存成功'); }); }; </script> <template> <div> <button @click="handleSave">保存设计</button> <fc-designer ref="designerRef" /> </div> </template>注意:
getRule()和getOption()返回的是设计器内部管理的响应式对象引用。直接保存这个引用进行JSON.stringify可能会遇到循环引用或函数丢失的问题。form-create-designer的方法通常已经处理了这个问题,返回的是可序列化的数据。但为了保险起见,在复杂场景下,你可以使用JSON.parse(JSON.stringify(...))进行深拷贝后再保存或传输。
4.2 自定义组件库:扩展你的“积木”
默认的组件库可能不满足你的业务需求。比如,你需要一个“图片上传”组件,或者一个展示特定格式的“金额输入”组件。form-create-designer支持强大的自定义组件扩展。
扩展自定义组件分为两步:
- 制作组件:创建一个符合
form-create规范的Vue组件。 - 注册到设计器:告诉设计器这个新“积木”的信息。
第一步,创建自定义组件。我们在src/components/custom下创建一个ImageUpload.vue。
<template> <div> <el-upload action="/api/upload" <!-- 替换为你的上传地址 --> list-type="picture-card" :on-success="handleSuccess" :before-upload="beforeUpload" > <el-icon><Plus /></el-icon> </el-upload> </div> </template> <script setup lang="ts"> import { Plus } from '@element-plus/icons-vue'; import type { UploadProps } from 'element-plus'; const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile) => { console.log('上传成功', response); // 这里需要将上传成功后的文件地址,同步到 form-create 的数据模型中 // 通常通过 `update` 事件或注入的 `form` 对象实现,此处为简化示例 }; const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => { if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') { console.error('只能上传JPG/PNG格式的图片!'); return false; } return true; }; </script>第二步,将这个组件包装并注册到form-create和设计器。这需要在应用初始化时完成。我们修改main.ts或在设计器页面组件的父级进行。
// 在一个单独的初始化文件里,例如 `src/plugins/formCreate.ts` import { createApp } from 'vue'; import formCreate from '@form-create/element-ui'; import FcDesigner from '@form-create/designer'; import ImageUpload from '@/components/custom/ImageUpload.vue'; // 定义组件的配置信息 const customComponents = [ { // 组件在规则中的类型标识 name: 'imageUpload', // 组件在设计器左侧菜单中显示的名称 label: '图片上传', // 组件在规则中的默认配置 rule() { return { type: 'imageUpload', field: 'imageUrl', // 默认字段名 title: '图片上传', value: '', // 默认值 props: { // 可以定义一些默认的props,会在设计器右侧面板显示为可配置项 action: '/api/upload', limit: 1, }, }; }, // 组件的预览图标(可选) icon: 'el-icon-picture', }, ]; // 注册到 form-create customComponents.forEach(item => { // 将我们的 Vue 组件转换为 form-create 能识制的“Maker” formCreate.component(item.name, ImageUpload); }); // 注册到设计器的菜单 // 注意:这里需要获取设计器的全局实例,通常在 App.vue 或设计器页面进行 // 以下代码展示思路,实际注册时机可能不同 export function setupCustomFormCreate(app: App) { const designer = app.config.globalProperties.$FcDesigner; if (designer) { customComponents.forEach(item => { designer.addComponent(item); }); } }在实际项目中,更常见的做法是在使用设计器的页面组件内,通过设计器实例的addComponent方法进行动态添加,这样更灵活。
<!-- FormDesigner.vue --> <script setup lang="ts"> import { ref, onMounted } from 'vue'; import ImageUpload from '@/components/custom/ImageUpload.vue'; import type { Api } from '@form-create/element-ui'; const designerRef = ref<Api>(); onMounted(() => { if (designerRef.value) { // 1. 将组件注册到 form-create 渲染器 designerRef.value.formCreate.component('imageUpload', ImageUpload); // 2. 将组件添加到设计器左侧菜单 designerRef.value.addComponent({ name: 'imageUpload', label: '图片上传', rule: () => ({ type: 'imageUpload', field: `image_${Date.now()}`, title: '图片上传', props: { action: '/api/upload', limit: 1 }, }), icon: 'el-icon-picture', }); } }); </script>完成以上步骤后,刷新设计器页面,你应该能在左侧组件列表里看到新增的“图片上传”组件,可以像使用内置组件一样拖拽它了。右侧属性面板会自动识别rule中props对象里的键,生成对应的配置项(如action、limit)。
4.3 自定义右侧配置面板
有时候,默认生成的右侧配置项不够用或者不符合你的习惯。例如,你希望为“输入框”组件增加一个“前缀图标”的配置,或者为自定义组件增加更复杂的配置逻辑。
form-create-designer允许你通过control属性来自定义右侧面板的渲染。这需要你编写一个Vue组件来覆盖默认的控制项。这是一个相对高级的功能,涉及到对设计器内部control系统的理解。通常,你可以查阅官方文档中关于control扩展的部分,它允许你为特定的组件类型(type)指定一个自定义的配置组件。
由于这部分代码较为复杂且依赖于具体需求,其基本思路是:创建一个配置组件,接收当前选中组件的规则(rule)作为prop,在这个组件里,你可以使用任何Element Plus表单组件来编辑这个rule对象的各个属性。然后,将这个配置组件注册到设计器的control映射表中。
对于大多数业务场景,默认的配置面板加上在自定义组件rule函数中定义好的props,已经足够强大。只有当你有非常特殊的UI配置需求时,才需要考虑深入自定义control。
5. 生成的表单在运行时渲染与交互
设计好的规则保存后,最终要在用户侧页面渲染出来。这是form-create大显身手的时候。
5.1 使用 form-create 渲染表单
假设我们有一个FormViewer.vue组件,用于展示和填写设计好的表单。
<template> <div class="form-viewer"> <div v-if="loading">加载中...</div> <div v-else> <!-- form-create 组件 --> <form-create v-if="formRule.length > 0" :rule="formRule" :option="formOption" @submit="onSubmit" ref="formRef" /> <el-button type="primary" @click="submitForm">提交表单</el-button> </div> </div> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue'; import formCreate from '@form-create/element-ui'; import type { Rule, Option, Api } from '@form-create/element-ui'; const formRef = ref<Api>(); // 用于获取 form-create 实例 const formRule = ref<Rule[]>([]); // 表单规则 const formOption = ref<Option>({}); // 表单选项 const loading = ref(true); // 模拟从后端获取表单配置 const fetchFormConfig = async (templateId: string) => { loading.value = true; try { const response = await fetch(`/api/form-template/${templateId}`); const data = await response.json(); formRule.value = data.rule; formOption.value = data.option || {}; } catch (error) { console.error('加载表单配置失败', error); } finally { loading.value = false; } }; // 提交表单 const submitForm = () => { if (!formRef.value) return; // 调用 form-create 实例的 submit 方法,会触发 @submit 事件并执行表单验证 formRef.value.submit(); }; const onSubmit = (formData: any) => { // 这里的 formData 是表单验证通过后的数据对象 console.log('表单提交数据:', formData); // 调用后端API提交数据 fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData), }).then(() => { console.log('提交成功'); }); }; onMounted(() => { // 从路由参数或全局状态获取模板ID const templateId = '123'; fetchFormConfig(templateId); }); </script>form-create组件会根据传入的rule和option,自动渲染出包含所有字段、布局和校验规则的表单。@submit事件会在表单验证通过后触发,并携带完整的表单数据。
5.2 动态操作表单:获取数据、设置数据与校验
通过formRef获取到的form-create实例Api,提供了丰富的方法来动态操作表单。
获取表单数据:除了在@submit事件中获取,你可以在任何时候获取当前表单数据。
const getFormData = () => { if (formRef.value) { const formData = formRef.value.formData(); console.log('当前表单数据:', formData); return formData; } };设置表单数据:在编辑现有数据的场景下,你需要将数据回填到表单中。
const setFormData = (data: Record<string, any>) => { if (formRef.value) { // 注意:这里设置的数据需要与规则中的 field 字段名对应 formRef.value.setValue(data); } }; // 例如,从后端获取到一条记录后回填 const loadRecord = async (recordId: string) => { const response = await fetch(`/api/record/${recordId}`); const recordData = await response.json(); setFormData(recordData); };手动触发表单校验:submit()方法会触发整体校验。你也可以单独校验某个字段。
// 校验整个表单 const validateForm = () => { if (formRef.value) { formRef.value.validate((valid: boolean) => { if (valid) { console.log('表单校验通过'); } else { console.log('表单校验失败'); } }); } }; // 校验特定字段 const validateField = (fieldName: string) => { if (formRef.value) { formRef.value.validateField(fieldName, (errorMessage: string) => { if (errorMessage) { console.log(`${fieldName} 字段校验失败:`, errorMessage); } }); } };动态更新表单规则:在某些高级场景下,你可能需要根据条件动态增减字段或修改字段属性。这可以通过直接修改formRule响应式变量来实现,form-create会监听其变化并重新渲染。但直接操作规则数组比较复杂,更推荐的方式是在设计器规则层面就通过“隐藏”、“禁用”或“联动”来配置,或者在渲染时根据条件过滤规则。
5.3 处理表单联动与复杂校验
表单联动(如选择A,则B字段显示或选项变化)和复杂校验(如密码强度、字段关联校验)是动态表单的难点和重点。form-create通过规则的props、validate和control属性来支持。
联动显示/隐藏:在规则中,可以通过hidden属性或props.disabled来控制,但其值可以是一个函数,依赖其他字段的值。
// 在设计器生成的规则中,某个字段的规则可能类似这样(简化表示) { type: 'select', field: 'country', title: '国家', value: 'CN', options: [ {label: '中国', value: 'CN'}, {label: '美国', value: 'US'} ] }, { type: 'input', field: 'state', title: '省份/州', // hidden 可以是一个函数,依赖 `country` 字段的值 hidden: (formData) => formData.country !== 'CN' }联动选项:下拉框的options也可以是一个函数。
{ type: 'select', field: 'city', title: '城市', options: (formData) => { // 根据 country 字段的值返回不同的城市列表 const cityMap = { 'CN': [{label: '北京', value: 'BJ'}, {label: '上海', value: 'SH'}], 'US': [{label: '纽约', value: 'NY'}, {label: '洛杉矶', value: 'LA'}] }; return cityMap[formData.country] || []; } }复杂校验:内置的required、pattern等可能不够用,你可以使用自定义校验函数。
{ type: 'input', field: 'password', title: '密码', validate: [ { required: true, message: '请输入密码', trigger: 'blur' }, { validator: (rule, value, callback) => { // 自定义校验逻辑 if (value && value.length < 8) { callback(new Error('密码长度不能少于8位')); } else if (!/[A-Z]/.test(value)) { callback(new Error('密码必须包含大写字母')); } else { callback(); } }, trigger: 'blur' } ] }重要提示:这些联动和复杂校验的规则,需要在
form-create-designer中通过编写“规则脚本”或使用高级配置功能来生成。设计器通常提供了图形化界面来配置简单的hidden和options依赖,但对于复杂的函数式配置,可能需要直接编辑生成的JSON,或者利用设计器提供的“自定义规则”扩展功能。这是form-create-designer进阶使用的关键,需要仔细阅读其文档中关于“规则”和“control”的高级用法。
6. 项目集成中的常见问题与性能优化
在实际项目大规模使用form-create-designer和form-create后,我积累了一些踩坑经验和优化心得。
6.1 规则JSON的存储与版本管理
表单规则JSON可能会变得非常庞大和复杂。直接将其以长文本形式存储在数据库的一个字段里虽然简单,但不利于查询和版本管理。
存储建议:
- 专用表结构:为表单模板创建专用表,至少包含
id、name、rule(JSON或TEXT类型)、option(JSON类型)、version、creator_id、created_at等字段。 - 版本控制:每次保存时,如果不是覆盖更新,可以插入一条新版本记录,并关联上一个版本的ID。这样便于回滚和审计。
- 分离静态资源:如果规则中包含了图片、文件的URL,确保这些资源已经上传到对象存储(如OSS、COS),规则中只保存相对路径或URL,而不是Base64编码,避免规则JSON膨胀。
传输优化:在前后端接口传输时,对规则JSON进行压缩(如gzip)可以显著减少数据量。对于特别复杂的表单,可以考虑将规则拆分为“基础结构”和“动态配置”两部分。
6.2 设计器与渲染器的样式隔离
form-create-designer和form-create都依赖于Element Plus,并且会全局注入一些样式。如果你的项目同时存在设计器页面和最终用户表单页面,需要注意样式冲突问题。
- 设计器页面:通常是一个全功能的独立后台页面,可以完整引入Element Plus样式,没有问题。
- 用户表单页面:可能嵌入在别的系统中,或者对包大小有严格要求。你可以考虑按需引入
form-create和Element Plus的组件,以减少体积。使用Vite的按需导入功能(如unplugin-vue-components)可以很好地解决这个问题。
另一个常见问题是,设计器内部预览的样式和最终渲染的样式可能有细微差别。这通常是因为设计器画布和最终页面所处的DOM上下文、CSS作用域不同。解决方法是确保最终渲染页面也正确引入了相同的Element Plus主题CSS,并且检查是否有其他全局样式覆盖了表单组件的样式。
6.3 复杂表单的性能考量
当单个表单的规则非常复杂(字段数超过100),或者页面中需要同时渲染多个动态表单时,性能可能成为瓶颈。
渲染性能优化:
- 虚拟滚动:对于超长表单,
form-create本身不提供虚拟滚动。如果表单字段极多,可以考虑将表单分步骤(分多个form-create实例)或分标签页展示。终极方案是修改form-create的渲染逻辑或寻找支持虚拟滚动的替代方案,但这成本很高。 - 减少不必要的响应式:确保传入
form-create的rule和option是稳定的引用。避免在父组件中频繁地重新生成或修改整个规则对象,这会导致form-create不必要的重渲染。使用computed或ref包装,只在必要时更新。 - 懒加载组件:对于自定义的复杂组件,确保它们被合理拆分和异步加载。
设计器性能优化:
- 操作防抖:设计器中的很多操作(如拖拽、修改属性)会实时更新内部规则并触发画布重绘。对于频繁的操作,可以考虑对规则更新逻辑进行防抖处理,减少即时计算压力。
- 限制历史记录:设计器通常会保存操作历史用于撤销/重做。对于超大型表单,可以限制历史记录的条数,避免内存占用过大。
6.4 与后端的数据交互与校验
动态表单的数据结构是动态的,这给后端接口设计带来了挑战。
提交数据:前端通过formRef.value.formData()获取的是一个扁平的对象,键是字段名(field),值是用户输入。后端需要有一个足够灵活的结构来接收它,比如直接存储为JSON,或者根据模板ID将数据解析后存入业务表。
后端校验:前端校验是必要的,但不可完全信任。后端必须根据表单模板的规则(可以从数据库再次查询)对提交的数据进行完全校验。这包括:
- 必填字段校验。
- 数据类型校验(数字、字符串、数组等)。
- 格式校验(正则表达式、邮箱、手机号等)。
- 业务逻辑校验(如字段A大于字段B)。
你可以编写一个通用的“表单规则校验器”,它接收“表单规则JSON”和“提交数据对象”,返回校验结果。这样,任何使用动态表单的功能,后端都可以复用这套校验逻辑。
文件上传处理:对于图片、文件上传组件,通常的做法是组件先上传到文件服务器,然后将返回的URL地址作为该字段的值。在提交整个表单时,这个字段的值就是一个URL字符串。后端只需要存储这个URL即可。
7. 进阶:自定义布局与主题
默认的表单布局是垂直排列,标签在左,控件在右。但业务中常常需要更复杂的布局,比如栅格、内联、卡片分组等。
7.1 利用布局组件
form-create-designer内置了row和col(对应Element Plus的el-row和el-col)布局组件。你可以在设计器中直接拖拽row到画布,然后在row内部拖拽col和其他的输入组件,从而构建出栅格布局。通过配置col组件的span属性(占据24份中的几份),可以轻松实现响应式布局。
对于更复杂的嵌套布局,你可以组合多个row和col。设计器画布本身就是一个灵活的容器,允许你任意嵌套这些布局组件。
7.2 自定义全局选项与主题
form-create的option属性可以控制表单的全局行为与样式。
const formOption = { form: { labelPosition: 'top', // 标签位置:'top' | 'left' | 'right' labelWidth: '100px', // 标签宽度 size: 'default', // 表单控件尺寸:'large' | 'default' | 'small' inline: false, // 是否行内表单 }, submitBtn: { show: true, // 是否显示提交按钮 innerText: '提交', // 按钮文字 // ... 其他按钮属性 }, resetBtn: { show: false, // 是否显示重置按钮 }, // 全局的样式类名 className: 'my-custom-form-class' };你可以在设计器中设置这些全局选项,并随规则一起保存。在渲染时传入相同的option,即可保证所见即所得。
对于主题定制,主要依赖于Element Plus的主题系统。你可以通过覆盖Element Plus的CSS变量来改变设计器和渲染表单的整体视觉风格。确保设计器页面和最终表单页面应用的是同一套主题变量。
7.3 插槽与自定义按钮栏
有时,你需要在表单内部或周围添加一些自定义内容,比如一段说明文字,或者一个特殊的操作按钮组。form-create组件提供了插槽(slot)支持。
<template> <form-create :rule="formRule" :option="formOption" @submit="onSubmit"> <!-- 在表单字段之前插入内容 --> <template #prepend> <div class="form-header"> <h3>请填写以下信息</h3> <p>带*号为必填项</p> </div> </template> <!-- 在表单字段之后,默认按钮之前插入内容 --> <template #append> <el-alert title="注意事项" type="info" :closable="false" /> </template> <!-- 完全自定义按钮栏,覆盖默认的submit/reset按钮 --> <template #btn> <div class="custom-btn-group"> <el-button type="primary" @click="submitForm">保存草稿</el-button> <el-button type="success" @click="submitForm">正式提交</el-button> <el-button @click="resetForm">重置</el-button> </div> </template> </form-create> </template>通过插槽,你可以极大地增强表单的灵活性和表现力,使其完美融入你的页面布局。
从手写每一个el-form-item的繁琐中解放出来,到通过可视化拖拽和JSON配置驱动表单渲染,form-create-designer与form-create的组合为Vue 3中后台项目提供了一套成熟、强大的动态表单解决方案。它不仅仅是一个工具,更是一种提升开发效率、增强系统可配置性的思路。虽然初期学习和集成需要一定成本,尤其是处理复杂联动和自定义组件时,但一旦跑通流程,其带来的长期收益是巨大的。