@@ -38,6 +38,11 @@ const contextMenuVisible = ref(false);
|
|||||||
const contextMenuPosition = ref({ x: 0, y: 0 });
|
const contextMenuPosition = ref({ x: 0, y: 0 });
|
||||||
const contextTargetConnection = ref<ConnectionInfo | null>(null);
|
const contextTargetConnection = ref<ConnectionInfo | null>(null);
|
||||||
|
|
||||||
|
// 标签右键菜单状态
|
||||||
|
const tagContextMenuVisible = ref(false);
|
||||||
|
const tagContextMenuPosition = ref({ x: 0, y: 0 });
|
||||||
|
const contextTargetTagGroup = ref<(typeof filteredAndGroupedConnections.value)[0] | null>(null);
|
||||||
|
|
||||||
// +++ 本地存储键名 +++
|
// +++ 本地存储键名 +++
|
||||||
const EXPANDED_GROUPS_STORAGE_KEY = 'workspaceConnectionListExpandedGroups';
|
const EXPANDED_GROUPS_STORAGE_KEY = 'workspaceConnectionListExpandedGroups';
|
||||||
|
|
||||||
@@ -380,6 +385,50 @@ const handleMenuAction = (action: 'add' | 'edit' | 'delete' | 'clone') => { //
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 显示标签右键菜单
|
||||||
|
const showTagContextMenu = (event: MouseEvent, groupData: (typeof filteredAndGroupedConnections.value)[0]) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation(); // 阻止事件冒泡到上层,例如关闭连接右键菜单的 document click listener
|
||||||
|
closeContextMenu(); // 如果连接的右键菜单是打开的,先关闭它
|
||||||
|
contextTargetTagGroup.value = groupData;
|
||||||
|
tagContextMenuPosition.value = { x: event.clientX, y: event.clientY };
|
||||||
|
tagContextMenuVisible.value = true;
|
||||||
|
// 添加全局点击监听器以关闭菜单
|
||||||
|
document.addEventListener('click', closeTagContextMenu, { once: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭标签右键菜单
|
||||||
|
const closeTagContextMenu = () => {
|
||||||
|
tagContextMenuVisible.value = false;
|
||||||
|
// contextTargetTagGroup.value = null; // 保留 targetGroup 直到菜单完全消失,以便动画(如果未来添加)
|
||||||
|
document.removeEventListener('click', closeTagContextMenu);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理标签右键菜单操作
|
||||||
|
const handleTagMenuAction = (action: 'connectAll') => {
|
||||||
|
const group = contextTargetTagGroup.value;
|
||||||
|
closeTagContextMenu(); // 先关闭菜单
|
||||||
|
|
||||||
|
if (group && action === 'connectAll') {
|
||||||
|
const sshConnections = group.connections.filter(conn => conn.type === 'SSH');
|
||||||
|
|
||||||
|
if (sshConnections.length > 0) {
|
||||||
|
sshConnections.forEach(conn => {
|
||||||
|
emitWorkspaceEvent('connection:connect', { connectionId: conn.id });
|
||||||
|
});
|
||||||
|
uiNotificationsStore.addNotification({
|
||||||
|
message: t('workspaceConnectionList.connectingAllSshInGroup', { count: sshConnections.length, groupName: group.groupName }),
|
||||||
|
type: 'info',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uiNotificationsStore.addNotification({
|
||||||
|
message: t('workspaceConnectionList.noSshConnectionsInGroup', { groupName: group.groupName }),
|
||||||
|
type: 'info',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 稍微延迟一下重置,以防是点击列表项导致的失焦
|
// 稍微延迟一下重置,以防是点击列表项导致的失焦
|
||||||
// 如果用户点击了列表项,handleConnect 会先触发
|
// 如果用户点击了列表项,handleConnect 会先触发
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -426,7 +475,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
// 处理中键点击(在新标签页打开) - 功能已移除
|
// 处理中键点击(在新标签页打开) - 功能已移除
|
||||||
|
|
||||||
// 新增:暴露聚焦搜索框的方法
|
// 暴露聚焦搜索框的方法
|
||||||
const focusSearchInput = (): boolean => {
|
const focusSearchInput = (): boolean => {
|
||||||
if (searchInputRef.value) {
|
if (searchInputRef.value) {
|
||||||
searchInputRef.value.focus();
|
searchInputRef.value.focus();
|
||||||
@@ -634,6 +683,7 @@ const cancelEditingTag = () => {
|
|||||||
class="group px-3 py-2 font-semibold flex items-center text-foreground rounded-md hover:bg-header/80 transition-colors duration-150"
|
class="group px-3 py-2 font-semibold flex items-center text-foreground rounded-md hover:bg-header/80 transition-colors duration-150"
|
||||||
:class="{ 'cursor-pointer': editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) }"
|
:class="{ 'cursor-pointer': editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) }"
|
||||||
@click="editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) ? toggleGroup(groupData.groupName) : null"
|
@click="editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) ? toggleGroup(groupData.groupName) : null"
|
||||||
|
@contextmenu.prevent="showTagContextMenu($event, groupData)"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
:class="['fas', expandedGroups[groupData.groupName] ? 'fa-chevron-down' : 'fa-chevron-right', 'mr-2 w-4 text-center text-text-secondary group-hover:text-foreground transition-transform duration-200 ease-in-out', {'transform rotate-0': !expandedGroups[groupData.groupName]}]"
|
:class="['fas', expandedGroups[groupData.groupName] ? 'fa-chevron-down' : 'fa-chevron-right', 'mr-2 w-4 text-center text-text-secondary group-hover:text-foreground transition-transform duration-200 ease-in-out', {'transform rotate-0': !expandedGroups[groupData.groupName]}]"
|
||||||
@@ -736,6 +786,33 @@ const cancelEditingTag = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 标签右键菜单 -->
|
||||||
|
<div
|
||||||
|
v-if="tagContextMenuVisible"
|
||||||
|
class="fixed bg-background border border-border/50 shadow-xl rounded-lg py-1.5 z-50 min-w-[200px]"
|
||||||
|
:style="{ top: `${tagContextMenuPosition.y}px`, left: `${tagContextMenuPosition.x}px` }"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<ul class="list-none p-0 m-0">
|
||||||
|
<li
|
||||||
|
v-if="contextTargetTagGroup && contextTargetTagGroup.connections.some((c: ConnectionInfo) => c.type === 'SSH')"
|
||||||
|
class="group px-4 py-1.5 cursor-pointer flex items-center text-foreground hover:bg-primary/10 hover:text-primary text-sm transition-colors duration-150 rounded-md mx-1"
|
||||||
|
@click="handleTagMenuAction('connectAll')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-network-wired mr-3 w-4 text-center text-text-secondary group-hover:text-primary"></i>
|
||||||
|
<span>{{ t('workspaceConnectionList.connectAllSshInGroupMenu') }}</span>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-else-if="contextTargetTagGroup"
|
||||||
|
class="group px-4 py-1.5 flex items-center text-text-disabled text-sm rounded-md mx-1 cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<i class="fas fa-ban mr-3 w-4 text-center text-text-disabled"></i>
|
||||||
|
<span>{{ t('workspaceConnectionList.noSshConnectionsToConnectMenu') }}</span>
|
||||||
|
</li>
|
||||||
|
<!-- Future: Add "Rename Tag" or "Delete Tag (if empty)" options here -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- --- 移除 RDP Modal 渲染 --- -->
|
<!-- --- 移除 RDP Modal 渲染 --- -->
|
||||||
<!-- <RemoteDesktopModal
|
<!-- <RemoteDesktopModal
|
||||||
v-if="showRdpModal"
|
v-if="showRdpModal"
|
||||||
|
|||||||
@@ -920,7 +920,15 @@
|
|||||||
"noResults": "No connections found matching \"{searchTerm}\".",
|
"noResults": "No connections found matching \"{searchTerm}\".",
|
||||||
"allConnectionsTaggedSuccess": "All connections tagged successfully.",
|
"allConnectionsTaggedSuccess": "All connections tagged successfully.",
|
||||||
"noConnectionsToTag": "No connections to tag.",
|
"noConnectionsToTag": "No connections to tag.",
|
||||||
"clickToEditTag": "Click to edit tag name"
|
"clickToEditTag": "Click to edit tag name",
|
||||||
|
"connectAllInGroup": "Connect All in Group (SSH)",
|
||||||
|
"connectingAllInGroup": "Connecting all in group '{groupName}'...",
|
||||||
|
"noConnectionsInGroup": "No connections to connect in group '{groupName}'.",
|
||||||
|
"noConnectionsToConnect": "No connections to connect",
|
||||||
|
"connectingAllSshInGroup": "Connecting {count} SSH connections in group '{groupName}'...",
|
||||||
|
"noSshConnectionsInGroup": "No SSH connections to connect in group '{groupName}'.",
|
||||||
|
"connectAllSshInGroupMenu": "Connect All",
|
||||||
|
"noSshConnectionsToConnectMenu": "No SSH Connections"
|
||||||
},
|
},
|
||||||
"remoteDesktopModal": {
|
"remoteDesktopModal": {
|
||||||
"title": "Remote Desktop",
|
"title": "Remote Desktop",
|
||||||
|
|||||||
@@ -1125,7 +1125,15 @@
|
|||||||
"untagged": "タグなし",
|
"untagged": "タグなし",
|
||||||
"allConnectionsTaggedSuccess": "すべての接続にタグが正常に追加されました。",
|
"allConnectionsTaggedSuccess": "すべての接続にタグが正常に追加されました。",
|
||||||
"noConnectionsToTag": "タグ付けする接続はありません。",
|
"noConnectionsToTag": "タグ付けする接続はありません。",
|
||||||
"clickToEditTag": "クリックしてタグ名を編集"
|
"clickToEditTag": "クリックしてタグ名を編集",
|
||||||
|
"connectAllInGroup": "グループ内のすべてに接続 (SSH)",
|
||||||
|
"connectingAllInGroup": "グループ '{groupName}' 内のすべてに接続しています...",
|
||||||
|
"noConnectionsInGroup": "グループ '{groupName}' 内に接続可能な項目がありません。",
|
||||||
|
"noConnectionsToConnect": "接続可能な項目がありません",
|
||||||
|
"connectingAllSshInGroup": "グループ '{groupName}' 内の {count} 個の SSH 接続に接続しています...",
|
||||||
|
"noSshConnectionsInGroup": "グループ '{groupName}' 内に接続可能な SSH 接続がありません。",
|
||||||
|
"connectAllSshInGroupMenu": "すべて接続",
|
||||||
|
"noSshConnectionsToConnectMenu": "SSH 接続なし"
|
||||||
},
|
},
|
||||||
"sshKeys": {
|
"sshKeys": {
|
||||||
"selector": {
|
"selector": {
|
||||||
|
|||||||
@@ -922,7 +922,15 @@
|
|||||||
"noResults": "未找到匹配 \"{searchTerm}\" 的连接。",
|
"noResults": "未找到匹配 \"{searchTerm}\" 的连接。",
|
||||||
"allConnectionsTaggedSuccess": "所有连接已成功添加标签。",
|
"allConnectionsTaggedSuccess": "所有连接已成功添加标签。",
|
||||||
"noConnectionsToTag": "没有需要添加标签的连接。",
|
"noConnectionsToTag": "没有需要添加标签的连接。",
|
||||||
"clickToEditTag": "点击编辑标签名称"
|
"clickToEditTag": "点击编辑标签名称",
|
||||||
|
"connectAllInGroup": "连接组内全部 (SSH)",
|
||||||
|
"connectingAllInGroup": "正在连接组 '{groupName}' 中的所有连接...",
|
||||||
|
"noConnectionsInGroup": "组 '{groupName}' 中没有可连接的项。",
|
||||||
|
"noConnectionsToConnect": "没有可连接的项",
|
||||||
|
"connectingAllSshInGroup": "正在连接组 '{groupName}' 中的 {count} 个 SSH 连接...",
|
||||||
|
"noSshConnectionsInGroup": "组 '{groupName}' 中没有 SSH 类型的连接可供连接。",
|
||||||
|
"connectAllSshInGroupMenu": "连接全部",
|
||||||
|
"noSshConnectionsToConnectMenu": "无 SSH 连接"
|
||||||
},
|
},
|
||||||
"remoteDesktopModal": {
|
"remoteDesktopModal": {
|
||||||
"title": "远程桌面",
|
"title": "远程桌面",
|
||||||
|
|||||||
Reference in New Issue
Block a user