可视化动态表单动态表单界的天花板--Formily(阿里开源)

文章目录

    • 1、Formily表单介绍
    • 2、安装依赖
      • 2.1、安装内核库
      • 2.2、 安装 UI 桥接库
      • 2.3、Formily 支持多种 UI 组件生态:
    • 3、表单设计器
      • 3.1、核心理念
      • 3.2、安装
      • 3.3、示例源码
    • 4、场景案例-登录注册
      • 4.1、Markup Schema 案例
      • 4.2、JSON Schema 案例
      • 4.3、纯 JSX 案例

1、Formily表单介绍

Formily 是一个由阿里开源的动态表单解决方案,主要用于构建和管理复杂的表单界面。支持多种前端框架,包括但不限于 React 和 Vue,支持图形可视化界面设计表单,支持多种 UI 组件集成,Formily 的核心优势在于其灵活性和扩展性,允许开发者以声明式的方式定义表单结构和行为。

同类表单产品比较:

能力Ant Design FormFusion FormFormikReact Final FormReact Schema FormReact Hook FormFormily1.xFormily2.x
自定义组件接入成本4.x接入成本低
性能4.x性能较好较好非常好非常好
是否支持动态渲染
是否开箱即用
是否支持跨端
开发效率一般一般一般一般一般
学习成本很高
视图代码可维护性
场景化封装能力
是否支持表单预览态

2、安装依赖

2.1、安装内核库

使用 Formily 必须要用到@formily/core,它负责管理表单的状态,表单校验,联动等等。

npm install --save @formily/core

2.2、 安装 UI 桥接库

单纯有了内核还不够,我们还需要一个 UI 库来接入内核数据,用来实现最终的表单交互效果,对于不同框架的用户,我们有不同的桥接库。

Vue 用户

npm install --save @formily/vue

2.3、Formily 支持多种 UI 组件生态:

包含 @formily/antd、@formily/antd-v5、@formily/antd-mobile、@formily/next、@formily/element、@formily/element-plus、@formily/antdv、@formily/vant 、@formily/semi、@formily/tdesign-react、aliyun teamix、antd-formily-boost。

3、表单设计器

Formily 表单设计器是基于designable而扩展出来的扩展包,它在继承了 designable 的基础能力上,提供了 Formily 基础表单的搭建和配置能力。

在这里插入图片描述

3.1、核心理念

Designable 的核心理念是将设计器搭建变成模块化组合,一切可替换,Designable 本身提供了一系列开箱即用的组件给用户使用,但是如果用户对组件不满意,是可以直接替换组件,从而实现最大化灵活定制,也就是 Designable 本身是不会提供任何插槽 Plugin 相关的 API

3.2、安装

Ant Design 用户

npm install --save @designable/formily-antd

Alibaba Fusion 用户

npm install --save @designable/formily-next

3.3、示例源码

