@formily-design/formily-designer
v1.0.10
Published
基于 [Designable](https://github.com/alibaba/designable) 和 [Formily](https://formilyjs.org/) 的表单设计器组件库,提供可视化表单设计器和 Schema 渲染表单两个核心组件。
Downloads
1,440
Readme
@formily-design/formily-designer
基于 Designable 和 Formily 的表单设计器组件库,提供可视化表单设计器和 Schema 渲染表单两个核心组件。
安装
npm install @formily-design/formily-designerPeer Dependencies
{
"react": ">=16.8.0 || >=17.0.0",
"react-dom": ">=16.8.0",
"antd": "^5.29.3",
"@formily/core": "^2.0.2",
"@formily/react": "^2.0.2",
"@formily/antd-v5": "^1.2.4",
"@formily/reactive": "^2.0.2",
"@formily/shared": "^2.0.2"
}导出
import {
// 核心组件
FormilyDesigner,
SchemaFormView,
// 自定义组件
FormButton,
FormButtonGroup,
LocationPicker,
ClickEventSetter,
ResourceIcon,
Upload,
// 工具函数
transformSchema,
transformScope,
asyncLoadDataSource,
executeFormApi,
shallowCompile,
parseStringVariables,
getLocalStorageData,
// 设计器 API(re-export from @formily-design/core)
Engine,
GlobalRegistry,
TreeNode,
createBehavior,
createResource,
createDesigner,
createLocales,
Shortcut,
KeyCode,
// 设计器 Hooks(re-export from @formily-design/react)
useDesigner,
DnFC,
useTreeNode,
useSelection,
useHistory,
useWorkspace,
usePrefix,
// 文本组件(多语言文本渲染组件,用于根据当前语言环境显示对应的界面文本)
TextWidget,
// Schema 转换(re-export from @formily-design/formily-transformer)
transformToSchema,
transformToTreeNode,
} from '@formily-design/formily-designer'
import type {
IFormilyDesignerProps,
ISchemaFormViewProps,
IResourceGroup,
ViewMode,
IFormApiConfig,
// 设计器类型
ITreeNode,
IBehavior,
IBehaviorCreator,
IResource,
IResourceCreator,
IDesignerLayoutProps,
// 转换器类型
ITransformerOptions,
IFormilySchema,
} from '@formily-design/formily-designer'FormilyDesigner
可视化表单设计器组件,提供拖拽设计、Schema 编辑、实时预览三种视图模式。
基础用法
import React from 'react'
import { FormilyDesigner } from '@formily-design/formily-designer'
export default () => <FormilyDesigner />Props
| 属性 | 类型 | 默认值 | 说明 |
| ------------------------- | ------------------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| logo | ReactNode | - | 顶部左侧 Logo |
| actions | ReactNode | - | 顶部右侧操作区 |
| customComponents | Record<string, React.ComponentType<any>> | - | 自定义组件映射,key 为组件名(x-component),value 为 DnFC 组件(需有 .Behavior 和 .Resource 静态属性) |
| customResourceGroups | IResourceGroup[] | - | 自定义资源分组,追加在内置分组之后 |
| extendResourceGroups | Record<string, any[]> | - | 扩展内置分组,key 为分组标题 locale key,value 为追加的 sources |
| customPreviewComponents | Record<string, React.ComponentType<any>> | - | 额外预览组件映射(仅当预览组件与设计时组件不同时需要) |
| customScope | Record<string, any> | - | 额外 SchemaField scope(用于预览和渲染时的表达式上下文) |
| rootComponentName | string | 'Form' | 根组件名 |
| viewModes | ViewMode[] | ['DESIGNABLE', 'JSONTREE', 'PREVIEW'] | 视图模式 |
| shortcuts | Shortcut[] | - | 快捷键配置 |
| position | 'fixed' \| 'absolute' \| 'relative' | 'fixed' | 设计器 position |
| style | CSSProperties | - | 容器样式 |
| className | string | - | 容器类名 |
| engineRef | Ref<Engine> | - | 外部通过 ref 获取 Engine 实例 |
内置资源分组
| 分组 | locale key | 包含组件 |
| -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 输入控件 | sources.Inputs | Input, Password, NumberPicker, Rate, Slider, Select, TreeSelect, Cascader, Transfer, Checkbox, Radio, DatePicker, TimePicker, Upload, Switch, ObjectContainer, FormButtonGroup, FormButton, LocationPicker |
| 布局组件 | sources.Layouts | Card, FormGrid, FormTab, FormLayout, FormCollapse, Space |
| 自增组件 | sources.Arrays | ArrayCards, ArrayTable |
| 展示组件 | sources.Displays | Text |
内置多语言
设计器内置了 zh-CN、en-US、ko-KR 三种语言,组件加载时自动注册。
SchemaFormView
根据 Formily Schema 渲染表单的组件,支持创建、编辑、详情、只读四种表单模式,以及远程数据源和 API 提交。
基础用法
import React from 'react'
import { SchemaFormView } from '@formily-design/formily-designer'
const schema = {
type: 'object',
properties: {
username: {
type: 'string',
title: '用户名',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
}
export default () => (
<SchemaFormView
schema={schema}
formMode="create"
onSubmitSuccess={(form) => {
console.log('提交数据:', form.values)
}}
/>
)Props
| 属性 | 类型 | 默认值 | 说明 |
| ------------------ | ------------------------------------------------------------------------ | ------ | ------------------------------------------------------------------------------------------ |
| schema | any | - | Formily Schema 对象 |
| customComponents | Record<string, React.ComponentType<any>> | - | 额外组件映射,合并到 createSchemaField 的 components |
| customScope | Record<string, any> | - | 额外 scope,用于表达式上下文 |
| formMode | 'create' \| 'update' \| 'detail' \| 'view' | - | 表单模式 |
| apiParams | Record<string, any> | {} | 表单 API 配置参数 |
| onSubmitSuccess | (form: any) => void | - | 表单提交成功回调 |
| formProps | { api?: IFormApiConfig; initApi?: IFormApiConfig; [key: string]: any } | - | 表单配置,api 用于提交,initApi 用于编辑/详情模式回填数据,其余属性透传给 Formily Form |
formMode 说明
| 模式 | readOnly | disabled | readPretty | 说明 |
| -------- | -------- | -------- | ---------- | ------------------------------------------------- |
| create | - | - | - | 新建模式,可编辑可提交 |
| update | - | - | - | 编辑模式,可编辑可提交,支持 initApi 加载初始数据 |
| detail | ✅ | ✅ | ✅ | 详情模式,只读展示,支持 initApi 加载数据 |
| view | ✅ | - | - | 只读模式,不可编辑但可交互 |
内置 Scope 变量
SchemaFormView 在渲染时自动注入以下 scope 变量,可在 Schema 表达式中使用:
| 变量 | 类型 | 说明 |
| --------------- | ------------------------- | ------------------------------------------- |
| $localStorage | Record<string, any> | localStorage 全部数据对象 |
| $globalData | Record<string, any> | 包含 apiParams 和 formMode 的全局数据 |
| apiParams | Record<string, any> | 传入的 API 配置参数 |
| submitFormApi | (formValues) => Promise | 表单提交 API 函数(需配置 formProps.api) |
高级场景
场景一:通过 engineRef 外部操作设计器
通过 engineRef 可以在组件外部获取 Engine 实例,使用 getCurrentTree / setCurrentTree 配合 transformToSchema / transformToTreeNode 实现获取/设置 Schema,通过 workspace.history 操作历史记录。
import React, { useRef } from 'react'
import {
FormilyDesigner,
Engine,
transformToSchema,
transformToTreeNode,
} from '@formily-design/formily-designer'
export default () => {
const engineRef = useRef<Engine>(null)
const getSchema = () => {
const engine = engineRef.current
if (!engine) return
const tree = engine.getCurrentTree()
if (!tree) return
const { schema } = transformToSchema(tree)
console.log('当前 Schema:', JSON.stringify(schema, null, 2))
}
const setSchema = (schema: any) => {
const engine = engineRef.current
if (!engine) return
engine.setCurrentTree(transformToTreeNode({ schema }))
}
const handleUndo = () => {
engineRef.current?.workbench?.currentWorkspace?.history?.undo()
}
const handleRedo = () => {
engineRef.current?.workbench?.currentWorkspace?.history?.redo()
}
return (
<div style={{ height: '100vh' }}>
<div style={{ padding: 8, background: '#f0f0f0' }}>
<button onClick={getSchema}>获取 Schema</button>
<button onClick={() => setSchema(mockSchema)}>设置 Schema</button>
<button onClick={handleUndo}>撤销</button>
<button onClick={handleRedo}>重做</button>
</div>
<FormilyDesigner engineRef={engineRef} />
</div>
)
}History API
历史记录实例挂载在 Workspace 上,通过 engine.workbench.currentWorkspace.history 访问。
| 方法 / 属性 | 签名 | 说明 |
| ------------- | --------------------- | -------------------------- |
| undo() | () | 撤销:回退到上一条历史记录 |
| redo() | () | 重做:前进到下一条历史记录 |
| goTo(index) | (index: number) | 跳转到指定索引的历史记录 |
| push(type?) | (type?: string) | 将当前状态快照推入历史栈 |
| clear() | () | 清空所有历史记录 |
| list() | () => HistoryItem[] | 获取完整历史记录列表 |
| allowUndo | get boolean | 是否可以撤销 |
| allowRedo | get boolean | 是否可以重做 |
const history = engineRef.current?.workbench?.currentWorkspace?.history
history?.undo()
history?.redo()
history?.goTo(2)
history?.push('custom-type')
history?.clear()
history?.list()
history?.allowUndo
history?.allowRedo场景二:自定义顶部操作区
通过 logo 和 actions 属性自定义设计器顶栏。actions 传入的组件位于 Designer 容器内部,可使用 useDesigner Hook 跨组件获取 Engine 实例,从而读取 Schema 数据。
import React from 'react'
import {
FormilyDesigner,
useDesigner,
transformToSchema,
} from '@formily-design/formily-designer'
import { Button, message } from 'antd'
/**
* 自定义顶部操作区
* 如果内部的 useDesigner 获取不到最新数据,试试引入 @formily/react 的 observer Hook 包裹 Actions 组件
* @example
* ```tsx
* import { observer } from '@formily/react'
* const Actions = observer(ActionsComponent)
* ```
*/
const Actions = () => {
const engine = useDesigner()
const handleSave = () => {
const tree = engine.getCurrentTree()
if (!tree) return
const { schema } = transformToSchema(tree)
console.log('保存 Schema:', JSON.stringify(schema, null, 2))
message.success('保存成功')
}
return (
<div style={{ display: 'flex', gap: 8 }}>
<Button type="primary" onClick={handleSave}>
保存
</Button>
<Button onClick={() => console.log('取消')}>取消</Button>
</div>
)
}
export default () => (
<FormilyDesigner
logo={<span style={{ fontWeight: 700, fontSize: 16 }}>表单设计器</span>}
actions={<Actions />}
/>
)注意:
useDesigner依赖DesignerEngineContext,只能在FormilyDesigner内部的子组件中使用。如果需要在FormilyDesigner外部访问 Engine,请使用engineRef(见场景一)。
场景三:扩展内置资源分组
通过 extendResourceGroups 向内置分组追加自定义组件资源,通过 customResourceGroups 添加全新的分组。
import React from 'react'
import {
FormilyDesigner,
createBehavior,
createResource,
} from '@formily-design/formily-designer'
const MyCustomInput = (props) => <input {...props} />
MyCustomInput.Behavior = createBehavior({
name: 'MyCustomInput',
selector: (node) => node.props?.['x-component'] === 'MyCustomInput',
designerProps: {
propsSchema: {
type: 'object',
properties: {
placeholder: { type: 'string', title: '占位符' },
},
},
},
})
MyCustomInput.Resource = createResource({
title: '自定义输入框',
icon: 'Input',
elements: [
{
componentName: 'Field',
props: {
type: 'string',
title: '自定义输入框',
'x-decorator': 'FormItem',
'x-component': 'MyCustomInput',
},
},
],
})
export default () => (
<FormilyDesigner
customComponents={{ MyCustomInput }}
extendResourceGroups={{
'sources.Inputs': [MyCustomInput.Resource],
}}
customResourceGroups={[
{
title: 'sources.Custom',
sources: [MyCustomInput.Resource],
},
]}
/>
)场景四:自定义预览组件
当设计时组件和预览时组件不同时(如设计时使用占位组件,预览时使用真实组件),可通过 customPreviewComponents 单独指定预览组件。
import React from 'react'
import { FormilyDesigner } from '@formily-design/formily-designer'
const DesignTimeChart = () => (
<div
style={{
height: 200,
background: '#f5f5f5',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
图表占位
</div>
)
const RuntimeChart = ({ data }) => (
<div>真实图表组件: {JSON.stringify(data)}</div>
)
export default () => (
<FormilyDesigner
customComponents={{ Chart: DesignTimeChart }}
customPreviewComponents={{ Chart: RuntimeChart }}
/>
)场景五:SchemaFormView 配合 API 提交与数据回填
SchemaFormView 支持通过 formProps 配置 API,实现表单提交和编辑/详情模式的数据回填。
import React from 'react'
import { SchemaFormView } from '@formily-design/formily-designer'
const schema = {
type: 'object',
properties: {
username: {
type: 'string',
title: '用户名',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
email: {
type: 'string',
title: '邮箱',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {
type: 'email',
},
},
},
}
const formProps = {
initApi: {
url: '/api/user/detail',
method: 'GET',
},
api: {
url: '/api/user/update',
method: 'POST',
},
}
export default () => (
<SchemaFormView
schema={schema}
formMode="update"
formProps={formProps}
apiParams={{ id: '123' }}
onSubmitSuccess={(form) => {
console.log('提交成功:', form.values)
}}
/>
)场景六:自定义 Scope 实现表达式联动
通过 customScope 注入自定义函数和变量,在 Schema 的 x-reactions 等表达式中使用。
import React from 'react'
import { SchemaFormView } from '@formily-design/formily-designer'
const schema = {
type: 'object',
properties: {
type: {
type: 'string',
title: '类型',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
options: [
{ label: '个人', value: 'personal' },
{ label: '企业', value: 'enterprise' },
],
},
},
companyName: {
type: 'string',
title: '企业名称',
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-reactions': {
dependencies: ['type'],
fulfill: {
state: {
visible: '{{$deps[0] === "enterprise"}}',
},
},
},
},
},
}
export default () => (
<SchemaFormView
schema={schema}
formMode="create"
customScope={{
$currentUser: { name: '张三', role: 'admin' },
}}
/>
)场景七:设计器与渲染表单联动
将 FormilyDesigner 设计出的 Schema 传递给 SchemaFormView 渲染,实现设计-渲染闭环。
import React, { useRef, useState } from 'react'
import {
FormilyDesigner,
SchemaFormView,
Engine,
transformToSchema,
} from '@formily-design/formily-designer'
import { Button, Modal } from 'antd'
export default () => {
const engineRef = useRef<Engine>(null)
const [previewSchema, setPreviewSchema] = useState(null)
const [modalOpen, setModalOpen] = useState(false)
const handlePreview = () => {
const engine = engineRef.current
if (!engine) return
const tree = engine.getCurrentTree()
if (!tree) return
const { schema } = transformToSchema(tree)
setPreviewSchema(schema)
setModalOpen(true)
}
return (
<div style={{ height: '100vh' }}>
<FormilyDesigner
engineRef={engineRef}
actions={
<Button type="primary" onClick={handlePreview}>
渲染预览
</Button>
}
/>
<Modal
title="渲染预览"
open={modalOpen}
onCancel={() => setModalOpen(false)}
footer={null}
width={800}
>
{previewSchema && (
<SchemaFormView
schema={previewSchema}
formMode="create"
onSubmitSuccess={(form) => {
console.log('表单数据:', form.values)
}}
/>
)}
</Modal>
</div>
)
}场景八:远程数据源联动
Schema 中支持通过 x-datasource-effect 配置远程数据源,组件会自动将其转换为 x-reactions 实现异步数据加载。
import React from 'react'
import { SchemaFormView } from '@formily-design/formily-designer'
const schema = {
type: 'object',
properties: {
city: {
type: 'string',
title: '城市',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-datasource-effect': {
url: '/api/cities',
method: 'GET',
headers: {},
params: {},
beforeRequest: '',
dataHandler:
'function(res){ return res.data.map(c => ({label: c.name, value: c.code})) }',
},
},
district: {
type: 'string',
title: '区县',
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-datasource-effect': {
url: '/api/districts',
method: 'GET',
headers: {},
params: {
cityCode: '{{$values.city}}',
},
beforeRequest: '',
dataHandler:
'function(res){ return res.data.map(d => ({label: d.name, value: d.code})) }',
},
},
},
}
export default () => <SchemaFormView schema={schema} formMode="create" />场景九:自定义 position 和样式
当设计器需要嵌入到非全屏容器中时,将 position 设为 absolute 或 relative。
import React from 'react'
import { FormilyDesigner } from '@formily-design/formily-designer'
export default () => (
<div style={{ height: 600, position: 'relative' }}>
<FormilyDesigner
position="absolute"
style={{ top: 0, left: 0, right: 0, bottom: 0 }}
/>
</div>
)自定义组件
库内置了以下自定义组件,可直接在设计器中使用:
| 组件 | 说明 |
| ------------------ | -------------------------------------------------------- |
| FormButton | 表单按钮,支持配置点击事件(API 请求、页面跳转、下载等) |
| FormButtonGroup | 按钮组容器 |
| LocationPicker | 地图位置选择器(基于腾讯地图,需安装 tlbs-map-react) |
| Upload | 文件上传组件 |
| ClickEventSetter | 点击事件配置器(用于属性面板) |
| ResourceIcon | 资源图标组件 |
工具函数
transformSchema(schema, scope)
遍历 Schema,将 x-datasource-effect 配置转换为 x-reactions 异步数据加载逻辑。
import { transformSchema } from '@formily-design/formily-designer'
const processedSchema = transformSchema(rawSchema, {
$globalData: { formMode: 'create' },
})transformScope(schema)
递归提取 Schema 中所有 x-datasource-effect 配置,生成 scope 函数映射。
import { transformScope } from '@formily-design/formily-designer'
const scope = transformScope(schema)asyncLoadDataSource(service, options)
创建异步数据加载的 reaction 函数,支持搜索防抖。
import { asyncLoadDataSource } from '@formily-design/formily-designer'
import type { Field } from '@formily/core'
const service = async (field: Field, searchValue?: string) => {
const res = await fetch(`/api/options?keyword=${searchValue ?? ''}`)
const data = await res.json()
return data
}
const reaction = asyncLoadDataSource(service, { fetchOnSearch: true })executeFormApi(apiConfig, formValues, options)
执行表单 API 请求,支持 beforeRequest / dataHandler 钩子和自定义请求函数。
import { executeFormApi } from '@formily-design/formily-designer'
const result = await executeFormApi(
{ url: '/api/submit', method: 'POST' },
{ name: 'test' },
{
requestFn: async (url, params, options) => {
const response = await fetch(url, {
method: options?.method,
headers: options?.headers,
body: JSON.stringify(params),
})
return response.json()
},
}
)shallowCompile(source, scope)
编译包含 {{}} 表达式的字符串。
import { shallowCompile } from '@formily-design/formily-designer'
shallowCompile('{{name}}', { name: 'hello' }) // 'hello'parseStringVariables(str, scope)
解析字符串中的变量占位符并替换。
import { parseStringVariables } from '@formily-design/formily-designer'
parseStringVariables('/api/user/{{$localStorage.userId}}', {
$localStorage: { userId: '123' },
})
// '/api/user/123'getLocalStorageData()
获取 localStorage 中所有数据,返回一个对象。
import { getLocalStorageData } from '@formily-design/formily-designer'
const data = getLocalStorageData()
// { token: '"abc"', userId: '"123"', ... }