前端UI组件【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址https://gitcode.com/gh_mirrors/fo/formily点击查看免费下载FormDrawer 是 Formily 体系formily/antd内置的命令式抽屉表单解决方案专用于在简单事件回调中快速打开一个承载完整表单的 Drawer 弹层。它把createForm、FormProvider、Drawer 渲染与提交结果回调封装成链式 API让你无需在页面顶层挂载弹层状态即可完成打开弹层 → 填写表单 → 提交 → 取回数据的完整闭环。读完本文你将掌握 FormDrawer 的 Markup Schema / JSON Schema / 纯 JSX 三种写法、forOpen中间件拦截、open/close链式调用与结果返回并理解其底层基于 Portal 与生命周期事件的实现原理。FormDrawer 是什么定位与适用场景按官方文档的描述FormDrawer 是抽屉表单Drawer form主要用在简单的事件打开表单场景。它与 FormDialog 同属 Formily 的弹层表单一族区别在于FormDialog使用居中模态框Modal承载表单FormDrawer使用从侧边滑出的抽屉Drawer承载表单适合承载信息量更大、需要边填写边查看上下文的场景。典型使用方式是在某个按钮的onClick事件中直接调用命令式 API例如本文档案例中的点击打开表单按钮Button onClick{() { FormDrawer(抽屉表单, () { /* 表单内容 */ }) .forOpen(...) // 可选的打开前拦截 .open({ initialValues: {...} }) // 打开并拿到返回 Promise .then(console.log) // 提交成功后拿到表单数据 }} 点我打开表单 /Button从 组件注册入口 可见FormDrawer由export * from ./form-drawer从formily/antd包导出完整实现位于 form-drawer/index.tsx。三种渲染写法FormDrawer 的第二个参数渲染函数可以返回任意 React 内容因此与 Formily 的三种 Schema 写法天然兼容。以下三个案例均来自 FormDrawer.md完整可直接运行。Markup Schema 写法使用createSchemaField注册FormItem、Input等组件在渲染函数中通过SchemaField.String nameaaa required title输入框1 x-decoratorFormItem x-componentInput /声明字段import React from react import { FormDrawer, FormItem, FormLayout, Input, Submit, Reset, FormButtonGroup, } from formily/antd import { createSchemaField } from formily/react import { Button } from antd const SchemaField createSchemaField({ components: { FormItem, Input, }, }) export default () { return ( Button onClick{() { FormDrawer(抽屉表单, () { return ( FormLayout labelCol{6} wrapperCol{10} SchemaField SchemaField.String nameaaa required title输入框1 x-decoratorFormItem x-componentInput / SchemaField.String namebbb required title输入框2 x-decoratorFormItem x-componentInput / SchemaField.String nameccc required title输入框3 x-decoratorFormItem x-componentInput / SchemaField.String nameddd required title输入框4 x-decoratorFormItem x-componentInput / /SchemaField FormDrawer.Extra FormButtonGroup alignright Submit onSubmit{() { return new Promise((resolve) { setTimeout(resolve, 1000) }) }} 提交 /Submit Reset重置/Reset /FormButtonGroup /FormDrawer.Extra /FormLayout ) }) .forOpen((props, next) { setTimeout(() { next() }, 1000) }) .open({ initialValues: { aaa: 123, }, }) .then(console.log) }} 点我打开表单 /Button ) }要点required会让该字段参与提交校验未填写时提交会被拦截并给出错误提示FormLayout labelCol{6} wrapperCol{10}控制标签与输入区的栅格宽度基于 antd 栅格系统24 栅格底部操作按钮放在FormDrawer.Extra内会通过 Portal 被渲染到 Drawer 头部区域的 extra 位置。JSON Schema 写法将 Schema 定义为纯对象通过SchemaField schema{schema} /传入const schema { type: object, properties: { aaa: { type: string, title: 输入框1, required: true, x-decorator: FormItem, x-component: Input, }, // bbb / ccc / ddd 结构与 aaa 相同 }, } export default () ( Button onClick{() { FormDrawer(弹窗表单, () { return ( FormLayout labelCol{6} wrapperCol{10} SchemaField schema{schema} / FormDrawer.Extra FormButtonGroup alignright Submit onSubmit{() new Promise((resolve) setTimeout(resolve, 1000))} 提交 /Submit Reset重置/Reset /FormButtonGroup /FormDrawer.Extra /FormLayout ) }) .open({ initialValues: { aaa: 123, }, }) .then(console.log) }} 点我打开表单 /Button )JSON Schema 与 Markup Schema 描述的是同一套字段模型type/required/title/x-decorator/x-component区别只是写法形态Markup 用 JSX 声明式语法糖JSON 便于从接口动态下发或存入后端配置。纯 JSX 写法不使用 Schema直接用formily/react的Field组件以 props 形式声明字段import { Field } from formily/react // ... FormDrawer(弹窗表单, () { return ( FormLayout labelCol{6} wrapperCol{10} Field nameaaa required title输入框1 decorator{[FormItem]} component{[Input]} / {/* bbb / ccc / ddd 结构与 aaa 相同 */} FormDrawer.Extra FormButtonGroup alignright Submit onSubmit{() new Promise((resolve) setTimeout(resolve, 1000))} 提交 /Submit Reset重置/Reset /FormButtonGroup /FormDrawer.Extra /FormLayout ) }) .open({ initialValues: { aaa: 123, }, }) .then(console.log)decorator{[FormItem]}与component{[Input]}等价于 Schema 中的x-decorator与x-component。三种写法任选其一均会在 Drawer 内部获得独立的FormProvider上下文。链式 API 与异步流程FormDrawer 返回的实例对象IFormDrawer提供三个链式方法类型定义来自 FormDrawer.md与 源码 IFormDrawer 接口 一致方法签名作用forOpen(middleware: (props: IFormProps, next: (props?: IFormProps) Promiseany) any) IFormDrawer中间件拦截器可拦截/改写 Drawer 打开行为open(props?: IFormProps) Promiseany打开弹窗接收表单属性initialValues/values/effects等resolve 出表单数据close() void关闭弹窗完整类型签名import { IFormProps, Form } from formily/core type FormDrawerRenderer | React.ReactElement | ((form: Form) React.ReactElement) interface IFormDrawer { forOpen( middleware: ( props: IFormProps, next: (props?: IFormProps) Promiseany ) any ): any // 中间件拦截器可以拦截 Drawer 打开 open(props: IFormProps): Promiseany // 打开弹窗接收表单属性返回表单数据 close(): void // 关闭弹窗 }调用约定函数重载FormDrawer本身是可调用函数支持四种重载形态interface FormDrawer { (title: IDrawerProps, id: string, renderer: FormDrawerRenderer): IFormDrawer (title: IDrawerProps, renderer: FormDrawerRenderer): IFormDrawer (title: DrawerTitle, id: string, renderer: FormDrawerRenderer): IFormDrawer (title: DrawerTitle, renderer: FormDrawerRenderer): IFormDrawer }第一个参数既可以是DrawerTitlestring/number/ReactElement也可以是完整的IDrawerProps对象此时可用title字段设置标题第二个参数若为函数或 ReactElement则被识别为rendererid采用默认值form-drawer见源码 FormDrawer 入口。从源码 isDrawerTitle/getDrawerProps 可以看到当首参是数字、字符串、布尔值或合法 ReactElement 时会被自动包装成{ title: props }。forOpen 中间件机制forOpen注册的中间件会在open()执行时、创建表单之前依次执行。源码实现是把中间件收集到env.openMiddlewares再由formily/shared的applyMiddleware串行组合执行见 form-drawer/index.tsx#L124-L129open: (props: IFormProps) { if (env.promise) return env.promise env.promise new Promise(async (resolve, reject) { try { props await loading(drawer.loadingText, () applyMiddleware(props, env.openMiddlewares) ) // ... 创建表单并渲染 } catch (e) { reject(e) } }) return env.promise }applyMiddleware的实现位于 packages/shared/src/middleware.ts它把中间件数组组合成洋葱式调用链前一个中间件调用next(props)才会进入下一个任何中间件都可以通过不调用next来中断打开流程也可以通过next({ ...props })改写最终传入createForm的表单属性。典型用途异步预取数据打开前从接口拉取初始值再通过next({ initialValues })注入权限/条件拦截校验不通过时不调用next阻止弹窗打开延迟打开如文档案例中setTimeout(() next(), 1000)模拟 1 秒后打开。文档案例中forOpen的用法异步注入初始值.forOpen((props, next) { setTimeout(() { next({ initialValues: { aaa: 123, }, }) }, 1000) })open 的 Promise 语义open(props)返回 Promise提交成功时 resolve 出表单数据form.values的 JS 化拷贝。源码关键路径form-drawer/index.tsx#L130-L158env.form env.form || createForm({ ...props, effects(form) { onFormSubmitSuccess(() { resolve(toJS(form.values)) formDrawer.close() }) props?.effects?.(form) }, })即通过createForm({...props, effects})创建表单实例props可以是initialValues、values、effects等任何IFormProps在表单 effects 中监听onFormSubmitSuccess核心生命周期事件定义见 packages/core/src/effects/onFormEffects.ts一旦表单提交成功用toJS(form.values)把响应式表单值转为普通 JS 对象并resolve给调用方即.then(console.log)拿到的数据自动调用formDrawer.close()关闭抽屉env.promise在首次open()后即缓存重复调用open直接返回同一个 Promise避免重复弹层打开过程中若中间件抛出异常Promise 会被reject需要调用方自行catch。提交动作由抽屉内的Submit按钮触发。其实现见 packages/antd/src/submit/index.tsxSubmit通过useParentForm()拿到当前表单点击时执行form.submit(onSubmit)内部走 Formily 的校验 → 提交 →onFormSubmitSuccess事件链路onSubmit返回 Promise 时按钮自动进入 loading 状态loading{form.submitting}防止重复提交。close 与关闭行为close()将抽屉 visible 置为false随后由 antd Drawer 的afterVisibleChange回调触发卸载form-drawer/index.tsx#L103-L107。注意只有props.onClose?.() ! false时才会真正关闭——即onClose返回false可以阻止关闭如拦截未保存内容。FormDrawer.Extra / Footer / Portal 静态属性FormDrawer函数对象上还挂载了三个静态组件赋值见 form-drawer/index.tsx#L227-L231静态属性属性说明FormDrawer.Extra无属性只接收子节点通过 Portal 把内容渲染到 Drawer 头部 extra 区域常用于放置操作按钮组FormDrawer.Footer无属性只接收子节点渲染到 Drawer 底部 footer 区域FormDrawer.Portal可选id默认form-drawer弹层 Portal 提供者多 prefixCls 场景下建议指定区域级 idExtra / Footer 的实现原理DrawerExtra与DrawerFooter的实现思路相同form-drawer/index.tsx#L167-L225组件自身渲染一个display: none的隐藏 div 作为锚点通过useLayoutEffect沿 DOM 向上寻找最近的.${prefixCls}-wrapper-body再在其中查找 antd 预设的.${prefixCls}-headerExtra或.${prefixCls}-footerFooter容器若目标容器不存在则动态创建并挂载随后用createPortal(props.children, container)把子节点搬运到 Drawer 对应区域。这就是为什么FormDrawer.Extra中可以直接放置FormButtonGroup且视觉上出现在抽屉头部/底部的关键。Portal 提供者与多 prefixClsFormDrawer.Portal createPortalProvider(form-drawer)。createPortalProvider定义在 packages/antd/src/builtins/portal.tsx内部维护一个可观察的PortalMap将id → ReactNode映射FormDrawer渲染时通过createPortalRoot(env.host, id)决定渲染位置若应用中存在同 id 的Portal提供者则把内容挂到该 Portal否则直接渲染到自建 host 节点id默认值为form-drawer。官方文档特别提示如果一个应用存在多个prefixCls且不同区域的弹窗内部prefixCls不一样推荐把id指定为区域级 id例如FormDrawer(props, region-a, renderer)避免不同主题区域的弹层内容被错误挂载到同一 Portal。IDrawerProps 扩展属性IDrawerProps继承自 antd 的DrawerPropsantd Drawer 的全部属性均可用width、placement、closable、mask等并新增两个扩展属性form-drawer/index.tsx#L59-L62export interface IDrawerProps extends DrawerProps { onClose?: (e: EventType) void | boolean // 返回 false 可以阻止关闭 loadingText?: React.ReactNode // 打开等待阶段的 loading 文案 }onClose关闭回调返回false可阻止关闭源码中if (props?.onClose?.(e) ! false) formDrawer.close()loadingText当forOpen中间件需要较长时间如异步拉数据时open()内部会调用 loading 工具 显示 loading 提示——其实现为延迟 100ms 后弹出message.loading(loadingText)任务结束后自动关闭默认文案为Loading...。FormDrawer( { title: 新增记录, width: 60%, loadingText: 数据加载中..., onClose: (e) { if (unsaved) return false // 阻止关闭 }, }, () MyForm / ) .open() .then(console.log)生命周期与数据流全景综合源码可以梳理出一次完整打开的调用链FormDrawer(title, renderer)创建独立envhost 节点、中间件队列、form 引用、promise并document.body.appendChild(env.host).forOpen(mw)把中间件压入env.openMiddlewares返回同一实例支持链式多次调用.open(props)依次执行loading包裹的applyMiddleware可异步改写 props→createForm({...props, effects})创建表单 → 渲染 Drawer先visiblefalse16ms 后visibletrue以触发进场动画用户在 Drawer 内填写并点击Submit触发form.submit→ 校验通过后进入onFormSubmitSuccess生命周期回调 resolve(toJS(form.values)) 并调用close()Drawer 动画结束后afterVisibleChange(false)触发root.unmount()清理 host 节点与 Portal 内容弹层资源完整释放。关于渲染层兼容性createPortalRoot使用的 render 工具 自动适配 React 18createRoot与旧版ReactDOM.render确保不同 React 版本下抽屉内容都能正确挂载与卸载。小结与建议三种写法Markup Schema / JSON Schema / 纯 JSX描述能力等价选择依据是团队偏好与 Schema 是否需要动态下发打开前需要异步准备数据或做权限校验时用forOpen中间件配合open的 Promise 拿到最终表单数据需要自定义抽屉标题、宽度、关闭拦截或加载文案时把第一个参数换成IDrawerProps对象多主题多 prefixCls应用务必为FormDrawer.Portal指定区域级id防止弹层挂载错乱提交逻辑建议在Submit.onSubmit中返回 Promise 完成异步请求FormDrawer 会自动在成功后关闭并 resolve 数据。相关文件速览完整实现 form-drawer/index.tsx、Portal 基础builtins/portal.tsx、中间件引擎 shared/src/middleware.ts、提交按钮 submit/index.tsx、提交成功生命周期 core/src/effects/onFormEffects.ts。赞分享前端UI组件【免费下载链接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3项目地址https://gitcode.com/gh_mirrors/fo/formily点击查看免费下载相关推荐Formily FormDrawer 抽屉表单完全指南API、三种 Schema 写法与源码原理Formily FormDrawer 抽屉表单完全指南API、三种 Schema 写法与源码原理 FormDrawer 是 Formily 体系 form前端UI组件Formily FormDrawer 抽屉表单实战指南四种写法、中间件拦截与源码原理Formily FormDrawer 抽屉表单实战指南四种写法、中间件拦截与源码原理 抽屉表单FormDrawer是 Formily 提供的命令式表单容器前端UI组件Formily Element 抽屉表单 FormDrawer 完整指南API 详解与三种 Schema 实战Formily Element 抽屉表单 FormDrawer 完整指南API 详解与三种 Schema 实战 抽屉表单FormDrawer是 Formi前端UI组件上一篇突破动画复用瓶颈mojs组件化设计模式实战指南下一篇终极指南Redux-Thunk异步操作的状态命名黄金法则创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
B站用户行为分析系统源码解析:Python爬虫与数据可视化实战 简介:这是一套面向课程设计与Python后端学习者的B站用户行为分析系统完整项目源码,适合数据科学、机器学习方向的学生和开发者作为实战参考。项目通过爬虫采集观看历史、弹幕、评论等行为数据,结合Pandas、Matplotlib完成清洗与可视化&#x… · 2026/9/23 22:45:52
BP神经网络仿真从入门到避坑:MATLAB调参与归一化实战指南 简介:面向需要在MATLAB/Simulink环境中实现BP神经网络的学习者与工程师,这份资源聚焦非线性数据的分类与回归预测。压缩包共4个文件,以S函数(.m)、Simulink模型(.slx)及txt说明文件为主… · 2026/9/23 22:45:38
OpenCV 4.2 + contrib 源码编译与避坑指南 简介:面向OpenCV开发者的Linux平台计算机视觉资源包,重点解决官网版opencv_contrib在Linux下编译时缺少文件、头文件找不到、无法顺利构建的常见问题。作者针对编译错误进行修正与补全,使扩展库与OpenCV 4.2主库能够一并编译通过,… · 2026/9/23 23:24:37
Python爬虫实战:从列表页到详情页批量下载图片的完整方案 前阵子接了个私活,对方给了一个图片站点,说“帮我把整站的套图抓下来”。我一看结构,就是个典型的分页列表站,图片直链藏在详情页里,没有登录墙,也没有太复杂的加密,用 Python 写个脚本就能搞定… · 2026/9/23 23:24:31
PaddleSpeech 服务端 BaseEngine 引擎基类全解析:单例设计、引擎生命周期与多任务服务编排 PaddleSpeech 服务端 BaseEngine 引擎基类全解析:单例设计、引擎生命周期与多任务服务编排 【免费下载链接】PaddleSpeech Easy-to-use Speech Toolkit including Self-Supervised Learning model, SOTA/Streaming ASR with punctuation, Streaming TTS with text f… · 2026/9/23 23:24:24
卫星遥感舰船检测数据集实战:VOC转YOLO全流程避坑指南 简介:面向卫星遥感舰船检测任务的数据集资源,适用于计算机视觉目标检测方向的学生、研究者和算法工程师,可直接服务于船舶识别模型的训练与验证。数据覆盖航空母舰、辅助舰、货船、集装箱船、巡洋舰、驱逐舰、护卫舰、气垫船、油轮、潜艇、游… · 2026/9/23 23:24:11
中文法律大模型微调实战:从词表扩展到LoRA训练全流程 简介:这份资源面向希望将大语言模型落地到中文法律场景的开发者与算法学习者,围绕法律问答、法条理解与指令微调等任务,提供了一套可复现的工程实践材料。包内共42个文件,以Python脚本、JSON配置与数据、Shell运行脚本为主&#x… · 2026/9/23 23:24:05
wired-elements 版本演进全解:从 1.0 到 3.0 的手绘风 Web Components 技术脉络 UI组件前端 【免费下载链接】wired-elements Collection of custom elements that appear hand drawn. Great for wireframes or a fun look. 项目地址: https://gitcode.com/gh_mirrors/wi/wired-elements 点击查看 免费下载 导读
wired-elements 是一个以"… · 2026/9/23 23:24:05
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29