import 'antd/dist/antd.less'
import React, { useMemo } from 'react'
import ReactDOM from 'react-dom'
import {Designer, //设计器根组件,主要用于下发上下文DesignerToolsWidget, //画板工具挂件ViewToolsWidget, //视图切换工具挂件Workspace, //工作区组件,核心组件,用于管理工作区内的拖拽行为,树节点数据等等...OutlineTreeWidget, //大纲树组件,它会自动识别当前工作区,展示出工作区内树节点ResourceWidget, //拖拽源挂件HistoryWidget, //历史记录挂件StudioPanel, //主布局面板CompositePanel, //左侧组合布局面板WorkspacePanel, //工作区布局面板ToolbarPanel, //工具栏布局面板ViewportPanel, //视口布局面板ViewPanel, //视图布局面板SettingsPanel, //右侧配置表单布局面板ComponentTreeWidget, //组件树渲染器
} from '@designable/react'
import { SettingsForm } from '@designable/react-settings-form'
import {createDesigner,GlobalRegistry,Shortcut,KeyCode,
} from '@designable/core'
import {LogoWidget,ActionsWidget,PreviewWidget,SchemaEditorWidget,MarkupSchemaWidget,
} from './widgets'
import { saveSchema } from './service'
import {Form,Field,Input,Select,TreeSelect,Cascader,Radio,Checkbox,Slider,Rate,NumberPicker,Transfer,Password,DatePicker,TimePicker,Upload,Switch,Text,Card,ArrayCards,ObjectContainer,ArrayTable,Space,FormTab,FormCollapse,FormLayout,FormGrid,
} from '../src'GlobalRegistry.registerDesignerLocales({'zh-CN': {sources: {Inputs: '输入控件',Layouts: '布局组件',Arrays: '自增组件',Displays: '展示组件',},},'en-US': {sources: {Inputs: 'Inputs',Layouts: 'Layouts',Arrays: 'Arrays',Displays: 'Displays',},},
})const App = () => {const engine = useMemo(() =>createDesigner({shortcuts: [new Shortcut({codes: [[KeyCode.Meta, KeyCode.S],[KeyCode.Control, KeyCode.S],],handler(ctx) {saveSchema(ctx.engine)},}),],rootComponentName: 'Form',}),[])return (<Designer engine={engine}><StudioPanel logo={<LogoWidget />} actions={<ActionsWidget />}><CompositePanel><CompositePanel.Item title="panels.Component" icon="Component"><ResourceWidgettitle="sources.Inputs"sources={[Input,Password,NumberPicker,Rate,Slider,Select,TreeSelect,Cascader,Transfer,Checkbox,Radio,DatePicker,TimePicker,Upload,Switch,ObjectContainer,]}/><ResourceWidgettitle="sources.Layouts"sources={[Card,FormGrid,FormTab,FormLayout,FormCollapse,Space,]}/><ResourceWidgettitle="sources.Arrays"sources={[ArrayCards, ArrayTable]}/><ResourceWidget title="sources.Displays" sources={[Text]} /></CompositePanel.Item><CompositePanel.Item title="panels.OutlinedTree" icon="Outline"><OutlineTreeWidget /></CompositePanel.Item><CompositePanel.Item title="panels.History" icon="History"><HistoryWidget /></CompositePanel.Item></CompositePanel><Workspace id="form"><WorkspacePanel><ToolbarPanel><DesignerToolsWidget /><ViewToolsWidgetuse={['DESIGNABLE', 'JSONTREE', 'MARKUP', 'PREVIEW']}/></ToolbarPanel><ViewportPanel><ViewPanel type="DESIGNABLE">{() => (<ComponentTreeWidgetcomponents={{Form,Field,Input,Select,TreeSelect,Cascader,Radio,Checkbox,Slider,Rate,NumberPicker,Transfer,Password,DatePicker,TimePicker,Upload,Switch,Text,Card,ArrayCards,ArrayTable,Space,FormTab,FormCollapse,FormGrid,FormLayout,ObjectContainer,}}/>)}</ViewPanel><ViewPanel type="JSONTREE" scrollable={false}>{(tree, onChange) => (<SchemaEditorWidget tree={tree} onChange={onChange} />)}</ViewPanel><ViewPanel type="MARKUP" scrollable={false}>{(tree) => <MarkupSchemaWidget tree={tree} />}</ViewPanel><ViewPanel type="PREVIEW">{(tree) => <PreviewWidget tree={tree} />}</ViewPanel></ViewportPanel></WorkspacePanel></Workspace><SettingsPanel title="panels.PropertySettings"><SettingsForm uploadAction="https://www.mocky.io/v2/5cc8019d300000980a055e76" /></SettingsPanel></StudioPanel></Designer>)
}ReactDOM.render(<App />, document.getElementById('root'))

4、场景案例-登录注册

4.1、Markup Schema 案例

在这里插入图片描述

