- 新增设计器静态依赖分析方案,阐述基于 AST 的代码片段解析与依赖图谱构建 - 新增实体生命周期引擎实现规范,定义引擎驱动的事件分发与交互触发策略 - 新增悬浮组件架构设计规范,明确底层数据统一与上层视图分离的核心原则 - 新增应用运行模式架构规范,严格区分设计态、预览态与运行态的边界 - 新增设计器选中与分组交互规范,定义选中态、激活态及下钻交互的行为矩阵
6.5 KiB
DatAlive 悬浮组件 (Overlay Components) 架构设计规范
在低代码平台(如 DatAlive)中,悬浮组件(如 Modal 弹窗、Drawer 抽屉、全局 Toast 等)由于其“脱离标准文档流、需要交互唤起、默认隐藏”的特性,一直是设计器架构、状态管理和交互设计的难点。
本规范基于 DatAlive 的核心设计哲学,详细界定了悬浮组件在底层数据蓝图(Schema)、物料元数据(Component Meta)、运行时状态(Runtime Store)以及设计器用户界面(UI)之间的流转与协作机制。
1. 核心原则:底层数据统一,上层视图分离
在架构设计初期,开发者极易陷入“为特殊组件定制特殊结构”的陷阱。DatAlive 坚决摒弃在 Schema 中为悬浮组件设立“特权结构”(例如在 Page 节点下新增 modals: Component[] 数组)的做法。
1.1 Schema 实例层 (Instance Schema) 的绝对纯净
在最终持久化存入数据库的 design-mode Schema 中,万物皆为平等的 Component 节点。
- 统一的树模型:悬浮组件仅仅是
Page.components树中的一个普通节点。它拥有与普通组件完全一致的接口:type,props,layout,style,children。 - 嵌套无特权:当用户向
Modal内部拖入一个Table时,底层发生的数据结构变更仅仅是:向该Modal节点的children数组中push了一个Table节点。这份包含了弹窗及其内部所有子组件的完整 JSON 树,将原封不动地被导出或保存。 - 架构优势:
- 极简的渲染引擎:引擎只需要一套极其简洁的递归遍历逻辑,即可完成包含弹窗在内的整棵组件树的解析,无需编写针对
modals的额外逻辑。 - 交互动作泛化:因为弹窗只是普通节点,所有的泛化 Action(如
{"target": {"id": "modal_1"}, "method": "open"})可以无缝作用于它,无需修改目标选择器逻辑。
- 极简的渲染引擎:引擎只需要一套极其简洁的递归遍历逻辑,即可完成包含弹窗在内的整棵组件树的解析,无需编写针对
1.2 物料元数据层 (Component Meta) 的二元解耦
悬浮组件之所以在 UI 上表现特殊,是因为它在**平台物料库的注册表(Component Registry)**中带有明确的静态元数据标识。我们必须严格区分“组件的静态特征”与“实例的运行时状态”。
// 伪代码:在设计器源码(或 SDK)中注册 Modal 物料
Registry.register({
type: 'Modal',
name: '对话框',
category: '反馈',
icon: 'icon-modal',
isOverlay: true, // 🚨 核心标识:告知设计器这是一个悬浮容器
isContainer: true, // 允许向其内部拖入子组件
setters: [ ... ] // 属性配置器
});
- 彻底的解耦机制:像
isOverlay这种描述“物料类”固有属性的标识,绝对不能被抽离或序列化到用户的页面 Schema JSON 中。Schema 只保存实例特有的变动数据(如props.title)。设计器在运行时通过type查字典(Type Mapping),动态获知该实例属于isOverlay: true的类别。
2. 设计器 UI 层面的协作机制 (MVC 映射)
基于上述底层架构,设计器在处理悬浮组件时,展现出极其优雅的 MVC(Model-View-Controller)协作,确保“Schema”、“图层面板”和“主画布”三方的绝对同步。
2.1 唯一真相来源 (Single Source of Truth)
Zustand/Redux 内存中的 Application Schema(特别是当前 Page 的被扁平化或深层嵌套的 components 树)是唯一的 Model。视图层(图层面板、画布)本身不保存任何层级或显隐状态。
2.2 图层树面板的“动态分叉 (Forking)”
为了避免主画布被多个隐藏的弹窗遮挡,设计器的左侧【图层面板】必须与普通组件隔离展示悬浮组件。
- 纯粹的受控渲染:图层面板订阅当前页面的
components树。 - 动态过滤与分组:遍历每个节点,通过
type查验 Meta 字典。若Registry[type].isOverlay === true,则将该节点分发至独立的“悬浮层(Overlays)”虚拟文件夹中进行渲染;否则留在“主图层”中。 - 所见即所得:无论组件在图层树的哪个文件夹,它们在底层的 Schema 数组中依然是平级的。修改图层树的任何属性(如重命名、拖拽排序),都会 Dispatch Action 修改底层 Schema,触发全链路重绘。
2.3 画布渲染引擎的 Portal 挂载
在运行模式(runtime)或预览模式(preview)下,渲染引擎解析到 type: 'Modal' 时,其对应的 React 组件内部实现应利用 ReactDOM.createPortal 技术。
这使得 Modal 及其 children 被“传送”挂载到 document.body 节点下,从而脱离当前容器的 overflow 限制和局部的层叠上下文(Stacking Context),实现真正的全屏悬浮覆盖。
3. 设计态的交互困境与双路径解法
在设计态下,悬浮组件默认是隐藏的(通常由 props.visible 控制)。为了让实施人员能够向其内部拖入子组件(如表单、表格),平台提供两条并行的交互路径:
路径 A:画布强制唤醒 (Canvas Override) —— 面向直观操作
这是为主流用户提供的所见即所得体验:
- 唤醒:用户在左侧图层树的“悬浮层”目录中,点击该
Modal节点旁的“眼睛(显示)”图标(修改 Schema 中的layer.hidden = false)。 - 劫持:设计器拦截其原有的业务可见性逻辑(如忽略绑定的
visible变量),强制在主画布最顶层渲染该弹窗,并赋予极高的 z-index 以防止遮挡。 - 拖拽:用户从物料区拖拽
Table组件,直接在弹窗的实体区域(DropZone)内释放(Drop)。 - 写入:拖拽引擎捕获释放事件,定位到目标容器,在底层修改 Schema 树,将
Table插入Modal的children数组。
路径 B:图层树直接盲投 (Tree Drop) —— 面向精准控制
这是为应对复杂层级遮挡或高级用户提供的极客体验:
- 绕过画布:用户从物料区拖拽组件,不经过中间的主画布。
- 树形释放:直接将组件拖拽至左侧【图层面板】中目标
Modal所在的文件树节点上悬停并释放。 - 写入:图层面板组件直接捕获 Drop 事件,并派发同样的 Action,将组件精准插入底层 Schema 对应节点的
children数组。
这两条路径完美互补,且底层收敛于唯一的树状结构修改代码。这种“上层交互多态,底层逻辑唯一”的设计,确保了平台在处理复杂嵌套容器时的绝对健壮性。