= {
00858| 'plan-view': getIcon('地图'),
00859| 'path': getIcon('地图'),
00860| 'walk': getIcon('漫游')
00861| };
00862| return icons[type] || getIcon('default');
00863| }
00864|
00865| private createIconButton(type: string, onClick: () => void): HTMLButtonElement {
00866| const btn = document.createElement('button');
00867| btn.className = `walk-icon-btn walk-icon-btn-${type}`;
00868| btn.innerHTML = this.getIconSVG(type);
00869| btn.addEventListener('click', onClick);
00870| return btn;
00871| }
00872| }
00873| ```
00874|
00875| ---
00876|
00877| **相关文档**:
00878| - 详细的图标清单和使用说明见 `docs/utils/icon-manager.md`
00879| - SVG 简化规范和主题适配详见同一文档
00880|
00881| ### 4.5 事件总线定义
00882|
00883| #### 事件类型 (`EngineEvents`)
00884|
00885| 定义在 `src/types/events.ts`:
00886|
00887| ```typescript
00888| interface EngineEvents {
00889| // UI 事件
00890| 'ui:open-dialog': { id: string; data?: any };
00891| 'ui:close-dialog': { id: string };
00892|
00893| // 引擎事件
00894| 'engine:model-loaded': { url: string };
00895| 'engine:object-clicked': { objectId: string; position: { x: number, y: number, z: number } };
00896|
00897| // 树组件事件
00898| 'ui:tree-node-check': { id: string; checked: boolean; node: any };
00899| 'ui:tree-node-select': { id: string; selected: boolean; node: any };
00900| 'ui:tree-node-expand': { id: string; expanded: boolean };
00901|
00902| // 系统事件
00903| 'sys:theme-changed': { theme: string };
00904| 'sys:locale-changed': { locale: string };
00905| }
00906| ```
00907|
00908| #### 事件命名规范
00909| - **UI 事件**: `ui:` 前缀,如 `ui:open-dialog`
00910| - **引擎事件**: `engine:` 前缀,如 `engine:model-loaded`
00911| - **系统事件**: `sys:` 前缀,如 `sys:theme-changed`
00912|
00913| #### 事件使用方式
00914|
00915| **发送事件**:
00916| ```typescript
00917| // 在 BimComponent 子类中
00918| this.emit('ui:open-dialog', { id: 'info' });
00919| ```
00920|
00921| **监听事件**:
00922| ```typescript
00923| // 在 BimComponent 子类中
00924| // 返回取消订阅函数
00925| const unsubscribe = this.on('ui:open-dialog', (payload) => {
00926| console.log('收到事件:', payload);
00927| });
00928|
00929| // 组件销毁时取消订阅
00930| unsubscribe();
00931| ```
00932|
00933| #### 使用场景判断
00934|
00935| **✅ 使用直接调用方法的场景**:
00936| - 简单的 API 调用,如 `engine.dialog.create()`
00937| - 需要立即得到结果的操作
00938| - 单一调用者,不需要多个监听者
00939| - 性能敏感的操作
00940|
00941| **✅ 使用事件总线的场景**:
00942| - 需要多个组件同时响应的事件(如模型加载完成)
00943| - 跨多层组件传递信息
00944| - 解耦需求,降低组件间直接依赖
00945| - 异步通知场景
00946| - 需要动态添加/移除监听者的场景
00947|
00948| **示例对比**:
00949|
00950| ```typescript
00951| // 场景 1: 简单操作 - 使用直接调用
00952| // 用户代码直接调用,简单直接
00953| engine.dialog.create({
00954| title: t('dialog.title'),
00955| content: 'Hello'
00956| });
00957|
00958| // 场景 2: 复杂场景 - 使用事件总线
00959| // 按钮点击后,需要通知多个组件(弹窗、日志、统计等)
00960| this.emit('ui:open-dialog', { id: 'info', data: { source: 'toolbar' } });
00961|
00962| // 多个组件可以同时监听
00963| // DialogManager 监听并打开弹窗
00964| this.on('ui:open-dialog', (payload) => {
00965| if (payload.id === 'info') {
00966| this.showInfoDialog();
00967| }
00968| });
00969|
00970| // AnalyticsManager 监听并记录统计
00971| this.on('ui:open-dialog', (payload) => {
00972| this.trackEvent('dialog_opened', payload);
00973| });
00974| ```
00975|
00976| ### 4.6 核心基类
00977|
00978| | 类名 | 文件路径 | 功能 | 继承/实现 |
00979| | -------------- | --------------------------- | -------------- | ------------------- |
00980| | `EventEmitter` | `src/core/event-emitter.ts` | 事件总线基础类 | - |
00981| | `BimComponent` | `src/core/component.ts` | 组件基类 | 抽象类 |
00982| | `BimEngine` | `src/bim-engine.ts` | 主引擎类 | 继承 `EventEmitter` |
00983|
00984| ---
00985|
00986| ## 4.7 国际化实现指南
00987|
00988| ### 4.7.1 国际化的重要性
00989|
00990| **所有用户可见的文本都必须支持国际化,这是强制要求。**
00991|
00992| - 项目支持多语言(目前:中文、英文)
00993| - 所有 UI 文本必须通过翻译函数获取
00994| - 严禁在代码中硬编码任何语言的文本
00995|
00996| ### 4.7.2 国际化实现方式
00997|
00998| #### 步骤 1: 在翻译字典中添加键值
00999|
01000| **在 `src/locales/types.ts` 中定义类型:**
01001| ```typescript
01002| export interface TranslationDictionary {
01003| // ... 现有键
01004| myComponent: {
01005| title: string;
01006| description: string;
01007| buttonText: string;
01008| };
01009| }
01010| ```
01011|
01012| **在 `src/locales/zh-CN.ts` 中添加中文翻译:**
01013| ```typescript
01014| export const zhCN: TranslationDictionary = {
01015| // ... 现有内容
01016| myComponent: {
01017| title: '我的组件',
01018| description: '这是组件描述',
01019| buttonText: '点击按钮',
01020| },
01021| };
01022| ```
01023|
01024| **在 `src/locales/en-US.ts` 中添加英文翻译:**
01025| ```typescript
01026| export const enUS: TranslationDictionary = {
01027| // ... 现有内容
01028| myComponent: {
01029| title: 'My Component',
01030| description: 'This is component description',
01031| buttonText: 'Click Button',
01032| },
01033| };
01034| ```
01035|
01036| #### 步骤 2: 在组件中使用翻译函数
01037|
01038| **导入翻译函数:**
01039| ```typescript
01040| import { t, localeManager } from '../../services/locale';
01041| ```
01042|
01043| **使用翻译函数:**
01044| ```typescript
01045| // 在组件中使用
01046| const title = t('myComponent.title'); // 获取翻译文本
01047|
01048| // 在 DOM 中使用
01049| const titleEl = document.createElement('div');
01050| titleEl.textContent = t('myComponent.title');
01051|
01052| // 在 HTML 字符串中使用
01053| const html = `${t('myComponent.description')}
`;
01054| ```
01055|
01056| #### 步骤 3: 监听语言变更
01057|
01058| **在组件中订阅语言变更:**
01059| ```typescript
01060| export class MyComponent implements IBimComponent {
01061| private unsubscribeLocale: (() => void) | null = null;
01062|
01063| public init() {
01064| // 初始化时订阅语言变更
01065| this.unsubscribeLocale = localeManager.subscribe(() => {
01066| this.setLocales(); // 更新所有文本
01067| });
01068|
01069| // 初始设置
01070| this.setLocales();
01071| }
01072|
01073| public setLocales(): void {
01074| // 更新所有用户可见的文本
01075| const titleEl = this.element.querySelector('.title');
01076| if (titleEl) {
01077| titleEl.textContent = t('myComponent.title');
01078| }
01079| }
01080|
01081| public destroy() {
01082| // 清理订阅
01083| if (this.unsubscribeLocale) {
01084| this.unsubscribeLocale();
01085| this.unsubscribeLocale = null;
01086| }
01087| }
01088| }
01089| ```
01090|
01091| ### 4.7.3 国际化注意事项
01092|
01093| #### ✅ 必须做的
01094|
01095| 1. **所有用户可见文本使用 `t(key)`**
01096| ```typescript
01097| // ✅ 正确
01098| button.textContent = t('toolbar.home');
01099|
01100| // ❌ 错误
01101| button.textContent = '首页';
01102| ```
01103|
01104| 2. **翻译键使用有意义的路径**
01105| ```typescript
01106| // ✅ 正确:按功能模块组织
01107| t('toolbar.home')
01108| t('dialog.title')
01109| t('button.save')
01110|
01111| // ❌ 错误:键名不清晰
01112| t('text1')
01113| t('label')
01114| ```
01115|
01116| 3. **在所有语言文件中添加翻译**
01117| - 添加新键时,必须同时更新 `zh-CN.ts` 和 `en-US.ts`
01118| - 确保所有语言文件的结构一致
01119|
01120| 4. **实现 `setLocales()` 方法**
01121| - 所有组件必须实现 `setLocales()` 方法
01122| - 在方法中更新所有用户可见的文本
01123|
01124| 5. **订阅语言变更**
01125| - 组件初始化时订阅 `localeManager.subscribe()`
01126| - 组件销毁时取消订阅
01127|
01128| #### ❌ 禁止做的
01129|
01130| 1. **禁止硬编码文本**
01131| ```typescript
01132| // ❌ 错误:硬编码中文
01133| const title = '首页';
01134|
01135| // ❌ 错误:硬编码英文
01136| const title = 'Home';
01137|
01138| // ✅ 正确:使用翻译函数
01139| const title = t('toolbar.home');
01140| ```
01141|
01142| 2. **禁止在翻译键中使用变量**
01143| ```typescript
01144| // ❌ 错误:动态拼接键名
01145| t(`toolbar.${buttonId}`);
01146|
01147| // ✅ 正确:使用完整的键名
01148| t('toolbar.home');
01149| ```
01150|
01151| 3. **禁止在翻译文本中拼接变量**
01152| ```typescript
01153| // ❌ 错误:在翻译文本中拼接
01154| const text = t('dialog.message') + userName;
01155|
01156| // ✅ 正确:使用占位符(需要在翻译字典中支持)
01157| // 注意:当前实现不支持占位符,需要扩展 LocaleManager
01158| ```
01159|
01160| 4. **禁止忽略语言变更**
01161| - 组件必须响应语言切换
01162| - 不能只在初始化时设置文本
01163|
01164| ### 4.7.4 国际化最佳实践
01165|
01166| #### 翻译键的组织结构
01167|
01168| ```typescript
01169| // 按功能模块组织
01170| TranslationDictionary {
01171| toolbar: { // 工具栏相关
01172| home: string;
01173| info: string;
01174| },
01175| dialog: { // 弹窗相关
01176| title: string;
01177| content: string;
01178| },
01179| button: { // 按钮相关
01180| save: string;
01181| cancel: string;
01182| },
01183| message: { // 消息相关
01184| success: string;
01185| error: string;
01186| }
01187| }
01188| ```
01189|
01190| #### 组件中的国际化实现示例
01191|
01192| ```typescript
01193| import { t, localeManager } from '../../services/locale';
01194| import { IBimComponent } from '../../types/component';
01195|
01196| export class MyDialog implements IBimComponent {
01197| private element: HTMLElement;
01198| private unsubscribeLocale: (() => void) | null = null;
01199|
01200| constructor(container: HTMLElement) {
01201| this.element = this.createDOM();
01202| container.appendChild(this.element);
01203| }
01204|
01205| public init() {
01206| // 订阅语言变更
01207| this.unsubscribeLocale = localeManager.subscribe(() => {
01208| this.setLocales();
01209| });
01210|
01211| // 初始设置
01212| this.setLocales();
01213| }
01214|
01215| public setLocales(): void {
01216| // 更新标题
01217| const titleEl = this.element.querySelector('.dialog-title');
01218| if (titleEl) {
01219| titleEl.textContent = t('dialog.myDialog.title');
01220| }
01221|
01222| // 更新内容
01223| const contentEl = this.element.querySelector('.dialog-content');
01224| if (contentEl) {
01225| contentEl.textContent = t('dialog.myDialog.content');
01226| }
01227|
01228| // 更新按钮
01229| const buttonEl = this.element.querySelector('.dialog-button');
01230| if (buttonEl) {
01231| buttonEl.textContent = t('button.confirm');
01232| }
01233| }
01234|
01235| public destroy() {
01236| if (this.unsubscribeLocale) {
01237| this.unsubscribeLocale();
01238| this.unsubscribeLocale = null;
01239| }
01240| this.element.remove();
01241| }
01242| }
01243| ```
01244|
01245| #### 添加新语言支持
01246|
01247| 1. 在 `src/locales/types.ts` 中扩展 `LocaleType`:
01248| ```typescript
01249| export type LocaleType = 'zh-CN' | 'en-US' | 'ja-JP'; // 添加日语
01250| ```
01251|
01252| 2. 创建新的翻译文件 `src/locales/ja-JP.ts`:
01253| ```typescript
01254| import { TranslationDictionary } from './types';
01255|
01256| export const jaJP: TranslationDictionary = {
01257| // 添加所有翻译
01258| };
01259| ```
01260|
01261| 3. 在 `src/services/locale.ts` 中注册新语言:
01262| ```typescript
01263| import { jaJP } from '../locales/ja-JP';
01264|
01265| private messages: Record = {
01266| 'zh-CN': zhCN,
01267| 'en-US': enUS,
01268| 'ja-JP': jaJP, // 添加新语言
01269| };
01270| ```
01271|
01272| ### 4.7.5 国际化检查清单
01273|
01274| 在开发新功能时,确保:
01275|
01276| - [ ] 所有用户可见的文本都使用 `t(key)` 函数
01277| - [ ] 在 `types.ts` 中定义了新的翻译键类型
01278| - [ ] 在 `zh-CN.ts` 中添加了中文翻译
01279| - [ ] 在 `en-US.ts` 中添加了英文翻译
01280| - [ ] 组件实现了 `setLocales()` 方法
01281| - [ ] 组件订阅了语言变更事件
01282| - [ ] 组件销毁时取消了语言订阅
01283| - [ ] 没有硬编码任何语言的文本
01284| - [ ] 翻译键名清晰、有意义
01285|
01286| ---
01287|
01288| ## 5. AI 工作规则
01289|
01290| ### 5.1 AI 编码规范
01291|
01292| #### 语言要求(强制)
01293| - **所有输出必须使用中文**,包括:
01294| - 代码注释 (**强制:所有代码注释必须使用中文,解释清晰详细**)
01295| - 文档说明
01296| - 与用户交流
01297| - 错误信息
01298| - 日志输出
01299| - **思考过程也必须使用中文**:
01300| - 分析问题时用中文思考
01301| - 解释代码逻辑时用中文
01302| - 讨论设计方案时用中文
01303| - **代码中的字符串**:根据业务需求,但注释和文档必须中文
01304|
01305| #### 代码可读性
01306| - **优先保证代码可读性**,有时可以不使用高级函数,使用低级函数
01307| - **必须添加详细的中文注释**,解释代码逻辑和设计意图
01308| - 函数、类、接口必须有 JSDoc 注释(使用中文)
01309| - 复杂逻辑必须添加行内注释说明
01310|
01311| #### 代码风格
01312| - 使用 TypeScript 严格模式
01313| - 遵循项目现有的命名规范(见 2.3 节)
01314| - 保持代码格式一致(使用项目配置的格式化工具)
01315| - 保持缩进和空行的一致性
01316|
01317| #### 类型安全
01318| - 充分利用 TypeScript 类型系统
01319| - 避免使用 `any`,必要时使用 `unknown`
01320| - 为所有公共 API 提供类型定义
01321| - 使用类型推断,但复杂类型必须显式声明
01322|
01323| #### 组件使用规范
01324| - **严禁直接使用组件类**,必须通过 Manager 使用(见 4.0 节)
01325| - 新增组件时,必须创建对应的 Manager
01326| - Manager 必须继承 `BimComponent` 基类
01327| - 组件必须实现 `IBimComponent` 接口
01328| - **组件必须支持国际化**(见 4.6 节)
01329|
01330| #### 注释规范
01331| ```typescript
01332| /**
01333| * 函数功能描述(中文)
01334| * @param param1 参数说明(中文)
01335| * @param param2 参数说明(中文)
01336| * @returns 返回值说明(中文)
01337| */
01338| function example(param1: string, param2: number): boolean {
01339| // 行内注释说明关键逻辑(中文)
01340| return true;
01341| }
01342| ```
01343|
01344| #### 错误处理规范
01345| - 所有异步操作必须有错误处理
01346| - 使用 `try-catch` 捕获可能的异常
01347| - 错误信息必须使用中文
01348| - 提供有意义的错误信息,帮助调试
01349|
01350| #### 性能考虑
01351| - 使用 `requestAnimationFrame` 优化动画性能
01352| - 避免频繁的 DOM 操作,使用批量更新
01353| - 合理使用事件委托,减少事件监听器数量
01354| - 及时清理不需要的监听器和定时器
01355|
01356| ### 5.2 问答方式和开发流程规范
01357|
01358| #### 用户询问"如何实现"时的处理流程
01359|
01360| **当用户询问如何实现某个功能时,必须遵循以下流程:**
01361|
01362| 1. **分析需求**:用中文理解用户需求,明确要实现的功能
01363| 2. **制定详细开发计划**:必须包含以下内容:
01364| - **需要修改的文件列表**:
01365| - 列出每个需要修改的文件路径
01366| - 说明每个文件的修改内容(添加什么、修改什么)
01367| - **需要新增的文件列表**:
01368| - 列出每个需要新增的文件路径
01369| - 说明每个文件的作用和内容概要
01370| - **需要删除的文件列表**:
01371| - 列出每个需要删除的文件路径
01372| - 说明删除的原因
01373| - **注意**:删除的文件必须移动到回收文件夹,不能直接删除
01374| - **每个文件的作用说明**:
01375| - 详细说明每个文件在整体功能中的作用
01376| - 说明文件之间的依赖关系
01377| - **对整体结构的影响**:
01378| - 说明对现有架构的影响
01379| - 说明对现有功能的影响
01380| - 说明是否需要更新文档
01381| - 说明可能的风险点
01382| 3. **展示开发计划**:以清晰的格式展示给用户
01383| 4. **等待用户确认**:**在用户明确同意之前,绝对不能修改任何代码**
01384| 5. **用户同意后执行**:只有在用户明确表示同意后,才能开始修改代码
01385|
01386| **示例格式**:
01387|
01388| ```
01389| ## 开发计划
01390|
01391| ### 需要修改的文件
01392| 1. `src/managers/dialog-manager.ts`
01393| - 修改:添加新的 `showCustomDialog()` 方法
01394| - 作用:提供自定义弹窗的快捷方法
01395|
01396| 2. `src/types/events.ts`
01397| - 修改:在 `EngineEvents` 接口中添加 `'ui:custom-dialog'` 事件类型
01398| - 作用:定义新的事件类型,支持事件总线通信
01399|
01400| ### 需要新增的文件
01401| 1. `src/components/dialog/customDialog/index.ts`
01402| - 作用:实现自定义弹窗组件
01403| - 内容:包含 CustomDialog 类,实现 IBimComponent 接口
01404|
01405| 2. `src/components/dialog/customDialog/index.css`
01406| - 作用:自定义弹窗的样式文件
01407|
01408| ### 需要删除的文件
01409| 1. `src/components/dialog/oldDialog.ts`
01410| - 原因:已被新组件替代
01411| - **处理方式**:移动到 `.recycle/YYYY-MM-DD/` 文件夹(使用当前日期)
01412|
01413| ### 对整体结构的影响
01414| - **架构影响**:新增一个 Dialog 子类型,不影响现有架构
01415| - **功能影响**:不影响现有弹窗功能,只是新增功能
01416| - **文档更新**:需要更新 AI_COLLABORATION.md 中的组件清单
01417| - **风险点**:无重大风险,向后兼容
01418|
01419| 请确认是否按照此计划进行开发?
01420| ```
01421|
01422| #### 用户直接要求修改时的处理
01423|
01424| **当用户直接要求修改代码时(如"帮我修改XXX"、"实现XXX功能"),可以:**
01425| - 直接执行代码修改
01426| - 但仍需遵循其他规范(国际化、组件使用规范等)
01427| - 删除文件时仍需移动到回收文件夹
01428|
01429| #### 文件删除规范(强制)
01430|
01431| **严禁直接删除文件,必须遵循以下流程:**
01432|
01433| 1. **创建回收文件夹**(如果不存在):
01434| - 在项目根目录创建 `.recycle/` 文件夹
01435| - 此文件夹用于存放被删除的文件
01436|
01437| 2. **按时间组织文件夹**:
01438| - 在 `.recycle/` 下按日期创建子文件夹,格式:`YYYY-MM-DD`
01439| - 例如:`2024-01-15`、`2024-12-25`
01440| - 同一天删除的文件存放在同一个日期文件夹中
01441|
01442| 3. **移动文件而非删除**:
01443| ```bash
01444| # 错误方式 ❌
01445| rm src/old-file.ts
01446|
01447| # 正确方式 ✅
01448| # 获取当前日期(格式:YYYY-MM-DD)
01449| DATE=$(date +%Y-%m-%d)
01450| mkdir -p .recycle/$DATE
01451| mv src/old-file.ts .recycle/$DATE/src/old-file.ts
01452| ```
01453|
01454| 4. **保持目录结构**:
01455| - 在日期文件夹中保持原文件的目录结构
01456| - 例如:`src/components/old.ts` → `.recycle/2024-01-15/src/components/old.ts`
01457|
01458| 5. **添加删除说明**(可选):
01459| - 在对应日期文件夹中创建 `README.md` 记录删除原因
01460| - 格式:
01461| ```
01462| ## 2024-01-15 删除的文件
01463|
01464| - `src/components/old.ts`: 被新组件替代
01465| - `src/managers/old-manager.ts`: 功能重构,使用新 Manager
01466| ```
01467|
01468| 6. **恢复文件**:
01469| - 如果发现误删,可以从对应日期文件夹中恢复
01470| - 恢复后从 `.recycle/` 中移除
01471|
01472| **回收文件夹结构示例**:
01473| ```
01474| .recycle/
01475| ├── 2024-01-15/
01476| │ ├── README.md
01477| │ └── src/
01478| │ ├── components/
01479| │ │ └── old-component.ts
01480| │ └── managers/
01481| │ └── old-manager.ts
01482| ├── 2024-02-20/
01483| │ ├── README.md
01484| │ └── src/
01485| │ └── types/
01486| │ └── old-types.ts
01487| └── 2024-12-25/
01488| ├── README.md
01489| └── demo/
01490| └── old-demo.html
01491| ```
01492|
01493| **注意事项**:
01494| - 日期格式必须使用 `YYYY-MM-DD`(如 `2024-01-15`)
01495| - 如果同一天删除多个文件,都存放在同一个日期文件夹中
01496| - 日期文件夹按时间倒序排列,最新的在最前面
01497|
01498| ### 5.3 文档更新规则
01499|
01500| #### 必须更新的情况
01501| - **新增文件**: 在 "文件结构说明" 部分添加文件描述
01502| - **删除文件**: 从文档中移除对应条目(但文件移动到 `.recycle/` 文件夹)
01503| - **修改文件功能**: 更新对应文件的描述
01504| - **新增组件**:
01505| - 在 "组件和事件清单" 部分添加
01506| - **必须创建对应的组件详细文档**(在 `docs/components/` 目录下)
01507| - **修改组件**:
01508| - 更新 "组件和事件清单" 部分
01509| - **必须同步更新对应的组件详细文档**(`docs/components/` 目录下的对应文档)
01510| - **新增事件**: 在 "事件总线定义" 部分添加
01511| - **修改架构**: 更新 "架构设计思想" 部分
01512| - **修改构建流程**: 更新 "构建和运行" 部分
01513|
01514| #### 更新方式
01515| 1. 修改代码后,立即更新本文档
01516| 2. 确保文档与代码保持一致
01517| 3. 如果发现文档过时,优先更新文档
01518|
01519| ### 5.4 思考和工作流程规范
01520|
01521| #### 思考过程要求
01522| - **所有思考过程必须使用中文**
01523| - 分析问题时,用中文描述问题、分析思路、得出结论
01524| - 解释代码时,用中文说明逻辑、设计意图、实现方式
01525| - 讨论方案时,用中文描述优缺点、选择理由
01526|
01527| #### 工作流程
01528| 1. **理解需求**:用中文理解用户需求,明确任务目标
01529| 2. **分析问题**:用中文分析问题,梳理相关代码和架构
01530| 3. **设计方案**:用中文设计解决方案,考虑边界情况
01531| 4. **实现代码**:编写代码,添加中文注释
01532| 5. **测试验证**:用中文描述测试结果和发现的问题
01533| 6. **更新文档**:用中文更新相关文档
01534|
01535| #### 代码审查要点
01536| - **检查开发流程规范**:
01537| - 如果是询问"如何实现",是否先提供了详细的开发计划
01538| - 是否在用户同意后才修改代码
01539| - 删除的文件是否移动到 `.recycle/` 文件夹而非直接删除
01540| - 检查是否遵循组件使用规范(通过 Manager)
01541| - **检查是否支持国际化**:
01542| - 所有用户可见文本是否使用 `t(key)` 函数
01543| - 是否在所有语言文件中添加了翻译
01544| - 组件是否实现了 `setLocales()` 方法
01545| - 组件是否订阅了语言变更事件
01546| - 检查注释是否完整且为中文
01547| - 检查错误处理是否完善
01548| - 检查资源是否正确释放
01549| - 检查类型定义是否完整
01550|
01551| ### 5.5 其他注意事项
01552|
01553| #### 组件开发
01554| - 所有 UI 组件必须实现 `IBimComponent` 接口
01555| - 所有 Manager 必须继承 `BimComponent` 基类
01556| - **组件必须通过 Manager 使用,不允许直接使用组件类**
01557| - **组件必须支持国际化**(见 4.6 节):
01558| - 所有用户可见文本使用 `t(key)` 函数
01559| - 实现 `setLocales()` 方法
01560| - 订阅语言变更事件
01561| - 严禁硬编码任何语言的文本
01562| - 组件销毁时必须清理所有资源(事件监听、定时器等)
01563| - 新增组件时,必须同时创建对应的 Manager
01564|
01565| #### 主题和国际化
01566| - 组件必须支持主题切换(通过 `setTheme()` 方法)
01567| - **组件必须支持国际化**(通过 `setLocales()` 方法)
01568| - **所有用户可见的文本必须使用 `t(key)` 函数进行翻译**
01569| - **严禁硬编码中文或英文文本**
01570| - 新增文本时,必须在所有语言文件中添加对应的翻译键
01571| - **详细实现方式请参考 4.6 节《国际化实现指南》**
01572|
01573| #### 事件总线使用规范
(Output truncated at 51200 bytes. Use 'offset' parameter to read beyond line 1573)