import React from 'react'
import { createForm } from '@formily/core'
import { createSchemaField } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import * as ICONS from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})const SchemaField = createSchemaField({components: {FormItem,Input,Password,VerifyCode,},scope: {icon(name) {return React.createElement(ICONS[name])},},
})export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField><SchemaField.Stringname="username"title="用户名"requiredx-decorator="FormItem"x-component="Input"x-validator={{required: true,}}x-component-props={{prefix: "{{icon('UserOutlined')}}",}}/><SchemaField.Stringname="password"title="密码"requiredx-decorator="FormItem"x-component="Password"x-component-props={{prefix: "{{icon('LockOutlined')}}",}}/></SchemaField><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField><SchemaField.Stringname="phone"title="手机号"requiredx-validator="phone"x-decorator="FormItem"x-component="Input"x-component-props={{prefix: "{{icon('PhoneOutlined')}}",}}/><SchemaField.Stringname="verifyCode"title="验证码"requiredx-decorator="FormItem"x-component="VerifyCode"x-component-props={{prefix: "{{icon('LockOutlined')}}",}}x-reactions={[{dependencies: ['.phone#value', '.phone#valid'],fulfill: {state: {'component[1].readyPost': '{{$deps[0] && $deps[1]}}','component[1].phoneNumber': '{{$deps[0]}}',},},},]}/></SchemaField><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}

4.2、JSON Schema 案例

在这里插入图片描述

import React from 'react'
import { createForm } from '@formily/core'
import { createSchemaField } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import * as ICONS from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})const SchemaField = createSchemaField({components: {FormItem,Input,Password,VerifyCode,},scope: {icon(name) {return React.createElement(ICONS[name])},},
})const normalSchema = {type: 'object',properties: {username: {type: 'string',title: '用户名',required: true,'x-decorator': 'FormItem','x-component': 'Input','x-component-props': {prefix: "{{icon('UserOutlined')}}",},},password: {type: 'string',title: '密码',required: true,'x-decorator': 'FormItem','x-component': 'Password','x-component-props': {prefix: "{{icon('LockOutlined')}}",},},},
}const phoneSchema = {type: 'object',properties: {phone: {type: 'string',title: '手机号',required: true,'x-validator': 'phone','x-decorator': 'FormItem','x-component': 'Input','x-component-props': {prefix: "{{icon('PhoneOutlined')}}",},},verifyCode: {type: 'string',title: '验证码',required: true,'x-decorator': 'FormItem','x-component': 'VerifyCode','x-component-props': {prefix: "{{icon('LockOutlined')}}",},'x-reactions': [{dependencies: ['.phone#value', '.phone#valid'],fulfill: {state: {'component[1].readyPost': '{{$deps[0] && $deps[1]}}','component[1].phoneNumber': '{{$deps[0]}}',},},},],},},
}export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField schema={normalSchema} /><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><SchemaField schema={phoneSchema} /><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}

4.3、纯 JSX 案例

import React from 'react'
import { createForm } from '@formily/core'
import { Field } from '@formily/react'
import { Form, FormItem, Input, Password, Submit } from '@formily/antd'
import { Tabs, Card } from 'antd'
import { UserOutlined, LockOutlined, PhoneOutlined } from '@ant-design/icons'
import { VerifyCode } from './VerifyCode'const normalForm = createForm({validateFirst: true,
})const phoneForm = createForm({validateFirst: true,
})export default () => {return (<divstyle={{display: 'flex',justifyContent: 'center',background: '#eee',padding: '40px 0',}}><Card style={{ width: 400 }}><Tabs style={{ overflow: 'visible', marginTop: -10 }}><Tabs.TabPane key="1" tab="账密登录"><Formform={normalForm}layout="vertical"size="large"onAutoSubmit={console.log}><Fieldname="username"title="用户名"requireddecorator={[FormItem]}component={[Input,{prefix: <UserOutlined />,},]}/><Fieldname="password"title="密码"requireddecorator={[FormItem]}component={[Password,{prefix: <LockOutlined />,},]}/><Submit block size="large">登录</Submit></Form></Tabs.TabPane><Tabs.TabPane key="2" tab="手机登录"><Formform={phoneForm}layout="vertical"size="large"onAutoSubmit={console.log}><Fieldname="phone"title="手机号"requiredvalidator="phone"decorator={[FormItem]}component={[Input,{prefix: <PhoneOutlined />,},]}/><Fieldname="verifyCode"title="验证码"requiredreactions={(field) => {const phone = field.query('.phone')field.setComponentProps({readyPost: phone.get('valid') && phone.get('value'),phoneNumber: phone.get('value'),})}}decorator={[FormItem]}component={[VerifyCode,{prefix: <LockOutlined />,},]}/><Submit block size="large">登录</Submit></Form></Tabs.TabPane></Tabs><divstyle={{display: 'flex',justifyContent: 'space-between',}}><a href="#新用户注册">新用户注册</a><a href="#忘记密码">忘记密码?</a></div></Card></div>)
}

