diff --git a/ROADMAP.md b/ROADMAP.md index 9272285..fc600e6 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -17,7 +17,7 @@ - [x] **命令日志** — 记录每次命令的耗时/连接/时间,最多 5000 条,只写模式过滤 - [x] **自动更新** — electron-updater 集成,启动检查,下载进度,发行说明 - [x] **多标签页** — 单连接支持同时打开 Status/Keys/CLI/SlowLog/内存分析/批量删除 多个标签 -- [ ] **多连接并行** — 同时打开多个连接,各自独立标签页 +- [x] **多连接并行** — 同时打开多个连接,各自独立标签页 - [ ] **Monaco 编辑器** — 替换 StringEditor 的 textarea,支持 JSON 折叠/展开/语法高亮 - [ ] **Redis 命令自动补全** — CLI 输入时智能提示(基于 commands.js 命令数据库) - [ ] **CLI SUBSCRIBE/MONITOR** — 实时消息流 + MONITOR 命令流 + 停止按钮 diff --git a/src/components/ConnectionCard.vue b/src/components/ConnectionCard.vue index 28ccdd4..c1d34b7 100644 --- a/src/components/ConnectionCard.vue +++ b/src/components/ConnectionCard.vue @@ -9,14 +9,19 @@ const emit = defineEmits<{ edit: [conn: ConnectionConfig]; delete: [id: string] const connStore = useConnectionStore() const isActive = computed(() => connStore.activeId === props.connection.id) +const isConn = computed(() => connStore.isConnectedById(props.connection.id)) const statusColor = computed(() => { - if (isActive.value) return 'var(--success)' + if (isConn.value) return 'var(--success)' return 'var(--text-muted)' }) function handleClick() { - if (isActive.value) { - connStore.disconnect() + if (isConn.value) { + if (isActive.value) { + connStore.disconnect() + } else { + connStore.switchTo(props.connection.id) + } } else { connStore.connect(props.connection) } diff --git a/src/components/MainArea.vue b/src/components/MainArea.vue index f1cc823..b2a70c4 100644 --- a/src/components/MainArea.vue +++ b/src/components/MainArea.vue @@ -133,24 +133,89 @@ function onWheel(e: WheelEvent) { activeTabId.value = tabs.value[nextIdx].id } -// Reset tabs when connection state changes +// Per-connection tab state +const tabStateMap = new Map() + +function saveTabState() { + if (connStore.activeId) { + tabStateMap.set(connStore.activeId, { + tabs: tabs.value, + activeTabId: activeTabId.value, + }) + } +} + +function restoreTabState(connId: string) { + const state = tabStateMap.get(connId) + if (state) { + tabs.value = state.tabs + activeTabId.value = state.activeTabId + } else { + tabs.value = [makeTab('status')] + activeTabId.value = tabs.value[0].id + } +} + +// Connected connections for switcher +const connectedConnections = computed(() => + connStore.connections.filter(c => connStore.connectedIds.includes(c.id)) +) + +// Switch active connection, save/restore tabs watch( - () => connStore.isConnected, - (connected) => { - if (connected) { - tabs.value = [makeTab('status')] - activeTabId.value = tabs.value[0].id + () => connStore.activeId, + (newId, oldId) => { + if (oldId) saveTabState() + if (newId) { + restoreTabState(newId) } else { tabs.value = [] activeTabId.value = '' } } ) + +// Clean up tab state when connection disconnected +watch( + () => connStore.connectedIds, + (ids) => { + // Remove tab state for disconnected connections + for (const key of tabStateMap.keys()) { + if (!ids.includes(key)) { + tabStateMap.delete(key) + } + } + }, + { deep: true } +)