fix(menu): refactor menu system to use pure config objects and fix submenu click events
This commit is contained in:
1183
docs/components/button-group.md
Normal file
1183
docs/components/button-group.md
Normal file
File diff suppressed because it is too large
Load Diff
882
docs/components/dialog.md
Normal file
882
docs/components/dialog.md
Normal file
@@ -0,0 +1,882 @@
|
||||
# Dialog 组件详细文档
|
||||
|
||||
> 本文档详细描述 Dialog 组件的实现细节,包括 API、UI 结构、逻辑流程等,供 AI 根据文档重现组件。
|
||||
|
||||
---
|
||||
|
||||
## 1. 组件概述
|
||||
|
||||
### 1.1 基本信息
|
||||
- **组件名称**: `BimDialog`
|
||||
- **文件路径**: `src/components/dialog/index.ts`
|
||||
- **类型定义**: `src/components/dialog/index.type.ts`
|
||||
- **样式文件**: `src/components/dialog/index.css`
|
||||
- **实现接口**: `IBimComponent`
|
||||
- **用途**: 提供可拖拽、可缩放的通用弹窗组件,支持自定义内容和样式
|
||||
|
||||
### 1.2 在 SDK 中的位置
|
||||
- Dialog 组件是独立的 UI 组件
|
||||
- 必须通过 `DialogManager` 使用,不允许直接使用
|
||||
- `DialogManager` 位于 `src/managers/dialog-manager.ts`
|
||||
|
||||
---
|
||||
|
||||
## 2. 组件类 API 文档
|
||||
|
||||
### 2.1 构造函数
|
||||
|
||||
```typescript
|
||||
constructor(options: DialogOptions)
|
||||
```
|
||||
|
||||
**参数**:
|
||||
- `options`: `DialogOptions` - 弹窗配置选项(详见类型定义)
|
||||
|
||||
**默认配置**:
|
||||
```typescript
|
||||
{
|
||||
title: 'Dialog',
|
||||
width: 300,
|
||||
height: 'auto',
|
||||
position: 'center',
|
||||
draggable: true,
|
||||
resizable: false,
|
||||
minWidth: 200,
|
||||
minHeight: 100
|
||||
}
|
||||
```
|
||||
|
||||
**行为**:
|
||||
- 合并用户配置和默认配置
|
||||
- 创建 DOM 结构
|
||||
- 自动调用 `init()` 方法
|
||||
|
||||
### 2.2 公共方法
|
||||
|
||||
#### `init(): void`
|
||||
初始化组件功能(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 将弹窗元素添加到容器
|
||||
- 初始化位置
|
||||
- 如果 `draggable` 为 true,初始化拖拽功能
|
||||
- 如果 `resizable` 为 true,初始化缩放功能
|
||||
- 订阅主题和语言变更
|
||||
- 调用 `onOpen` 回调
|
||||
|
||||
**调用时机**: 构造函数中自动调用,也可手动调用
|
||||
|
||||
#### `setTheme(theme: ThemeConfig): void`
|
||||
设置主题(实现 `IBimComponent` 接口)
|
||||
|
||||
**参数**:
|
||||
- `theme`: `ThemeConfig` - 全局主题配置
|
||||
|
||||
**功能**:
|
||||
- 将主题颜色映射到 CSS 变量
|
||||
- 如果用户未自定义颜色,使用主题颜色
|
||||
- 如果用户已自定义颜色,保持用户自定义
|
||||
|
||||
**CSS 变量映射**:
|
||||
- `--bim-dialog-bg` ← `theme.panelBackground` (如果未自定义 `backgroundColor`)
|
||||
- `--bim-dialog-header-bg` ← `theme.componentHover` (如果未自定义 `headerBackgroundColor`)
|
||||
- `--bim-dialog-title-color` ← `theme.textPrimary` (如果未自定义 `titleColor`)
|
||||
- `--bim-dialog-text-color` ← `theme.textPrimary` (如果未自定义 `textColor`)
|
||||
- `--bim-dialog-border-color` ← `theme.border` (如果未自定义 `borderColor`)
|
||||
|
||||
#### `setLocales(): void`
|
||||
设置语言(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 更新标题文本(如果标题是翻译键)
|
||||
- 使用 `t()` 函数获取翻译后的文本
|
||||
|
||||
**行为**:
|
||||
- 查找 `.bim-dialog-title` 元素
|
||||
- 如果 `options.title` 存在,使用 `t(options.title)` 更新文本
|
||||
|
||||
#### `setContent(content: HTMLElement | string): void`
|
||||
动态设置弹窗内容
|
||||
|
||||
**参数**:
|
||||
- `content`: `HTMLElement | string` - 内容元素或 HTML 字符串
|
||||
|
||||
**功能**:
|
||||
- 清空当前内容
|
||||
- 设置新内容(支持 HTML 字符串或 DOM 元素)
|
||||
|
||||
#### `close(): void`
|
||||
关闭弹窗并销毁
|
||||
|
||||
**功能**:
|
||||
- 清理 `requestAnimationFrame`(如果存在)
|
||||
- 取消主题和语言订阅
|
||||
- 从 DOM 中移除元素
|
||||
- 标记为已销毁
|
||||
- 调用 `onClose` 回调
|
||||
|
||||
#### `destroy(): void`
|
||||
销毁组件(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 调用 `close()` 方法
|
||||
|
||||
### 2.3 私有方法(供理解实现细节)
|
||||
|
||||
#### `createDom(): HTMLElement`
|
||||
创建弹窗的 DOM 结构
|
||||
|
||||
**返回**: 创建的弹窗根元素
|
||||
|
||||
**DOM 结构**:
|
||||
```html
|
||||
<div class="bim-dialog" [id="..."]>
|
||||
<div class="bim-dialog-header" [class="draggable"]>
|
||||
<span class="bim-dialog-title">标题文本</span>
|
||||
<span class="bim-dialog-close">×</span>
|
||||
</div>
|
||||
<div class="bim-dialog-content">
|
||||
<!-- 用户内容 -->
|
||||
</div>
|
||||
[<div class="bim-dialog-resize-handle"></div>] <!-- 如果 resizable -->
|
||||
</div>
|
||||
```
|
||||
|
||||
**关键实现**:
|
||||
1. 创建根元素,应用 CSS 变量和尺寸
|
||||
2. 创建标题栏,包含标题和关闭按钮
|
||||
3. 创建内容区域,支持 HTML 字符串或 DOM 元素
|
||||
4. 如果 `resizable` 为 true,创建缩放手柄
|
||||
5. **事件拦截**: 绑定所有鼠标/触摸事件,使用 `stopPropagation()` 阻止冒泡,防止传递给 3D 引擎
|
||||
|
||||
**事件拦截列表**:
|
||||
```typescript
|
||||
['click', 'dblclick', 'contextmenu', 'wheel',
|
||||
'mousedown', 'mouseup', 'mousemove',
|
||||
'touchstart', 'touchend', 'touchmove',
|
||||
'pointerdown', 'pointerup', 'pointermove',
|
||||
'pointerenter', 'pointerleave', 'pointerover', 'pointerout']
|
||||
```
|
||||
|
||||
#### `initPosition(): void`
|
||||
初始化弹窗位置
|
||||
|
||||
**功能**:
|
||||
- 根据 `position` 选项计算弹窗位置
|
||||
- 支持预设位置(如 'center', 'top-left' 等)或坐标对象 `{ x, y }`
|
||||
- 确保弹窗不超出容器边界
|
||||
|
||||
**位置计算逻辑**:
|
||||
- `center`: `left = (containerWidth - dialogWidth) / 2`, `top = (containerHeight - dialogHeight) / 2`
|
||||
- `top-left`: `left = 0`, `top = 0`
|
||||
- `top-center`: `left = (containerWidth - dialogWidth) / 2`, `top = 0`
|
||||
- `top-right`: `left = containerWidth - dialogWidth`, `top = 0`
|
||||
- `left-center`: `left = 0`, `top = (containerHeight - dialogHeight) / 2`
|
||||
- `right-center`: `left = containerWidth - dialogWidth`, `top = (containerHeight - dialogHeight) / 2`
|
||||
- `bottom-left`: `left = 0`, `top = containerHeight - dialogHeight`
|
||||
- `bottom-center`: `left = (containerWidth - dialogWidth) / 2`, `top = containerHeight - dialogHeight`
|
||||
- `bottom-right`: `left = containerWidth - dialogWidth`, `top = containerHeight - dialogHeight`
|
||||
- `{ x, y }`: 直接使用坐标值
|
||||
|
||||
**边界限制**:
|
||||
```typescript
|
||||
left = Math.max(0, Math.min(left, containerWidth - dialogWidth));
|
||||
top = Math.max(0, Math.min(top, containerHeight - dialogHeight));
|
||||
```
|
||||
|
||||
#### `initDrag(): void`
|
||||
初始化拖拽功能
|
||||
|
||||
**功能**:
|
||||
- 在标题栏上绑定 `mousedown` 事件
|
||||
- 实现拖拽移动功能
|
||||
- 使用 `requestAnimationFrame` 优化性能
|
||||
- 使用 `capture: true` 确保事件在捕获阶段处理
|
||||
|
||||
**拖拽流程**:
|
||||
1. `mousedown`: 记录起始位置和弹窗当前位置,缓存容器和弹窗尺寸
|
||||
2. `mousemove`: 计算偏移量,更新弹窗位置,限制在容器边界内
|
||||
3. `mouseup`: 清理事件监听和动画帧
|
||||
|
||||
**性能优化**:
|
||||
- 使用 `requestAnimationFrame` 节流更新
|
||||
- 缓存容器和弹窗尺寸,减少 reflow
|
||||
- 使用 `capture: true` 确保事件处理
|
||||
|
||||
#### `initResize(): void`
|
||||
初始化缩放功能
|
||||
|
||||
**功能**:
|
||||
- 在缩放手柄上绑定 `mousedown` 事件
|
||||
- 实现右下角拖拽缩放功能
|
||||
- 使用 `requestAnimationFrame` 优化性能
|
||||
- 限制最小尺寸
|
||||
|
||||
**缩放流程**:
|
||||
1. `mousedown`: 记录起始位置和弹窗当前尺寸
|
||||
2. `mousemove`: 计算偏移量,更新弹窗尺寸,限制最小尺寸
|
||||
3. `mouseup`: 清理事件监听和动画帧
|
||||
|
||||
**尺寸限制**:
|
||||
```typescript
|
||||
newWidth = Math.max(minWidth || 100, startWidth + deltaX);
|
||||
newHeight = Math.max(minHeight || 50, startHeight + deltaY);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 分化组件说明
|
||||
|
||||
### 3.1 BimInfoDialog
|
||||
|
||||
**文件路径**: `src/components/dialog/bimInfoDialog/index.ts`
|
||||
|
||||
**继承关系**: `BimInfoDialog extends BimDialog`
|
||||
|
||||
**特殊功能**:
|
||||
- 预定义了信息展示的内容结构
|
||||
- 包含标题、信息列表和操作按钮
|
||||
- 使用固定的配置(宽度 320px,可缩放,可拖拽)
|
||||
|
||||
**实现方式**:
|
||||
- 在构造函数中创建内容 DOM
|
||||
- 调用父类构造函数,传入预定义的配置
|
||||
- 不需要重写其他方法,直接继承父类功能
|
||||
|
||||
**内容结构**:
|
||||
```html
|
||||
<div class="bim-info-dialog-content">
|
||||
<h3>Model Information</h3>
|
||||
<ul>
|
||||
<li><strong>Name:</strong> Sample Project</li>
|
||||
<li><strong>Version:</strong> 1.0.0</li>
|
||||
<li><strong>Date:</strong> 当前日期</li>
|
||||
<li><strong>Status:</strong> <span style="color: green;">Active</span></li>
|
||||
</ul>
|
||||
<button>Update Status</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
**与父类的差异**:
|
||||
- 固定了内容结构
|
||||
- 固定了部分配置选项
|
||||
- 其他功能完全继承自 `BimDialog`
|
||||
|
||||
---
|
||||
|
||||
## 4. Manager API 文档
|
||||
|
||||
### 4.1 DialogManager 类
|
||||
|
||||
**文件路径**: `src/managers/dialog-manager.ts`
|
||||
|
||||
**继承关系**: `DialogManager extends BimComponent`
|
||||
|
||||
### 4.2 构造函数
|
||||
|
||||
```typescript
|
||||
constructor(engine: BimEngine, container: HTMLElement)
|
||||
```
|
||||
|
||||
**参数**:
|
||||
- `engine`: `BimEngine` - 引擎实例
|
||||
- `container`: `HTMLElement` - 弹窗挂载的目标容器
|
||||
|
||||
**行为**:
|
||||
- 保存容器引用
|
||||
- 监听 `ui:open-dialog` 事件
|
||||
- 如果事件 payload.id 是 'info',自动打开信息弹窗
|
||||
|
||||
### 4.3 公共方法
|
||||
|
||||
#### `create(options: Omit<DialogOptions, 'container'>): BimDialog`
|
||||
创建一个通用弹窗
|
||||
|
||||
**参数**:
|
||||
- `options`: `Omit<DialogOptions, 'container'>` - 弹窗配置(不需要传 container)
|
||||
|
||||
**返回**: `BimDialog` 实例
|
||||
|
||||
**功能**:
|
||||
- 自动使用 Manager 绑定的容器
|
||||
- 创建 `BimDialog` 实例
|
||||
- 应用当前主题
|
||||
- 将弹窗添加到活跃列表
|
||||
- 在 `onClose` 回调中自动从列表移除
|
||||
|
||||
**使用示例**:
|
||||
```typescript
|
||||
const dialog = engine.dialog.create({
|
||||
title: '测试弹窗',
|
||||
content: '这是内容',
|
||||
width: 400,
|
||||
height: 300
|
||||
});
|
||||
```
|
||||
|
||||
#### `showInfoDialog(): void`
|
||||
显示二次封装的模型信息弹窗
|
||||
|
||||
**功能**:
|
||||
- 创建 `BimInfoDialog` 实例
|
||||
- 直接显示信息弹窗
|
||||
|
||||
**注意**: 此方法创建的弹窗不会自动加入管理列表(遗留逻辑)
|
||||
|
||||
#### `updateTheme(theme: ThemeConfig): void`
|
||||
响应全局主题变更
|
||||
|
||||
**参数**:
|
||||
- `theme`: `ThemeConfig` - 全局主题配置
|
||||
|
||||
**功能**:
|
||||
- 遍历所有活跃弹窗
|
||||
- 调用每个弹窗的 `setTheme()` 方法
|
||||
|
||||
#### `destroy(): void`
|
||||
销毁管理器
|
||||
|
||||
**功能**:
|
||||
- 销毁所有活跃弹窗
|
||||
- 清空活跃列表
|
||||
|
||||
---
|
||||
|
||||
## 5. UI 详细描述
|
||||
|
||||
### 5.1 DOM 结构
|
||||
|
||||
**完整 HTML 结构**:
|
||||
```html
|
||||
<div class="bim-dialog" id="[可选ID]" style="[CSS变量和尺寸]">
|
||||
<!-- 标题栏 -->
|
||||
<div class="bim-dialog-header [draggable]">
|
||||
<span class="bim-dialog-title">标题文本</span>
|
||||
<span class="bim-dialog-close">×</span>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="bim-dialog-content">
|
||||
<!-- 用户内容(HTML字符串或DOM元素) -->
|
||||
</div>
|
||||
|
||||
<!-- 缩放手柄(如果 resizable) -->
|
||||
<div class="bim-dialog-resize-handle"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 5.2 CSS 类名和样式
|
||||
|
||||
#### `.bim-dialog` (根元素)
|
||||
- `position: absolute` - 绝对定位
|
||||
- `background-color: var(--bim-dialog-bg)` - 背景色(CSS 变量)
|
||||
- `border: 1px solid var(--bim-dialog-border-color)` - 边框
|
||||
- `border-radius: 6px` - 圆角
|
||||
- `box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3)` - 阴影
|
||||
- `display: flex` + `flex-direction: column` - 垂直布局
|
||||
- `z-index: 10001` - 层级(确保在 3D 引擎之上)
|
||||
- `min-width: 200px` - 最小宽度
|
||||
- `min-height: 100px` - 最小高度
|
||||
- `pointer-events: auto` - 确保可接收事件
|
||||
|
||||
#### `.bim-dialog-header` (标题栏)
|
||||
- `height: 32px` - 固定高度
|
||||
- `background-color: var(--bim-dialog-header-bg)` - 背景色
|
||||
- `display: flex` + `align-items: center` + `justify-content: space-between` - 水平布局
|
||||
- `padding: 0 10px` - 内边距
|
||||
- `cursor: default` - 默认光标
|
||||
- `user-select: none` - 禁止选中
|
||||
- `border-bottom: 1px solid var(--bim-dialog-border-color)` - 底边框
|
||||
|
||||
#### `.bim-dialog-header.draggable` (可拖拽标题栏)
|
||||
- `cursor: move` - 移动光标
|
||||
|
||||
#### `.bim-dialog-title` (标题文本)
|
||||
- `font-size: 14px` - 字体大小
|
||||
- `font-weight: 500` - 字体粗细
|
||||
- `white-space: nowrap` - 不换行
|
||||
- `overflow: hidden` - 隐藏溢出
|
||||
- `text-overflow: ellipsis` - 文本省略
|
||||
- `color: var(--bim-dialog-title-color)` - 文字颜色
|
||||
|
||||
#### `.bim-dialog-close` (关闭按钮)
|
||||
- `cursor: pointer` - 指针光标
|
||||
- `font-size: 18px` - 字体大小
|
||||
- `color: #999` - 默认颜色
|
||||
- `line-height: 1` - 行高
|
||||
- `margin-left: 8px` - 左边距
|
||||
|
||||
#### `.bim-dialog-close:hover` (关闭按钮悬停)
|
||||
- `color: #fff` - 悬停颜色
|
||||
|
||||
#### `.bim-dialog-content` (内容区域)
|
||||
- `flex: 1` - 占据剩余空间
|
||||
- `padding: 10px` - 内边距
|
||||
- `overflow: auto` - 内容溢出时滚动
|
||||
- `font-size: 14px` - 字体大小
|
||||
- `color: var(--bim-dialog-text-color)` - 文字颜色
|
||||
|
||||
#### `.bim-dialog-resize-handle` (缩放手柄)
|
||||
- `position: absolute` - 绝对定位
|
||||
- `width: 10px` + `height: 10px` - 尺寸
|
||||
- `bottom: 0` + `right: 0` - 右下角位置
|
||||
- `cursor: se-resize` - 缩放光标
|
||||
- `z-index: 10` - 层级
|
||||
|
||||
#### `.bim-dialog-resize-handle::after` (缩放装饰)
|
||||
- 使用伪元素创建右下角斜线装饰
|
||||
- `border-right` + `border-bottom` - 创建斜线效果
|
||||
|
||||
### 5.3 CSS 变量
|
||||
|
||||
**定义位置**: `:root` 或元素内联样式
|
||||
|
||||
**变量列表**:
|
||||
- `--bim-dialog-bg`: 窗体背景颜色(默认 `rgba(17, 17, 17, 0.95)`)
|
||||
- `--bim-dialog-header-bg`: 标题栏背景颜色(默认 `#2a2a2a`)
|
||||
- `--bim-dialog-title-color`: 标题文字颜色(默认 `#fff`)
|
||||
- `--bim-dialog-text-color`: 内容文字颜色(默认 `#ccc`)
|
||||
- `--bim-dialog-border-color`: 边框颜色(默认 `#444`)
|
||||
|
||||
### 5.4 交互行为
|
||||
|
||||
#### 拖拽
|
||||
- **触发区域**: 标题栏(`.bim-dialog-header`)
|
||||
- **触发条件**: `draggable: true`
|
||||
- **行为**:
|
||||
- 鼠标按下标题栏时开始拖拽
|
||||
- 鼠标移动时弹窗跟随移动
|
||||
- 鼠标释放时结束拖拽
|
||||
- 弹窗位置限制在容器边界内
|
||||
|
||||
#### 缩放
|
||||
- **触发区域**: 缩放手柄(`.bim-dialog-resize-handle`)
|
||||
- **触发条件**: `resizable: true`
|
||||
- **行为**:
|
||||
- 鼠标按下缩放手柄时开始缩放
|
||||
- 鼠标移动时弹窗尺寸跟随变化
|
||||
- 鼠标释放时结束缩放
|
||||
- 尺寸限制在最小尺寸以上
|
||||
|
||||
#### 关闭
|
||||
- **触发方式**:
|
||||
- 点击关闭按钮(`.bim-dialog-close`)
|
||||
- 调用 `close()` 方法
|
||||
- **行为**: 弹窗从 DOM 移除,调用 `onClose` 回调
|
||||
|
||||
### 5.5 响应式行为
|
||||
|
||||
- 弹窗位置会根据容器尺寸自动调整,确保不超出边界
|
||||
- 内容区域支持滚动(`overflow: auto`)
|
||||
- 标题文本过长时显示省略号(`text-overflow: ellipsis`)
|
||||
|
||||
---
|
||||
|
||||
## 6. 逻辑流程详细描述
|
||||
|
||||
### 6.1 初始化流程
|
||||
|
||||
1. **构造函数调用**:
|
||||
- 合并配置选项(用户配置 + 默认配置)
|
||||
- 调用 `createDom()` 创建 DOM 结构
|
||||
- 保存 header 和 contentArea 引用
|
||||
- 自动调用 `init()`
|
||||
|
||||
2. **init() 方法执行**:
|
||||
- 检查是否已初始化,如果是则返回
|
||||
- 将弹窗元素添加到容器
|
||||
- 调用 `initPosition()` 计算并设置位置
|
||||
- 如果 `draggable` 为 true,调用 `initDrag()`
|
||||
- 如果 `resizable` 为 true,调用 `initResize()`
|
||||
- 标记为已初始化
|
||||
- 调用 `onOpen` 回调(如果存在)
|
||||
- 订阅主题变更:`themeManager.subscribe()`
|
||||
- 订阅语言变更:`localeManager.subscribe()`
|
||||
|
||||
### 6.2 生命周期
|
||||
|
||||
#### 创建阶段
|
||||
- 构造函数 → `createDom()` → `init()`
|
||||
|
||||
#### 运行阶段
|
||||
- 响应主题变更:`setTheme()` 被调用
|
||||
- 响应语言变更:`setLocales()` 被调用
|
||||
- 用户交互:拖拽、缩放、关闭
|
||||
|
||||
#### 销毁阶段
|
||||
- `close()` 或 `destroy()` 被调用
|
||||
- 清理 `requestAnimationFrame`
|
||||
- 取消主题和语言订阅
|
||||
- 从 DOM 移除元素
|
||||
- 调用 `onClose` 回调
|
||||
|
||||
### 6.3 事件处理流程
|
||||
|
||||
#### 拖拽事件流程
|
||||
1. 用户在标题栏按下鼠标 → `mousedown` 事件
|
||||
2. 记录起始位置和弹窗当前位置
|
||||
3. 缓存容器和弹窗尺寸
|
||||
4. 在 document 上绑定 `mousemove` 和 `mouseup`(捕获阶段)
|
||||
5. 鼠标移动时 → `mousemove` 事件
|
||||
- 计算偏移量
|
||||
- 使用 `requestAnimationFrame` 更新位置
|
||||
- 限制在容器边界内
|
||||
6. 鼠标释放时 → `mouseup` 事件
|
||||
- 清理事件监听
|
||||
- 清理动画帧
|
||||
|
||||
#### 缩放事件流程
|
||||
1. 用户在缩放手柄按下鼠标 → `mousedown` 事件
|
||||
2. 记录起始位置和弹窗当前尺寸
|
||||
3. 在 document 上绑定 `mousemove` 和 `mouseup`(捕获阶段)
|
||||
4. 鼠标移动时 → `mousemove` 事件
|
||||
- 计算偏移量
|
||||
- 使用 `requestAnimationFrame` 更新尺寸
|
||||
- 限制在最小尺寸以上
|
||||
5. 鼠标释放时 → `mouseup` 事件
|
||||
- 清理事件监听
|
||||
- 清理动画帧
|
||||
|
||||
#### 事件拦截流程
|
||||
- 弹窗根元素上绑定所有鼠标/触摸事件
|
||||
- 使用 `stopPropagation()` 阻止事件冒泡
|
||||
- 防止事件传递给 3D 引擎
|
||||
- 使用 `passive: false` 允许阻止默认行为
|
||||
|
||||
### 6.4 状态管理
|
||||
|
||||
#### 内部状态
|
||||
- `_isInitialized`: 是否已初始化
|
||||
- `_isDestroyed`: 是否已销毁
|
||||
- `rafId`: `requestAnimationFrame` 的 ID(用于节流)
|
||||
|
||||
#### 订阅管理
|
||||
- `unsubscribeTheme`: 主题订阅取消函数
|
||||
- `unsubscribeLocale`: 语言订阅取消函数
|
||||
|
||||
### 6.5 与其他组件的交互
|
||||
|
||||
- **与 DialogManager**: 通过 Manager 创建和管理
|
||||
- **与 ThemeManager**: 订阅主题变更
|
||||
- **与 LocaleManager**: 订阅语言变更
|
||||
- **与 3D 引擎**: 通过事件拦截防止交互冲突
|
||||
|
||||
---
|
||||
|
||||
## 7. 国际化支持
|
||||
|
||||
### 7.1 使用的翻译键
|
||||
|
||||
- `options.title`: 弹窗标题(如果提供,会通过 `t()` 函数翻译)
|
||||
|
||||
### 7.2 语言变更处理
|
||||
|
||||
- 组件订阅 `localeManager.subscribe()`
|
||||
- 语言变更时,`setLocales()` 方法被调用
|
||||
- 更新标题文本:`titleEl.textContent = t(this.options.title)`
|
||||
|
||||
### 7.3 实现细节
|
||||
|
||||
```typescript
|
||||
public setLocales(): void {
|
||||
if (this.options.title) {
|
||||
const titleEl = this.header.querySelector('.bim-dialog-title');
|
||||
if (titleEl) {
|
||||
titleEl.textContent = t(this.options.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 主题支持
|
||||
|
||||
### 8.1 使用的主题变量
|
||||
|
||||
- `theme.panelBackground` → `--bim-dialog-bg`
|
||||
- `theme.componentHover` → `--bim-dialog-header-bg`
|
||||
- `theme.textPrimary` → `--bim-dialog-title-color` 和 `--bim-dialog-text-color`
|
||||
- `theme.border` → `--bim-dialog-border-color`
|
||||
|
||||
### 8.2 主题变更处理
|
||||
|
||||
- 组件订阅 `themeManager.subscribe()`
|
||||
- 主题变更时,`setTheme()` 方法被调用
|
||||
- 如果用户未自定义颜色,使用主题颜色
|
||||
- 如果用户已自定义颜色,保持用户自定义
|
||||
|
||||
### 8.3 实现细节
|
||||
|
||||
```typescript
|
||||
public setTheme(theme: ThemeConfig) {
|
||||
const style = this.element.style;
|
||||
if (!this.options.backgroundColor)
|
||||
style.setProperty('--bim-dialog-bg', theme.panelBackground);
|
||||
if (!this.options.headerBackgroundColor)
|
||||
style.setProperty('--bim-dialog-header-bg', theme.componentHover);
|
||||
// ... 其他颜色映射
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 使用示例
|
||||
|
||||
### 9.1 基本使用(通过 Manager)
|
||||
|
||||
```typescript
|
||||
import { BimEngine } from 'bim-engine-sdk';
|
||||
|
||||
const engine = new BimEngine('container');
|
||||
|
||||
// 创建简单弹窗
|
||||
const dialog = engine.dialog.create({
|
||||
title: 'dialog.testTitle', // 使用翻译键
|
||||
content: '这是内容',
|
||||
width: 400,
|
||||
height: 300
|
||||
});
|
||||
|
||||
// 创建可缩放弹窗
|
||||
const resizableDialog = engine.dialog.create({
|
||||
title: '可缩放弹窗',
|
||||
content: '<div>内容</div>',
|
||||
width: 500,
|
||||
height: 400,
|
||||
resizable: true,
|
||||
draggable: true
|
||||
});
|
||||
|
||||
// 创建自定义位置弹窗
|
||||
const positionedDialog = engine.dialog.create({
|
||||
title: '自定义位置',
|
||||
content: '内容',
|
||||
position: { x: 100, y: 100 },
|
||||
width: 300,
|
||||
height: 200
|
||||
});
|
||||
```
|
||||
|
||||
### 9.2 高级使用
|
||||
|
||||
```typescript
|
||||
// 创建带自定义样式的弹窗
|
||||
const styledDialog = engine.dialog.create({
|
||||
title: '自定义样式',
|
||||
content: '<div>内容</div>',
|
||||
backgroundColor: 'rgba(100, 0, 0, 0.95)',
|
||||
headerBackgroundColor: '#cc0000',
|
||||
titleColor: '#ffffff',
|
||||
textColor: '#ffcccc',
|
||||
borderColor: '#ff6666',
|
||||
width: 300,
|
||||
height: 200
|
||||
});
|
||||
|
||||
// 创建带回调的弹窗
|
||||
const callbackDialog = engine.dialog.create({
|
||||
title: '带回调',
|
||||
content: '内容',
|
||||
onOpen: () => {
|
||||
console.log('弹窗已打开');
|
||||
},
|
||||
onClose: () => {
|
||||
console.log('弹窗已关闭');
|
||||
}
|
||||
});
|
||||
|
||||
// 动态更新内容
|
||||
const dynamicDialog = engine.dialog.create({
|
||||
title: '动态内容',
|
||||
content: '初始内容'
|
||||
});
|
||||
|
||||
// 稍后更新内容
|
||||
dynamicDialog.setContent('<div>新内容</div>');
|
||||
```
|
||||
|
||||
### 9.3 使用信息弹窗
|
||||
|
||||
```typescript
|
||||
// 通过 Manager 显示信息弹窗
|
||||
engine.dialog.showInfoDialog();
|
||||
|
||||
// 通过事件总线打开(如果配置了监听)
|
||||
engine.emit('ui:open-dialog', { id: 'info' });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 实现细节(供 AI 重现)
|
||||
|
||||
### 10.1 关键算法
|
||||
|
||||
#### 位置计算算法
|
||||
```typescript
|
||||
// 计算居中位置
|
||||
function calculateCenterPosition(containerWidth, containerHeight, dialogWidth, dialogHeight) {
|
||||
return {
|
||||
left: (containerWidth - dialogWidth) / 2,
|
||||
top: (containerHeight - dialogHeight) / 2
|
||||
};
|
||||
}
|
||||
|
||||
// 边界限制算法
|
||||
function clampPosition(left, top, containerWidth, containerHeight, dialogWidth, dialogHeight) {
|
||||
return {
|
||||
left: Math.max(0, Math.min(left, containerWidth - dialogWidth)),
|
||||
top: Math.max(0, Math.min(top, containerHeight - dialogHeight))
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### 拖拽算法
|
||||
```typescript
|
||||
// 拖拽位置更新
|
||||
function updateDragPosition(startX, startY, currentX, currentY, startLeft, startTop) {
|
||||
const deltaX = currentX - startX;
|
||||
const deltaY = currentY - startY;
|
||||
return {
|
||||
left: startLeft + deltaX,
|
||||
top: startTop + deltaY
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### 缩放算法
|
||||
```typescript
|
||||
// 缩放尺寸更新
|
||||
function updateResizeSize(startX, startY, currentX, currentY, startWidth, startHeight, minWidth, minHeight) {
|
||||
const deltaX = currentX - startX;
|
||||
const deltaY = currentY - startY;
|
||||
return {
|
||||
width: Math.max(minWidth, startWidth + deltaX),
|
||||
height: Math.max(minHeight, startHeight + deltaY)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 性能优化点
|
||||
|
||||
1. **requestAnimationFrame 节流**:
|
||||
- 拖拽和缩放使用 `requestAnimationFrame` 节流更新
|
||||
- 避免频繁的 DOM 操作
|
||||
|
||||
2. **尺寸缓存**:
|
||||
- 拖拽开始时缓存容器和弹窗尺寸
|
||||
- 减少 `getBoundingClientRect()` 调用
|
||||
|
||||
3. **事件捕获**:
|
||||
- 使用 `capture: true` 确保事件在捕获阶段处理
|
||||
- 即使内部元素阻止冒泡,也能捕获事件
|
||||
|
||||
4. **CSS 变量**:
|
||||
- 使用 CSS 变量应用主题
|
||||
- 主题变更时无需重新渲染 DOM
|
||||
|
||||
### 10.3 注意事项和边界情况
|
||||
|
||||
1. **容器尺寸为 0**:
|
||||
- 位置计算时可能出现除零或负数
|
||||
- 使用 `Math.max()` 确保位置不为负
|
||||
|
||||
2. **弹窗尺寸大于容器**:
|
||||
- 位置计算时确保弹窗不超出容器
|
||||
- 使用边界限制算法
|
||||
|
||||
3. **快速连续操作**:
|
||||
- 使用 `rafId` 防止多个动画帧同时执行
|
||||
- 确保动画帧正确清理
|
||||
|
||||
4. **事件清理**:
|
||||
- 组件销毁时必须清理所有事件监听
|
||||
- 必须取消主题和语言订阅
|
||||
|
||||
5. **事件拦截**:
|
||||
- 必须拦截所有鼠标/触摸事件
|
||||
- 防止事件传递给 3D 引擎
|
||||
|
||||
6. **翻译键处理**:
|
||||
- 标题可能是翻译键或普通文本
|
||||
- 只有翻译键才需要通过 `t()` 函数处理
|
||||
|
||||
---
|
||||
|
||||
## 11. 类型定义
|
||||
|
||||
### 11.1 DialogOptions
|
||||
|
||||
```typescript
|
||||
interface DialogOptions extends DialogColors {
|
||||
container: HTMLElement; // 弹窗挂载的父容器(必需)
|
||||
title?: string; // 弹窗标题(可选,支持翻译键)
|
||||
content?: HTMLElement | string; // 弹窗内容(可选)
|
||||
width?: number | string; // 宽度(可选,默认 300)
|
||||
height?: number | string; // 高度(可选,默认 'auto')
|
||||
position?: DialogPosition; // 位置(可选,默认 'center')
|
||||
draggable?: boolean; // 是否可拖拽(可选,默认 true)
|
||||
resizable?: boolean; // 是否可缩放(可选,默认 false)
|
||||
minWidth?: number; // 最小宽度(可选,默认 200)
|
||||
minHeight?: number; // 最小高度(可选,默认 100)
|
||||
onClose?: () => void; // 关闭回调(可选)
|
||||
onOpen?: () => void; // 打开回调(可选)
|
||||
id?: string; // 弹窗 ID(可选)
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 DialogPosition
|
||||
|
||||
```typescript
|
||||
type DialogPosition =
|
||||
| 'center'
|
||||
| 'top-left' | 'top-center' | 'top-right'
|
||||
| 'left-center' | 'right-center'
|
||||
| 'bottom-left' | 'bottom-center' | 'bottom-right'
|
||||
| { x: number; y: number };
|
||||
```
|
||||
|
||||
### 11.3 DialogColors
|
||||
|
||||
```typescript
|
||||
interface DialogColors {
|
||||
backgroundColor?: string; // 窗体背景颜色
|
||||
headerBackgroundColor?: string; // 标题栏背景颜色
|
||||
titleColor?: string; // 标题文字颜色
|
||||
textColor?: string; // 内容文字颜色
|
||||
borderColor?: string; // 边框颜色
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 文件清单
|
||||
|
||||
### 12.1 相关文件
|
||||
|
||||
- `src/components/dialog/index.ts` - 主组件类
|
||||
- `src/components/dialog/index.type.ts` - 类型定义
|
||||
- `src/components/dialog/index.css` - 样式文件
|
||||
- `src/components/dialog/bimInfoDialog/index.ts` - 信息弹窗(分化组件)
|
||||
- `src/components/dialog/bimInfoDialog/index.css` - 信息弹窗样式
|
||||
- `src/managers/dialog-manager.ts` - 管理器类
|
||||
|
||||
### 12.2 依赖文件
|
||||
|
||||
- `src/types/component.ts` - IBimComponent 接口
|
||||
- `src/themes/types.ts` - ThemeConfig 类型
|
||||
- `src/services/theme.ts` - ThemeManager
|
||||
- `src/services/locale.ts` - LocaleManager
|
||||
|
||||
---
|
||||
|
||||
## 13. 更新记录
|
||||
|
||||
| 日期 | 修改内容 | 修改人 |
|
||||
|------|---------|--------|
|
||||
| 2024-XX-XX | 初始创建 | AI Assistant |
|
||||
|
||||
---
|
||||
|
||||
**重要提醒**: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!
|
||||
|
||||
601
docs/components/engine.md
Normal file
601
docs/components/engine.md
Normal file
@@ -0,0 +1,601 @@
|
||||
# Engine 组件详细文档
|
||||
|
||||
> 本文档详细描述 Engine 组件的实现细节,包括 API、UI 结构、逻辑流程等,供 AI 根据文档重现组件。
|
||||
|
||||
---
|
||||
|
||||
## 1. 组件概述
|
||||
|
||||
### 1.1 基本信息
|
||||
- **组件名称**: `Engine`
|
||||
- **文件路径**: `src/components/engine/index.ts`
|
||||
- **类型定义**: `src/components/engine/types.ts`
|
||||
- **实现接口**: `IBimComponent`
|
||||
- **用途**: 封装第三方 3D 引擎 SDK,提供统一的 3D 引擎管理接口
|
||||
|
||||
### 1.2 在 SDK 中的位置
|
||||
- Engine 组件是独立的 3D 引擎组件
|
||||
- 必须通过 `EngineManager` 使用,不允许直接使用
|
||||
- `EngineManager` 位于 `src/managers/engine-manager.ts`
|
||||
- 采用延迟初始化模式,需要用户主动调用 `init()` 方法
|
||||
|
||||
### 1.3 第三方 SDK 依赖
|
||||
- 依赖 `src/bim-engine-sdk.es.js` 中的 `createEngine` 函数
|
||||
- 通过依赖注入方式使用,不直接导入
|
||||
|
||||
---
|
||||
|
||||
## 2. 组件类 API 文档
|
||||
|
||||
### 2.1 构造函数
|
||||
|
||||
```typescript
|
||||
constructor(options: EngineOptions)
|
||||
```
|
||||
|
||||
**参数**:
|
||||
- `options`: `EngineOptions` - 3D 引擎配置选项
|
||||
|
||||
**默认配置**:
|
||||
```typescript
|
||||
{
|
||||
backgroundColor: 0x1a1a1a, // 默认深色背景
|
||||
version: 'v1', // 默认使用 v1 版本
|
||||
showStats: false, // 默认不显示统计
|
||||
showViewCube: true // 默认显示视图立方体
|
||||
}
|
||||
```
|
||||
|
||||
**行为**:
|
||||
- 解析容器元素
|
||||
- 如果容器没有 id,生成一个唯一的 id
|
||||
- 保存配置选项(设置默认值)
|
||||
|
||||
### 2.2 公共方法
|
||||
|
||||
#### `init(): void`
|
||||
初始化组件(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 检查是否已初始化或已销毁
|
||||
- 创建引擎配置对象
|
||||
- 调用 `createEngineSDK()` 创建引擎实例
|
||||
- 标记为已初始化
|
||||
- 订阅主题变化
|
||||
- 应用当前主题
|
||||
|
||||
**错误处理**:
|
||||
- 如果创建失败,抛出错误并标记为未初始化
|
||||
|
||||
#### `setTheme(theme: ThemeConfig): void`
|
||||
设置主题(实现 `IBimComponent` 接口)
|
||||
|
||||
**参数**:
|
||||
- `theme`: `ThemeConfig` - 全局主题配置
|
||||
|
||||
**功能**:
|
||||
- 根据主题名称选择背景色:
|
||||
- `dark`: 使用深色背景 `0x1a1a1a`
|
||||
- `light`: 使用浅色背景 `0xf5f5f5`
|
||||
- `custom`: 使用配置中的 `backgroundColor` 或默认值
|
||||
- 尝试更新引擎背景色:
|
||||
- 如果引擎有 `setBackgroundColor()` 方法,调用它
|
||||
- 否则,如果引擎有 `scene.background`,设置其颜色
|
||||
|
||||
#### `setLocales(): void`
|
||||
设置语言(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 3D 引擎组件暂时不需要本地化
|
||||
- 方法为空实现
|
||||
|
||||
#### `isInitialized(): boolean`
|
||||
检查是否已初始化
|
||||
|
||||
**返回**: `boolean` - 是否已初始化
|
||||
|
||||
#### `loadModel(url: string, options?: ModelLoadOptions): void`
|
||||
加载 3D 模型
|
||||
|
||||
**参数**:
|
||||
- `url`: `string` - 模型文件 URL(必需)
|
||||
- `options`: `ModelLoadOptions` (可选) - 加载选项
|
||||
|
||||
**功能**:
|
||||
- 检查引擎是否已初始化
|
||||
- 检查 URL 是否提供
|
||||
- 调用引擎的 `loader.loadModel()` 方法加载模型
|
||||
|
||||
**加载选项**:
|
||||
- `position`: `[number, number, number]` - 模型位置 [x, y, z]
|
||||
- `rotation`: `[number, number, number]` - 模型旋转(弧度)[x, y, z]
|
||||
- `scale`: `[number, number, number]` - 模型缩放 [x, y, z]
|
||||
- `id`: `string` - 模型 ID(可选)
|
||||
|
||||
#### `getEngine(): any`
|
||||
获取原始 3D 引擎实例
|
||||
|
||||
**返回**: 第三方 3D 引擎实例或 `null`
|
||||
|
||||
**功能**:
|
||||
- 返回底层 3D 引擎实例
|
||||
- 用于直接调用第三方引擎的其他 API
|
||||
|
||||
#### `destroy(): void`
|
||||
销毁组件(实现 `IBimComponent` 接口)
|
||||
|
||||
**功能**:
|
||||
- 取消主题订阅
|
||||
- 清空容器内容
|
||||
- 标记为已销毁和未初始化
|
||||
|
||||
**注意**: 不销毁引擎实例本身(由第三方 SDK 管理)
|
||||
|
||||
---
|
||||
|
||||
## 3. 分化组件说明
|
||||
|
||||
**Engine 组件没有分化组件**。
|
||||
|
||||
---
|
||||
|
||||
## 4. Manager API 文档
|
||||
|
||||
### 4.1 EngineManager 类
|
||||
|
||||
**文件路径**: `src/managers/engine-manager.ts`
|
||||
|
||||
**继承关系**: `EngineManager extends BimComponent`
|
||||
|
||||
### 4.2 构造函数
|
||||
|
||||
```typescript
|
||||
constructor(engine: BimEngine, container: HTMLElement)
|
||||
```
|
||||
|
||||
**参数**:
|
||||
- `engine`: `BimEngine` - 引擎实例
|
||||
- `container`: `HTMLElement` - 3D 引擎挂载的目标容器
|
||||
|
||||
**行为**:
|
||||
- 保存容器引用
|
||||
- **不自动初始化引擎**(延迟初始化模式)
|
||||
|
||||
### 4.3 公共方法
|
||||
|
||||
#### `initialize(options?: Omit<EngineOptions, 'container'>): boolean`
|
||||
初始化 3D 引擎
|
||||
|
||||
**参数**:
|
||||
- `options`: `Omit<EngineOptions, 'container'>` (可选) - 引擎配置选项
|
||||
|
||||
**返回**: `boolean` - 是否初始化成功
|
||||
|
||||
**功能**:
|
||||
- 如果已经初始化,先销毁旧的实例
|
||||
- 创建 `Engine` 组件实例
|
||||
- 调用组件的 `init()` 方法初始化引擎
|
||||
- 返回初始化结果
|
||||
|
||||
**错误处理**:
|
||||
- 如果初始化失败,返回 `false` 并输出错误信息
|
||||
|
||||
#### `isInitialized(): boolean`
|
||||
检查 3D 引擎是否已初始化
|
||||
|
||||
**返回**: `boolean` - 是否已初始化
|
||||
|
||||
#### `loadModel(url: string, options?: ModelLoadOptions): void`
|
||||
加载 3D 模型
|
||||
|
||||
**参数**:
|
||||
- `url`: `string` - 模型文件 URL
|
||||
- `options`: `ModelLoadOptions` (可选) - 加载选项
|
||||
|
||||
**功能**:
|
||||
- 检查引擎是否已初始化
|
||||
- 如果未初始化,输出错误信息
|
||||
- 调用引擎组件的 `loadModel()` 方法
|
||||
|
||||
#### `getEngine(): any`
|
||||
获取原始 3D 引擎实例
|
||||
|
||||
**返回**: 第三方 3D 引擎实例或 `null`
|
||||
|
||||
**功能**:
|
||||
- 用于直接调用第三方引擎的其他 API
|
||||
- 如果引擎未初始化,返回 `null` 并输出警告
|
||||
|
||||
#### `destroy(): void`
|
||||
销毁 3D 引擎实例
|
||||
|
||||
**功能**:
|
||||
- 调用引擎组件的 `destroy()` 方法
|
||||
- 清空引擎实例引用
|
||||
|
||||
---
|
||||
|
||||
## 5. UI 详细描述
|
||||
|
||||
### 5.1 DOM 结构
|
||||
|
||||
**Engine 组件不直接创建 UI 元素**,而是:
|
||||
|
||||
1. **使用容器元素**:
|
||||
- 容器元素由外部提供(通过 `EngineOptions.container`)
|
||||
- 如果容器没有 id,组件会生成一个唯一的 id
|
||||
|
||||
2. **第三方 SDK 创建 UI**:
|
||||
- 第三方 SDK 的 `createEngine()` 函数会在容器内创建 3D 场景
|
||||
- 可能包括:
|
||||
- Canvas 元素(WebGL 渲染)
|
||||
- 统计面板(如果 `showStats: true`)
|
||||
- 视图立方体(如果 `showViewCube: true`)
|
||||
|
||||
### 5.2 容器 ID 生成
|
||||
|
||||
**算法**:
|
||||
```typescript
|
||||
containerId = `engine-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
```
|
||||
|
||||
**格式**: `engine-container-[时间戳]-[随机字符串]`
|
||||
|
||||
**示例**: `engine-container-1704067200000-k3j9x2m1p`
|
||||
|
||||
### 5.3 第三方 SDK 配置
|
||||
|
||||
**传递给 `createEngine()` 的配置对象**:
|
||||
```typescript
|
||||
{
|
||||
containerId: string, // 容器 ID
|
||||
backgroundColor: number, // 背景色(十六进制数字)
|
||||
version: 'v1' | 'v2', // WebGL 版本
|
||||
showStats: boolean, // 是否显示统计
|
||||
showViewCube: boolean // 是否显示视图立方体
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 逻辑流程详细描述
|
||||
|
||||
### 6.1 初始化流程
|
||||
|
||||
1. **构造函数调用**:
|
||||
- 解析容器元素
|
||||
- 生成或使用容器 ID
|
||||
- 保存配置选项(设置默认值)
|
||||
|
||||
2. **init() 方法执行**(用户主动调用):
|
||||
- 检查是否已初始化,如果是则返回
|
||||
- 检查是否已销毁,如果是则报错
|
||||
- 创建引擎配置对象
|
||||
- 调用 `createEngineSDK()` 创建引擎实例
|
||||
- 检查引擎实例是否创建成功
|
||||
- 标记为已初始化
|
||||
- 订阅主题变化:`themeManager.subscribe()`
|
||||
- 应用当前主题
|
||||
|
||||
### 6.2 生命周期
|
||||
|
||||
#### 创建阶段
|
||||
- 构造函数 → 保存配置
|
||||
|
||||
#### 初始化阶段(延迟)
|
||||
- 用户调用 `init()` → 创建引擎实例 → 订阅主题
|
||||
|
||||
#### 运行阶段
|
||||
- 响应主题变更:`setTheme()` 被调用
|
||||
- 加载模型:`loadModel()` 被调用
|
||||
- 用户交互:3D 场景交互(由第三方 SDK 处理)
|
||||
|
||||
#### 销毁阶段
|
||||
- `destroy()` 被调用
|
||||
- 取消主题订阅
|
||||
- 清空容器
|
||||
- 更新状态
|
||||
|
||||
### 6.3 主题变更流程
|
||||
|
||||
1. 主题变更 → `themeManager` 通知订阅者
|
||||
2. `setTheme()` 方法被调用
|
||||
3. 根据主题名称选择背景色:
|
||||
- `dark` → `0x1a1a1a`
|
||||
- `light` → `0xf5f5f5`
|
||||
- `custom` → 使用配置值或默认值
|
||||
4. 尝试更新引擎背景色:
|
||||
- 方法 1: 调用 `engine.setBackgroundColor(backgroundColor)`
|
||||
- 方法 2: 设置 `engine.scene.background.setHex(backgroundColor)`
|
||||
|
||||
### 6.4 模型加载流程
|
||||
|
||||
1. 用户调用 `loadModel(url, options)`
|
||||
2. 检查引擎是否已初始化
|
||||
3. 检查 URL 是否提供
|
||||
4. 调用 `engine.loader.loadModel(url, options)`
|
||||
5. 第三方 SDK 处理模型加载
|
||||
|
||||
### 6.5 状态管理
|
||||
|
||||
#### 内部状态
|
||||
- `_isInitialized`: 是否已初始化
|
||||
- `_isDestroyed`: 是否已销毁
|
||||
- `engine`: 第三方 3D 引擎实例
|
||||
- `containerId`: 容器 ID
|
||||
- `options`: 引擎配置选项
|
||||
|
||||
#### 订阅管理
|
||||
- `unsubscribeTheme`: 主题订阅取消函数
|
||||
|
||||
### 6.6 与其他组件的交互
|
||||
|
||||
- **与 EngineManager**: 通过 Manager 创建和管理
|
||||
- **与 ThemeManager**: 订阅主题变更
|
||||
- **与第三方 SDK**: 通过 `createEngine()` 函数创建引擎实例
|
||||
|
||||
---
|
||||
|
||||
## 7. 国际化支持
|
||||
|
||||
### 7.1 使用的翻译键
|
||||
|
||||
**Engine 组件不使用翻译键**(3D 引擎不需要国际化)。
|
||||
|
||||
### 7.2 语言变更处理
|
||||
|
||||
- `setLocales()` 方法为空实现
|
||||
- 不订阅语言变更
|
||||
|
||||
---
|
||||
|
||||
## 8. 主题支持
|
||||
|
||||
### 8.1 使用的主题变量
|
||||
|
||||
- 根据主题名称选择背景色:
|
||||
- `dark` 主题 → `0x1a1a1a`(深色背景)
|
||||
- `light` 主题 → `0xf5f5f5`(浅色背景)
|
||||
- `custom` 主题 → 使用配置值或默认值
|
||||
|
||||
### 8.2 主题变更处理
|
||||
|
||||
- 组件订阅 `themeManager.subscribe()`
|
||||
- 主题变更时,`setTheme()` 方法被调用
|
||||
- 根据主题名称选择背景色
|
||||
- 尝试更新引擎背景色(通过引擎 API)
|
||||
|
||||
### 8.3 实现细节
|
||||
|
||||
```typescript
|
||||
public setTheme(theme: ThemeConfig): void {
|
||||
let backgroundColor: number;
|
||||
if (theme.name === 'dark') {
|
||||
backgroundColor = 0x1a1a1a;
|
||||
} else if (theme.name === 'light') {
|
||||
backgroundColor = 0xf5f5f5;
|
||||
} else {
|
||||
backgroundColor = this.options.backgroundColor ?? 0x1a1a1a;
|
||||
}
|
||||
|
||||
// 尝试更新引擎背景色
|
||||
if (this.engine && typeof this.engine.setBackgroundColor === 'function') {
|
||||
this.engine.setBackgroundColor(backgroundColor);
|
||||
} else if (this.engine && this.engine.scene) {
|
||||
if (this.engine.scene.background) {
|
||||
this.engine.scene.background.setHex(backgroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 使用示例
|
||||
|
||||
### 9.1 基本使用(通过 EngineManager)
|
||||
|
||||
```typescript
|
||||
import { BimEngine } from 'bim-engine-sdk';
|
||||
|
||||
const engine = new BimEngine('container');
|
||||
|
||||
// 初始化 3D 引擎(延迟初始化)
|
||||
const success = engine.initEngine({
|
||||
backgroundColor: 0x333333,
|
||||
version: 'v1',
|
||||
showStats: true,
|
||||
showViewCube: true
|
||||
});
|
||||
|
||||
if (success) {
|
||||
console.log('3D 引擎初始化成功');
|
||||
}
|
||||
|
||||
// 加载模型
|
||||
engine.engine.loadModel('/model/building.glb', {
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1]
|
||||
});
|
||||
```
|
||||
|
||||
### 9.2 高级使用
|
||||
|
||||
```typescript
|
||||
// 检查引擎是否已初始化
|
||||
if (engine.engine.isInitialized()) {
|
||||
// 加载模型
|
||||
engine.engine.loadModel('/model/model.glb');
|
||||
}
|
||||
|
||||
// 获取原始引擎实例,调用第三方 API
|
||||
const rawEngine = engine.engine.getEngine();
|
||||
if (rawEngine) {
|
||||
// 调用第三方 SDK 的其他方法
|
||||
rawEngine.someOtherMethod();
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 主题切换
|
||||
|
||||
```typescript
|
||||
// 切换主题会自动更新 3D 场景背景色
|
||||
engine.setTheme('dark'); // 深色背景
|
||||
engine.setTheme('light'); // 浅色背景
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 实现细节(供 AI 重现)
|
||||
|
||||
### 10.1 关键算法
|
||||
|
||||
#### 容器 ID 生成算法
|
||||
```typescript
|
||||
function generateContainerId(): string {
|
||||
const timestamp = Date.now();
|
||||
const random = Math.random().toString(36).substr(2, 9);
|
||||
return `engine-container-${timestamp}-${random}`;
|
||||
}
|
||||
```
|
||||
|
||||
#### 主题背景色选择算法
|
||||
```typescript
|
||||
function selectBackgroundColor(theme: ThemeConfig, defaultColor: number): number {
|
||||
if (theme.name === 'dark') {
|
||||
return 0x1a1a1a; // 深色背景
|
||||
} else if (theme.name === 'light') {
|
||||
return 0xf5f5f5; // 浅色背景
|
||||
} else {
|
||||
return defaultColor; // 自定义主题,使用配置值
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 引擎背景色更新算法
|
||||
```typescript
|
||||
function updateEngineBackground(engine: any, color: number): void {
|
||||
// 方法 1: 如果引擎有 setBackgroundColor 方法
|
||||
if (engine && typeof engine.setBackgroundColor === 'function') {
|
||||
engine.setBackgroundColor(color);
|
||||
return;
|
||||
}
|
||||
|
||||
// 方法 2: 如果引擎有 scene.background
|
||||
if (engine && engine.scene && engine.scene.background) {
|
||||
engine.scene.background.setHex(color);
|
||||
return;
|
||||
}
|
||||
|
||||
// 方法 3: 无法更新(引擎可能不支持)
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 性能优化点
|
||||
|
||||
1. **延迟初始化**:
|
||||
- 引擎不自动初始化,由用户主动调用
|
||||
- 减少不必要的资源消耗
|
||||
|
||||
2. **状态检查**:
|
||||
- 初始化前检查状态,避免重复初始化
|
||||
- 销毁后禁止初始化,避免错误
|
||||
|
||||
3. **错误处理**:
|
||||
- 所有操作都有错误检查
|
||||
- 提供有意义的错误信息
|
||||
|
||||
### 10.3 注意事项和边界情况
|
||||
|
||||
1. **容器 ID 唯一性**:
|
||||
- 容器 ID 必须唯一
|
||||
- 如果容器已有 id,使用现有 id
|
||||
- 如果容器没有 id,生成唯一 id
|
||||
|
||||
2. **延迟初始化**:
|
||||
- 引擎不会自动初始化
|
||||
- 用户必须主动调用 `init()` 或 `initialize()`
|
||||
- 初始化前调用其他方法会报错
|
||||
|
||||
3. **引擎实例管理**:
|
||||
- 引擎实例由第三方 SDK 创建
|
||||
- 组件不负责销毁引擎实例本身
|
||||
- 只负责清理订阅和容器
|
||||
|
||||
4. **主题更新兼容性**:
|
||||
- 不同第三方 SDK 的 API 可能不同
|
||||
- 需要尝试多种方式更新背景色
|
||||
- 如果都不支持,静默失败
|
||||
|
||||
5. **模型加载**:
|
||||
- 模型 URL 必须是可访问的
|
||||
- 模型格式由第三方 SDK 支持
|
||||
- 加载选项由第三方 SDK 处理
|
||||
|
||||
6. **错误处理**:
|
||||
- 初始化失败时,标记为未初始化
|
||||
- 提供有意义的错误信息
|
||||
- 不抛出未捕获的异常
|
||||
|
||||
7. **第三方 SDK 依赖**:
|
||||
- 依赖 `bim-engine-sdk.es.js` 文件
|
||||
- 通过动态导入或全局变量访问
|
||||
- 需要确保 SDK 文件已加载
|
||||
|
||||
---
|
||||
|
||||
## 11. 类型定义
|
||||
|
||||
### 11.1 EngineOptions
|
||||
|
||||
```typescript
|
||||
interface EngineOptions {
|
||||
container: HTMLElement; // 容器元素(必需)
|
||||
backgroundColor?: number; // 背景颜色(可选,默认 0x1a1a1a)
|
||||
version?: 'v1' | 'v2'; // WebGL 版本(可选,默认 'v1')
|
||||
showStats?: boolean; // 是否显示性能统计(可选,默认 false)
|
||||
showViewCube?: boolean; // 是否显示视图立方体(可选,默认 true)
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 ModelLoadOptions
|
||||
|
||||
```typescript
|
||||
interface ModelLoadOptions {
|
||||
position?: [number, number, number]; // 模型位置 [x, y, z](可选)
|
||||
rotation?: [number, number, number]; // 模型旋转(弧度)[x, y, z](可选)
|
||||
scale?: [number, number, number]; // 模型缩放 [x, y, z](可选)
|
||||
id?: string; // 模型 ID(可选)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 文件清单
|
||||
|
||||
### 12.1 相关文件
|
||||
|
||||
- `src/components/engine/index.ts` - 主组件类
|
||||
- `src/components/engine/types.ts` - 类型定义
|
||||
- `src/managers/engine-manager.ts` - 管理器类
|
||||
- `src/bim-engine-sdk.es.js` - 第三方 SDK(依赖)
|
||||
|
||||
### 12.2 依赖文件
|
||||
|
||||
- `src/types/component.ts` - IBimComponent 接口
|
||||
- `src/themes/types.ts` - ThemeConfig 类型
|
||||
- `src/services/theme.ts` - ThemeManager
|
||||
|
||||
---
|
||||
|
||||
## 13. 更新记录
|
||||
|
||||
| 日期 | 修改内容 | 修改人 |
|
||||
|------|---------|--------|
|
||||
| 2024-XX-XX | 初始创建 | AI Assistant |
|
||||
|
||||
---
|
||||
|
||||
**重要提醒**: 本文档必须与组件代码保持同步。任何组件修改都必须更新本文档!
|
||||
|
||||
66
docs/components/menu.md
Normal file
66
docs/components/menu.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Menu 组件文档
|
||||
|
||||
## 1. 组件概述
|
||||
|
||||
`BimMenu` 是一个通用的菜单列表组件。
|
||||
- **位置**: `src/components/menu/index.ts`
|
||||
- **功能**: 渲染一组 `BimMenuItem`,支持分组、排序、图标、快捷键提示和多级子菜单。
|
||||
- **特点**: 数据驱动(通过 Item 类实例),支持国际化。
|
||||
|
||||
## 2. 组件类 API
|
||||
|
||||
### `BimMenu`
|
||||
|
||||
#### 构造函数
|
||||
`new BimMenu(options: MenuOptions)`
|
||||
|
||||
#### 方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
|:----|:---|:---|:---|
|
||||
| `init` | `()` | `void` | 初始化,渲染 DOM |
|
||||
| `destroy` | `()` | `void` | 销毁,清理事件和子菜单 |
|
||||
| `getElement` | `()` | `HTMLElement` | 获取根 DOM 元素 (实现 IRightKeyContent) |
|
||||
|
||||
### `BimMenuItem` (基类)
|
||||
|
||||
所有菜单项必须继承此抽象类。
|
||||
|
||||
| 方法/属性 | 类型 | 描述 |
|
||||
|:----|:---|:---|
|
||||
| `id` | `string` | 唯一标识 |
|
||||
| `group` | `string` | 分组 ID (默认 'default') |
|
||||
| `order` | `number` | 排序权重 |
|
||||
| `getLabel()` | `string` | 获取显示文本 |
|
||||
| `onClick()` | `void` | 点击回调 |
|
||||
| `getIcon()` | `string?` | 获取图标 HTML |
|
||||
| `getChildren()` | `BimMenuItem[]?` | 获取子菜单项 |
|
||||
|
||||
## 3. UI 详细描述
|
||||
|
||||
- **根元素**: `<ul class="bim-menu">`
|
||||
- **分组分割线**: `<li class="bim-menu-divider">`
|
||||
- **菜单项**: `<li class="bim-menu-item">`
|
||||
- 图标槽: `.bim-menu-item-icon`
|
||||
- 文本槽: `.bim-menu-item-label`
|
||||
- 箭头槽: `.bim-menu-item-arrow` (仅当有子菜单时)
|
||||
|
||||
## 4. 逻辑流程
|
||||
|
||||
1. **分组与排序**:
|
||||
- 根据 `item.group` 将项归类。
|
||||
- 根据 `options.groupOrder` 对组进行排序。
|
||||
- 组内根据 `item.order` 排序。
|
||||
2. **多级菜单**:
|
||||
- 鼠标进入 (`mouseenter`) 带子菜单的项时,创建新的 `BimMenu` 实例。
|
||||
- 将新实例挂载到临时的 fixed 容器中。
|
||||
- 自动计算位置(通常在父项右侧,空间不足时翻转)。
|
||||
|
||||
## 5. 类型定义
|
||||
|
||||
```typescript
|
||||
export interface MenuOptions {
|
||||
items: BimMenuItem[];
|
||||
groupOrder?: string[];
|
||||
}
|
||||
```
|
||||
58
docs/components/right-key.md
Normal file
58
docs/components/right-key.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# RightKey 组件文档
|
||||
|
||||
## 1. 组件概述
|
||||
|
||||
`BimRightKey` 是一个通用的右键浮层容器组件。
|
||||
- **位置**: `src/components/right-key/index.ts`
|
||||
- **功能**: 负责在指定坐标 (x, y) 显示内容,处理边界检测防止溢出屏幕,以及点击外部自动关闭。
|
||||
- **特点**: 它不关心内容是什么,通过 `mount()` 方法挂载任意实现 `IRightKeyContent` 接口的内容组件。
|
||||
|
||||
## 2. 组件类 API
|
||||
|
||||
### `BimRightKey`
|
||||
|
||||
#### 方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
|:----|:---|:---|:---|
|
||||
| `init` | `()` | `void` | 初始化,绑定全局点击事件 |
|
||||
| `mount` | `(content: IRightKeyContent)` | `void` | 挂载内容组件 |
|
||||
| `show` | `(x: number, y: number)` | `void` | <20><><EFBFBD>指定位置显示,会自动调整位置防止溢出 |
|
||||
| `hide` | `()` | `void` | 隐藏容器 |
|
||||
| `destroy` | `()` | `void` | 销毁容器,解绑事件 |
|
||||
| `setOnClose` | `(callback: () => void)` | `void` | 设置关闭时的回调 |
|
||||
|
||||
## 3. Manager API
|
||||
|
||||
### `RightKeyManager`
|
||||
|
||||
- **位置**: `src/managers/right-key-manager.ts`
|
||||
- **功能**: 监听容器的 `contextmenu` 事件,决定显示什么内容。
|
||||
|
||||
#### 方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
|:----|:---|:---|:---|
|
||||
| `registerHandler` | `(handler: (e) => BimMenuItem[])` | `void` | 注册上下文处理器 |
|
||||
| `showMenu` | `(x, y, items, groupOrder?)` | `void` | 手动显示菜单 |
|
||||
| `hide` | `()` | `void` | 隐藏 |
|
||||
|
||||
## 4. UI 详细描述
|
||||
|
||||
- **容器**: `<div class="bim-right-key"></div>`
|
||||
- **定位**: `position: fixed`
|
||||
- **层级**: `z-index: 10000` (默认)
|
||||
|
||||
## 5. 实现细节
|
||||
|
||||
- **边界检测**: 在 `show(x, y)` 时,会计算容器宽高和视口宽高。如果 `x + width > viewportWidth`,则 `x = x - width`(向左展开)。同理处理垂直方向。
|
||||
- **点击外部关闭**: 监听 `document` 的 `mousedown` 事件,如果点击目标不在容器内,则调用 `hide()`。
|
||||
|
||||
## 6. 类型定义
|
||||
|
||||
```typescript
|
||||
export interface IRightKeyContent {
|
||||
getElement(): HTMLElement;
|
||||
destroy(): void;
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user