ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

NocoBase RunJS 视图上下文 `ctx.view` 完全指南:弹窗、抽屉与内嵌视图的控制核心

NocoBase RunJS 视图上下文 `ctx.view` 完全指南:弹窗、抽屉与内嵌视图的控制核心 NocoBase RunJS 视图上下文ctx.view完全指南弹窗、抽屉与内嵌视图的控制核心【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase在 NocoBase 的 RunJS 执行环境中ctx.view是当前激活视图弹窗、抽屉、气泡层、嵌入式区域等的控制器它既是视图级信息的读取入口视图类型、打开参数也是视图级操作的执行入口关闭、更新、渲染头部与底部、页面内 Tab 导航。本指南将围绕ctx.view的类型定义、常用属性方法、inputArgs参数体系、与ctx.viewer/ctx.openView的分工关系展开讲解并结合packages/core/flow-engine的源码实现帮助你在弹窗表单、关联选择器、JSBlock、事件流等场景中熟练操控视图生命周期。什么是ctx.viewctx.view代表「当前所在」的视图实例它由FlowViewContext提供仅在通过ctx.viewer或ctx.openView打开的视图内容中可用。从源码看ctx.view的注入位置位于packages/core/flow-engine/src/views/useDialog.tsx与packages/core/flow-engine/src/views/useDrawer.tsx当调用dialog()/drawer()打开视图时会创建一个独立的作用域引擎createViewScopedEngine随后通过ctx.defineProperty(view, { get: () currentDialog })把当前视图实例挂到上下文中再用FlowViewContextProvider包住视图内容FlowContextProvider.tsx。// useDialog.tsx / useDrawer.tsx 中的核心装配逻辑简化 const scopedEngine createViewScopedEngine(flowContext.engine); ctx.defineProperty(view, { get: () currentDialog }); return ( FlowEngineProvider engine{scopedEngine} FlowViewContextProvider context{ctx} DialogWithContext / /FlowViewContextProvider /FlowEngineProvider );对应的 React Hooks 封装位于 FlowContextProvider.tsxexport function useFlowViewContextT FlowEngineContext() { return useContext(FlowViewContext) as T; } export function useFlowView() { const ctx useFlowContext(); return ctx.view as FlowView; }也就是说在 JSX 渲染的视图内容中你可以用useFlowViewContext()拿到完整上下文再访问ctx.view在 RunJS 脚本中则直接使用ctx.view。注意ctx.view仅在有视图上下文的 RunJS 环境中可用如ctx.viewer.dialog()的 content 内、弹窗表单、关联选择器内部在普通页面或后端上下文中为undefined使用时建议做可选链判断ctx.view?.close?.()。适用场景场景说明弹窗/抽屉内容在content中通过ctx.view.close()关闭当前视图或使用Header、Footer渲染标题和底部表单提交后提交成功后调用ctx.view.close(result)关闭并回传结果JSBlock / Action根据ctx.view.type判断当前视图类型或读取ctx.view.inputArgs中的打开参数关联选择、子表格读取inputArgs中的collectionName、filterByTk、parentId等做数据加载类型定义ctx.view的完整类型定义可在源码 FlowView.tsx 中找到与文档中的描述一一对应type FlowView { type: drawer | popover | dialog | embed; inputArgs: Recordstring, any; Header: React.FC{ title?: React.ReactNode; extra?: React.ReactNode } | null; Footer: React.FC{ children?: React.ReactNode } | null; close: (result?: any, force?: boolean) void; update: (newConfig: any) void; navigation?: ViewNavigation; destroy?: () void; submit?: () Promiseany; // 流配置视图中可用 };从源码实现看还有两个文档未列出的可选成员值得注意beforeClose?: FlowViewBeforeCloseHandler关闭前的钩子函数返回false可阻止关闭具体逻辑见 runViewBeforeClose.tspreventClose?: boolean由打开配置传入为true时close()默认返回false不关闭除非显式传force true。其中close的完整签名在源码中为(result?: any, force?: boolean) Promiseboolean | void | boolean | void即它可能返回一个布尔值表示是否真正关闭当beforeClose拦截或preventClose生效时返回false。视图类型type类型说明drawer抽屉从侧边滑出适合编辑类操作popover气泡层轻量浮层dialog居中弹窗Modal适合详情/确认类操作embed嵌入式区域不遮挡页面主体无独立 zIndex在 FlowView.tsx 中可以看到一个细节embed类型不会设置过高的zIndex以避免遮挡菜单折叠按钮图标其余类型会按打开的先后顺序递增zIndex。常用属性和方法属性/方法类型说明typedrawer \| popover \| dialog \| embed当前视图类型inputArgsRecordstring, any打开视图时传入的参数见下方HeaderReact.FC \| null头部组件用于渲染标题、操作区FooterReact.FC \| null底部组件用于渲染按钮等close(result?, force?)void关闭当前视图可传result回传给调用方update(newConfig)void更新视图配置如宽度、标题navigationViewNavigation \| undefined页面内视图导航含 Tab 切换等目前仅dialog和drawer支持Header和Footer。Header / Footer 的实现原理Header与Footer并非普通渲染组件而是「插槽」组件它们在视图内部被渲染时通过useEffect把children/title/extra同步到视图容器的头部与底部区域自身渲染null见 useDialog.tsx。// useDialog.tsx 中的 Footer 组件简化 const FooterComponent: React.FC{ children?: React.ReactNode } ({ children }) { React.useEffect(() { currentFooter children; dialogRef.current?.setFooter(children); return () { currentFooter null; dialogRef.current?.setFooter(null); }; }, [children]); return null; // Footer 组件本身不渲染内容 };这意味着你可以在视图内容的任意位置声明Header title... /与Footer.../Footer它们会自动「上浮」到弹窗/抽屉的标题栏和底部按钮栏。close 的完整流程close(result?, force?)的执行路径以 dialog 为例见 useDialog.tsx若preventClose !force直接返回false不关闭调用runViewBeforeClose(currentDialog, { result, force })执行beforeClose钩子若钩子返回false则不关闭runViewBeforeClose.ts若视图由路由触发且存在navigation.back则交由路由系统销毁视图并清理 URL否则调用destroy(result)销毁 DOM 元素、执行onClose回调、resolvePromise(result)并通过事件通知上层视图恢复激活状态VIEW_ACTIVATED_EVENT。其中resolvePromise(result)意味着close(result)的result会传递给ctx.viewer.open()返回的 Promise调用方可以在await ctx.viewer.dialog({...})之后拿到关闭时回传的结果。navigation页面内视图导航ctx.view.navigation是ViewNavigation实例实现位于 ViewNavigation.ts它维护一个不可变的viewStack视图栈提供changeTo(viewParam)替换栈顶视图参数并用replace方式导航如切换 TabnavigateTo(viewParam, opts?)向栈中压入新视图并用push方式导航back()弹出栈顶视图并导航回上一级。URL 由generatePathnameFromViewParams生成例如[{ viewUid: xxx }, { viewUid: yyy }]会得到/admin/xxx/view/yyy其中filterByTk、sourceId、tabUid都会被编码进路径段因此视图状态是可刷新保持的。inputArgs 常见字段不同打开场景下inputArgs字段不同常见包括字段说明viewUid视图 UIDcollectionName数据表名filterByTk主键筛选单条详情parentId父级 ID关联场景sourceId来源记录 IDparentItem父项数据scene场景如create、edit、selectonChange选择/变更后的回调tabUid当前 Tab UID页面内inputArgs直接来自打开时的配置config.inputArgs || {}并在视图中保持响应式。通过ctx.getVar(ctx.view.inputArgs.xxx)或ctx.view.inputArgs.xxx访问。值得说明的是inputArgs中还有一个特殊的hidden字段形如{ value: boolean }用于「先渲染但暂不显示」的视图场景见 useDialog.tsx首次挂载时若hidden.value为真视图内容会先返回null。示例关闭当前视图// 提交成功后关闭弹窗 await ctx.resource.runAction(create, { data: formData }); ctx.view?.close(); // 关闭并回传结果 ctx.view?.close({ id: newRecord.id, name: newRecord.name });结合上文源码可以知道第二个示例中{ id, name }会成为ctx.viewer.open()返回 Promise 的 resolve 值方便调用方在打开处继续处理。在 content 中使用 Header / Footerfunction DialogContent() { const ctx useFlowViewContext(); const { Header, Footer, close } ctx.view; return ( div Header title编辑 extra{Button sizesmall帮助/Button} / div表单内容.../div Footer Button onClick{() close()}取消/Button Button typeprimary onClick{handleSubmit}确定/Button /Footer /div ); }注意Header/Footer只在dialog与drawer中有效popover/embed中它们为null需要先做判空或按视图类型分支。根据视图类型或 inputArgs 做分支if (ctx.view?.type embed) { // 嵌入式视图中隐藏头部 ctx.model.setProps(headerStyle, { display: none }); } const collectionName ctx.view?.inputArgs?.collectionName; if (collectionName users) { // 用户选择器场景 }这种分支写法在关联字段选择器、子表格编辑等「同一组件被多种视图复用」的场景中非常实用。仓库中packages/core/client-v2/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx等文件就大量使用了useFlowViewContext与ctx.view来感知当前所在视图并读取打开参数。与 ctx.viewer、ctx.openView 的关系用途推荐用法打开新视图ctx.viewer.dialog()/ctx.viewer.drawer()或ctx.openView()操作当前视图ctx.view.close()、ctx.view.update()获取打开参数ctx.view.inputArgsctx.viewer负责「打开」视图ctx.view表示「当前」所在视图实例ctx.openView用于打开已配置的流程视图。三个 API 的分工可以从源码层面进一步确认ctx.viewer是FlowViewer类的实例FlowView.tsx提供dialog()、drawer()、popover()、embed()四个方法统一走open()方法分配递增的zIndex并挂载视图其 content 可以是任意 React 内容字符串内容会经DOMPurify.sanitize净化后渲染ctx.view是FlowViewer.open()返回对象的一部分Object.assign(promise, currentDialog)因此你甚至可以直接拿到返回对象调用close()/update()而不必等视图内部自己关闭ctx.openView(uid, options)打开的是 FlowPageChildPageModel内部渲染完整流程页面若 uid 对应的模型不存在会自动创建 PopupActionModel 并持久化。更详细的行为与参数可参考 ctx.openView() 文档。另外与轻量级弹窗相关的ctx.modal信息、确认等可在 ctx.modal 文档 中查看。注意事项ctx.view仅在视图内部可用普通页面中为undefined使用可选链ctx.view?.close?.()避免在无视图上下文时报错close(result)的result会传递给ctx.viewer.open()返回的 Promise若视图配置了preventClose普通close()会被拒绝需要传force true强制关闭Header/Footer目前仅在dialog和drawer中生效destroy具备幂等保护源码中的destroyed标志多次调用不会重复销毁因此路由清理与手动关闭可以安全叠加相关ctx.openView()打开已配置的流程视图ctx.modal轻量级弹窗信息、确认等FlowView.tsxFlowView类型与FlowViewer类源码useDialog.tsx / useDrawer.tsx视图生命周期与上下文装配实现FlowContextProvider.tsxFlowViewContext与useFlowViewContext/useFlowView/useFlowViewer定义ctx.viewer提供dialog()、drawer()、popover()、embed()等方法打开视图其打开的content内可访问ctx.view。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表