2026-01-23 16:27:04 +08:00
|
|
|
|
# Core 模块文档
|
|
|
|
|
|
|
|
|
|
|
|
## 模块概述
|
|
|
|
|
|
|
|
|
|
|
|
| 项目 | 内容 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| **模块名** | core |
|
|
|
|
|
|
| **职责** | 提供 SDK 的核心基础设施:事件发射、Manager 注册表、Manager 生命周期管理 |
|
|
|
|
|
|
| **公开 API** | 4 个类 |
|
|
|
|
|
|
| **状态** | ✅ 稳定 |
|
|
|
|
|
|
|
|
|
|
|
|
## 代码地图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/core/
|
|
|
|
|
|
├── event-emitter.ts # 事件发射器
|
|
|
|
|
|
├── manager-registry.ts # Manager 注册表(单例)
|
|
|
|
|
|
├── base-manager.ts # Manager 抽象基类
|
|
|
|
|
|
└── base-dialog-manager.ts # 对话框 Manager 基类
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 行数 | 职责 | 导出 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| `event-emitter.ts` | 70 | 发布/订阅事件系统 | `EventEmitter` |
|
|
|
|
|
|
| `manager-registry.ts` | 127 | 全局单例注册表 | `ManagerRegistry` |
|
|
|
|
|
|
| `base-manager.ts` | 57 | Manager 基类 | `BaseManager` |
|
|
|
|
|
|
| `base-dialog-manager.ts` | 145 | 对话框 Manager 基类 | `BaseDialogManager` |
|
|
|
|
|
|
|
|
|
|
|
|
## 架构设计
|
|
|
|
|
|
|
|
|
|
|
|
### 类关系图
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
EventEmitter
|
|
|
|
|
|
↑ (组合)
|
|
|
|
|
|
ManagerRegistry (单例)
|
|
|
|
|
|
↑ (访问)
|
|
|
|
|
|
BaseManager (抽象类)
|
|
|
|
|
|
↑ (继承)
|
|
|
|
|
|
BaseDialogManager (抽象类)
|
|
|
|
|
|
↑ (继承)
|
|
|
|
|
|
具体 Manager 类
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 设计模式
|
|
|
|
|
|
|
|
|
|
|
|
| 模式 | 应用 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| 单例模式 | ManagerRegistry | 全局唯一实例 |
|
|
|
|
|
|
| 发布/订阅 | EventEmitter | 解耦事件通信 |
|
|
|
|
|
|
| 模板方法 | BaseDialogManager | 定义对话框生命周期 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## EventEmitter
|
|
|
|
|
|
|
|
|
|
|
|
### 概述
|
|
|
|
|
|
|
|
|
|
|
|
轻量级事件发射器,提供发布/订阅功能。
|
|
|
|
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
class EventEmitter {
|
|
|
|
|
|
// 订阅事件,返回取消订阅函数
|
|
|
|
|
|
on(event: string, listener: (payload: any) => void): () => void;
|
|
|
|
|
|
|
|
|
|
|
|
// 取消订阅
|
|
|
|
|
|
off(event: string, listener: (payload: any) => void): void;
|
|
|
|
|
|
|
|
|
|
|
|
// 发送事件
|
|
|
|
|
|
emit(event: string, payload?: any): void;
|
|
|
|
|
|
|
|
|
|
|
|
// 清除所有监听器
|
|
|
|
|
|
clear(): void;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const emitter = new EventEmitter();
|
|
|
|
|
|
|
|
|
|
|
|
// 订阅
|
|
|
|
|
|
const unsubscribe = emitter.on('user:login', (data) => {
|
|
|
|
|
|
console.log('User logged in:', data);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 发送
|
|
|
|
|
|
emitter.emit('user:login', { userId: 123 });
|
|
|
|
|
|
|
|
|
|
|
|
// 取消订阅
|
|
|
|
|
|
unsubscribe();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ManagerRegistry
|
|
|
|
|
|
|
|
|
|
|
|
### 概述
|
|
|
|
|
|
|
|
|
|
|
|
全局单例注册表,集中管理所有 Manager 实例,提供跨 Manager 通信。
|
|
|
|
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
class ManagerRegistry {
|
|
|
|
|
|
// 获取单例
|
|
|
|
|
|
static getInstance(): ManagerRegistry;
|
|
|
|
|
|
|
|
|
|
|
|
// 重置单例(用于测试)
|
|
|
|
|
|
static reset(): void;
|
|
|
|
|
|
|
|
|
|
|
|
// 容器元素
|
|
|
|
|
|
container: HTMLElement | null;
|
|
|
|
|
|
wrapper: HTMLElement | null;
|
|
|
|
|
|
|
|
|
|
|
|
// Manager 实例
|
|
|
|
|
|
toolbar: ToolbarManager | null;
|
|
|
|
|
|
dialog: DialogManager | null;
|
|
|
|
|
|
engine3d: EngineManager | null;
|
|
|
|
|
|
buttonGroup: ButtonGroupManager | null;
|
|
|
|
|
|
rightKey: RightKeyManager | null;
|
|
|
|
|
|
constructTree: ConstructTreeManagerBtn | null;
|
2026-01-28 17:19:36 +08:00
|
|
|
|
componentDetail: ComponentDetailManager | null;
|
2026-01-23 16:27:04 +08:00
|
|
|
|
measure: MeasureDialogManager | null;
|
|
|
|
|
|
walkControl: WalkControlManager | null;
|
|
|
|
|
|
map: MapDialogManager | null;
|
|
|
|
|
|
sectionPlane: SectionPlaneDialogManager | null;
|
|
|
|
|
|
sectionAxis: SectionAxisDialogManager | null;
|
|
|
|
|
|
sectionBox: SectionBoxDialogManager | null;
|
|
|
|
|
|
|
|
|
|
|
|
// 事件方法
|
|
|
|
|
|
emit<K extends keyof EngineEvents>(event: K, payload: EngineEvents[K]): void;
|
|
|
|
|
|
on<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): () => void;
|
|
|
|
|
|
off<K extends keyof EngineEvents>(event: K, listener: (payload: EngineEvents[K]) => void): void;
|
|
|
|
|
|
clearEvents(): void;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const registry = ManagerRegistry.getInstance();
|
|
|
|
|
|
|
|
|
|
|
|
// 访问 Manager
|
|
|
|
|
|
registry.toolbar?.show();
|
|
|
|
|
|
registry.measure?.switchMode('distance');
|
|
|
|
|
|
|
|
|
|
|
|
// 发送事件
|
|
|
|
|
|
registry.emit('engine:model-loaded', { url: 'model.gltf' });
|
|
|
|
|
|
|
|
|
|
|
|
// 订阅事件
|
|
|
|
|
|
const unsubscribe = registry.on('engine:object-clicked', (payload) => {
|
|
|
|
|
|
console.log('Object clicked:', payload.objectId);
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 支持的事件
|
|
|
|
|
|
|
|
|
|
|
|
| 事件 | 数据类型 | 说明 |
|
|
|
|
|
|
|------|---------|------|
|
|
|
|
|
|
| `ui:open-dialog` | `{ id: string; data?: any }` | 打开对话框 |
|
|
|
|
|
|
| `ui:close-dialog` | `{ id: string }` | 关闭对话框 |
|
|
|
|
|
|
| `engine:model-loaded` | `{ url: string }` | 模型加载完成 |
|
|
|
|
|
|
| `engine:object-clicked` | `{ objectId: string; position: Point3D }` | 点击对象 |
|
|
|
|
|
|
| `ui:tree-node-check` | `{ id: string; checked: boolean; node: any }` | 树节点勾选 |
|
|
|
|
|
|
| `ui:tree-node-select` | `{ id: string; selected: boolean; node: any }` | 树节点选择 |
|
|
|
|
|
|
| `sys:theme-changed` | `{ theme: string }` | 主题变更 |
|
|
|
|
|
|
| `sys:locale-changed` | `{ locale: string }` | 语言变更 |
|
|
|
|
|
|
| `walk:speed-change` | `{ speed: number }` | 漫游速度变更 |
|
|
|
|
|
|
| `walk:path-mode-toggle` | `{ isActive: boolean }` | 路径模式切换 |
|
|
|
|
|
|
| `map:opened` | `{}` | 地图打开 |
|
|
|
|
|
|
| `map:closed` | `{}` | 地图关闭 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## BaseManager
|
|
|
|
|
|
|
|
|
|
|
|
### 概述
|
|
|
|
|
|
|
|
|
|
|
|
所有 Manager 的抽象基类,提供注册表访问和事件订阅管理。
|
|
|
|
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
abstract class BaseManager {
|
|
|
|
|
|
// 受保护属性
|
|
|
|
|
|
protected registry: ManagerRegistry;
|
|
|
|
|
|
|
|
|
|
|
|
// 受保护方法
|
|
|
|
|
|
protected subscribe<K extends keyof EngineEvents>(
|
|
|
|
|
|
event: K,
|
|
|
|
|
|
handler: (payload: EngineEvents[K]) => void
|
|
|
|
|
|
): void;
|
|
|
|
|
|
|
|
|
|
|
|
protected emit<K extends keyof EngineEvents>(
|
|
|
|
|
|
event: K,
|
|
|
|
|
|
payload: EngineEvents[K]
|
|
|
|
|
|
): void;
|
|
|
|
|
|
|
|
|
|
|
|
// 公开方法
|
|
|
|
|
|
destroy(): void;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export class CustomManager extends BaseManager {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
super();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public initialize(): void {
|
|
|
|
|
|
// 订阅事件(自动管理生命周期)
|
|
|
|
|
|
this.subscribe('engine:model-loaded', (payload) => {
|
|
|
|
|
|
console.log('Model loaded:', payload.url);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public doSomething(): void {
|
|
|
|
|
|
// 发送事件
|
|
|
|
|
|
this.emit('custom:action', { data: 'value' });
|
|
|
|
|
|
|
|
|
|
|
|
// 访问其他 Manager
|
|
|
|
|
|
this.registry.toolbar?.show();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public destroy(): void {
|
|
|
|
|
|
// 自动清理所有订阅
|
|
|
|
|
|
super.destroy();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 特性
|
|
|
|
|
|
|
|
|
|
|
|
- **自动资源清理**: `destroy()` 时自动取消所有订阅
|
|
|
|
|
|
- **类型安全**: 使用 TypeScript 泛型确保事件类型
|
|
|
|
|
|
- **注册表访问**: 可通过 `this.registry` 访问所有 Manager
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## BaseDialogManager
|
|
|
|
|
|
|
|
|
|
|
|
### 概述
|
|
|
|
|
|
|
|
|
|
|
|
对话框 Manager 的抽象基类,封装对话框的完整生命周期。
|
|
|
|
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
abstract class BaseDialogManager extends BaseManager {
|
|
|
|
|
|
// 抽象属性(子类必须实现)
|
|
|
|
|
|
protected abstract get dialogId(): string;
|
|
|
|
|
|
protected abstract get dialogTitle(): string;
|
|
|
|
|
|
|
|
|
|
|
|
// 抽象方法(子类必须实现)
|
|
|
|
|
|
protected abstract createContent(): HTMLElement;
|
|
|
|
|
|
|
|
|
|
|
|
// 可选属性(子类可重写)
|
|
|
|
|
|
protected get dialogWidth(): number; // 默认: 300
|
|
|
|
|
|
protected get dialogHeight(): number | 'auto'; // 默认: 'auto'
|
|
|
|
|
|
protected get dialogOptions(): DialogManagerOptions;
|
|
|
|
|
|
|
|
|
|
|
|
// 钩子方法(子类可重写)
|
|
|
|
|
|
protected onDialogCreated(): void;
|
|
|
|
|
|
protected onDialogClose(): void;
|
|
|
|
|
|
protected onBeforeDestroy(): void;
|
|
|
|
|
|
protected getDialogPosition(): { x: number; y: number };
|
|
|
|
|
|
|
|
|
|
|
|
// 公开方法
|
|
|
|
|
|
show(): void;
|
|
|
|
|
|
hide(): void;
|
|
|
|
|
|
toggle(): void;
|
|
|
|
|
|
isOpen(): boolean;
|
|
|
|
|
|
destroy(): void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface DialogManagerOptions {
|
|
|
|
|
|
draggable?: boolean; // 默认 true
|
|
|
|
|
|
resizable?: boolean; // 默认 false
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 使用示例
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
export class MeasureDialogManager extends BaseDialogManager {
|
|
|
|
|
|
private panel: MeasurePanel | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
protected get dialogId(): string {
|
|
|
|
|
|
return 'measure-dialog';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected get dialogTitle(): string {
|
|
|
|
|
|
return 'measure.dialogTitle'; // i18n key
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected get dialogWidth(): number {
|
|
|
|
|
|
return 250;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected createContent(): HTMLElement {
|
|
|
|
|
|
this.panel = new MeasurePanel({
|
|
|
|
|
|
onModeChange: (mode) => {
|
|
|
|
|
|
this.registry.engine3d?.activateMeasure(mode);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
this.panel.init();
|
|
|
|
|
|
|
|
|
|
|
|
const wrapper = document.createElement('div');
|
|
|
|
|
|
wrapper.appendChild(this.panel.element);
|
|
|
|
|
|
return wrapper;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected onDialogCreated(): void {
|
|
|
|
|
|
this.dialog?.fitHeight(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected onDialogClose(): void {
|
|
|
|
|
|
this.registry.toolbar?.setBtnActive('measure', false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected onBeforeDestroy(): void {
|
|
|
|
|
|
this.panel?.destroy();
|
|
|
|
|
|
this.panel = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 业务方法
|
|
|
|
|
|
public switchMode(mode: string): void {
|
|
|
|
|
|
this.panel?.switchMode(mode);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 生命周期
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
show()
|
|
|
|
|
|
↓
|
|
|
|
|
|
销毁旧对话框
|
|
|
|
|
|
↓
|
|
|
|
|
|
createContent() ← 子类实现
|
|
|
|
|
|
↓
|
|
|
|
|
|
创建 BimDialog
|
|
|
|
|
|
↓
|
|
|
|
|
|
onDialogCreated() ← 钩子
|
|
|
|
|
|
↓
|
|
|
|
|
|
显示中...
|
|
|
|
|
|
↓
|
|
|
|
|
|
hide() / 关闭按钮
|
|
|
|
|
|
↓
|
|
|
|
|
|
onDialogClose() ← 钩子
|
|
|
|
|
|
↓
|
|
|
|
|
|
onBeforeDestroy() ← 钩子
|
|
|
|
|
|
↓
|
|
|
|
|
|
销毁对话框
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 依赖关系
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
BaseDialogManager
|
|
|
|
|
|
↓ 继承
|
|
|
|
|
|
BaseManager
|
|
|
|
|
|
↓ 依赖
|
|
|
|
|
|
ManagerRegistry
|
|
|
|
|
|
↓ 组合
|
|
|
|
|
|
EventEmitter
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 被依赖情况
|
|
|
|
|
|
|
|
|
|
|
|
| 模块 | 依赖方式 |
|
|
|
|
|
|
|------|---------|
|
|
|
|
|
|
| managers/* | 继承 BaseManager 或 BaseDialogManager |
|
|
|
|
|
|
| components/* | 间接依赖(通过 Manager) |
|
|
|
|
|
|
| BimEngine | 使用 ManagerRegistry |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**文档生成时间**: 2026-01-23
|