42 KiB
BIM Engine SDK - AI 协作文档
本文档用于向 AI 助手传递项目信息,帮助 AI 更好地理解和维护本项目。每次代码改动后,请务必更新本文档的相关部分。
📋 目录
1. 项目基本信息
1.1 项目描述
BIM Engine SDK 是一个通用的 3D BIM 引擎 SDK 开发框架,旨在通过一次编码,同时支持 Vue 2、Vue 3、React 和纯 HTML 环境。
1.2 技术栈
- 语言: TypeScript
- 构建工具: Vite (Library Mode)
- 类型生成: vite-plugin-dts
- CSS 注入: vite-plugin-css-injected-by-js
- 3D 引擎: 基于第三方 SDK (bim-engine-sdk.es.js)
1.3 项目结构
engine/
├── src/ # SDK 源代码
│ ├── core/ # 核心基础类
│ ├── components/ # UI 组件
│ ├── managers/ # 管理器类
│ ├── services/ # 服务类(主题、语言)
│ ├── themes/ # 主题配置
│ ├── locales/ # 国际化配置
│ ├── types/ # 类型定义
│ ├── bim-engine.ts # 主引擎类
│ └── index.ts # 入口文件
├── dist/ # 构建产物 (ESM + UMD + .d.ts)
├── demo/ # HTML 示例
├── demo-vue/ # Vue 示例
├── docs/ # 文档目录
│ └── components/ # 组件详细文档(每个组件一份)
├── .recycle/ # 回收文件夹(按日期 YYYY-MM-DD 组织,存放被删除的文件)
├── vite.config.ts # Vite 构建配置
├── tsconfig.json # TypeScript 配置
└── package.json # 项目元数据
1.4 构建和运行
构建 SDK
npm run build
# 生成 dist/ 目录,包含:
# - bim-engine-sdk.es.js (ESM 格式)
# - bim-engine-sdk.umd.js (UMD 格式)
# - index.d.ts (TypeScript 类型定义)
# - *.map (Source Map 文件)
运行 Demo
运行 HTML Demo (纯 JS):
npm run dev:demo
# 开发服务器运行在 http://localhost:3000
# 自动打开 /demo/index.html
运行 Vue Demo:
npm run dev:demo-vue
# 开发服务器运行在 http://localhost:3000
# 自动打开 Vue 示例页面
同时运行两个 Demo:
npm run dev:all
# 同时启动 HTML Demo 和 Vue Demo
注意:
- 运行 Demo 前,建议先执行
npm run build构建 SDK,确保 Demo 使用的是最新构建的 SDK - Demo 会自动从
dist/目录或通过开发服务器加载 SDK
1.5 发布配置
package.json 配置了 exports 字段,确保不同环境自动加载正确的文件:
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/bim-engine-sdk.es.js",
"require": "./dist/bim-engine-sdk.umd.js"
}
}
2. 架构设计思想
2.1 核心架构模式
Manager 模式
项目采用 Manager 模式 来管理不同类型的组件:
- BimEngine: 总控制器,组合各个 Manager
- DialogManager: 管理所有弹窗实例
- ToolbarManager: 管理底部工具栏
- ButtonGroupManager: 管理通用按钮组
- EngineManager: 管理 3D 引擎(延迟初始化)
事件总线模式
项目采用 混合通信模式,根据场景选择最合适的方式:
- BimEngine 继承
EventEmitter,作为全局事件总线 - BimComponent 基类提供
emit()和on()辅助方法 - 所有 Manager 继承
BimComponent,可方便地发送和监听事件 - 事件类型通过
EngineEvents接口进行类型约束
使用场景选择原则:
-
简单场景 → 直接调用方法
- 一对一调用,调用链简单
- 需要立即得到返回值
- 性能要求高的场景
- 示例:
// 直接调用 Manager 方法 engine.dialog.create({ title: '测试' }); engine.toolbar.addButton({ id: 'btn1', ... });
-
复杂场景 → 使用事件总线(发布订阅)
- 需要多个组件监听同一事件(一对多)
- 调用链复杂,需要跨多层传递
- 需要解耦,降低组件间依赖
- 异步通知场景
- 示例:
// 发送事件,多个组件可以监听 this.emit('ui:open-dialog', { id: 'info' }); // 多个组件可以同时监听 this.on('engine:model-loaded', (payload) => { // 处理模型加载完成 });
依赖注入模式
EngineManager 采用依赖注入模式,不直接依赖具体的 3D 引擎实现:
Engine组件通过参数接收createEngine函数- 可以在运行时决定使用哪个引擎实现
- 便于测试和切换不同的 3D 引擎
2.2 组件设计原则
IBimComponent 接口
所有 UI 组件必须实现 IBimComponent 接口:
interface IBimComponent {
init(): void | Promise<void>; // 初始化组件
setTheme(theme: ThemeConfig): void; // 设置主题
setLocales(): void; // 设置语言
destroy(): void; // 销毁组件
}
BimComponent 基类
所有 Manager 类继承 BimComponent 基类:
- 自动注入
BimEngine实例 - 提供
emit()和on()方法访问事件总线 - 必须实现
destroy()方法
组件生命周期
- 构造阶段: 创建实例,设置配置
- 初始化阶段: 调用
init()创建 DOM、绑定事件 - 运行阶段: 响应主题/语言变更,处理用户交互
- 销毁阶段: 调用
destroy()清理资源
2.3 代码组织规范
目录结构约定
core/: 核心基础类(EventEmitter、BimComponent)components/: UI 组件(Dialog、ButtonGroup、Engine)managers/: 管理器类(统一管理组件实例)services/: 服务类(单例模式,如 ThemeManager、LocaleManager)themes/: 主题配置(预设主题和类型定义)locales/: 国际化配置(多语言翻译)types/: 类型定义(接口、类型别名)
命名规范
- 类名: 大驼峰,如
BimDialog、DialogManager - 接口名: 大驼峰,以
I开头表示接口,如IBimComponent - 类型名: 大驼峰,如
ThemeConfig、DialogOptions - 文件名: 小写短横线或直接小写,如
event-emitter.ts、index.ts - CSS 类名: 小写短横线,以
bim-开头,如bim-dialog、bim-btn-group
导入顺序
- 第三方库
- 项目内部类型定义
- 项目内部组件/类
- 项目内部服务/工具
- CSS 文件(使用
import './index.css')
2.4 重要设计决策
延迟初始化
- 3D 引擎: 采用延迟初始化,用户需主动调用
initEngine()方法 - 原因: 3D 引擎资源消耗大,延迟初始化可以优化性能
单例服务
- ThemeManager: 全局单例,所有组件共享同一主题状态
- LocaleManager: 全局单例,所有组件共享同一语言状态
- 原因: 主题和语言是全局状态,单例模式确保一致性
CSS 变量
- 组件使用 CSS 变量来应用主题颜色
- 主题变更时,只需更新 CSS 变量值
- 优势: 性能好,无需重新渲染 DOM
事件拦截
- Dialog 组件会拦截所有鼠标/触摸事件,防止传递给 3D 引擎
- 使用
stopPropagation()阻止事件冒泡 - 原因: 确保弹窗交互不影响 3D 场景操作
3. 文件结构说明
3.1 核心文件 (src/core/)
event-emitter.ts
- 作用: 事件总线基础类
- 功能:
on(event, listener): 订阅事件off(event, listener): 取消订阅emit(event, payload): 发送事件clear(): 清空所有事件监听
- 使用:
BimEngine继承此类,作为全局事件总线
component.ts
- 作用: 组件基类
- 功能:
- 注入
BimEngine实例 - 提供
emit()和on()辅助方法 - 定义抽象
destroy()方法
- 注入
- 使用: 所有 Manager 类继承此类
3.2 主引擎文件
bim-engine.ts
- 作用: SDK 主入口类
- 功能:
- 初始化所有 Manager
- 管理主题和语言
- 提供事件总线功能
- 暴露公共 API
- 依赖: EventEmitter、所有 Manager、Service
index.ts
- 作用: SDK 入口文件
- 功能: 导出所有公共 API
- 导出内容:
BimEngine主类BimButtonGroup、Toolbar组件- 类型定义 (
EngineOptions、ModelLoadOptions等) createEngine函数(从第三方 SDK 重新导出)
3.3 组件目录 (src/components/)
dialog/
index.ts:BimDialog类 - 通用弹窗组件- 支持拖拽、缩放
- 支持自定义内容和样式
- 实现
IBimComponent接口
index.type.ts: 弹窗类型定义 (DialogOptions、DialogPosition等)index.css: 弹窗样式bimInfoDialog/index.ts:BimInfoDialog类 - 信息弹窗(继承BimDialog)
button-group/
index.ts:BimButtonGroup类 - 通用按钮组组件- 支持按钮、菜单类型
- 支持分组、嵌套菜单
- 支持主题和国际化
index.type.ts: 按钮组类型定义 (ButtonConfig、ButtonGroupOptions等)index.css: 按钮组样式toolbar/index.ts:Toolbar类 - 底部工具栏(继承BimButtonGroup)toolbar/buttons/: 工具栏按钮配置home/: 首页按钮info/: 信息按钮location/: 定位按钮setting/: 设置按钮walk/: 漫游相关按钮(菜单、人物、鸟瞰)
engine/
index.ts:Engine类 - 3D 引擎组件- 封装第三方 3D 引擎 SDK
- 实现延迟初始化
- 管理引擎生命周期
types.ts: 引擎类型定义 (EngineOptions、ModelLoadOptions)
3.4 管理器目录 (src/managers/)
dialog-manager.ts
- 作用: 弹窗管理器
- 功能:
- 创建和管理弹窗实例
- 监听
ui:open-dialog事件 - 统一应用主题
- 继承:
BimComponent
toolbar-manager.ts
- 作用: 底部工具栏管理器
- 功能:
- 创建和管理工具栏实例
- 提供工具栏操作 API(添加按钮、设置可见性等)
- 继承:
BimComponent
button-group-manager.ts
- 作用: 通用按钮组管理器
- 功能:
- 创建和管理通用按钮组实例
- 支持多个按钮组并存
- 继承:
BimComponent
engine-manager.ts
- 作用: 3D 引擎管理器
- 功能:
- 管理 3D 引擎生命周期
- 提供引擎操作 API(初始化、加载模型等)
- 延迟初始化模式
- 继承:
BimComponent
3.5 服务目录 (src/services/)
theme.ts
- 作用: 主题管理器(单例)
- 功能:
- 管理当前主题配置
- 提供主题切换 API
- 支持主题变更订阅
- 导出:
themeManager单例实例
locale.ts
- 作用: 语言管理器(单例)
- 功能:
- 管理当前语言配置
- 提供语言切换 API
- 提供翻译函数
t(key) - 支持语言变更订阅
- 导出:
localeManager单例实例、t()翻译函数
3.6 主题目录 (src/themes/)
types.ts
- 作用: 主题类型定义
- 内容:
ThemeConfig接口、ThemeType类型
presets.ts
- 作用: 预设主题配置
- 内容:
darkTheme、lightTheme预设配置对象
3.7 国际化目录 (src/locales/)
types.ts
- 作用: 国际化类型定义
- 内容:
LocaleType: 支持的语言类型('zh-CN' | 'en-US')TranslationDictionary: 翻译字典接口,定义所有翻译键的结构
zh-CN.ts
- 作用: 中文翻译字典
- 内容: 所有中文翻译文本,结构必须与
TranslationDictionary接口一致
en-US.ts
- 作用: 英文翻译字典
- 内容: 所有英文翻译文本,结构必须与
TranslationDictionary接口一致
3.8 类型定义目录 (src/types/)
component.ts
- 作用: 组件接口定义
- 内容:
IBimComponent接口
events.ts
- 作用: 事件类型定义
- 内容:
EngineEvents接口,定义所有事件类型和 payload 结构
3.9 样式文件
bim-engine.css
- 作用: 全局样式
- 位置:
src/bim-engine.css - 内容: 基础样式、CSS 变量定义
3.10 组件详细文档目录 (docs/components/)
文档说明
- 作用: 存放每个组件的详细文档
- 用途: 供 AI 根据文档重现组件,包含完整的实现细节
- 维护规则: 组件有更改时,必须同步更新对应的组件文档
文档列表
dialog.md- Dialog 组件详细文档button-group.md- ButtonGroup 组件详细文档engine.md- Engine 组件详细文档
文档内容结构
每个组件文档包含以下部分:
- 组件概述(基本信息、在 SDK 中的位置)
- 组件类 API 文档(所有公共方法、参数、返回值)
- 分化组件说明(如 Toolbar 继承自 ButtonGroup)
- Manager API 文档(Manager 的所有公共方法)
- UI 详细描述(DOM 结构、CSS 类名、交互行为)
- 逻辑流程详细描述(初始化、生命周期、事件处理)
- 国际化支持(使用的翻译键、语言变更处理)
- 主题支持(使用的主题变量、主题变更处理)
- 使用示例(基本使用、高级使用)
- 实现细节(关键算法、性能优化、注意事项)
- 类型定义
- 文件清单
重要: 组件文档必须非常详细,详细到其他 AI 可以直接根据文档重现组件。
4. 组件和事件清单
4.0 组件与 Manager 的关系(重要)
核心原则
组件是独立的实现,但使用组件必须通过 Manager,不允许直接使用组件类。
设计理念
- 组件 (
components/): 独立的 UI 组件实现,负责具体的功能逻辑 - Manager (
managers/): 组件管理器,负责创建、管理和协调组件实例 - BimEngine: 总控制器,通过 Manager 统一管理所有组件
为什么必须通过 Manager?
- 统一管理: Manager 负责组件的生命周期管理,确保资源正确释放
- 主题和语言: Manager 统一应用主题和国际化,保证一致性
- 事件总线: Manager 可以监听和发送事件,实现组件间解耦通信
- 简单场景:直接调用 Manager 方法
- 复杂场景:通过事件总线进行发布订阅
- 容器管理: Manager 管理组件的挂载容器,避免冲突
- API 封装: Manager 提供统一的公共 API,隐藏组件实现细节
使用示例
❌ 错误方式 - 直接使用组件:
// 错误:直接创建和使用组件
import { BimDialog } from 'bim-engine-sdk';
const dialog = new BimDialog({
container: document.getElementById('container'),
title: '测试弹窗',
content: '这是内容'
});
dialog.init();
// 问题:没有通过 Manager 管理,无法统一应用主题、语言等
✅ 正确方式 - 通过 Manager 使用:
// 正确:通过 BimEngine 的 Manager 使用组件
import { BimEngine } from 'bim-engine-sdk';
const engine = new BimEngine('container', {
locale: 'zh-CN',
theme: 'dark'
});
// 通过 DialogManager 创建弹窗
const dialog = engine.dialog.create({
title: '测试弹窗',
content: '这是内容'
});
// 优势:
// 1. 自动应用当前主题
// 2. 自动应用当前语言
// 3. 统一管理弹窗实例
// 4. 可以监听事件总线
✅ 另一个正确示例 - 工具栏按钮:
// 正确:通过 ToolbarManager 操作工具栏
import { BimEngine } from 'bim-engine-sdk';
const engine = new BimEngine('container');
// 通过 ToolbarManager 添加按钮
engine.toolbar.addButton({
id: 'my-button',
groupId: 'group-1',
type: 'button',
label: 'toolbar.myButton',
icon: '<svg>...</svg>',
onClick: (button) => {
console.log('按钮被点击');
}
});
// 通过 ToolbarManager 控制可见性
engine.toolbar.setButtonVisibility('my-button', false);
组件导出说明
虽然 src/index.ts 中导出了 BimButtonGroup 和 Toolbar 组件,但这是为了:
- 高级用户需要完全自定义的场景
- 内部 Manager 的实现需要
- 不推荐 外部用户直接使用,应该通过 Manager
4.1 Manager 类清单
| 类名 | 文件路径 | 功能 | 继承关系 |
|---|---|---|---|
DialogManager |
src/managers/dialog-manager.ts |
管理弹窗实例 | BimComponent |
ToolbarManager |
src/managers/toolbar-manager.ts |
管理底部工具栏 | BimComponent |
ButtonGroupManager |
src/managers/button-group-manager.ts |
管理通用按钮组 | BimComponent |
EngineManager |
src/managers/engine-manager.ts |
管理 3D 引擎 | BimComponent |
4.2 组件类清单
| 类名 | 文件路径 | 功能 | 实现接口 |
|---|---|---|---|
BimDialog |
src/components/dialog/index.ts |
通用弹窗组件 | IBimComponent |
BimInfoDialog |
src/components/dialog/bimInfoDialog/index.ts |
信息弹窗组件 | 继承 BimDialog |
BimButtonGroup |
src/components/button-group/index.ts |
通用按钮组组件 | IBimComponent |
Toolbar |
src/components/button-group/toolbar/index.ts |
底部工具栏组件 | 继承 BimButtonGroup |
Engine |
src/components/engine/index.ts |
3D 引擎组件 | IBimComponent |
4.3 服务类清单
| 类名 | 文件路径 | 功能 | 模式 |
|---|---|---|---|
ThemeManager |
src/services/theme.ts |
主题管理 | 单例 |
LocaleManager |
src/services/locale.ts |
语言管理 | 单例 |
4.4 事件总线定义
事件类型 (EngineEvents)
定义在 src/types/events.ts:
interface EngineEvents {
// UI 事件
'ui:open-dialog': { id: string; data?: any };
'ui:close-dialog': { id: string };
// 引擎事件
'engine:model-loaded': { url: string };
'engine:object-clicked': { objectId: string; position: { x: number, y: number, z: number } };
// 系统事件
'sys:theme-changed': { theme: string };
'sys:locale-changed': { locale: string };
}
事件命名规范
- UI 事件:
ui:前缀,如ui:open-dialog - 引擎事件:
engine:前缀,如engine:model-loaded - 系统事件:
sys:前缀,如sys:theme-changed
事件使用方式
发送事件:
// 在 BimComponent 子类中
this.emit('ui:open-dialog', { id: 'info' });
监听事件:
// 在 BimComponent 子类中
// 返回取消订阅函数
const unsubscribe = this.on('ui:open-dialog', (payload) => {
console.log('收到事件:', payload);
});
// 组件销毁时取消订阅
unsubscribe();
使用场景判断
✅ 使用直接调用方法的场景:
- 简单的 API 调用,如
engine.dialog.create() - 需要立即得到结果的操作
- 单一调用者,不需要多个监听者
- 性能敏感的操作
✅ 使用事件总线的场景:
- 需要多个组件同时响应的事件(如模型加载完成)
- 跨多层组件传递信息
- 解耦需求,降低组件间直接依赖
- 异步通知场景
- 需要动态添加/移除监听者的场景
示例对比:
// 场景 1: 简单操作 - 使用直接调用
// 用户代码直接调用,简单直接
engine.dialog.create({
title: t('dialog.title'),
content: 'Hello'
});
// 场景 2: 复杂场景 - 使用事件总线
// 按钮点击后,需要通知多个组件(弹窗、日志、统计等)
this.emit('ui:open-dialog', { id: 'info', data: { source: 'toolbar' } });
// 多个组件可以同时监听
// DialogManager 监听并打开弹窗
this.on('ui:open-dialog', (payload) => {
if (payload.id === 'info') {
this.showInfoDialog();
}
});
// AnalyticsManager 监听并记录统计
this.on('ui:open-dialog', (payload) => {
this.trackEvent('dialog_opened', payload);
});
4.5 核心基类
| 类名 | 文件路径 | 功能 | 继承/实现 |
|---|---|---|---|
EventEmitter |
src/core/event-emitter.ts |
事件总线基础类 | - |
BimComponent |
src/core/component.ts |
组件基类 | 抽象类 |
BimEngine |
src/bim-engine.ts |
主引擎类 | 继承 EventEmitter |
4.6 国际化实现指南
4.6.1 国际化的重要性
所有用户可见的文本都必须支持国际化,这是强制要求。
- 项目支持多语言(目前:中文、英文)
- 所有 UI 文本必须通过翻译函数获取
- 严禁在代码中硬编码任何语言的文本
4.6.2 国际化实现方式
步骤 1: 在翻译字典中添加键值
在 src/locales/types.ts 中定义类型:
export interface TranslationDictionary {
// ... 现有键
myComponent: {
title: string;
description: string;
buttonText: string;
};
}
在 src/locales/zh-CN.ts 中添加中文翻译:
export const zhCN: TranslationDictionary = {
// ... 现有内容
myComponent: {
title: '我的组件',
description: '这是组件描述',
buttonText: '点击按钮',
},
};
在 src/locales/en-US.ts 中添加英文翻译:
export const enUS: TranslationDictionary = {
// ... 现有内容
myComponent: {
title: 'My Component',
description: 'This is component description',
buttonText: 'Click Button',
},
};
步骤 2: 在组件中使用翻译函数
导入翻译函数:
import { t, localeManager } from '../../services/locale';
使用翻译函数:
// 在组件中使用
const title = t('myComponent.title'); // 获取翻译文本
// 在 DOM 中使用
const titleEl = document.createElement('div');
titleEl.textContent = t('myComponent.title');
// 在 HTML 字符串中使用
const html = `<div>${t('myComponent.description')}</div>`;
步骤 3: 监听语言变更
在组件中订阅语言变更:
export class MyComponent implements IBimComponent {
private unsubscribeLocale: (() => void) | null = null;
public init() {
// 初始化时订阅语言变更
this.unsubscribeLocale = localeManager.subscribe(() => {
this.setLocales(); // 更新所有文本
});
// 初始设置
this.setLocales();
}
public setLocales(): void {
// 更新所有用户可见的文本
const titleEl = this.element.querySelector('.title');
if (titleEl) {
titleEl.textContent = t('myComponent.title');
}
}
public destroy() {
// 清理订阅
if (this.unsubscribeLocale) {
this.unsubscribeLocale();
this.unsubscribeLocale = null;
}
}
}
4.6.3 国际化注意事项
✅ 必须做的
-
所有用户可见文本使用
t(key)// ✅ 正确 button.textContent = t('toolbar.home'); // ❌ 错误 button.textContent = '首页'; -
翻译键使用有意义的路径
// ✅ 正确:按功能模块组织 t('toolbar.home') t('dialog.title') t('button.save') // ❌ 错误:键名不清晰 t('text1') t('label') -
在所有语言文件中添加翻译
- 添加新键时,必须同时更新
zh-CN.ts和en-US.ts - 确保所有语言文件的结构一致
- 添加新键时,必须同时更新
-
实现
setLocales()方法- 所有组件必须实现
setLocales()方法 - 在方法中更新所有用户可见的文本
- 所有组件必须实现
-
订阅语言变更
- 组件初始化时订阅
localeManager.subscribe() - 组件销毁时取消订阅
- 组件初始化时订阅
❌ 禁止做的
-
禁止硬编码文本
// ❌ 错误:硬编码中文 const title = '首页'; // ❌ 错误:硬编码英文 const title = 'Home'; // ✅ 正确:使用翻译函数 const title = t('toolbar.home'); -
禁止在翻译键中使用变量
// ❌ 错误:动态拼接键名 t(`toolbar.${buttonId}`); // ✅ 正确:使用完整的键名 t('toolbar.home'); -
禁止在翻译文本中拼接变量
// ❌ 错误:在翻译文本中拼接 const text = t('dialog.message') + userName; // ✅ 正确:使用占位符(需要在翻译字典中支持) // 注意:当前实现不支持占位符,需要扩展 LocaleManager -
禁止忽略语言变更
- 组件必须响应语言切换
- 不能只在初始化时设置文本
4.6.4 国际化最佳实践
翻译键的组织结构
// 按功能模块组织
TranslationDictionary {
toolbar: { // 工具栏相关
home: string;
info: string;
},
dialog: { // 弹窗相关
title: string;
content: string;
},
button: { // 按钮相关
save: string;
cancel: string;
},
message: { // 消息相关
success: string;
error: string;
}
}
组件中的国际化实现示例
import { t, localeManager } from '../../services/locale';
import { IBimComponent } from '../../types/component';
export class MyDialog implements IBimComponent {
private element: HTMLElement;
private unsubscribeLocale: (() => void) | null = null;
constructor(container: HTMLElement) {
this.element = this.createDOM();
container.appendChild(this.element);
}
public init() {
// 订阅语言变更
this.unsubscribeLocale = localeManager.subscribe(() => {
this.setLocales();
});
// 初始设置
this.setLocales();
}
public setLocales(): void {
// 更新标题
const titleEl = this.element.querySelector('.dialog-title');
if (titleEl) {
titleEl.textContent = t('dialog.myDialog.title');
}
// 更新内容
const contentEl = this.element.querySelector('.dialog-content');
if (contentEl) {
contentEl.textContent = t('dialog.myDialog.content');
}
// 更新按钮
const buttonEl = this.element.querySelector('.dialog-button');
if (buttonEl) {
buttonEl.textContent = t('button.confirm');
}
}
public destroy() {
if (this.unsubscribeLocale) {
this.unsubscribeLocale();
this.unsubscribeLocale = null;
}
this.element.remove();
}
}
添加新语言支持
-
在
src/locales/types.ts中扩展LocaleType:export type LocaleType = 'zh-CN' | 'en-US' | 'ja-JP'; // 添加日语 -
创建新的翻译文件
src/locales/ja-JP.ts:import { TranslationDictionary } from './types'; export const jaJP: TranslationDictionary = { // 添加所有翻译 }; -
在
src/services/locale.ts中注册新语言:import { jaJP } from '../locales/ja-JP'; private messages: Record<LocaleType, TranslationDictionary> = { 'zh-CN': zhCN, 'en-US': enUS, 'ja-JP': jaJP, // 添加新语言 };
4.6.5 国际化检查清单
在开发新功能时,确保:
- 所有用户可见的文本都使用
t(key)函数 - 在
types.ts中定义了新的翻译键类型 - 在
zh-CN.ts中添加了中文翻译 - 在
en-US.ts中添加了英文翻译 - 组件实现了
setLocales()方法 - 组件订阅了语言变更事件
- 组件销毁时取消了语言订阅
- 没有硬编码任何语言的文本
- 翻译键名清晰、有意义
5. AI 工作规则
5.1 AI 编码规范
语言要求(强制)
- 所有输出必须使用中文,包括:
- 代码注释
- 文档说明
- 与用户交流
- 错误信息
- 日志输出
- 思考过程也必须使用中文:
- 分析问题时用中文思考
- 解释代码逻辑时用中文
- 讨论设计方案时用中文
- 代码中的字符串:根据业务需求,但注释和文档必须中文
代码可读性
- 优先保证代码可读性,有时可以不使用高级函数,使用低级函数
- 必须添加详细的中文注释,解释代码逻辑和设计意图
- 函数、类、接口必须有 JSDoc 注释(使用中文)
- 复杂逻辑必须添加行内注释说明
代码风格
- 使用 TypeScript 严格模式
- 遵循项目现有的命名规范(见 2.3 节)
- 保持代码格式一致(使用项目配置的格式化工具)
- 保持缩进和空行的一致性
类型安全
- 充分利用 TypeScript 类型系统
- 避免使用
any,必要时使用unknown - 为所有公共 API 提供类型定义
- 使用类型推断,但复杂类型必须显式声明
组件使用规范
- 严禁直接使用组件类,必须通过 Manager 使用(见 4.0 节)
- 新增组件时,必须创建对应的 Manager
- Manager 必须继承
BimComponent基类 - 组件必须实现
IBimComponent接口 - 组件必须支持国际化(见 4.6 节)
注释规范
/**
* 函数功能描述(中文)
* @param param1 参数说明(中文)
* @param param2 参数说明(中文)
* @returns 返回值说明(中文)
*/
function example(param1: string, param2: number): boolean {
// 行内注释说明关键逻辑(中文)
return true;
}
错误处理规范
- 所有异步操作必须有错误处理
- 使用
try-catch捕获可能的异常 - 错误信息必须使用中文
- 提供有意义的错误信息,帮助调试
性能考虑
- 使用
requestAnimationFrame优化动画性能 - 避免频繁的 DOM 操作,使用批量更新
- 合理使用事件委托,减少事件监听器数量
- 及时清理不需要的监听器和定时器
5.2 问答方式和开发流程规范
用户询问"如何实现"时的处理流程
当用户询问如何实现某个功能时,必须遵循以下流程:
- 分析需求:用中文理解用户需求,明确要实现的功能
- 制定详细开发计划:必须包含以下内容:
- 需要修改的文件列表:
- 列出每个需要修改的文件路径
- 说明每个文件的修改内容(添加什么、修改什么)
- 需要新增的文件列表:
- 列出每个需要新增的文件路径
- 说明每个文件的作用和内容概要
- 需要删除的文件列表:
- 列出每个需要删除的文件路径
- 说明删除的原因
- 注意:删除的文件必须移动到回收文件夹,不能直接删除
- 每个文件的作用说明:
- 详细说明每个文件在整体功能中的作用
- 说明文件之间的依赖关系
- 对整体结构的影响:
- 说明对现有架构的影响
- 说明对现有功能的影响
- 说明是否需要更新文档
- 说明可能的风险点
- 需要修改的文件列表:
- 展示开发计划:以清晰的格式展示给用户
- 等待用户确认:在用户明确同意之前,绝对不能修改任何代码
- 用户同意后执行:只有在用户明确表示同意后,才能开始修改代码
示例格式:
## 开发计划
### 需要修改的文件
1. `src/managers/dialog-manager.ts`
- 修改:添加新的 `showCustomDialog()` 方法
- 作用:提供自定义弹窗的快捷方法
2. `src/types/events.ts`
- 修改:在 `EngineEvents` 接口中添加 `'ui:custom-dialog'` 事件类型
- 作用:定义新的事件类型,支持事件总线通信
### 需要新增的文件
1. `src/components/dialog/customDialog/index.ts`
- 作用:实现自定义弹窗组件
- 内容:包含 CustomDialog 类,实现 IBimComponent 接口
2. `src/components/dialog/customDialog/index.css`
- 作用:自定义弹窗的样式文件
### 需要删除的文件
1. `src/components/dialog/oldDialog.ts`
- 原因:已被新组件替代
- **处理方式**:移动到 `.recycle/YYYY-MM-DD/` 文件夹(使用当前日期)
### 对整体结构的影响
- **架构影响**:新增一个 Dialog 子类型,不影响现有架构
- **功能影响**:不影响现有弹窗功能,只是新增功能
- **文档更新**:需要更新 AI_COLLABORATION.md 中的组件清单
- **风险点**:无重大风险,向后兼容
请确认是否按照此计划进行开发?
用户直接要求修改时的处理
当用户直接要求修改代码时(如"帮我修改XXX"、"实现XXX功能"),可以:
- 直接执行代码修改
- 但仍需遵循其他规范(国际化、组件使用规范等)
- 删除文件时仍需移动到回收文件夹
文件删除规范(强制)
严禁直接删除文件,必须遵循以下流程:
-
创建回收文件夹(如果不存在):
- 在项目根目录创建
.recycle/文件夹 - 此文件夹用于存放被删除的文件
- 在项目根目录创建
-
按时间组织文件夹:
- 在
.recycle/下按日期创建子文件夹,格式:YYYY-MM-DD - 例如:
2024-01-15、2024-12-25 - 同一天删除的文件存放在同一个日期文件夹中
- 在
-
移动文件而非删除:
# 错误方式 ❌ rm src/old-file.ts # 正确方式 ✅ # 获取当前日期(格式:YYYY-MM-DD) DATE=$(date +%Y-%m-%d) mkdir -p .recycle/$DATE mv src/old-file.ts .recycle/$DATE/src/old-file.ts -
保持目录结构:
- 在日期文件夹中保持原文件的目录结构
- 例如:
src/components/old.ts→.recycle/2024-01-15/src/components/old.ts
-
添加删除说明(可选):
- 在对应日期文件夹中创建
README.md记录删除原因 - 格式:
## 2024-01-15 删除的文件 - `src/components/old.ts`: 被新组件替代 - `src/managers/old-manager.ts`: 功能重构,使用新 Manager
- 在对应日期文件夹中创建
-
恢复文件:
- 如果发现误删,可以从对应日期文件夹中恢复
- 恢复后从
.recycle/中移除
回收文件夹结构示例:
.recycle/
├── 2024-01-15/
│ ├── README.md
│ └── src/
│ ├── components/
│ │ └── old-component.ts
│ └── managers/
│ └── old-manager.ts
├── 2024-02-20/
│ ├── README.md
│ └── src/
│ └── types/
│ └── old-types.ts
└── 2024-12-25/
├── README.md
└── demo/
└── old-demo.html
注意事项:
- 日期格式必须使用
YYYY-MM-DD(如2024-01-15) - 如果同一天删除多个文件,都存放在同一个日期文件夹中
- 日期文件夹按时间倒序排列,最新的在最前面
5.3 文档更新规则
必须更新的情况
- 新增文件: 在 "文件结构说明" 部分添加文件描述
- 删除文件: 从文档中移除对应条目(但文件移动到
.recycle/文件夹) - 修改文件功能: 更新对应文件的描述
- 新增组件:
- 在 "组件和事件清单" 部分添加
- 必须创建对应的组件详细文档(在
docs/components/目录下)
- 修改组件:
- 更新 "组件和事件清单" 部分
- 必须同步更新对应的组件详细文档(
docs/components/目录下的对应文档)
- 新增事件: 在 "事件总线定义" 部分添加
- 修改架构: 更新 "架构设计思想" 部分
- 修改构建流程: 更新 "构建和运行" 部分
更新方式
- 修改代码后,立即更新本文档
- 确保文档与代码保持一致
- 如果发现文档过时,优先更新文档
5.4 思考和工作流程规范
思考过程要求
- 所有思考过程必须使用中文
- 分析问题时,用中文描述问题、分析思路、得出结论
- 解释代码时,用中文说明逻辑、设计意图、实现方式
- 讨论方案时,用中文描述优缺点、选择理由
工作流程
- 理解需求:用中文理解用户需求,明确任务目标
- 分析问题:用中文分析问题,梳理相关代码和架构
- 设计方案:用中文设计解决方案,考虑边界情况
- 实现代码:编写代码,添加中文注释
- 测试验证:用中文描述测试结果和发现的问题
- 更新文档:用中文更新相关文档
代码审查要点
- 检查开发流程规范:
- 如果是询问"如何实现",是否先提供了详细的开发计划
- 是否在用户同意后才修改代码
- 删除的文件是否移动到
.recycle/文件夹而非直接删除
- 检查是否遵循组件使用规范(通过 Manager)
- 检查是否支持国际化:
- 所有用户可见文本是否使用
t(key)函数 - 是否在所有语言文件中添加了翻译
- 组件是否实现了
setLocales()方法 - 组件是否订阅了语言变更事件
- 所有用户可见文本是否使用
- 检查注释是否完整且为中文
- 检查错误处理是否完善
- 检查资源是否正确释放
- 检查类型定义是否完整
5.5 其他注意事项
组件开发
- 所有 UI 组件必须实现
IBimComponent接口 - 所有 Manager 必须继承
BimComponent基类 - 组件必须通过 Manager 使用,不允许直接使用组件类
- 组件必须支持国际化(见 4.6 节):
- 所有用户可见文本使用
t(key)函数 - 实现
setLocales()方法 - 订阅语言变更事件
- 严禁硬编码任何语言的文本
- 所有用户可见文本使用
- 组件销毁时必须清理所有资源(事件监听、定时器等)
- 新增组件时,必须同时创建对应的 Manager
主题和国际化
- 组件必须支持主题切换(通过
setTheme()方法) - 组件必须支持国际化(通过
setLocales()方法) - 所有用户可见的文本必须使用
t(key)函数进行翻译 - 严禁硬编码中文或英文文本
- 新增文本时,必须在所有语言文件中添加对应的翻译键
- 详细实现方式请参考 4.6 节《国际化实现指南》
事件总线使用规范
- 根据场景选择通信方式:
- 简单场景:使用直接调用方法(如
engine.dialog.create()) - 复杂场景:使用事件总线(需要多个监听者、跨多层传递、解耦需求)
- 简单场景:使用直接调用方法(如
- 事件命名遵循规范(
ui:、engine:、sys:前缀) - 事件监听必须清理:组件销毁时取消所有事件订阅
- 类型安全:使用
EngineEvents接口确保事件类型正确
性能优化
- 使用
requestAnimationFrame优化动画性能 - 避免频繁的 DOM 操作,使用批量更新
- 合理使用事件委托,减少事件监听器数量
错误处理
- 所有异步操作必须有错误处理
- 使用
try-catch捕获可能的异常 - 提供有意义的错误信息
测试
- 确保代码修改后不影响现有功能
- 在 demo 中测试新功能
- 检查控制台是否有错误或警告
5.6 常见任务指南
添加新组件
- 在
src/components/创建组件目录 - 实现
IBimComponent接口 - 创建对应的类型定义文件
- 创建对应的样式文件
- 必须创建对应的 Manager(在
src/managers/目录) - 实现国际化支持:
- 在
src/locales/types.ts中添加翻译键类型 - 在
src/locales/zh-CN.ts和en-US.ts中添加翻译 - 在组件中实现
setLocales()方法 - 订阅语言变更事件
- 在
- 在
BimEngine中初始化 Manager 并暴露 - 在
src/index.ts导出(如需要,但不推荐直接导出组件) - 创建组件详细文档:
- 在
docs/components/目录下创建对应的组件文档(如dialog.md) - 文档必须包含以下 12 个部分(参考现有组件文档):
- 组件概述
- 组件类 API 文档
- 分化组件说明
- Manager API 文档
- UI 详细描述
- 逻辑流程详细描述
- 国际化支持
- 主题支持
- 使用示例
- 实现细节(供 AI 重现)
- 类型定义
- 文件清单
- 文档必须非常详细,详细到其他 AI 可以直接根据文档重现组件
- 在
- 更新本文档
重要:
- 组件必须通过 Manager 使用,不要直接导出组件类供外部使用
- 组件必须支持国际化,所有用户可见文本使用
t(key)函数 - 组件有更改时,必须同步更新对应的组件详细文档(
docs/components/目录下的对应文档)
添加新 Manager
- 在
src/managers/创建 Manager 文件 - 继承
BimComponent基类 - 在
BimEngine中初始化 - 在
BimEngine中暴露公共属性 - 更新本文档
添加新事件
- 判断是否需要事件总线:
- 如果只需要简单的一对一调用 → 使用直接方法调用
- 如果需要多个监听者或跨多层传递 → 使用事件总线
- 如果使用事件总线,在
src/types/events.ts的EngineEvents接口中添加事件类型 - 在相关组件中发送/监听事件
- 确保组件销毁时取消事件订阅
- 更新本文档
修改主题配置
- 在
src/themes/presets.ts修改预设主题 - 或在
src/themes/types.ts扩展ThemeConfig接口 - 确保所有组件正确应用新主题属性
- 更新本文档(如主题结构有变化)
添加新的翻译文本
- 在
src/locales/types.ts的TranslationDictionary接口中添加新的键 - 在
src/locales/zh-CN.ts中添加中文翻译 - 在
src/locales/en-US.ts中添加英文翻译 - 在相关组件中使用
t(key)函数获取翻译 - 确保组件实现了
setLocales()方法并订阅语言变更 - 更新本文档(如添加新语言支持)
📝 文档维护记录
| 日期 | 修改内容 | 修改人 |
|---|---|---|
| 2024-XX-XX | 初始创建 | AI Assistant |
重要提醒: 本文档是 AI 协作的重要参考,请保持文档与代码同步更新!