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

351 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构迁移完成指南 - 事件驱动架构
## 当前状态
已创建以下新文件:
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 行
**原代码**
```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
<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"
/>
```
**修改为**
```vue
<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 行)
**原代码**
```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. **错误边界**
- 全局错误捕获
- 错误恢复机制
- 用户友好的错误提示
---
## 总结
新的事件驱动架构解决了当前的核心问题:
**状态丢失问题** - 单例模式确保全局唯一实例
**响应式失效问题** - 自动事件通知,无需手动追踪
**调试困难问题** - 完整的日志体系,清晰的事件流
**组件通信问题** - 事件总线解耦,易于维护
**下一步**:按照上述步骤手动完成代码迁移,然后测试验证。