Files
datalive-design/docs/设计器悬浮组件架构规范.md
skycurtain c3846da8ae docs: 新增 DatAlive 设计器核心架构设计文档
- 新增设计器静态依赖分析方案,阐述基于 AST 的代码片段解析与依赖图谱构建
- 新增实体生命周期引擎实现规范,定义引擎驱动的事件分发与交互触发策略
- 新增悬浮组件架构设计规范,明确底层数据统一与上层视图分离的核心原则
- 新增应用运行模式架构规范,严格区分设计态、预览态与运行态的边界
- 新增设计器选中与分组交互规范,定义选中态、激活态及下钻交互的行为矩阵
2026-04-10 23:03:45 +08:00

79 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)**中带有明确的静态元数据标识。我们必须严格区分“组件的静态特征”与“实例的运行时状态”。
```typescript
// 伪代码:在设计器源码(或 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 映射)
基于上述底层架构,设计器在处理悬浮组件时,展现出极其优雅的 MVCModel-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) —— 面向直观操作
这是为主流用户提供的所见即所得体验:
1. **唤醒**:用户在左侧图层树的“悬浮层”目录中,点击该 `Modal` 节点旁的“眼睛(显示)”图标(修改 Schema 中的 `layer.hidden = false`)。
2. **劫持**:设计器拦截其原有的业务可见性逻辑(如忽略绑定的 `visible` 变量),强制在主画布最顶层渲染该弹窗,并赋予极高的 z-index 以防止遮挡。
3. **拖拽**:用户从物料区拖拽 `Table` 组件,直接在弹窗的实体区域(DropZone)内释放(Drop)。
4. **写入**:拖拽引擎捕获释放事件,定位到目标容器,在底层修改 Schema 树,将 `Table` 插入 `Modal``children` 数组。
### 路径 B:图层树直接盲投 (Tree Drop) —— 面向精准控制
这是为应对复杂层级遮挡或高级用户提供的极客体验:
1. **绕过画布**:用户从物料区拖拽组件,**不经过中间的主画布**。
2. **树形释放**:直接将组件拖拽至左侧【图层面板】中目标 `Modal` 所在的文件树节点上悬停并释放。
3. **写入**:图层面板组件直接捕获 Drop 事件,并派发同样的 Action,将组件精准插入底层 Schema 对应节点的 `children` 数组。
这两条路径完美互补,且底层收敛于唯一的树状结构修改代码。这种“上层交互多态,底层逻辑唯一”的设计,确保了平台在处理复杂嵌套容器时的绝对健壮性。