diff --git a/pxterm/public/icons/copy.svg b/pxterm/public/icons/copy.svg new file mode 100644 index 0000000..4a08b11 --- /dev/null +++ b/pxterm/public/icons/copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pxterm/src/stores/serverStore.ts b/pxterm/src/stores/serverStore.ts index 78a046b..517e99c 100644 --- a/pxterm/src/stores/serverStore.ts +++ b/pxterm/src/stores/serverStore.ts @@ -198,6 +198,41 @@ export const useServerStore = defineStore("server", () => { } } + async function copyServer(serverId: string): Promise { + const sourceServer = servers.value.find((item) => item.id === serverId); + if (!sourceServer) { + throw new Error("源服务器不存在"); + } + + // 创建新服务器,复制所有信息 + const copiedServer: ServerProfile = { + ...sourceServer, + id: `srv-${crypto.randomUUID()}`, + name: `${sourceServer.name}-copy`, + projectPresets: [...sourceServer.projectPresets], + tags: [...sourceServer.tags], + lastConnectedAt: "" + }; + + // 复制凭据 + try { + const sourceCredential = await getCredentialInput(serverId); + if (sourceCredential) { + await saveCredential(copiedServer.id, sourceCredential); + } + } catch (error) { + // 如果凭据复制失败,仍然创建服务器,但不复制凭据 + console.warn("复制凭据失败:", error); + } + + // 保存新服务器(插入到源服务器后面) + const sourceIndex = servers.value.findIndex((item) => item.id === serverId); + const nextServers = [...servers.value]; + nextServers.splice(sourceIndex + 1, 0, copiedServer); + await persistServerOrder(nextServers); + selectedServerId.value = copiedServer.id; + } + /** * 将指定服务器上移一位。 * 返回: @@ -371,6 +406,7 @@ export const useServerStore = defineStore("server", () => { createServer, saveServer, deleteServer, + copyServer, moveServerUp, moveServerDown, applyServerOrder, diff --git a/pxterm/src/styles/main.css b/pxterm/src/styles/main.css index 36c23fa..ad05db8 100644 --- a/pxterm/src/styles/main.css +++ b/pxterm/src/styles/main.css @@ -637,6 +637,7 @@ select.input { margin-left: 3px; } +.server-copy-btn, .server-ai-btn, .server-move-btn, .connect-icon-btn { @@ -652,12 +653,18 @@ select.input { transition: background-color 0.16s ease, box-shadow 0.16s ease, opacity 0.16s ease; } +.server-copy-icon, .server-ai-icon { width: 22px; height: 22px; background-color: var(--btn); } +.server-copy-btn:hover:not(:disabled) { + background: color-mix(in srgb, var(--accent) 16%, transparent); + border-radius: 6px; +} + .server-move-btn { border-radius: 6px; cursor: grab; diff --git a/pxterm/src/views/ConnectView.vue b/pxterm/src/views/ConnectView.vue index 20d92a3..7f68b82 100644 --- a/pxterm/src/views/ConnectView.vue +++ b/pxterm/src/views/ConnectView.vue @@ -68,6 +68,15 @@

{{ item.name }}

+