form-create 动态表单设计器实战:从拖拽设计到主子表与流程表单绑定 form-create 动态表单设计器实战从拖拽设计到主子表与流程表单绑定演示地址http://ruoyioffice.com | 源码1ruoyi-office-vben | 源码2ruoyi-office | 源码3ruoyi-office | 微信17156169080备注「RuoYi Office」企业系统里 80% 的审批单都是「填一张表」请假、报销、用印、采购……如果每张表都让前端写一遍a-form业务一变就要发版。动态表单low-code form的价值就在于业务人员拖拽设计表单系统存成 JSON运行时动态渲染——改字段不改代码。RuoYi Office 基于form-create生态搭了这套能力fc-designer-pro负责拖拽设计form-create/ant-design-vue负责运行时渲染再深度接入 Flowable 流程表单、主子表、字段权限。本文基于真实源码把这条链路讲透。▲ 表单低代码全景fc-designer-pro 拖拽设计 → conffields 编码存库 → BPM 模型绑定 formId → 运行时 form-create 渲染 字段权限控制HTML 源文件见images/form-create-architecture.html引言动态表单到底难在哪「拖个表单」听起来简单做成企业级却有一堆坑痛点一设计态和运行态要解耦。设计器产出的是一套 JSON 规则rule运行时要能脱离设计器独立渲染——两者不能耦合在一个组件里。痛点二标准组件不够用。企业表单要「选用户」「选部门」「选字典」「上传到 MinIO」「富文本」——这些不是 Input/Select 能覆盖的必须能往设计器里注入自定义组件。痛点三要和流程引擎绑定。审批表单不是孤立的要绑到流程模型上发起页、审批页都用同一份表单定义渲染还要支持流程变量回填。痛点四主子表。一张采购单主表挂 N 行采购明细子表动态表单原生的 subform 体验差企业更想要 Excel 式表格编辑。痛点五字段权限。同一张表发起人能填全部审批人只能看一部分、改一部分——字段级的读/写/隐藏/必填要能按流程节点配置。现状后果每张表手写 Vue 组件改字段就发版迭代慢设计器与渲染耦合运行时被迫加载整个设计器只有基础组件选人/选部门/字典全要自研对接表单游离于流程之外审批表单两套维护无字段权限敏感字段全员可见可改一、技术选型fc-designer-pro form-create 运行时form-create 是一个由 JSON 规则驱动表单渲染的开源框架你给它一段 rule描述有哪些字段、什么类型、什么校验它就渲染出完整表单。RuoYi Office 的选型是「设计器 运行时」两个包分工角色包版本职责设计器fc-designer-pro6.2.0商业 Pro 编译包拖拽设计 UI运行时form-create/ant-design-vue^3.3.1JSON → 表单渲染注意RuoYi Office 用的是商业 Pro 版设计器含 VxeTable 主子表、更多企业组件不是开源的form-create/designer但运行时渲染用的是开源的form-create/ant-design-vue二者通过统一的 rule JSON 协议衔接。设计器入口在views/bpm/form/designer/index.vueimportFcDesignerfromfc-designer-pro;import{buildBpmProcessGlobalData,buildBpmProcessGlobalVariable,setConfAndFields,useFormCreateDesigner,}from#/components/form-create;二、设计态自定义组件注入开箱的 form-create 只有基础组件企业要的「选人/选部门/字典/上传/富文本」靠useFormCreateDesigner()注入到设计器的拖拽面板。项目把这些封装在src/components/form-create/下components/form-create/ ├── helpers.ts # encodeConf/encodeFields/decodeFields、useFormCreateDesigner ├── bpm-context.ts # 流程上下文 globalData/globalVariable ├── components/ │ ├── dict-select.vue # 字典下拉 │ ├── dept-select.vue # 部门选择 │ ├── area-select.vue # 地区级联 │ └── use-api-select.tsx # 远程 API 下拉 └── rules/ # 各组件的拖拽规则定义 ├── use-upload-file-rule.ts ├── use-dict-select.ts └── use-editor-rule.ts注入后业务人员在设计器左侧就能直接拖出「用户选择器」「部门选择器」「字典下拉」等企业组件和拖一个普通 Input 体验一致。三、存储态conf fields 编解码设计完成后要存到后端。form-create 的表单定义拆成两部分conf表单全局配置option一个 JSON 字符串fields每个字段一条 rule存成「字符串数组」每条 rule 独立 JSON后端表bpm_form对应字段// api/bpm/form/index.tsexportinterfaceForm{id?:number;name:string;conf:string;// form-create option JSON 字符串fields:string[];// 每条 rule 独立 JSON 字符串status:number;category?:string;// bound 表示绑定物理表主子表}保存前用encodeConf/encodeFields编码// components/form-create/helpers.tsexportfunctionencodeConf(designerRef:any){returnformCreate.toJson(designerRef.value.getOption());}exportfunctionencodeFields(designerRef:any){construledesignerRef.value.getRule();constfields:string[][];rule.forEach((item:any){constencodedRulecloneDeep(item);stripDynamicSelectorDefaultValue(encodedRule);// 清掉动态选择器的默认值fields.push(formCreate.toJson(encodedRule));// 逐条序列化});returnfields;}保存到后端designer/modules/form.vuedata.confencodeConf(designerComponent);data.fieldsencodeFields(designerComponent);if(formData.value?.ideditorAction.value!copy){awaitupdateForm(data);// PUT /bpm/form/update}else{savedFormIdawaitcreateForm(data);// POST /bpm/form/create}编辑时反向回显用setConfAndFields把字符串解码回设计器constformDetailawaitgetForm(id);setConfAndFields(designerRef,formDetail.conf,formDetail.fields);designerRef.value.setGlobalData(buildBpmProcessGlobalData());// 注入流程上下文designerRef.value.setGlobalVariable(buildBpmProcessGlobalVariable());四、绑定态与 BPM 流程模型联动表单设计完要绑到流程模型上才能在审批中使用。完整链路表单设计 → bpm_form 表(conf fields) ↓ 流程模型「表单设计」步骤选 formId model.formId ↓ 流程定义发布快照 processDefinition.formConf / formFields ↓ 发起页 / 审批页 → form-create 渲染模型绑定流程模型编辑的「表单设计」步骤model/form/modules/form-design.vue选一个formId发起流程processInstance/create/modules/form.vue用setConfAndFields2(detailForm, formConf, formFields, formVariables)渲染formVariables可回填流程变量审批详情processInstance/detail/index.vue同样用form-createfApiform-create 的命令式 API控制字段发布时表单定义会被「快照」进流程定义因此改了表单不影响已发起的流程实例保证历史数据稳定。五、进阶一主子表fcVxeDataTable企业表单的硬需求是主子表。RuoYi Office 用 Pro 版内置的fcVxeDataTable基于 VxeGrid实现 Excel 式子表。当表单category bound时把物理表的列注入设计器全局数据// designer/index.vue 把物理表结构注入 $globalDatadesignerRef.value.setGlobalData({physicalTable:mainTableColumns,// 主表列physicalDetail:detailTableColumns,// 子表列明细});运行时识别 Vxe 规则单独加载明细数据// processInstance/detail/index.vuefunctionisVxeDataTableRule(rule:any){return(rule?.typefcVxeDataTable||rule?.typedataTable||rule?._fc_drag_tagfcVxeDataTable);}子表绑定关系通过saveFormTableBinding→/bpm/form-data/binding/save持久化实现「表单字段 ↔ 物理表列」的映射。六、进阶二字段权限运行时控制字段权限是审批表单的灵魂。先看权限枚举simple-process-design/consts.tsexportenumFieldPermissionType{READ1,// 只读WRITE2,// 可编辑NONE3,// 隐藏REQUIRED4,// 必填}设计期从fields[]解析出所有字段parseFormFields递归取field/title在流程节点配置每个字段的权限。运行期后端返回formFieldsPermission前端通过 form-create 的命令式 API 动态应用不改 rule JSON 本身// processInstance/detail/index.vuefunctionsetFieldPermission(field:string,permission:string){if(permissionFieldPermissionType.READ){fApi.value?.disabled(true,field);// 只读}if(permissionFieldPermissionType.WRITE){fApi.value?.disabled(false,field);// 可编辑}if(permissionFieldPermissionType.REQUIRED){fApi.value?.disabled(false,field);// 可编辑 动态追加 required 校验}if(permissionFieldPermissionType.NONE){fApi.value?.hidden(true,field);// 隐藏}}这种「rule 定义 运行时 API 控制」的解耦设计意味着同一份表单定义可在不同节点呈现不同的可编辑形态无需为每个节点复制一份表单。七、技术亮点总结设计要点实现方式价值设计/运行解耦fc-designer-pro 设计 form-create 渲染运行时轻量自定义组件useFormCreateDesigner 注入选人/部门/字典开箱即用表单存储conf fields[] 字符串结构清晰逐字段可控编解码encodeConf/encodeFields/setConfAndFields保存回显闭环流程绑定model.formId 发布快照改表单不影响历史实例主子表fcVxeDataTable 物理表 globalDataExcel 式子表编辑字段权限fApi.disabled/hidden 运行时一份表单多节点形态流程变量回填setConfAndFields2 formVariables发起页带入上下文八、快速体验在线演示http://ruoyioffice.com/web/账号admin/admin123操作路径工作流程 → 表单管理 → 新建表单拖拽设计→ 流程模型 → 表单设计步骤绑定该表单推荐体验流程新建表单拖入用户选择器、字典下拉、上传组件保存后到流程模型在「表单设计」步骤选这张表单配置某节点的字段权限部分只读、部分隐藏发起流程观察发起页表单渲染走到该节点审批观察字段权限生效仓库地址前端GitCode后端GitCode · GitHub常见问题FAQRuoYi Office 的表单设计器用的是开源 form-create 吗设计器用的是商业版fc-designer-pro6.2.0含 VxeTable 主子表等企业组件运行时渲染用开源的form-create/ant-design-vue3.3.1。两者通过统一的 rule JSON 协议协作运行时不依赖设计器部署更轻量。表单设计结果存在哪、什么格式存在后端bpm_form表分conf表单全局配置 option单个 JSON 字符串和fields每个字段一条 rule字符串数组两部分分别用encodeConf/encodeFields编码、setConfAndFields解码回显。改了表单会影响已经发起的流程吗不会。流程定义发布时会把表单定义快照进processDefinition.formConf / formFields已发起的流程实例使用快照版本后续修改表单只影响新发起的流程。动态表单支持主子表吗支持。通过商业 Pro 版内置的fcVxeDataTable基于 VxeGrid实现 Excel 式明细表表单categorybound时绑定物理表列子表映射通过/bpm/form-data/binding/save持久化。字段权限是怎么实现的权限分只读(1)/可编辑(2)/隐藏(3)/必填(4) 四种在流程节点上按字段配置。运行时后端返回formFieldsPermission前端通过 form-create 的fApi.disabled()/hidden()动态控制不修改表单 rule 本身因此同一份表单可在不同节点呈现不同形态。想要体验 RuoYi Office 的强大功能在线演示http://ruoyioffice.com/web/账号 admin / admin123源码仓库GitCode | GitHub技术咨询添加微信17156169080备注「RuoYi Office」⭐如果觉得不错请给个 Star 支持一下