在这里插入图片描述


没有谁能击垮你,除非你自甘堕落。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38430.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++::多态

目录 一.多态的概念 二.多态的定义及实现 二.1多态的构成条件 二.2虚函数 1.虚函数的写法 2.虚函数的重写/覆盖 3.协变 二.3析构函数的重写 二.4override和final关键字 ​编辑二.5重载/重写/隐藏的对比 三.多态的运行原理&#xff08;一部分&#xff09; 四.多态的常…

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…

从零开发数据可视化

一、可视化模版展示 二、知识及素材准备 div css 布局flex布局Less原生js jquery 的使用rem适配echarts基础 相关js、images、font百度网盘下载链接&#xff1a; 通过百度网盘分享的文件&#xff1a;素材1 链接: https://pan.baidu.com/s/1vmZHbhykcvfLzzQT5USr8w?pwdwjx9…

WSL git文件异常 所有文件均显示已修改

如图&#xff0c;文件中没有任何修改&#xff0c;但是都显示多了一个^M 原因&#xff1a;是因为在Windows系统中git clone的文件夹&#xff0c;在WSL中会显示冲突。 解决方案&#xff1a;删掉之前在windows下git clone的文件夹&#xff0c; 然后在WSL中重新git clone

基于STM32进行FFT滤波并计算插值DA输出

文章目录 一、前言背景二、项目构思1. 确定FFT点数、采样率、采样点数2. 双缓存设计 三、代码实现1. STM32CubeMX配置和HAL库初始化2. 核心代码 四、效果展示和后话五、项目联想与扩展1. 倍频2. 降频3. 插值3.1 线性插值3.2 样条插值 一、前言背景 STM32 对 AD 采样信号进行快…

ENSP学习day9

ACL访问控制列表实验 ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种用于控制用户或系统对资源&#xff08;如文件、文件夹、网络等&#xff09;访问权限的机制。通过ACL&#xff0c;系统管理员可以定义哪些用户或系统可以访问特定资源&#x…

Ubuntu22.04通过DKMS包安装Intel WiFi系列适配器(网卡驱动)

下载驱动包 访问 backport-iwlwifi-dkmshttps://launchpad.net/ubuntu/source/backport-iwlwifi-dkms 网站&#xff0c;找到适用于Ubuntu 22.04的update版本&#xff08;如backport-iwlwifi-dkms_xxxx_all.deb&#xff09;&#xff0c;下载至本地。 安装驱动 在下载目录中执行以…

c#难点整理2

1.对象池的使用 就是先定义一系列的对象&#xff0c;用一个&#xff0c;调一个。 public class ObjectPool<T> where T : new(){private Queue<T> pool; // 用于存储对象的队列private int maxSize; // 对象池的最大容量// 构造函数public ObjectPool(int maxSi…

音频录制小妙招-自制工具-借助浏览器录一段单声道16000采样率wav格式音频

先看效果 1、打开页面 2、点击开始录音&#xff0c;弹出权限提示&#xff0c;点击“仅这次访问时允许” 3、录完后&#xff0c;点击停止 4、文件自动下载到默认目录 上代码 js 部分 document.addEventListener(DOMContentLoaded, () > {const startBtn document.getEleme…

C++:背包问题习题

