@@ -38,6 +38,11 @@ const contextMenuVisible = ref(false);
|
||||
const contextMenuPosition = ref({ x: 0, y: 0 });
|
||||
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';
|
||||
|
||||
@@ -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 会先触发
|
||||
setTimeout(() => {
|
||||
@@ -426,7 +475,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
// 处理中键点击(在新标签页打开) - 功能已移除
|
||||
|
||||
// 新增:暴露聚焦搜索框的方法
|
||||
// 暴露聚焦搜索框的方法
|
||||
const focusSearchInput = (): boolean => {
|
||||
if (searchInputRef.value) {
|
||||
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="{ 'cursor-pointer': editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) }"
|
||||
@click="editingTagId !== (groupData.tagId === null ? 'untagged' : groupData.tagId) ? toggleGroup(groupData.groupName) : null"
|
||||
@contextmenu.prevent="showTagContextMenu($event, groupData)"
|
||||
>
|
||||
<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]}]"
|
||||
@@ -736,6 +786,33 @@ const cancelEditingTag = () => {
|
||||
</ul>
|
||||
</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 渲染 --- -->
|
||||
<!-- <RemoteDesktopModal
|
||||
v-if="showRdpModal"
|
||||
|
||||
@@ -920,7 +920,15 @@
|
||||
"noResults": "No connections found matching \"{searchTerm}\".",
|
||||
"allConnectionsTaggedSuccess": "All connections tagged successfully.",
|
||||
"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": {
|
||||
"title": "Remote Desktop",
|
||||
|
||||
@@ -1125,7 +1125,15 @@
|
||||
"untagged": "タグなし",
|
||||
"allConnectionsTaggedSuccess": "すべての接続にタグが正常に追加されました。",
|
||||
"noConnectionsToTag": "タグ付けする接続はありません。",
|
||||
"clickToEditTag": "クリックしてタグ名を編集"
|
||||
"clickToEditTag": "クリックしてタグ名を編集",
|
||||
"connectAllInGroup": "グループ内のすべてに接続 (SSH)",
|
||||
"connectingAllInGroup": "グループ '{groupName}' 内のすべてに接続しています...",
|
||||
"noConnectionsInGroup": "グループ '{groupName}' 内に接続可能な項目がありません。",
|
||||
"noConnectionsToConnect": "接続可能な項目がありません",
|
||||
"connectingAllSshInGroup": "グループ '{groupName}' 内の {count} 個の SSH 接続に接続しています...",
|
||||
"noSshConnectionsInGroup": "グループ '{groupName}' 内に接続可能な SSH 接続がありません。",
|
||||
"connectAllSshInGroupMenu": "すべて接続",
|
||||
"noSshConnectionsToConnectMenu": "SSH 接続なし"
|
||||
},
|
||||
"sshKeys": {
|
||||
"selector": {
|
||||
|
||||
@@ -922,7 +922,15 @@
|
||||
"noResults": "未找到匹配 \"{searchTerm}\" 的连接。",
|
||||
"allConnectionsTaggedSuccess": "所有连接已成功添加标签。",
|
||||
"noConnectionsToTag": "没有需要添加标签的连接。",
|
||||
"clickToEditTag": "点击编辑标签名称"
|
||||
"clickToEditTag": "点击编辑标签名称",
|
||||
"connectAllInGroup": "连接组内全部 (SSH)",
|
||||
"connectingAllInGroup": "正在连接组 '{groupName}' 中的所有连接...",
|
||||
"noConnectionsInGroup": "组 '{groupName}' 中没有可连接的项。",
|
||||
"noConnectionsToConnect": "没有可连接的项",
|
||||
"connectingAllSshInGroup": "正在连接组 '{groupName}' 中的 {count} 个 SSH 连接...",
|
||||
"noSshConnectionsInGroup": "组 '{groupName}' 中没有 SSH 类型的连接可供连接。",
|
||||
"connectAllSshInGroupMenu": "连接全部",
|
||||
"noSshConnectionsToConnectMenu": "无 SSH 连接"
|
||||
},
|
||||
"remoteDesktopModal": {
|
||||
"title": "远程桌面",
|
||||
|
||||
Reference in New Issue
Block a user