Private
Public Access
1
0
Files
u-desk/docs/02-架构设计/架构迁移完成指南.md

9.2 KiB
Raw Blame History

架构迁移完成指南 - 事件驱动架构

当前状态

已创建以下新文件:

  1. frontend/src/views/db-cli/composables/useEventBus.ts - 事件总线

    • 类型安全的事件定义
    • 支持事件订阅/取消/触发
    • 自动错误处理和日志
  2. frontend/src/views/db-cli/composables/useStructureStore.ts - 新的表结构 Store

    • 单例模式,全局共享状态
    • 事件驱动的状态更新
    • 清晰的日志追踪
  3. frontend/src/views/db-cli/composables/useStructureStoreLegacy.ts - 旧版本(已重命名)

    • useStructureState.ts 的副本
    • 保留用于兼容和参考
  4. frontend/src/views/db-cli/composables/MIGRATION.md - 迁移文档

    • 详细的对表和迁移步骤
    • 使用示例和注意事项

手动完成迁移步骤

步骤 1修改 index.vue 的导入

位置frontend/src/views/db-cli/index.vue 第 120 行

原代码

import { useStructureState } from './composables/useStructureState'

修改为

import { useStructureStore } from './composables/useStructureStore'

步骤 2替换状态初始化第 166-219 行)

原代码(删除第 166-219 行):

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 行之后添加):

// 新架构:使用单例 Store事件驱动
const structureStore = useStructureStore()
// 直接使用 Store 的状态(无需计算属性,无需 watch
// 状态是只读的,通过 Store 方法修改

步骤 3修改组件传参第 65-68 行)

原代码

<ResultPanel
  :structure-loading="computedStructureLoading"
  :structure-error="computedStructureError"
  :structure-data="computedStructureData"
  :structure-info="computedStructureInfo || undefined"
  :edit-mode="structureEditMode"
  @toggle-editor="toggleEditor"
  @refresh-structure="refreshStructure"
  @switch-to-edit-mode="handleSwitchToEditMode"
  @switch-to-view-mode="handleSwitchToViewMode"
  @save-structure="handleSaveStructure"
  @cancel-edit="handleCancelEdit"
/>

修改为

<ResultPanel
  :structure-loading="structureStore.loading"
  :structure-error="structureStore.error"
  :structure-data="structureStore.data"
  :structure-info="structureStore.info"
  :edit-mode="structureEditMode"
  @toggle-editor="toggleEditor"
  @refresh-structure="structureStore.refreshStructure"
  @switch-to-edit-mode="handleSwitchToEditMode"
  @switch-to-view-mode="handleSwitchToViewMode"
  @save-structure="handleSaveStructure"
  @cancel-edit="handleCancelEdit"
/>

步骤 4修改 handleTableStructure 函数(第 357-389 行)

原代码

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)
  }
}

修改为

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 行)

原代码

const handleRefreshStructure = async () => {
  await refreshStructure()
}

修改为

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. 错误边界

    • 全局错误捕获
    • 错误恢复机制
    • 用户友好的错误提示

总结

新的事件驱动架构解决了当前的核心问题:

状态丢失问题 - 单例模式确保全局唯一实例 响应式失效问题 - 自动事件通知,无需手动追踪 调试困难问题 - 完整的日志体系,清晰的事件流 组件通信问题 - 事件总线解耦,易于维护

下一步:按照上述步骤手动完成代码迁移,然后测试验证。