1. 货币系统 1371. 货币系统 - AcWing题库 给定 V 种货币&#xff08;单位&#xff1a;元&#xff09;&#xff0c;每种货币使用的次数不限。 不同种类的货币&#xff0c;面值可能是相同的。 现在&#xff0c;要你用这 V 种货币凑出 N 元钱&#xff0c;请问共有多少种不同的…

Python设计模式 - 适配器模式

定义 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它用于将一个类的接口转换为客户端所期待的另一个接口。 注&#xff1a;在适配器模式定义中所提及的接口是指广义的接口&#xff0c;它可以表示一个方法或者一组方法的集合。 结构 …

Word中公式自动标号带章节编号

&#xff08;1&#xff09;插入一行三列的表格&#xff0c;设置宽度分别为0.5&#xff0c;13.39和1.5&#xff0c;设置纵向居中&#xff0c;中间列居中对齐&#xff0c;最右侧列靠右对齐&#xff0c;设置段落如下 &#xff08;2&#xff09;插入域代码 【Word】利用域代码快速实…

OSASIS(One-Shot Structure-Aware Stylized Image Synthesis)

文章目录 摘要abstract论文摘要方法损失函数实验结论 总结 摘要 本周阅读了一篇关于新型图像风格化的论文《One-Shot Structure-Aware Stylized Image Synthesis》&#xff0c;旨在解决现有GAN模型在风格化过程中难以保持输入图像结构的问题。通过分离图像的结构和语义信息&am…

优先队列 priority_queue详解

说到&#xff0c;priority_queue优先队列。必须先要了解啥是堆与运算符重载(我在下方有解释)。 否则只知皮毛&#xff0c;极易忘记寸步难行。 但在开头&#xff0c;还是简单的说下怎么用 首先&#xff0c;你需要调用 #include <queue> 在main函数中&#xff0c;声明…

Matplotlib

一、Matplotlib快速入门 学习目标 了解什么是matplotlib 为什么要学习matplotlib matplotlib简单图形的绘制 1、什么是Matplotlib 是专门用于开发2D图表(包括3D图表) 以渐进、交互式方式实现数据可视化 2、为什么要学习Matplotlib 可视化是在整个数据挖掘的关键辅助工…

【leetcode hot 100 131】分割回文串

解法一&#xff1a;回溯法动态规划法 回溯法&#xff1a; 假设我们当前搜索到字符串的第 i 个字符&#xff0c;且 s[0…i−1] 位置的所有字符已经被分割成若干个回文串&#xff0c;并且分割结果被放入了答案数组 ans 中&#xff0c;那么我们就需要枚举下一个回文串的右边界 j…

ToDesk云电脑各类鼠标有什么区别?虚拟/3D/游戏鼠标等各有利

不知道各位在使用ToDesk云电脑的时候是否是有注意到&#xff0c;这其中的鼠标竟有多种名称、多种模式可以选&#xff0c;比如锁定鼠标、3D鼠标、游戏鼠标这几项。 那么这些不同名称的鼠标都代表什么意思呐&#xff0c;又应该怎么选择、怎么用呐&#xff1f;本篇内容小编就为大…

手机怎么换网络IP有什么用?操作指南与场景应用‌

在数字化时代&#xff0c;手机已经成为我们日常生活中不可或缺的一部分&#xff0c;无论是工作、学习还是娱乐&#xff0c;手机都扮演着至关重要的角色。而在手机的使用过程中&#xff0c;网络IP地址作为设备在互联网上的唯一标识符&#xff0c;其重要性和作用不容忽视。本文将…

Bulk Rename Utility(BRU)——大批量重命名实用程序

Bulk Rename Utility&#xff08;BRU&#xff09;——大批量重命名实用程序 博主要给博客网站搞博客封面&#xff0c;几百张图没编号&#xff0c;一弄这个就好了&#xff0c;亲测十分好用&#xff0c;下面的b站教程更是一绝&#xff0c;快快使用起来 文章目录 Bulk Rename Ut…