Private
Public Access
1
0
Files

217 lines
6.3 KiB
Markdown
Raw Permalink 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.
# 数据库客户端组件拆分方案
## 组件架构设计
### 组件拆分
`index.vue` 拆分为以下组件:
1. **ConnectionTree.vue** - 左侧连接树形列表
2. **SqlEditor.vue** - SQL编辑器区域
3. **ResultPanel.vue** - 结果展示区域
4. **index.vue** - 主组件(布局和状态管理)
### 组件职责划分
#### ConnectionTree.vue
- **职责**:连接列表管理、树形结构展示、数据库/表展开
- **状态**connections, treeData, loading, loadingNodes
- **方法**loadConnections, loadDatabases, loadTables
- **事件**
- `connection-select`: 连接被选中
- `connection-edit`: 编辑连接
- `connection-delete`: 删除连接
- `connection-refresh`: 需要刷新连接列表
- `table-select`: 表被选中用于生成SQL
- `new-connection`: 新建连接
#### SqlEditor.vue
- **职责**SQL编辑器、标签页管理、工具栏
- **Props**
- `currentConnection`: 当前选中的连接对象
- **状态**tabs, activeTab, editorView
- **方法**initEditor, handleAddTab, handleDeleteTab, handleExecute, handleExecuteSelected, handleFormat
- **事件**
- `execute`: 执行SQL完整内容
- `execute-selected`: 执行选中的SQL
- `format`: 格式化SQL
- `sql-insert`: 插入SQL到编辑器由表选择触发
- `tab-change`: 标签页切换
- `sql-change`: SQL内容变化
#### ResultPanel.vue
- **职责**结果展示表格、JSON、消息
- **Props**
- `loading`: 加载状态
- `error`: 错误信息
- `data`: 结果数据
- `mode`: 展示模式table/json
- `stats`: 执行统计信息
- `messages`: 消息列表
- **状态**resultTab
- **方法**formatJSON
- **事件**:无(纯展示组件)
#### index.vue主组件
- **职责**
- 布局管理(左侧、右侧、底部)
- 状态协调(当前连接、执行结果)
- 组件通信桥梁
- 连接表单管理
### 组件通信方式
#### 1. Props 向下传递
- `currentConnection` → SqlEditor
- `loading, error, data, mode, stats, messages` → ResultPanel
#### 2. Events 向上传递
- ConnectionTree 的事件 → index.vue 处理
- SqlEditor 的事件 → index.vue 处理
#### 3. 数据流向
```
ConnectionTree
└─ connection-select ──→ index.vue ──→ SqlEditor (currentConnection prop)
└─→ ResultPanel (clear data)
SqlEditor
└─ execute ──→ index.vue ──→ ExecuteSQL API ──→ ResultPanel (result props)
ConnectionTree
└─ table-select ──→ index.vue ──→ SqlEditor (sql-insert event)
```
### 状态管理
#### 主组件 (index.vue) 管理的状态:
- `currentConnection`: 当前选中的连接(需要传递给 SqlEditor
- `resultLoading, resultError, resultData, resultMode, resultStats`: 执行结果(需要传递给 ResultPanel
- `messages`: 消息列表(需要传递给 ResultPanel
- `showConnectionForm, editingConnectionId`: 连接表单状态
#### 子组件自己管理的状态:
- ConnectionTree: connections, treeData, loading, loadingNodes
- SqlEditor: tabs, activeTab, editorView
- ResultPanel: resultTab
### 优势
1. **职责清晰**:每个组件只关注自己的功能
2. **可维护性强**:修改某个功能只需修改对应组件
3. **可复用性**ResultPanel 可以在其他地方复用
4. **测试友好**:每个组件可以独立测试
5. **性能优化**:可以针对单个组件进行优化
### 后续扩展
如果功能继续增加,可以考虑:
1. 引入 Pinia/Vuex 进行全局状态管理
2. 使用 provide/inject 传递深层数据
3. 提取公共逻辑到 composables
## 实现步骤
### 步骤1创建 ConnectionTree.vue ✅
已完成,组件位置:`components/ConnectionTree.vue`
### 步骤2创建 SqlEditor.vue
需要提取的代码:
- 编辑器相关initEditor, editorView, tabs, activeTab
- 标签页管理handleAddTab, handleDeleteTab
- 执行方法handleExecute, handleExecuteSelected通过emit传递SQL给父组件
- 格式化handleFormat
- SQL插入insertSQL用于接收表选择事件
### 步骤3创建 ResultPanel.vue
需要提取的代码:
- 结果展示resultLoading, resultError, resultData, resultMode, resultStats, resultColumns
- 消息列表messages
- 格式化formatJSON
### 步骤4重构 index.vue
- 移除已提取的代码
- 引入新组件
- 实现组件通信逻辑:
- 监听 ConnectionTree 的事件
- 调用 ExecuteSQL API
- 传递数据到 ResultPanel
## 通信示例代码
### index.vue 中的通信代码
```vue
<template>
<a-layout class="db-cli-layout">
<a-layout-sider :width="280">
<ConnectionTree
:current-connection-id="currentConnection?.id"
@connection-select="handleConnectionSelect"
@connection-edit="handleConnectionEdit"
@connection-delete="handleConnectionDelete"
@table-select="handleTableSelect"
@new-connection="showConnectionForm = true"
/>
</a-layout-sider>
<a-layout class="right-layout">
<a-layout-content>
<SqlEditor
:current-connection="currentConnection"
@execute="handleExecuteSQL"
@execute-selected="handleExecuteSelectedSQL"
@sql-insert="handleSQLInsert"
/>
</a-layout-content>
<a-layout-footer>
<ResultPanel
:loading="resultLoading"
:error="resultError"
:data="resultData"
:mode="resultMode"
:stats="resultStats"
:messages="messages"
/>
</a-layout-footer>
</a-layout>
</a-layout>
</template>
<script setup>
// 主组件只负责状态管理和组件协调
const currentConnection = ref(null)
const resultLoading = ref(false)
// ... 其他状态
// 连接选择
const handleConnectionSelect = (conn) => {
currentConnection.value = conn
// 清空结果
clearResults()
}
// SQL执行
const handleExecuteSQL = async (sql) => {
resultLoading.value = true
try {
const result = await window.go.main.App.ExecuteSQL(currentConnection.value.id, sql)
// 处理结果,更新 resultData, resultStats 等
} catch (error) {
// 处理错误
} finally {
resultLoading.value = false
}
}
// SQL插入
const handleSQLInsert = (sql) => {
// 通过 ref 调用 SqlEditor 的方法
sqlEditorRef.value?.insertSQL(sql)
}
</script>
```