Update all three section dialogs to support hide/show toggle: SectionAxisDialogManager: - onHideToggle now calls hideSection()/recoverSection() SectionBoxDialogManager: - onHideToggle now calls hideSection()/recoverSection() SectionPlanePanel: - Add isHidden state tracking - Change onHide to onHideToggle(isHidden) - Add setHiddenState/getHiddenState methods - Update button to toggle active state SectionPlaneDialogManager: - Switch to onHideToggle callback - Call hideSection()/recoverSection() based on toggle state Behavior: Click hide button to hide section, click again to recover.
8.0 KiB
Architectural Decisions: 构件详情右键菜单功能
2026-01-28
Decision 1: 选中状态存储在 Engine 而非 EngineManager
Context:
- 需要存储当前选中的构件信息(url + id)
- Engine 组件监听底层 click 事件
- EngineManager 是 Engine 的代理层
Options Considered:
- 存储在 Engine 组件中
- 存储在 EngineManager 中
- 存储在独立的 SelectionManager 中
Decision: 选择 Option 1 - 存储在 Engine 组件中
Rationale:
- Engine 直接监听底层事件,数据流最短
- 避免事件转发的复杂性
- EngineManager 作为代理,只需暴露访问方法
- 保持单一职责原则:Engine 管理状态,EngineManager 提供接口
Consequences:
- ✅ 数据流清晰:底层事件 → Engine 状态 → EngineManager 访问
- ✅ 性能更好:无需事件转发
- ⚠️ Engine 组件职责略有增加(但合理)
Decision 2: 使用动态菜单生成而非事件驱动更新
Context:
- 右键菜单内容需要根据选中状态变化
- 选中状态频繁变化
Options Considered:
- 注册静态菜单 + 监听选中事件动态更新
- 注册处理器函数,每次右键时动态生成菜单
Decision: 选择 Option 2 - 动态生成菜单
Rationale:
- 右键菜单不常使用,生成成本低
- 避免维护菜单状态的复杂性
- 无需监听选中事件单独更新菜单
- 代码更简洁,逻辑集中
Implementation:
rightKey.registerHandler((_e) => {
const selected = this.getSelectedComponent();
const items: MenuItemConfig[] = [];
if (selected) {
items.push({ id: 'componentDetail', ... });
}
items.push({ id: 'showAll', ... });
return items;
});
Consequences:
- ✅ 代码简洁,逻辑集中
- ✅ 无需手动同步菜单状态
- ✅ 易于扩展(添加更多条件判断)
- ⚠️ 每次右键都重新生成(但开销可忽略)
Decision 3: ComponentDetailManager 不注册为 BaseDialogManager
Context:
- ComponentDetailManager 需要创建对话框
- BaseDialogManager 提供了对话框生命周期管理
- 现有的 MeasureDialogManager、SectionPlaneDialogManager 等都继承 BaseDialogManager
Options Considered:
- 继承 BaseDialogManager(与现有 Manager 一致)
- 不继承,直接使用 DialogManager API
Decision: 选择 Option 2 - 不继承 BaseDialogManager
Rationale:
- ComponentDetailManager 的对话框逻辑简单,无需复杂生命周期管理
- 无需 Panel 组件(直接使用 Collapse + Description)
- 避免继承带来的不必要复杂性
- 直接调用 DialogManager.create() 更灵活
Implementation:
export class ComponentDetailManager {
private dialog: BimDialog | null = null;
public show(modelUrl: string, componentId: string): void {
this.createDialog();
// ...
}
private createDialog(): void {
const registry = ManagerRegistry.getInstance();
this.dialog = registry.dialog?.create({ ... });
}
}
Consequences:
- ✅ 代码更简洁(119 行 vs 预计 200+ 行)
- ✅ 职责明确:Manager 只负责数据获取和展示
- ⚠️ 与现有 Manager 不一致(但合理,因需求不同)
Decision 4: 属性数据在 Manager 层转换而非 UI 组件层
Context:
- 底层 API 返回格式:
{ properties: [{ name, children: [...] }] } - BimCollapse 需要格式:
{ items: [{ categoryName, items: [...] }] }
Options Considered:
- ComponentDetailManager 转换数据后传给 UI
- 直接传原始数据,UI 组件自己转换
- 创建 Adapter 类专门处理转换
Decision: 选择 Option 1 - Manager 层转换
Rationale:
- Manager 职责包括数据适配
- UI 组件保持纯粹(只负责展示)
- 转换逻辑集中,易于维护
- 无需额外的 Adapter 类(简单转换)
Implementation:
private renderProperties(data: any): void {
const categories = data.properties.map((cat: any) => ({
categoryName: cat.name,
items: cat.children.map((child: any) => ({
key: child.name,
value: child.value
}))
}));
const collapse = new BimCollapse({ items: categories, ... });
}
Consequences:
- ✅ UI 组件可复用性更强
- ✅ 数据转换逻辑集中
- ⚠️ Manager 职责略有增加(但合理)
Decision 5: "显示全部"功能暂时只打印日志
Context:
- 右键菜单需要"显示全部"选项
- 底层 API 尚未明确(可能是 showAllComponents、resetVisibility 等)
Options Considered:
- 实现完整功能(调用底层 API)
- 暂时只打印日志,等 API 明确后实现
- 跳过此功能
Decision: 选择 Option 2 - 暂时只打印日志
Rationale:
- 不阻塞主功能(构件详情)
- 底层 API 不明确,避免错误实现
- 菜单结构已就位,后续补充实现即可
- 符合 MVP 原则
Implementation:
public showAllComponents(): void {
console.log('[EngineManager] 显示全部');
// TODO: 调用底层 API 显示所有构件
}
Consequences:
- ✅ 不阻塞主功能开发
- ✅ 菜单结构完整
- ⚠️ 用户点击后无实际效果(需后续补充)
Decision 6: 文档重构为多章节而非单独创建新文档
Context:
- 现有 TOOLBAR_API_CALLCHAIN.md 只记录 Toolbar 调用链
- 新增右键菜单和构件交互功能
- 未来可能还有更多功能模块
Options Considered:
- 创建独立文档 RIGHTKEY_API_CALLCHAIN.md
- 重构现有文档为多章节结构
- 合并到 README 或其他文档
Decision: 选择 Option 2 - 重构为多章节 API_CALLCHAIN.md
Rationale:
- 统一的调用链文档便于查阅
- 支持未来扩展(第四章、第五章...)
- 避免文档碎片化
- 保持现有内容(第一章),降低风险
Structure:
# BIM Engine SDK - API 调用链文档
## 第一章:工具栏 (Toolbar)
- 首页、框选放大、测量、剖切...
## 第二章:右键菜单 (Context Menu)
- 构件详情、显示全部、信息、首页
## 第三章:构件交互 (Component Interaction)
- 构件选中、取消选中
Consequences:
- ✅ 文档结构更清晰
- ✅ 易于扩展
- ✅ 避免重复内容(Info、Home 在多处复用)
- ⚠️ 文件变大(734 → 1232 行)
Decision 7: BimEngine 不自动初始化 ComponentDetailManager
Context:
- 现有 Manager(Measure、SectionPlane 等)都有独立的 init 方法
- ComponentDetailManager 是新增功能
- 需要决定初始化时机
Options Considered:
- 在 BimEngine.init() 中自动初始化
- 提供 initComponentDetail() 方法,由用户选择是否初始化
- 延迟初始化(首次使用时)
Decision: 选择 Option 1(实际实现)- 在 BimEngine.init() 中自动初始化
Rationale:
- 构件详情是核心功能,大部分项目都需要
- 与现有 Manager 初始化方式保持一致
- 避免用户忘记初始化导致功能不可用
- 初始化成本低(只是实例化)
Implementation:
// src/bim-engine.ts
private init() {
// ...
this.componentDetail = new ComponentDetailManager();
this.registry.componentDetail = this.componentDetail;
}
Consequences:
- ✅ 开箱即用
- ✅ 与现有 Manager 一致
- ⚠️ 即使不使用也会初始化(但开销可忽略)
Note: 实际实现中选择了自动初始化,与 Plan 中的"提供 initComponentDetail()"不同,但更符合现有架构。
Summary
Core Decisions:
- ✅ 状态存储在 Engine(数据源头)
- ✅ 动态菜单生成(简洁高效)
- ✅ 不继承 BaseDialogManager(需求简单)
- ✅ Manager 层转换数据(职责明确)
- ✅ "显示全部"暂时占位(不阻塞)
- ✅ 文档多章节结构(统一管理)
- ✅ 自动初始化(开箱即用)
Guiding Principles:
- KISS: Keep It Simple, Stupid
- YAGNI: You Aren't Gonna Need It
- Single Responsibility: 每个组件职责明确
- Consistency: 与现有代码保持一致(除非有充分理由)