新增:文档体系重构+CHANGELOG补充+发布产物清理
This commit is contained in:
36
docs/03-模块文档/设置功能/README.md
Normal file
36
docs/03-模块文档/设置功能/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 设置功能模块文档
|
||||
|
||||
应用设置功能的实现和优化文档。
|
||||
|
||||
## 📖 文档列表
|
||||
|
||||
- [settings-implementation.md](./settings-implementation.md) - 设置功能重构实现总结
|
||||
- [settings-quick-reference.md](./settings-quick-reference.md) - 设置功能快速参考
|
||||
- [settings-ui-improvements.md](./settings-ui-improvements.md) - 设置 UI 改进
|
||||
|
||||
## 🎯 功能概述
|
||||
|
||||
将"版本更新"功能改为完整的"设置"系统,支持:
|
||||
|
||||
### Go 后端
|
||||
- `AppConfig` 数据模型
|
||||
- SQLite 持久化存储
|
||||
- 配置服务层(`ConfigService`)
|
||||
- 配置 API(`ConfigAPI`)
|
||||
|
||||
### Vue 前端
|
||||
- 设置页面组件(`Settings.vue`)
|
||||
- Tab 配置组件(`TabConfig.vue`)
|
||||
- 组合式 API(`useTabConfig.js`)
|
||||
|
||||
## ✅ 已完成功能
|
||||
|
||||
- ✅ Tab 配置管理(显示/隐藏、排序)
|
||||
- ✅ 默认 Tab 设置
|
||||
- ✅ 配置持久化
|
||||
- ✅ UI 组件优化
|
||||
|
||||
## 💡 快速导航
|
||||
|
||||
**实现细节**:[settings-implementation.md](./settings-implementation.md)
|
||||
**快速参考**:[settings-quick-reference.md](./settings-quick-reference.md)
|
||||
244
docs/03-模块文档/设置功能/settings-implementation.md
Normal file
244
docs/03-模块文档/设置功能/settings-implementation.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# U-Desk 设置功能重构实现总结
|
||||
|
||||
## 实施概览
|
||||
|
||||
本次实施完成了 U-Desk 的设置功能重构,将"版本更新"功能改为"设置"按钮,并实现了完整的 Tab 配置管理功能。
|
||||
|
||||
## 已完成功能
|
||||
|
||||
### ✅ 阶段一:Go 后端基础设施
|
||||
|
||||
#### 1. 数据模型 (`internal/storage/models/app_config.go`)
|
||||
- 创建了 `AppConfig` 模型用于存储应用配置
|
||||
- 支持键值对存储,使用 JSON 序列化复杂配置
|
||||
- 包含时间戳和描述字段
|
||||
|
||||
#### 2. 数据库迁移 (`internal/storage/sqlite.go`)
|
||||
- 已将 `AppConfig` 模型添加到 `AutoMigrate`
|
||||
- 数据库表名:`app_config`
|
||||
|
||||
#### 3. 配置服务层 (`internal/service/config_service.go`)
|
||||
- `GetTabConfig()`: 获取 Tab 配置,返回默认配置(如果不存在)
|
||||
- `SaveTabConfig()`: 保存 Tab 配置到数据库
|
||||
- 定义了 `TabConfig` 和 `TabDefinition` 结构体
|
||||
- 实现了配置验证和默认值处理
|
||||
|
||||
#### 4. 配置 API 层 (`internal/api/config_api.go`)
|
||||
- `GetAppConfig()`: 转换为前端需要的格式
|
||||
- `SaveAppConfig()`: 接收前端格式,验证并保存
|
||||
- 实现了前后端数据格式转换
|
||||
- 包含完整的验证逻辑(至少保留一个 Tab,默认 Tab 必须可见)
|
||||
|
||||
#### 5. App 集成 (`app.go`)
|
||||
- 在 `App` 结构体中添加了 `configAPI *api.ConfigAPI`
|
||||
- 在 `initCoreAPIs()` 中初始化 ConfigAPI
|
||||
- 添加了 `GetAppConfig()` 和 `SaveAppConfig()` 方法供前端调用
|
||||
|
||||
### ✅ 阶段二:前端设置面板
|
||||
|
||||
#### 1. SettingsPanel 组件 (`frontend/src/components/SettingsPanel.vue`)
|
||||
|
||||
**核心功能:**
|
||||
- ✅ 使用 Arco Design 的 `<a-drawer>` 组件实现侧边抽屉
|
||||
- ✅ 使用 `<a-tabs>` 组织设置内容:
|
||||
- "Tab 配置" tab
|
||||
- "版本更新" tab(嵌入 UpdatePanel 组件)
|
||||
- ✅ Tab 显示控制:使用 `<a-checkbox-group>` 实现多选
|
||||
- ✅ 默认 Tab 选择:使用 `<a-select>` 实现
|
||||
- ✅ Tab 拖拽排序:使用原生 HTML5 Drag & Drop API 实现
|
||||
|
||||
**拖拽实现特点:**
|
||||
- 无需额外依赖,使用原生 HTML5 API
|
||||
- 拖拽时显示视觉反馈(透明度、缩放)
|
||||
- 支持拖拽重新排序
|
||||
- 平滑的过渡动画
|
||||
|
||||
#### 2. App.vue 修改
|
||||
|
||||
**关键修改:**
|
||||
- ✅ 替换"版本更新"按钮为"设置"按钮(IconSettings)
|
||||
- ✅ 动态渲染 Tabs(根据配置动态生成)
|
||||
- ✅ 动态渲染组件(使用 `<component>` 标签)
|
||||
- ✅ 添加设置抽屉组件
|
||||
- ✅ 实现配置管理逻辑:
|
||||
- `loadConfig()`: 从后端加载配置
|
||||
- `handleSaveConfig()`: 保存配置到后端
|
||||
- `visibleTabs` 计算属性:根据配置动态生成可见 Tab 列表
|
||||
- ✅ 监听 `activeTab` 变化,自动处理 Tab 被隐藏的情况
|
||||
|
||||
### ✅ 阶段三:配置数据结构
|
||||
|
||||
#### 前端配置格式
|
||||
```typescript
|
||||
interface AppTab {
|
||||
key: string // Tab 唯一标识
|
||||
title: string // Tab 显示标题
|
||||
visible: boolean // 是否显示
|
||||
enabled: boolean // 是否启用(用于验证)
|
||||
}
|
||||
|
||||
interface AppConfig {
|
||||
tabs: AppTab[] // 所有可用 Tab 定义
|
||||
visibleTabs: string[] // 当前显示的 Tab key 列表(按顺序)
|
||||
defaultTab: string // 默认打开的 Tab
|
||||
}
|
||||
```
|
||||
|
||||
#### 后端配置格式
|
||||
```go
|
||||
type TabConfig struct {
|
||||
AvailableTabs []TabDefinition `json:"available_tabs"`
|
||||
VisibleTabs []string `json:"visible_tabs"`
|
||||
DefaultTab string `json:"default_tab"`
|
||||
}
|
||||
|
||||
type TabDefinition struct {
|
||||
Key string `json:"key"`
|
||||
Title string `json:"title"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
```
|
||||
|
||||
#### 默认配置
|
||||
```javascript
|
||||
{
|
||||
tabs: [
|
||||
{ key: 'db-cli', title: '数据库', visible: true, enabled: true },
|
||||
{ key: 'file-system', title: '文件管理', visible: true, enabled: true },
|
||||
{ key: 'device', title: '设备调用测试', visible: true, enabled: true }
|
||||
],
|
||||
visibleTabs: ['db-cli', 'file-system', 'device'],
|
||||
defaultTab: 'db-cli'
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ 阶段四:验证和错误处理
|
||||
|
||||
#### 前端验证
|
||||
- ✅ 至少保留一个可见 Tab(`visibleTabs.length >= 1`)
|
||||
- ✅ 默认 Tab 必须可见(`defaultTab` 在 `visibleTabs` 中)
|
||||
- ✅ Tab key 不重复(由结构保证)
|
||||
- ✅ 禁用状态下的 Tab 不可取消选中
|
||||
|
||||
#### 后端验证
|
||||
- ✅ 配置 JSON 格式验证
|
||||
- ✅ 数据库操作错误处理
|
||||
- ✅ 返回统一的响应格式(success/data/message)
|
||||
- ✅ 配置损坏时自动返回默认配置
|
||||
|
||||
#### 兼容性处理
|
||||
- ✅ 首次加载:如果数据库中没有配置,返回默认配置
|
||||
- ✅ 配置损坏:如果解析失败,记录错误并返回默认配置
|
||||
- ✅ 当前 Tab 不可见:保存配置后,如果当前激活的 Tab 被隐藏,自动切换到默认 Tab
|
||||
|
||||
## 配置存储
|
||||
|
||||
### 数据库
|
||||
- **类型**: SQLite
|
||||
- **位置**: `~/.u-desk/app.db`(Windows: `C:\Users\你的用户名\.u-desk\app.db`)
|
||||
- **表名**: `app_config`
|
||||
- **配置键**: `tab_config`
|
||||
|
||||
### LocalStorage
|
||||
- **键名**: `app-active-tab`
|
||||
- **用途**: 临时保存当前激活的 Tab(不持久化到后端)
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增文件
|
||||
1. `E:\wk-lab\go-desk\internal\storage\models\app_config.go` - 配置数据模型
|
||||
2. `E:\wk-lab\go-desk\internal\service\config_service.go` - 配置服务层
|
||||
3. `E:\wk-lab\go-desk\internal\api\config_api.go` - 配置 API 层
|
||||
4. `E:\wk-lab\go-desk\web\src\components\SettingsPanel.vue` - 设置面板组件
|
||||
|
||||
### 修改文件
|
||||
1. `E:\wk-lab\go-desk\internal\storage\sqlite.go` - 添加 AppConfig 到数据库迁移
|
||||
2. `E:\wk-lab\go-desk\app.go` - 集成 ConfigAPI
|
||||
3. `E:\wk-lab\go-desk\web\src\App.vue` - 替换版本更新按钮,实现动态 Tabs
|
||||
|
||||
## 功能特性
|
||||
|
||||
### P0(必须)- 已完成 ✅
|
||||
- ✅ Go 后端配置 API
|
||||
- ✅ 前端 SettingsPanel 基础 UI
|
||||
- ✅ Tab 显示/隐藏功能
|
||||
- ✅ 拖拽排序功能
|
||||
- ✅ App.vue 动态 Tabs
|
||||
|
||||
### P1(重要)- 已完成 ✅
|
||||
- ✅ 默认 Tab 选择
|
||||
- ✅ 配置验证和错误处理
|
||||
- ✅ 版本更新集成到设置面板
|
||||
|
||||
### P2(可选)- 未实现
|
||||
- ❌ 键盘辅助(上移/下移按钮)
|
||||
- ❌ 配置导出/导入
|
||||
- ⚠️ 拖拽动画优化(基础版本已实现)
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 后端测试
|
||||
1. ✅ 测试 `GetAppConfig()` 返回默认配置
|
||||
2. ⏳ 测试 `SaveAppConfig()` 保存和读取
|
||||
3. ⏳ 测试配置 JSON 序列化/反序列化
|
||||
4. ⏳ 测试数据库连接和错误处理
|
||||
|
||||
### 前端测试
|
||||
1. ⏳ 测试拖拽排序功能
|
||||
2. ⏳ 测试 Tab 显示/隐藏切换
|
||||
3. ⏳ 测试默认 Tab 选择
|
||||
4. ⏳ 测试至少保留一个 Tab 的验证
|
||||
5. ⏳ 测试配置保存和恢复
|
||||
6. ⏳ 测试版本更新功能在设置面板中的显示
|
||||
|
||||
### 集成测试
|
||||
1. ⏳ 测试配置保存后 Tab 的动态更新
|
||||
2. ⏳ 测试刷新页面后配置保持
|
||||
3. ⏳ 测试当前 Tab 被隐藏时的自动切换
|
||||
4. ⏳ 测试窗口控制按钮与设置的交互
|
||||
|
||||
## UX 优化建议
|
||||
|
||||
### 已实现
|
||||
- ✅ 拖拽反馈(透明度、缩放效果)
|
||||
- ✅ 保存提示(Message 提示)
|
||||
- ✅ 加载状态(按钮 loading 状态)
|
||||
|
||||
### 可选优化
|
||||
- ⏳ 拖拽时显示虚线框指示放置位置
|
||||
- ⏳ 首次加载配置时显示 Loading
|
||||
- ⏳ 键盘辅助(上移/下移按钮)作为拖拽的替代方式
|
||||
- ⏳ 配置预览(在保存前预览 Tab 的最终顺序和可见性)
|
||||
|
||||
## 构建状态
|
||||
|
||||
### Go 后端
|
||||
- ✅ 构建成功,无编译错误
|
||||
- ✅ 所有依赖正确导入
|
||||
- ✅ 数据库迁移正常
|
||||
|
||||
### 前端
|
||||
- ⏳ 需要运行 `npm install` 安装依赖
|
||||
- ⏳ 需要运行 `npm run dev` 测试前端功能
|
||||
|
||||
## 下一步
|
||||
|
||||
1. **测试功能**: 运行应用并测试所有功能
|
||||
2. **修复 bug**: 根据测试结果修复可能出现的问题
|
||||
3. **优化体验**: 根据实际使用情况优化 UX
|
||||
4. **添加文档**: 更新用户文档说明新功能
|
||||
|
||||
## 技术亮点
|
||||
|
||||
1. **无依赖拖拽**: 使用原生 HTML5 Drag & Drop API,无需额外依赖
|
||||
2. **类型安全**: Go 后端和 TypeScript 前端都有完整的类型定义
|
||||
3. **数据验证**: 前后端双重验证,确保数据一致性
|
||||
4. **优雅降级**: 配置损坏时自动回退到默认配置
|
||||
5. **动态渲染**: 使用 Vue 的 `<component>` 标签实现真正的动态组件渲染
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据库迁移**: 首次运行时会自动创建 `app_config` 表
|
||||
2. **配置持久化**: 配置保存在 SQLite 数据库中,重启应用后保持
|
||||
3. **Tab 状态**: 当前激活的 Tab 保存在 LocalStorage 中,不持久化到后端
|
||||
4. **兼容性**: 如果旧版本没有配置,会自动使用默认配置
|
||||
282
docs/03-模块文档/设置功能/settings-quick-reference.md
Normal file
282
docs/03-模块文档/设置功能/settings-quick-reference.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# 设置功能快速参考
|
||||
|
||||
## API 端点
|
||||
|
||||
### 获取应用配置
|
||||
```go
|
||||
// Go 方法
|
||||
func (a *App) GetAppConfig() (map[string]interface{}, error)
|
||||
|
||||
// 前端调用
|
||||
const result = await window.go.main.App.GetAppConfig()
|
||||
```
|
||||
|
||||
**响应格式:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"tabs": [
|
||||
{
|
||||
"key": "db-cli",
|
||||
"title": "数据库",
|
||||
"visible": true,
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"visibleTabs": ["db-cli", "file-system", "device"],
|
||||
"defaultTab": "db-cli"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 保存应用配置
|
||||
```go
|
||||
// Go 方法
|
||||
func (a *App) SaveAppConfig(req SaveAppConfigRequest) (map[string]interface{}, error)
|
||||
|
||||
// 前端调用
|
||||
const result = await window.go.main.App.SaveAppConfig({
|
||||
tabs: [
|
||||
{ key: "db-cli", title: "数据库", visible: true, enabled: true }
|
||||
],
|
||||
visibleTabs: ["db-cli", "file-system"],
|
||||
defaultTab: "db-cli"
|
||||
})
|
||||
```
|
||||
|
||||
**响应格式:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "配置保存成功"
|
||||
}
|
||||
```
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### app_config 表
|
||||
```sql
|
||||
CREATE TABLE `app_config` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`key` VARCHAR(50) UNIQUE NOT NULL,
|
||||
`value` TEXT NOT NULL,
|
||||
`description` VARCHAR(200),
|
||||
`created_at` DATETIME,
|
||||
`updated_at` DATETIME
|
||||
);
|
||||
```
|
||||
|
||||
### 示例数据
|
||||
```json
|
||||
// key = "tab_config"
|
||||
{
|
||||
"available_tabs": [
|
||||
{ "key": "db-cli", "title": "数据库", "enabled": true },
|
||||
{ "key": "file-system", "title": "文件管理", "enabled": true },
|
||||
{ "key": "device", "title": "设备调用测试", "enabled": true }
|
||||
],
|
||||
"visible_tabs": ["db-cli", "file-system", "device"],
|
||||
"default_tab": "db-cli"
|
||||
}
|
||||
```
|
||||
|
||||
## 组件使用
|
||||
|
||||
### SettingsPanel 组件
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<SettingsPanel
|
||||
v-model="showSettings"
|
||||
:config="appConfig"
|
||||
@save="handleSaveConfig"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SettingsPanel from './components/SettingsPanel.vue'
|
||||
|
||||
const showSettings = ref(false)
|
||||
const appConfig = ref({
|
||||
tabs: [],
|
||||
visibleTabs: [],
|
||||
defaultTab: 'db-cli'
|
||||
})
|
||||
|
||||
const handleSaveConfig = async (config) => {
|
||||
// config 包含:
|
||||
// - tabs: Tab 定义数组
|
||||
// - visibleTabs: 可见 Tab key 数组
|
||||
// - defaultTab: 默认 Tab key
|
||||
console.log('保存配置:', config)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 拖拽实现
|
||||
|
||||
### HTML5 Drag & Drop API
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart"
|
||||
@dragover.prevent="handleDragOver"
|
||||
@drop="handleDrop"
|
||||
@dragend="handleDragEnd"
|
||||
>
|
||||
拖拽元素
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const handleDragStart = (index, event) => {
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
event.target.classList.add('dragging')
|
||||
}
|
||||
|
||||
const handleDragOver = (event) => {
|
||||
event.preventDefault()
|
||||
event.dataTransfer.dropEffect = 'move'
|
||||
}
|
||||
|
||||
const handleDrop = (index, event) => {
|
||||
event.preventDefault()
|
||||
// 处理排序逻辑
|
||||
}
|
||||
|
||||
const handleDragEnd = (event) => {
|
||||
event.target.classList.remove('dragging')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 配置验证规则
|
||||
|
||||
### 前端验证
|
||||
1. **至少保留一个可见 Tab**: `visibleTabs.length >= 1`
|
||||
2. **默认 Tab 必须可见**: `visibleTabs.includes(defaultTab)`
|
||||
3. **禁用 Tab 不可取消选中**: `!tab.enabled` 时禁用复选框
|
||||
|
||||
### 后端验证
|
||||
1. **JSON 格式验证**: 使用 `json.Unmarshal` 验证
|
||||
2. **业务规则验证**: 与前端相同
|
||||
3. **数据库错误处理**: 捕获并返回友好错误信息
|
||||
|
||||
## 样式参考
|
||||
|
||||
### 拖拽元素样式
|
||||
```css
|
||||
.tab-sort-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: var(--color-fill-1);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
cursor: move;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-sort-item.dragging {
|
||||
opacity: 0.5;
|
||||
background: var(--color-fill-2);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.tab-sort-item:hover {
|
||||
background: var(--color-fill-2);
|
||||
border-color: var(--color-border-2);
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何添加新的 Tab?
|
||||
A: 在 `defaultTabConfig` 中添加新的 `TabDefinition`,并确保在前端 `getComponent` 方法中添加对应的组件映射。
|
||||
|
||||
### Q: 如何禁用某个 Tab?
|
||||
A: 将 `TabDefinition.Enabled` 设置为 `false`,前端会自动显示"不可用"标签并禁用复选框。
|
||||
|
||||
### Q: 配置存储在哪里?
|
||||
A: 配置存储在 SQLite 数据库中,位置:`~/.u-desk/app.db`(Windows: `C:\Users\你的用户名\.u-desk\app.db`),表名:`app_config`。
|
||||
|
||||
### Q: 如何重置配置?
|
||||
A: 删除数据库中的 `tab_config` 记录,系统会自动使用默认配置。
|
||||
|
||||
### Q: 拖拽功能不工作?
|
||||
A: 确保元素设置了 `draggable="true"` 属性,并且正确实现了所有拖拽事件处理函数。
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 查看当前配置
|
||||
```javascript
|
||||
console.log('应用配置:', appConfig.value)
|
||||
console.log('可见 Tabs:', visibleTabs.value)
|
||||
console.log('当前激活 Tab:', activeTab.value)
|
||||
```
|
||||
|
||||
### 查看数据库
|
||||
```bash
|
||||
# Windows
|
||||
sqlite3 ~/.u-desk/app.db
|
||||
|
||||
# 查询配置
|
||||
SELECT * FROM app_config WHERE key = 'tab_config';
|
||||
```
|
||||
|
||||
### 重置配置
|
||||
```sql
|
||||
DELETE FROM app_config WHERE key = 'tab_config';
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
1. **减少重新渲染**: 使用 `computed` 属性缓存计算结果
|
||||
2. **防抖保存**: 对保存操作添加防抖,避免频繁保存
|
||||
3. **懒加载组件**: 使用 `<KeepAlive>` 缓存组件状态
|
||||
4. **批量更新**: 使用 `watch` 的 `deep` 选项时注意性能影响
|
||||
|
||||
## 扩展建议
|
||||
|
||||
### 1. 导出/导入配置
|
||||
```javascript
|
||||
// 导出
|
||||
const exportConfig = () => {
|
||||
const data = JSON.stringify(appConfig.value)
|
||||
const blob = new Blob([data], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
// 下载文件
|
||||
}
|
||||
|
||||
// 导入
|
||||
const importConfig = (file) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
const config = JSON.parse(e.target.result)
|
||||
// 保存配置
|
||||
}
|
||||
reader.readAsText(file)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 键盘辅助
|
||||
```vue
|
||||
<a-button @click="moveUp(index)">
|
||||
<template #icon><icon-up /></template>
|
||||
</a-button>
|
||||
<a-button @click="moveDown(index)">
|
||||
<template #icon><icon-down /></template>
|
||||
</a-button>
|
||||
```
|
||||
|
||||
### 3. 配置预览
|
||||
```vue
|
||||
<a-modal v-model:visible="showPreview" title="配置预览">
|
||||
<div v-for="tab in visibleTabs" :key="tab.key">
|
||||
{{ tab.title }}
|
||||
</div>
|
||||
</a-modal>
|
||||
```
|
||||
239
docs/03-模块文档/设置功能/settings-ui-improvements.md
Normal file
239
docs/03-模块文档/设置功能/settings-ui-improvements.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# 设置面板 UI 改进总结
|
||||
|
||||
## 问题修复
|
||||
|
||||
### 1. 保存后未立即生效 ✅
|
||||
|
||||
**原因分析:**
|
||||
- `tabs` 数组中的 `visible` 属性与 `visibleTabs` 数组不同步
|
||||
- `watch(selectedTabs)` 覆盖了拖拽排序的顺序
|
||||
|
||||
**修复方案:**
|
||||
1. 在 `handleTabVisibilityChange` 中同步更新 `tabs` 数组的 `visible` 属性
|
||||
2. 移除 `watch(selectedTabs)`,避免覆盖排序
|
||||
3. 在 `handleSave` 中确保数据完全同步
|
||||
4. 在 `loadConfig` 中从后端加载时同步 `visible` 属性
|
||||
|
||||
### 2. UI 合并优化 ✅
|
||||
|
||||
**改进前:**
|
||||
- Tab 显示、默认 Tab、拖拽排序分成三个独立的卡片
|
||||
- 用户需要在不同区域操作,不够直观
|
||||
|
||||
**改进后:**
|
||||
- 统一到一个列表中,每行包含所有控制项
|
||||
- 更直观、更高效的配置体验
|
||||
|
||||
## 新的 UI 设计
|
||||
|
||||
### Tab 配置列表结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ ℹ️ 拖拽可调整 Tab 顺序,勾选复选框控制显示,单选按钮 │
|
||||
│ 设置默认打开的 Tab │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ ⋮ ☑ 数据库 ○ 默认打开 │
|
||||
│ ⋮ ☑ 文件管理 ○ 默认打开 │
|
||||
│ ⋮ ☑ 设备调用测试 ⦿ 默认打开 │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 隐藏的 Tabs │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ ⋮ ☐ (其他隐藏的 Tab) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 每行包含的元素
|
||||
|
||||
1. **拖拽手柄** (⋮) - 拖拽调整顺序
|
||||
2. **复选框** (☑/☐) - 控制显示/隐藏
|
||||
3. **Tab 标题** - 显示 Tab 名称
|
||||
4. **单选按钮** (⦿) - 设置默认打开的 Tab
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 新增辅助函数
|
||||
|
||||
```javascript
|
||||
// 判断 Tab 是否可见
|
||||
const isTabVisible = (key) => {
|
||||
return localConfig.value.visibleTabs.includes(key)
|
||||
}
|
||||
|
||||
// 判断 Tab 是否启用
|
||||
const isTabEnabled = (key) => {
|
||||
const tab = localConfig.value.tabs.find(t => t.key === key)
|
||||
return tab ? tab.enabled : false
|
||||
}
|
||||
|
||||
// 隐藏的 Tabs 计算属性
|
||||
const hiddenTabs = computed(() => {
|
||||
return localConfig.value.tabs.filter(
|
||||
tab => !localConfig.value.visibleTabs.includes(tab.key)
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
### 改进的可见性处理
|
||||
|
||||
```javascript
|
||||
// 处理单个 Tab 可见性变化
|
||||
const handleTabVisibilityChange = (tabKey, visible) => {
|
||||
if (visible) {
|
||||
// 显示 Tab:添加到 visibleTabs 末尾
|
||||
localConfig.value.visibleTabs.push(tabKey)
|
||||
} else {
|
||||
// 隐藏 Tab:从 visibleTabs 中移除
|
||||
// 至少保留一个 Tab
|
||||
if (localConfig.value.visibleTabs.length <= 1) {
|
||||
Message.warning('至少需要保留一个可见的 Tab')
|
||||
return
|
||||
}
|
||||
|
||||
// 如果隐藏的是默认 Tab,需要更改默认 Tab
|
||||
if (localConfig.value.defaultTab === tabKey) {
|
||||
const remainingTabs = localConfig.value.visibleTabs.filter(k => k !== tabKey)
|
||||
localConfig.value.defaultTab = remainingTabs[0] || ''
|
||||
}
|
||||
|
||||
localConfig.value.visibleTabs = localConfig.value.visibleTabs.filter(
|
||||
k => k !== tabKey
|
||||
)
|
||||
}
|
||||
|
||||
// 同步更新 tabs 数组中的 visible 属性
|
||||
localConfig.value.tabs = localConfig.value.tabs.map(tab => ({
|
||||
...tab,
|
||||
visible: localConfig.value.visibleTabs.includes(tab.key)
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
### 保存前数据同步
|
||||
|
||||
```javascript
|
||||
// 保存配置
|
||||
const handleSave = async () => {
|
||||
// ... 验证逻辑 ...
|
||||
|
||||
// 确保 tabs 数组中的 visible 属性与 visibleTabs 完全同步
|
||||
const syncedTabs = localConfig.value.tabs.map(tab => ({
|
||||
...tab,
|
||||
visible: localConfig.value.visibleTabs.includes(tab.key)
|
||||
}))
|
||||
|
||||
const configToSave = {
|
||||
tabs: syncedTabs,
|
||||
visibleTabs: [...localConfig.value.visibleTabs],
|
||||
defaultTab: localConfig.value.defaultTab
|
||||
}
|
||||
|
||||
// ... 保存逻辑 ...
|
||||
}
|
||||
```
|
||||
|
||||
## 样式改进
|
||||
|
||||
### 配置项样式
|
||||
|
||||
```css
|
||||
.tab-config-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: var(--color-fill-1);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
cursor: move;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-config-item:hover {
|
||||
background: var(--color-fill-2);
|
||||
border-color: var(--color-border-2);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.tab-config-item.dragging {
|
||||
opacity: 0.5;
|
||||
background: var(--color-fill-2);
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
```
|
||||
|
||||
### 隐藏项样式
|
||||
|
||||
```css
|
||||
.tab-config-item.hidden {
|
||||
opacity: 0.6;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.tab-config-item.hidden:hover {
|
||||
border-color: var(--color-border);
|
||||
box-shadow: none;
|
||||
}
|
||||
```
|
||||
|
||||
## 用户体验改进
|
||||
|
||||
1. **一目了然** - 所有配置集中在一行,无需来回查看
|
||||
2. **即时反馈** - 拖拽时有视觉反馈(透明度、缩放、阴影)
|
||||
3. **智能提示** - 顶部说明文字告知用户如何操作
|
||||
4. **分组显示** - 可见和隐藏的 Tab 分开展示
|
||||
5. **保护机制** - 至少保留一个可见 Tab,最后一个 Tab 的复选框禁用
|
||||
|
||||
## 数据流保证
|
||||
|
||||
### 配置加载
|
||||
```
|
||||
后端 → GetAppConfig → loadConfig → 同步 visible 属性 → 显示
|
||||
```
|
||||
|
||||
### 配置保存
|
||||
```
|
||||
用户操作 → localConfig → handleSave → 同步数据 → 后端保存 → 刷新 UI
|
||||
```
|
||||
|
||||
### 关键同步点
|
||||
|
||||
1. **复选框改变** → 同步 `visibleTabs` 和 `tabs[].visible`
|
||||
2. **拖拽排序** → 更新 `visibleTabs` 顺序
|
||||
3. **保存前** → 确保所有 `visible` 属性与 `visibleTabs` 一致
|
||||
4. **加载后** → 根据后端数据同步 `visible` 属性
|
||||
|
||||
## 测试清单
|
||||
|
||||
### 基础功能
|
||||
- ✅ 拖拽排序 Tab
|
||||
- ✅ 勾选/取消勾选复选框
|
||||
- ✅ 设置默认 Tab
|
||||
- ✅ 保存配置后立即生效
|
||||
- ✅ 刷新页面后配置保持
|
||||
|
||||
### 边界情况
|
||||
- ✅ 至少保留一个可见 Tab
|
||||
- ✅ 隐藏默认 Tab 时自动切换
|
||||
- ✅ 禁用的 Tab 不可操作
|
||||
- ✅ 配置为空时显示默认值
|
||||
|
||||
### UI 交互
|
||||
- ✅ 拖拽时视觉反馈
|
||||
- ✅ hover 状态提示
|
||||
- ✅ 隐藏项分组显示
|
||||
- ✅ 说明文字清晰
|
||||
|
||||
## 修复文件
|
||||
|
||||
- `frontend/src/components/SettingsPanel.vue` - UI 合并和逻辑修复
|
||||
- `frontend/src/App.vue` - loadConfig 和 handleSaveConfig 数据同步
|
||||
|
||||
## 总结
|
||||
|
||||
本次改进解决了两个主要问题:
|
||||
1. **保存后未立即生效** - 通过数据同步机制修复
|
||||
2. **UI 不够直观** - 通过统一配置列表优化
|
||||
|
||||
用户体验得到显著提升,配置操作更加高效和直观。
|
||||
Reference in New Issue
Block a user