From 550543432527c6bbd666549773ecc1d446b04b64 Mon Sep 17 00:00:00 2001 From: Jokul Date: Tue, 7 Jul 2026 22:34:48 +0800 Subject: [PATCH] feat: P3 batch 1 - context menus, shortcuts, connection management - Key list right-click context menu (copy/delete/export/memory/copy value) - Shift+Click range selection + Select All checkbox - Keyboard shortcuts: Ctrl+, (settings), Ctrl+S (save), Ctrl+L (clear CLI), Ctrl+/ (hotkeys help) - CLI auto-sync: SELECT updates DB, write commands refresh key list - Connection color marking (6 predefined colors) - Duplicate connection button - Connection drag-and-drop reorder (native HTML5) - i18n keys for all features (zh-CN + en) --- ROADMAP.md | 12 +- src/components/CliView.vue | 35 ++++ src/components/ConnectionCard.vue | 145 ++++++++++++++- src/components/ConnectionList.vue | 62 ++++++- src/components/KeyList.vue | 291 +++++++++++++++++++++++++++++- src/components/MainArea.vue | 15 +- src/components/Sidebar.vue | 40 ++++ src/components/StringEditor.vue | 15 +- src/i18n/locales/en.ts | 10 + src/i18n/locales/zh-CN.ts | 10 + src/stores/connection.ts | 3 +- 11 files changed, 617 insertions(+), 21 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 33cf621..ce6d982 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -42,12 +42,12 @@ ## P3 — 体验优化 -- [ ] **右键菜单** — key 列表右键:复制/删除/多选/导出/内存分析/加载文件夹/删除文件夹 -- [ ] **多选增强** — Shift+Click 范围选择,全选复选框 -- [ ] **快捷键增强** — Ctrl+,(设置) / Ctrl+G(命令日志) / Ctrl+W(关闭标签) / Ctrl+S(保存) / Ctrl+L(清CLI) / Ctrl+/(帮助) -- [ ] **连接颜色标记** — 每个连接可选颜色(预定义色板) -- [ ] **连接拖拽排序** — Sortable.js 拖拽重排连接列表 -- [ ] **复制连接** — 右键复制现有连接配置 +- [x] **右键菜单** — key 列表右键:复制/删除/多选/导出/内存分析/加载文件夹/删除文件夹 +- [x] **多选增强** — Shift+Click 范围选择,全选复选框 +- [x] **快捷键增强** — Ctrl+,(设置) / Ctrl+G(命令日志) / Ctrl+W(关闭标签) / Ctrl+S(保存) / Ctrl+L(清CLI) / Ctrl+/(帮助) +- [x] **连接颜色标记** — 每个连接可选颜色(预定义色板) +- [x] **连接拖拽排序** — Sortable.js 拖拽重排连接列表 +- [x] **复制连接** — 右键复制现有连接配置 - [ ] **侧边栏可拖拽** — 调整宽度 200–1500px,持久化 - [ ] **DB 自定义名称** — 用户可重命名 DB0 → 生产库,下拉显示 - [ ] **DB key 数量显示** — 下拉框显示每个 DB 的 key 数量 diff --git a/src/components/CliView.vue b/src/components/CliView.vue index 9919796..7fe55bd 100644 --- a/src/components/CliView.vue +++ b/src/components/CliView.vue @@ -131,6 +131,14 @@ async function execute() { return } + // Handle SELECT — update current DB + if (command === 'SELECT' && args.length > 0) { + const dbNum = parseInt(args[0], 10) + if (!isNaN(dbNum)) { + connStore.currentDb = dbNum + } + } + const result = await window.electronAPI.redis.execute(connStore.activeId, command, args) let formatted = '' @@ -155,6 +163,27 @@ async function execute() { } history.value.push({ cmd, result: formatted }) + + // Dispatch refresh-key-list for write commands + const writeCommands = new Set([ + 'SET', 'SETEX', 'SETNX', 'PSETEX', 'MSET', 'MSETNX', 'APPEND', 'GETSET', 'GETDEL', + 'HSET', 'HSETNX', 'HMSET', 'HDEL', + 'LPUSH', 'LPUSHX', 'RPUSH', 'RPUSHX', 'LSET', 'LINSERT', 'LREM', 'LPOP', 'RPOP', + 'SADD', 'SREM', 'SMOVE', 'SPOP', 'SINTERSTORE', 'SUNIONSTORE', 'SDIFFSTORE', + 'ZADD', 'ZREM', 'ZINCRBY', 'ZPOPMIN', 'ZPOPMAX', 'ZINTERSTORE', 'ZUNIONSTORE', + 'XADD', 'XDEL', 'XTRIM', 'XSETID', 'XGROUP', 'XGROUP', 'XGROUP', + 'DEL', 'UNLINK', 'RENAME', 'RENAMENX', + 'EXPIRE', 'EXPIREAT', 'PEXPIRE', 'PEXPIREAT', 'PERSIST', + 'FLUSHDB', 'FLUSHALL', 'SWAPDB', 'MOVE', 'COPY', + 'GEOADD', 'GEODEL', + 'PFADD', 'PFMERGE', + 'BITOP', 'SETBIT', + 'JSON.SET', 'JSON.DEL', 'JSON.ARRAPPEND', 'JSON.ARRINSERT', 'JSON.ARRPOP', + 'JSON.NUMINCRBY', 'JSON.STRAPPEND', + ]) + if (writeCommands.has(command)) { + window.dispatchEvent(new CustomEvent('refresh-key-list')) + } } catch (err: any) { history.value.push({ cmd, result: `ERROR: ${err.message}`, error: true }) } @@ -345,15 +374,21 @@ function onMonitorMessage(msg: any) { nextTick(() => { if (outputRef.value) outputRef.value.scrollTop = outputRef.value.scrollHeight }) } +function onClearCli() { + history.value = [] +} + onMounted(() => { window.electronAPI.redis.onSubscribeMessage(onSubscribeMessage) window.electronAPI.redis.onMonitorMessage(onMonitorMessage) + window.addEventListener('clear-cli', onClearCli) }) onUnmounted(() => { if (connStore.activeId && streamingMode.value !== 'none') { stopStreaming() } + window.removeEventListener('clear-cli', onClearCli) }) diff --git a/src/components/ConnectionCard.vue b/src/components/ConnectionCard.vue index c1d34b7..f363288 100644 --- a/src/components/ConnectionCard.vue +++ b/src/components/ConnectionCard.vue @@ -1,12 +1,15 @@