# 架构迁移完成指南 - 事件驱动架构 ## 当前状态 已创建以下新文件: 1. **`web/src/views/db-cli/composables/useEventBus.ts`** - 事件总线 - 类型安全的事件定义 - 支持事件订阅/取消/触发 - 自动错误处理和日志 2. **`web/src/views/db-cli/composables/useStructureStore.ts`** - 新的表结构 Store - 单例模式,全局共享状态 - 事件驱动的状态更新 - 清晰的日志追踪 3. **`web/src/views/db-cli/composables/useStructureStoreLegacy.ts`** - 旧版本(已重命名) - 原 `useStructureState.ts` 的副本 - 保留用于兼容和参考 4. **`web/src/views/db-cli/composables/MIGRATION.md`** - 迁移文档 - 详细的对表和迁移步骤 - 使用示例和注意事项 ## 手动完成迁移步骤 ### 步骤 1:修改 `index.vue` 的导入 **位置**:`web/src/views/db-cli/index.vue` 第 120 行 **原代码**: ```typescript import { useStructureState } from './composables/useStructureState' ``` **修改为**: ```typescript import { useStructureStore } from './composables/useStructureStore' ``` --- ### 步骤 2:替换状态初始化(第 166-219 行) **原代码**(删除第 166-219 行): ```typescript const structureState = useStructureState() const { structureLoading, structureError, structureData, structureInfo, loadStructure, clearStructure, refreshStructure } = structureState // 使用计算属性确保响应式传递到子组件 const computedStructureLoading = computed(() => { const val = structureState.structureLoading.value console.log('🔵 computedStructureLoading 计算:', val) return val }) const computedStructureError = computed(() => { const val = structureState.structureError.value console.log('🔵 computedStructureError 计算:', val) return val }) const computedStructureData = computed(() => { const val = structureState.structureData.value console.log('🔵 computedStructureData 计算:', val) return val }) const computedStructureInfo = computed(() => { const val = structureState.structureInfo.value console.log('🔵 computedStructureInfo 计算:', val) return val }) // 添加调试监听,检查响应式 watch(() => structureState.structureInfo.value, (newVal, oldVal) => { // ... 所有 watch 代码 }, { deep: true, immediate: true }) watch(() => structureState.structureData.value, (newVal, oldVal) => { // ... 所有 watch 代码 }, { deep: true, immediate: true }) ``` **替换为**(在第 164 行之后添加): ```typescript // 新架构:使用单例 Store(事件驱动) const structureStore = useStructureStore() // 直接使用 Store 的状态(无需计算属性,无需 watch) // 状态是只读的,通过 Store 方法修改 ``` --- ### 步骤 3:修改组件传参(第 65-68 行) **原代码**: ```vue ``` **修改为**: ```vue ``` --- ### 步骤 4:修改 `handleTableStructure` 函数(第 357-389 行) **原代码**: ```typescript const handleTableStructure = async (data: TableStructureEvent) => { console.log('handleTableStructure 被调用:', data) // ... Tab 切换代码 ... // 加载表结构数据(在Tab切换后加载,确保用户能看到加载状态) try { await loadStructure( data.connectionId, data.database, data.tableName, data.dbType, data.nodeType ) // ... 大量调试日志 ... } catch (error) { console.error('handleTableStructure 出错:', error) } } ``` **修改为**: ```typescript const handleTableStructure = async (data: TableStructureEvent) => { console.log('🚀 handleTableStructure 被调用:', data) // 如果结果面板隐藏,自动显示编辑器(这样结果面板也会显示) if (!editorVisible.value) { toggleEditor() } // 先切换到结果面板的"结构"Tab(确保Tab可见) if (resultPanelRef.value) { (resultPanelRef.value as any).switchToStructureTab() } // 等待一下确保Tab切换完成 await new Promise(resolve => setTimeout(resolve, 100)) // 新架构:直接调用 Store 的 loadStructure 方法 // Store 会自动管理状态和事件通知,无需手动追踪 await structureStore.loadStructure( data.connectionId, data.database, data.tableName, data.dbType, data.nodeType ) console.log('✅ 加载完成,Store 当前状态:', { loading: structureStore.loading.value, data: structureStore.data.value, info: structureStore.info.value, error: structureStore.error.value }) } ``` --- ### 步骤 5:修改 `handleRefreshStructure` 函数(第 456-462 行) **原代码**: ```typescript const handleRefreshStructure = async () => { await refreshStructure() } ``` **修改为**: ```typescript const handleRefreshStructure = async () => { await structureStore.refreshStructure() } ``` --- ### 步骤 6:删除未使用的导入 检查是否有其他 `useStructureState` 的使用,全部替换为 `useStructureStore` --- ## 验证迁移 完成以上步骤后,验证以下内容: ### 1. 检查日志输出 运行应用,点击表结构,应该看到以下日志: ``` 🚀 handleTableStructure 被调用: { connectionId: 6, database: 'flux_pro', tableName: 'clue_info', dbType: 'mysql', nodeType: 'table' } 📢 事件触发 [structure:loading]: { loading: true } 🏪 Store.loadStructure 开始: { connectionId: 6, database: 'flux_pro', tableName: 'clue_info', dbType: 'mysql', nodeType: 'table' } 🏪 表结构加载成功: { connectionId: 6, database: 'flux_pro', tableName: 'clue_info', result: {...} } 🏪 Store.setData: { data: {...}, info: {...} } 📢 事件触发 [structure:data]: { data: {...}, info: {...} } 📢 事件触发 [structure:loading]: { loading: false } ✅ 加载完成,Store 当前状态: { loading: false, data: {...}, info: {...}, error: '' } ``` ### 2. 检查界面 切换到"结构"标签页,应该能看到: - ✅ 红色测试框(如果存在) - ✅ 调试信息块显示正确的数据 - ✅ 表结构数据正常显示 ### 3. 删除调试代码 确认功能正常后,删除: - `ResultPanel.vue` 中的红色调试框 - `ResultPanel.vue` 中的全局调试信息 - `index.vue` 中不必要的日志 --- ## 新架构的优势 ### 1. 单一数据源 - 所有状态集中在 Store - 避免多个 Composable 实例 - 全局共享,不会丢失 ### 2. 事件驱动 - 所有状态变更自动通知 - 可追踪完整的事件流 - 易于调试和问题定位 ### 3. 自动响应式 - Store 自动处理响应式 - 无需手动计算属性 - 无需 watch 监听 ### 4. 类型安全 - 完整的 TypeScript 类型定义 - 事件和状态都有类型约束 - 编译时错误检查 ### 5. 清晰的日志 - 所有关键操作都有日志 - 使用 emoji 标识不同的日志来源 - 易于过滤和搜索 --- ## 故障排除 ### 问题:Store 数据为 null **可能原因**: 1. 组件未正确引用 Store 2. 事件未正确触发 3. Store 方法未正确调用 **解决方法**: 1. 检查控制台是否有 `🏪` 开头的日志 2. 检查是否有 `📢` 开头的日志 3. 确认 Store 是单例(只有一次 `useStructureStore` 调用) ### 问题:Tab 内容不显示 **可能原因**: 1. Arco Tabs 配置问题 2. CSS 样式冲突 3. 数据未正确传递 **解决方法**: 1. 检查 props 是否正确传递 2. 检查 CSS 中 `display: flex !important` 是否生效 3. 检查浏览器开发工具中的元素状态 --- ## 后续改进 1. **引入 Pinia**(可选) - 更强大的状态管理 - 内置 DevTools 支持 - 持久化支持 2. **添加单元测试** - 测试 Store 的各种场景 - 测试事件总线的可靠性 - 提高代码质量 3. **性能优化** - 使用 `shallowRef` 处理大数据 - 添加防抖和节流 - 优化事件监听 4. **错误边界** - 全局错误捕获 - 错误恢复机制 - 用户友好的错误提示 --- ## 总结 新的事件驱动架构解决了当前的核心问题: ✅ **状态丢失问题** - 单例模式确保全局唯一实例 ✅ **响应式失效问题** - 自动事件通知,无需手动追踪 ✅ **调试困难问题** - 完整的日志体系,清晰的事件流 ✅ **组件通信问题** - 事件总线解耦,易于维护 **下一步**:按照上述步骤手动完成代码迁移,然后测试验证。