基于 VUE3 可视化低代码表单设计器
格子表单/GRID-FORM已在Github开源,如能帮到您麻烦给个星点此查看在线文档及演示
楔子
大概4年前,设计我做了一个简单的器源动态表单功能,开发人员通过UI界面配置表单(其实就是码表添加常用的控件,如文本框、单设下拉框等)就能向用户提供数据查询,计器源码出售哪个平台好反响不错,源码尤其是表单偏后端开发的小伙伴。
时至今日,设计上述功能存在以下问题:
目前书面上已经有不少优秀开源的器源同类产品,这里列出可二次开发的码表,同时具备表单渲染、单设表单设计的计器工具(截止至年底)
再造个轮子吧
同类型的开源产品各有千秋,适合不同的源码应用场景,然而跟我想要的表单还不够契合。权衡后,还是觉得自己弄一个。技术选型为vue3 + naive UI,使用pnpm进行包管理(monorepo结构)。
不同于同类型产品的组件拖拽,我采用栅栏布局来堆积组件(实现起来简单,省事,暂不支持容器嵌套、子表单),通过设置组件占据的格子数可以使其独占一行,故取名GRID-FORM(栅栏表单),源码详见Github。
表单设计器
得益于VUE的响应式,设计器所见即所得显得尤为丝滑,不然得自己手撸监听配置项变动事件及界面重绘。
编辑器分为左中右三个区域(这是业内约定俗成的标准设计),有别于兄弟产品,TB 源码 支撑阻力我把左区域用作表单整体的参数编辑。为方便用户自定义组件,设计器对外暴露组件库参数,并封装了常用的组件(诸如输入框、单选/多选框、日期选择)。
组件分为数据型(对应上图中的输入组件、选择组件)及展示型(上图的展示组件)两类,后者不参与表单提交。
渲染器组件渲染
每个组件有唯一编号,渲染函数为一个Object(key即为组件编号),需要扩展组件时添加对应的渲染函数即可。渲染时属性分为基本信息(名称我用_开头加以区分)及组件层面两类,分别对应了组件渲染函数的两个参数:attrs、props。
渲染引擎处理完属性后,调用Render函数(不同UI库各自实现,使用者可根据业务需要自行覆写)得到组件实例。此处以文本输入框INPUT为例:
默认值
表单项默认值可以填写常量或占位符(在初始化时被模板引擎赋值),占位符格式为${ code},用户可自行扩展处理函数。
校验
此处校验分为非空、内容格式两种
当表单项勾选是否必填,则在提交前渲染器会对该值进行非空检测;若设置了校验正则,则对非空值进行正则表达式校验。
事件&钩子函数
参数说明
数据联动
常规的做法是输入类表单项增加事件(如onChange、onBlur、onFocus等),但是这样操作(实现)繁琐(困难),我的做法只需要填写一处代码(直观简单)
要启用联动需要满足以下条件:
渲染器初始化后,会对勾选监听值变动的node博客模板源码表单项开启监听(没错,是每个表单值有独立的监听)从而获取到新旧值。注意,若在回调函数中对form改动会重新触发onChange事件。
示例
适配更多UI库
目前已实现Naive UI、Vant4的渲染器
我封装了渲染器的基础框架(组合式API),帮助使用者根据需要快速适配心仪的UI库。
结语
因个人能力有限,此工具在设计、实现上存在诸多不足,仅作学习交流。
XRender - 开源之路
XRender,阿里飞猪孵化的开源产品,目前在GitHub上获得4.2k star。本文旨在分享XRender在过去一年内的发展和变革,以及它如何成为更多前端开发者的朋友。
一、前言
1. 什么是 XRender?XRender 是一套基于React.js框架的轻量、易用、易上手的中后台「表单 / 表格 / 图表」解决方案,已经在阿里飞猪内部服务三年,未来将持续为用户提供服务。
2. 为什么需要 XRender?对于中后台业务而言,表单+表格能覆盖%的业务场景,且大部分场景重复度高。开发人员无需浪费时间在切图上,XRender这类工具能有效提升开发效率。
二、XRender 的自我革新
三年前,FormRender作为表单解决方案在GitHub上开源,成为XRender家族的首成员。面对复杂业务场景,怎么查看代币源码FormRender的旧技术方案面临挑战,内部决定升级FormRender,并增加更多Render方案,以提升内部前端开发者的效率。
现在的XRender包括FormRender、FRGenetator、TableRender和ChartRender四个组件,统称为XRender。
1.「FormRender」:协议驱动的表单解决方案。代码示例展示其核心功能不变,遵循「协议驱动渲染」原则。
代码重构,面向未来:FormRender 1.x 对内核进行彻底重构,全面拥抱React Hooks和Antd Design 4.x,增加Typescript类型定义,简化编写方式。新增beforeFinish、onFinish钩子,用于表单提交前的校验和数据提交,以及onMount方法,类似于React的componentDidMount。引入userForm方法,方便操作表单和schema,提供动态修改功能。新增watch变量,用于数据监听,增加组件丰富度,如rate、treeSelect等内置支持,以及通过JSON Schema的format属性自定义组件。
2.「表单设计器」:中后台表单可视化搭建工具,美剧源码视频提供可拖拽、可搭建能力,并支持导出schema。
3.「TableRender」:表格解决方案,内置搜索、重置、分页功能,简化开发流程。
4.「ChartRender」:基于@ant-design/charts的图表解决方案,提供快速生成图表的能力。
三、适合场景
XRender广泛应用于阿里飞猪的中后台业务,同时被阿里云、高德、淘宝、蚂蚁等BU的开发者使用。对于面向运营的搭建平台,推荐使用XRender。
四、未来规划
1. 内置组件支持自定义:开放List、Array类型的嵌套组件样式定制化,满足不同业务需求。
2. XRender 2.0开发:计划于年中旬完成,支持移动端渲染引擎Rax和Ant Design Mobile v5。
五、结尾
感谢XRender的开发者,特别制作视频以致敬社区。如果你希望在项目中使用XRender,可访问文档站点快速上手。若对源代码感兴趣或有建议,欢迎访问GitHub仓库。如XRender对你有帮助,请在GitHub上给予支持。
开源Vue表单设计器
开源Vue表单设计器详解
Vue表单设计器是一个开源的创新项目,旨在简化前端开发者在构建动态表单时的开发过程。它提供了一种直观且灵活的方式来设计和定制各种复杂的表单,无需深入编码即可实现高效开发。 项目的首要功能是生成高度可定制的表单,用户可以根据需求选择字段类型、布局和验证规则,轻松构建出符合业务需求的表单界面。设计师和开发者可以在此基础上进行扩展,满足不同场景的复杂需求。 在技术选型上,Vue表单设计器充分利用了Vue.js的强大组件化能力,结合HTML、CSS和Vue的指令,实现了组件化的设计和开发模式。此外,它还可能采用了现代前端框架的最佳实践,如Vuex进行状态管理,Webpack进行模块打包和优化,确保了项目的高效运行和稳定性能。 界面展示方面,设计者注重用户体验,界面简洁直观,易于上手。无论是表单的预览还是实际应用,都能让用户在轻松愉快的环境中完成设计。丰富的可视化工具和实时预览功能,使得设计过程更加直观和高效。 如果你对这个开源项目感兴趣,可以直接访问源码地址,查看代码、贡献或获取帮助。源码地址提供了项目的最新进展和开发者社区支持,是深入学习和参与该项目的绝佳入口。activitiå¨çº¿è®¾è®¡-Activiti6.0ä¸å¦ä½è®¾è®¡åå ³è表åï¼
activiti6çæµç¨ç¼è¾å¨æä¹æ´åè¿èªå·±ç项ç®ä¸
å»ºè®®ä½ å¯ä»¥çä¸ä¸åå¡å çç¸å ³ææ¯åæ:
ç½é¡µé¾æ¥
,
activiti6åactiviti5æ´åæ¹å¼ä¸è´
1.为ä»ä¹è¦æ´å
Activiti5.çæ¬æåæ¬ç¬ç«çActivitiModeler模åæ´åå°äºActivitiExplorerä¸,两è ç¸ç»å使ç¨èµ·æ¥å¾æ¹ä¾¿,éè¿Modeler设计çæµç¨æ¨¡åå¯ä»¥ç´æ¥é¨ç½²å°å¼æ,ä¹å¯ä»¥æå·²ç»é¨ç½²çæµç¨è½¬æ¢ä¸ºModelä»èå¨Modelerä¸ç¼è¾ã
å¨å®é åºç¨ä¸ä¹æè¿æ ·çéæ±,æModeleræ´åå°ä¸å¡ç³»ç»ä¸å¯ä»¥ä¾ç®¡çå使ç¨,æè ä½ä¸ºBPMå¹³å°çä¸é¨ååå¨,å¾éæ¾å®æ¹æ²¡æç»åºå¦ä½æ´åModelerçææ¡£ã
2.æ´åå·¥ä½
2.1ä¸è½½æºç
é¦å éè¦ä»Githubä¸è½½æºç :
2.2å¤å¶æ件
å¤å¶çæææ件åå¨activiti-webapp-explorer2ç®å½ä¸ã
src/main/resourcesä¸çããå°é¡¹ç®æºç çæºç æ ¹ç®å½,ä¿è¯ç¼è¯ä¹åå¨classesæ ¹ç®å½
src/main/webappä¸çapiãeditorãexplorerãlibså°é¡¹ç®çwebappç®å½(ä¸WEB-INFç®å½å级)
2.3æ·»å ä¾èµ
<dependency>__<groupid></groupid>__<artifactid>activiti-explorer</artifactid>__<version>5.</version>__<exclusions>____<exclusion>______<artifactid>vaadin</artifactid>______<groupid></groupid>____</exclusion>____<exclusion>______<artifactid>dcharts-widget</artifactid>______<groupid></groupid>____</exclusion>____<exclusion>______<artifactid>activiti-simple-workflow</artifactid>______<groupid></groupid>____</exclusion>__</exclusions></dependency><dependency>__<groupid></groupid>__<artifactid>activiti-modeler</artifactid>__<version>5.</version></dependency>
2.4æ·»å Javaç±»
æ·»å ä¸ä¸ªç±»ä¿åå°é¡¹ç®ä¸,注åäºä¸äºRESTè·¯ç±ã
package;
import;import;import;import;import;import;
publicclassExplorerRestApplicationextendsActivitiRestApplication{
publicExplorerRestApplication(){ __super();_}_/**_*CreatesarootRestletthatwillreceiveallincomingcalls._*/_@Override_publicsynchronizedRestletcreateInboundRoot(){ __Routerrouter=newRouter(getContext());__();__(router);__(router);__JsonpFilterjsonpFilter=newJsonpFilter(getContext());__(router);__returnjsonpFilter;_}
}
2.5é ç½®
å¨æ件ä¸æ·»å å¦ä¸é ç½®:
<!--Restletadapter,usedtoexposemodelerfunctionalitythroughREST--><servlet>__<servlet-name>RestletServlet</servlet-name>__<servlet-class></servlet-class>__<init-param>____<!--Applicationclassname-->____<param-name></param-name>____<param-value></param-value>__</init-param></servlet>
<!--Catchallservicerequests--><servlet-mapping>__<servlet-name>RestletServlet</servlet-name>__<url-pattern>/service/*</url-pattern></servlet-mapping>
2.6æ§å¶å¨
使ç¨SpringMVCåäºä¸ä¸ªç®åçå°è£ ,ä¹å¯ä»¥ä½¿ç¨å ¶ä»çMVCå®ç°ã
package;
import;import;
import;import;
import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;import;
/***æµç¨æ¨¡åæ§å¶å¨**@authorhenryyan*/@Controller@RequestMapping(value="/workflow/model")publicclassModelController{
protectedLoggerlogger=(getClass());
@Autowired_RepositoryServicerepositoryService;
/**_*模åå表_*/_@RequestMapping(value="list")_publicModelAndViewmodelList(){ __ModelAndViewmav=newModelAndView("workflow/model-list");__List<model>list=().list();__("list",list);__returnmav;_}
/**_*å建模å_*/_@RequestMapping(value="create")_publicvoidcreate(@RequestParam("name")Stringname,@RequestParam("key")Stringkey,@RequestParam("description")Stringdescription,_____HttpServletRequestrequest,HttpServletResponseresponse){ __try{ ___ObjectMapperobjectMapper=newObjectMapper();___ObjectNodeeditorNode=();___("id","canvas");___("resourceId","canvas");___ObjectNodestencilSetNode=();___("namespace","#");___("stencilset",stencilSetNode);___ModelmodelData=();
ObjectNodemodelObjectNode=();___(_NAME,name);___(_REVISION,1);___description=(description);___(_DESCRIPTION,description);___(());___(name);___((key));
(modelData);___((),().getBytes("utf-8"));
(()+"/service/editor?id="+());__}catch(Exceptione){ ___("å建模å失败:",e);__}_}
/**_*æ ¹æ®Modelé¨ç½²æµç¨_*/_@RequestMapping(value="deploy/{ modelId}")_publicStringdeploy(@PathVariable("modelId")StringmodelId,RedirectAttributesredirectAttributes){ __try{ ___ModelmodelData=(modelId);___ObjectNodemodelNode=(ObjectNode)newObjectMapper().readTree((()));___byte[]bpmnBytes=null;
BpmnModelmodel=newBpmnJsonConverter().convertToBpmnModel(modelNode);___bpmnBytes=newBpmnXMLConverter().convertToXML(model);
StringprocessName=()+".";___Deploymentdeployment=().name(()).addString(processName,newString(bpmnBytes)).deploy();___("message","é¨ç½²æå,é¨ç½²ID="+());__}catch(Exceptione){ ___("æ ¹æ®æ¨¡åé¨ç½²æµç¨å¤±è´¥:modelId={ }",modelId,e);__}__return"redirect:/workflow/model/list";_}
/**_*导åºmodelçxmlæ件_*/_@RequestMapping(value="export/{ modelId}")_publicvoidexport(@PathVariable("modelId")StringmodelId,HttpServletResponseresponse){ __try{ ___ModelmodelData=(modelId);___BpmnJsonConverterjsonConverter=newBpmnJsonConverter();___JsonNodeeditorNode=newObjectMapper().readTree((()));___BpmnModelbpmnModel=(editorNode);___BpmnXMLConverterxmlConverter=newBpmnXMLConverter();___byte[]bpmnBytes=(bpmnModel);
ByteArrayInputStreamin=newByteArrayInputStream(bpmnBytes);___(in,());___Stringfilename=().getId()+".";___("Content-Disposition","attachment;filename="+filename);___();__}catch(Exceptione){ ___("导åºmodelçxmlæ件失败:modelId={ }",modelId,e);__}_}
}</pre>
###2.7注æäºé¡¹
å¦æ使ç¨Spring代çå¼æ,并ä¸å¨é¡¹ç®ä¸åæ¶ææ件(ä¸ç®¡å¨main/resourcesè¿æ¯test/resourcesç®å½),å¨éé¢çå¼æä¸æ·»å ä¸é¢çé ç½®åæ°,å¦åä¼å¯¼è´æå¼Modelerçç¼è¾é¡µé¢æ¶è¯»åæ°æ®è¿å****ç¶æç ã
<preclass="brush:xml"><propertyname="processEngineName"value="test"></property></pre>
å¼æé»è®¤å称为default,()æ¥è¯¢æ¶ä¼å æ£ç´¢main/resources,ç¶ååæ£ç´¢test/resourcesçåæ件,æ以å½main/resourcesçæµä¸å°æå®æ件æ¶å°±ä¼å¯¼è´è¯¥å¼æ被å½åwebåºç¨çå¼æ对象,è¿æ ·ä¼å¯¼è´æ两个å¼æ,æ以æå¼æçå称æ¹ä¸ºéé»è®¤çâdefaultâã
##3.ä¸æä¹±ç é®é¢è§£å³åæ³
å¨JVMåæ°ä¸æ·»å åæ°:
>-=UTF-8-=UTF-8
**åè**:[å¨ActivitiModelerä¸è®¾è®¡çæµç¨å å«å¥æ°ä¸ªä¸ææ¶ä¸è½é¨ç½²é®é¢](
##4.æææªå¾
å¨ææ°çkft-activiti-demoçæ¬(1.7.0)ä¸å·²ç»éæäºActivitiModeler,å¯ä»¥å¨çº¿è®¿é®,ä¹å¯ä»¥ä¸è½½æºç å¦ä¹ å¦ä½é ç½®ã
ç»å½[
![kft-activiti-demoä¸çæææªå¾](/files///)![kft-activiti-demoä¸çæææªå¾](/files///)</model>
Activiti6.0ä¸å¦ä½è®¾è®¡åå ³è表åï¼æ°å¢äºç¯èç¨æ·ä»»å¡,è¿æ¶ç´§è·çæ°å¢ä¸ä¸ªè¡¨åå³å¯,以åèæ¬ãæ ·å¼ãWebAPI;å é¤äºç¯èç¨æ·ä»»å¡,è¿æ¶å¯ä»¥ä¸ç¨ç®¡,为顾åæ§çæ¬çæµç¨å®ä¹,å ¶å¯¹åºç表åè¿éè¦ä¿ç,并ä¸è½å é¤;ä¿®æ¹äºç¯èç¨æ·ä»»å¡,æ¯å¦å¨Aç¯èæ°å¢äºä¸¤ä¸ªå段,åæ¶å¨Bç¯èåå°äºä¸¤ä¸ªå段,è¿æ¶å°±è¦ä¸ºAãB两个ç¯èåèªéæ°å建表åãéæ°å建èæ¬ãæ ·å¼ã以åéæ°å建WebAPI,å 为表ååäº,é£ä¹ä¸å¡ä¹å°±åäº,SQL(表)ä¹é½è·çåäºã
ãActivitiå®æãepubä¸è½½å¨çº¿é è¯»å ¨æï¼æ±ç¾åº¦ç½çäºèµæºãActivitiå®æã(é«æ´ªç£)çµå书ç½çä¸è½½å è´¹å¨çº¿é 读
é¾æ¥:
æåç :xdni
书å:Activitiå®æ
è±ç£è¯å:7.2
ä½è :é«æ´ªç£
åºç社:_åã²å éå«
åºçå¹´:-1-1
页æ°:
å 容ç®ä»:
ãActivitiå®æãç«è¶³äºå®è·µ,ä¸ä» 让读è ç¥å ¶ç¶,å ¨é¢ææ¡Activitiæ¶æãåè½ãç¨æ³ãæå·§åæä½³å®è·µ,广度足å¤;èä¸è®©è¯»è ç¥å ¶æ以ç¶,æ·±å ¥ç解Activitiçæºä»£ç å®ç°ã设计模å¼åPVM,深度ä¹è¶³å¤ã
ãActivitiå®æãä¸å ±å个é¨å:åå¤ç¯(1~2ç« )ä»ç»äºActivitiçæ¦å¿µãç¹ç¹ãåºç¨ãä½ç³»ç»æ,以åå¼åç¯å¢çæ建åé ç½®;åºç¡ç¯(3~4ç« )é¦å 讲解äºActivitiModelerãActivitiDesigner两ç§æµç¨è®¾è®¡å·¥å ·ç详ç»ä½¿ç¨,ç¶å详ç»è®²è§£äºBPMN2.0è§è;å®æç¯(5~ç« )ç³»ç»è®²è§£äºActivitiçç¨æ³ãæå·§åæä½³å®è·µ,å å«æµç¨å®ä¹ãæµç¨å®ä¾ãä»»å¡ãåæµç¨ãå¤å®ä¾ãäºä»¶ä»¥åçå¬å¨ç;é«çº§ç¯(~)éè¿éæWebServiceãè§åå¼æãJPAãESBçåç§æå¡åä¸é´ä»¶æ¥éè¿°äºActivitiä¸ä» ä» æ¯å¼æ,å®é ä¸æ¯ä¸ä¸ªBPMå¹³å°,æåè¿éè¿æºä»£ç 对å®ç设计模å¼åPVMè¿è¡äºåæã
ä½è ç®ä»:
é«æ´ªç£(åå¡å )èµæ·±è½¯ä»¶å¼åå·¥ç¨å¸åæ¶æå¸,为Activitiè´¡ç®äºå¤§é代ç ,为Activitiå¨ä¸å½çæ¨å¹¿ä¸æ®ååäºå¤§éçå·¥ä½,å¨ç¤¾ç¾¤ä¸æå¾é«çå¨æåç¥å度,被称为ä¸å½Activitié¢åç第ä¸äººãå¤å¹´æ¥ä¸ç´ä»äºOAãERPçç³»ç»çå¼åä¸æ¶æ设计工ä½,æç»å ³æ³¨å¹¶æ·±å ¥ç 究工ä½æµå¼æ,ç®åå°±èäºå°é©¬è´è½¦,æ ä»»æ¶æå¸ä¸è,并è´è´£å ¬å¸å é¨å·¥ä½æµå¹³å°ç建设工ä½ã
聊聊低代码的表单引擎
表单在数字化世界的基石作用不容忽视,无论是构建企业服务、内部工具还是面向消费者的C端应用,它的设计直接影响用户体验和业务效率。随着低代码平台的兴起,表单设计成为了创新的重要推动力。
表单可能看似平凡,实则无处不在。从商场会员注册、后台数据录入到各类办公单据、统计调查问卷,甚至是财务报销和税务报表,表单扮演着至关重要的信息收集角色。它的核心目标是用户便捷地提交信息,无论产品类型如何,表单都是构建其功能不可或缺的组成部分。
特别提到的是JNPF表单设计,作为低代码平台的核心功能,它提供了可视化建模工具,如电子表单或表单引擎,让用户能够轻松创建和定制表单。JNPF以Vue低代码表单为基石,通过一键生成源码,释放开发者的创新时间。
尽管JNPF以直观的“拖拽”操作为主,但它也针对专业前端开发者优化,不仅提供全面的配置解决方案,还能简化重复性工作,让开发者专注于高级交互设计。其开放的架构允许开发者深度定制,即使初学者也能在短时间内上手,掌握诸如联动、格式验证等复杂功能。
总结来说,低代码中的表单设计并非易事,但它带来的挑战与深度思考的乐趣并存。深入理解并实践低代码,不仅有助于提升产品设计,也是提升业务洞察力的过程。不妨尝试一下,探索低代码表单设计的无限可能吧。
可能是你见过最专业的表单方案---解密Formily2.0
Formily2.0官网:v2.formilyjs.org/,源码地址:github.com/alibaba/formilyjs。项目由笔者发起,特别感谢阿里数字供应链事业部对Formily项目的重视与支持,以及宋思辰为Formily2.0贡献了高性能的@formily/vue组件,潇泽贡献了智能网格布局组件FormGrid。
如果你是初次接触Formily,可以阅读介绍以了解其如何解决表单问题。对于已有使用经验的用户,你会发现Formily2.0的定位从复杂场景扩展到了企业级表单的专业解决方案,专业性体现在以下几个方面:
Formily2.0自信地表示它足够专业,并且在性能优化、依赖关系管理、包设计、答疑成本控制等方面进行了深入改进。
关于性能优化,解决性能问题的关键在于减少初次渲染的阻塞式计算,通过引入Reactive模式并采用类似Mobx的解决方案,优化了性能,同时减少了props脏检查的副作用。此外,引入被动联动模式,借助@formily/reactive,实现了响应式领域模型,大大提高了性能。
依赖关系问题上,移除了styled-components、immerjs和rxjs的依赖,改用组件库自身的样式体系,如antd,或替代方案,如less和scss。这不仅减少了体积,还提高了可控性和稳定性。
在包设计方面,统一组件包到@formily/antd,抽离了@formily/json-schema包,移除了@formily/react-shared-components,确保每个包的职责明确。
答疑成本问题得到缓解,通过定义新Schema Type Void、引入x-decorator/x-decorator-props描述包装器、维护dataSource状态、定义x-reactions响应器概念,以及废弃自动删值的默认行为,使答疑更加清晰。
自定义组件扩展机制采用工厂式注册,使用@formily/reactive实现更优雅的开发方式,引入readPretty模式,使自定义组件更加灵活。
文档体系的完善使得查找文档变得容易,便于维护和使用者查找。
发量问题得到了解决,通过解决所有之前的问题,确保了系统更加高效稳定。
Formily2.0的亮点包括独立的响应式解决方案@formily/reactive,更优雅的开发方式,支持Vue2/Vue3,以及Effects局部状态、智能网格布局、响应式并发渲染等特性。
总结来说,Formily2.0在多个方面进行了全面改进,旨在提供专业级的企业级表单解决方案,通过引入Reactive响应式编程模式,解决了性能、依赖、包设计、答疑成本等核心问题,为开发者提供了一个高效、灵活且易于维护的表单框架。
2024-12-26 14:56
2024-12-26 14:19
2024-12-26 14:16
2024-12-26 13:48
2024-12-26 13:06