refactor: 引入 workspaceEvents 并迁移核心组件事件处理

This commit is contained in:
Baobhan Sith
2025-05-09 13:45:20 +08:00
parent 70b2d92d8a
commit fdf5c18dfb
12 changed files with 231 additions and 236 deletions
@@ -8,19 +8,14 @@ import { useSettingsStore } from '../stores/settings.store';
import { useQuickCommandsStore } from '../stores/quickCommands.store';
import { useCommandHistoryStore } from '../stores/commandHistory.store';
import QuickCommandsModal from './QuickCommandsModal.vue'; // +++ Import the modal component +++
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
// Disable attribute inheritance as this component has multiple root nodes (div + modal)
defineOptions({ inheritAttrs: false });
const emit = defineEmits([
'send-command',
'search',
'find-next',
'find-previous',
'close-search',
'clear-terminal',
'toggle-virtual-keyboard' // +++ Add new emit +++
]);
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
const emit = defineEmits(['toggle-virtual-keyboard']);
const { t } = useI18n();
const focusSwitcherStore = useFocusSwitcherStore();
const settingsStore = useSettingsStore();
@@ -72,7 +67,7 @@ const currentSessionCommandInput = computed({
const sendCommand = () => {
const command = currentSessionCommandInput.value; // 使用计算属性获取值
console.log(`[CommandInputBar] Sending command: ${command || '<Enter>'} `);
emit('send-command', command);
emitWorkspaceEvent('terminal:sendCommand', { command });
// 清空 store 中的值
if (activeSessionId.value) {
updateSessionCommandInput(activeSessionId.value, '');
@@ -83,7 +78,7 @@ const toggleSearch = () => {
isSearching.value = !isSearching.value;
if (!isSearching.value) {
searchTerm.value = ''; // 关闭搜索时清空
emit('close-search'); // 通知父组件关闭搜索
emitWorkspaceEvent('search:close'); // 通知父组件关闭搜索
} else {
// 可以在这里聚焦搜索输入框
// nextTick(() => searchInputRef.value?.focus());
@@ -91,16 +86,16 @@ const toggleSearch = () => {
};
const performSearch = () => {
emit('search', searchTerm.value);
emitWorkspaceEvent('search:start', { term: searchTerm.value });
// 实际的计数更新逻辑应该由父组件通过 props 或事件传递回来
};
const findNext = () => {
emit('find-next');
emitWorkspaceEvent('search:findNext');
};
const findPrevious = () => {
emit('find-previous');
emitWorkspaceEvent('search:findPrevious');
};
// 监听搜索词变化,执行搜索
@@ -151,7 +146,7 @@ const handleCommandInputKeydown = (event: KeyboardEvent) => {
if (selectedCommand !== undefined) {
event.preventDefault();
console.log(`[CommandInputBar] Enter detected with selection. Sending selected command: ${selectedCommand}`);
emit('send-command', selectedCommand); // 发送选中命令 (移除多余的 \n)
emitWorkspaceEvent('terminal:sendCommand', { command: selectedCommand }); // 发送选中命令
if (activeSessionId.value) {
updateSessionCommandInput(activeSessionId.value, ''); // 清空输入框
}
@@ -190,7 +185,7 @@ const handleCommandInputKeydown = (event: KeyboardEvent) => {
// Handle Ctrl+C when input is empty
event.preventDefault();
console.log('[CommandInputBar] Ctrl+C detected with empty input. Sending SIGINT.');
emit('send-command', '\x03'); // Send ETX character (Ctrl+C)
emitWorkspaceEvent('terminal:sendCommand', { command: '\x03' }); // Send ETX character (Ctrl+C)
} else if (!event.altKey && event.key === 'Enter') {
// Handle regular Enter key press - send current input (empty or not)
event.preventDefault(); // Prevent default if needed, e.g., form submission
@@ -288,7 +283,7 @@ const closeQuickCommandsModal = () => {
// +++ Handler for command execution from the modal +++
const handleQuickCommandExecute = (command: string) => {
console.log(`[CommandInputBar] Executing quick command: ${command}`);
emit('send-command', command); // Emit the command to the parent
emitWorkspaceEvent('terminal:sendCommand', { command }); // Emit the command to the parent
closeQuickCommandsModal(); // Close the modal after selection
};
</script>
@@ -298,7 +293,7 @@ const handleQuickCommandExecute = (command: string) => {
<div class="flex-grow flex items-center bg-transparent relative gap-1 px-2"> <!-- Added px-2 here -->
<!-- Clear Terminal Button -->
<button
@click="emit('clear-terminal')"
@click="emitWorkspaceEvent('terminal:clear')"
class="flex-shrink-0 flex items-center justify-center w-8 h-8 border border-border/50 rounded-lg text-text-secondary transition-colors duration-200 hover:bg-border hover:text-foreground"
:title="t('commandInputBar.clearTerminal', '清空终端')"
>
@@ -10,8 +10,10 @@ import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++
import { useSessionStore } from '../stores/session.store'; // +++ 导入会话 Store +++
import { useSettingsStore } from '../stores/settings.store'; // +++ 导入设置 Store +++
import { storeToRefs } from 'pinia'; // +++ 导入 storeToRefs +++
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
const { t } = useI18n();
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
const sessionStore = useSessionStore(); // +++ 实例化会话 Store +++
const settingsStore = useSettingsStore(); // +++ 实例化设置 Store +++
@@ -33,18 +35,7 @@ const props = defineProps({
},
});
// --- Emits ---
const emit = defineEmits<{
(e: 'activate-tab', tabId: string): void;
(e: 'close-tab', tabId: string): void;
(e: 'request-save', tabId: string): void; // 发送保存请求,携带 tabId
(e: 'update:content', payload: { tabId: string; content: string }): void; // 用于 v-model 同步
(e: 'change-encoding', payload: { tabId: string; encoding: string }): void; // +++ 新增:编码更改事件 +++
// +++ 新增:传递右键菜单关闭事件 +++
(e: 'close-other-tabs', tabId: string): void;
(e: 'close-tabs-to-right', tabId: string): void;
(e: 'close-tabs-to-left', tabId: string): void;
}>();
// --- 计算属性,用于模板绑定 ---
@@ -112,7 +103,7 @@ watch(localEditorContent, (newContent) => {
if (activeTab.value && newContent !== activeTab.value.content) {
// console.log(`[EditorContainer] Emitting update:content for tab ${activeTab.value.id}`);
// 只有当内容实际改变时才发出事件
emit('update:content', { tabId: activeTab.value.id, content: newContent });
emitWorkspaceEvent('editor:updateContent', { tabId: activeTab.value.id, content: newContent });
// 注意:isModified 状态应该由 Store 根据 content 和 originalContent 计算
}
});
@@ -199,7 +190,7 @@ const encodingOptions = ref([
// --- 事件处理 ---
const handleSaveRequest = () => {
if (activeTab.value) {
emit('request-save', activeTab.value.id); // 发出保存请求事件
emitWorkspaceEvent('editor:saveTab', { tabId: activeTab.value.id }); // 发出保存请求事件
}
};
@@ -209,7 +200,7 @@ const handleEncodingChange = (event: Event) => {
const newEncoding = target.value;
if (activeTab.value && newEncoding && newEncoding !== currentSelectedEncoding.value) {
console.log(`[EditorContainer] Encoding changed to ${newEncoding} for tab ${activeTab.value.id}`);
emit('change-encoding', { tabId: activeTab.value.id, encoding: newEncoding });
emitWorkspaceEvent('editor:changeEncoding', { tabId: activeTab.value.id, encoding: newEncoding });
}
};
@@ -278,7 +269,7 @@ const handleKeyDown = (event: KeyboardEvent) => {
const nextTabId = props.tabs[nextIndex]?.id;
if (nextTabId) {
emit('activate-tab', nextTabId);
emitWorkspaceEvent('editor:activateTab', { tabId: nextTabId });
}
}
};
@@ -292,11 +283,11 @@ const handleKeyDown = (event: KeyboardEvent) => {
<FileEditorTabs
:tabs="orderedTabs"
:active-tab-id="props.activeTabId"
@activate-tab="(tabId: string) => emit('activate-tab', tabId)"
@close-tab="(tabId: string) => emit('close-tab', tabId)"
@close-other-tabs="(tabId: string) => emit('close-other-tabs', tabId)"
@close-tabs-to-right="(tabId: string) => emit('close-tabs-to-right', tabId)"
@close-tabs-to-left="(tabId: string) => emit('close-tabs-to-left', tabId)"
@activate-tab="(tabId: string) => emitWorkspaceEvent('editor:activateTab', { tabId })"
@close-tab="(tabId: string) => emitWorkspaceEvent('editor:closeTab', { tabId })"
@close-other-tabs="(tabId: string) => emitWorkspaceEvent('editor:closeOtherTabs', { tabId })"
@close-tabs-to-right="(tabId: string) => emitWorkspaceEvent('editor:closeTabsToRight', { tabId })"
@close-tabs-to-left="(tabId: string) => emitWorkspaceEvent('editor:closeTabsToLeft', { tabId })"
/>
<!-- 2. 编辑器头部 (显示当前激活标签信息) -->
@@ -11,7 +11,7 @@ import { useFileEditorStore } from '../stores/fileEditor.store'; // <-- Import F
import { useSettingsStore } from '../stores/settings.store'; // +++ Import SettingsStore +++
import { useSidebarResize } from '../composables/useSidebarResize'; // +++ Import useSidebarResize +++
import { storeToRefs } from 'pinia';
import { defineEmits } from 'vue';
// import { defineEmits } from 'vue'; // --- 移除 ---
// --- Props ---
const props = defineProps({
@@ -44,39 +44,9 @@ const props = defineProps({
type: Boolean,
default: false,
},
// Removed terminalManager prop definition
});
// --- Emits ---
// *** 新增:声明所有需要转发的事件 (使用对象语法) ***
const emit = defineEmits({
'sendCommand': null, // (command: string) - No validation needed here for now
'terminalInput': null, // (payload: { sessionId: string; data: string })
'terminalResize': null, // (payload: { sessionId: string; dims: { cols: number; rows: number } })
'closeEditorTab': null, // (tabId: string)
'activateEditorTab': null, // (tabId: string)
'updateEditorContent': null, // (payload: { tabId: string; content: string })
'saveEditorTab': null, // (tabId: string)
'connect-request': null, // (id: number)
'open-new-session': null, // (id: number)
'request-add-connection': null, // ()
'request-edit-connection': null, // (conn: any)
// *** 修正:更新 terminal-ready 事件的 payload 类型 ***
'terminal-ready': (payload: { sessionId: string; terminal: any }) => // 使用 any 简化类型检查,或导入 Terminal
typeof payload === 'object' && typeof payload.sessionId === 'string' && typeof payload.terminal === 'object',
// *** 新增:声明搜索相关事件 ***
'search': null, // (searchTerm: string)
'find-next': null, // ()
'find-previous': null, // ()
'close-search': null, // ()
'clear-terminal': null, // () +++ 添加 clear-terminal 事件 +++
'change-encoding': null, // +++ 添加 change-encoding 事件 +++
// +++ 添加文件编辑器标签页关闭事件 +++
'close-other-tabs': null, // (tabId: string)
'close-tabs-to-right': null, // (tabId: string)
'close-tabs-to-left': null, // (tabId: string)
// --- 移除 RDP 事件 ---
});
// --- Setup ---
const layoutStore = useLayoutStore();
@@ -160,17 +130,8 @@ const componentProps = computed(() => {
return {
sessionId: props.activeSessionId ?? '', // 如果 activeSessionId 为 null,则传递空字符串
isActive: true,
// *** 添加日志并修正事件处理 ***
onReady: (payload: { sessionId: string; terminal: any }) => {
console.log(`[LayoutRenderer ${props.activeSessionId}] 收到内部 Terminal 的 'ready' 事件:`, payload); // 添加日志
emit('terminal-ready', payload); // 直接转发收到的 payload
},
onData: (data: string) => emit('terminalInput', { sessionId: props.activeSessionId ?? '', data }), // 包装成 payload,确保 sessionId 不为 null
onResize: (dims: { cols: number; rows: number }) => emit('terminalResize', { sessionId: props.activeSessionId ?? '', dims }), // 包装成 payload,确保 sessionId 不为 null
// --- 移除事件转发 ---
};
// --- 添加日志:确认 onReady 是否在 props 中 ---
console.log(`[LayoutRenderer ${props.activeSessionId}] Terminal componentProps 计算完成,包含 onReady。`);
// -----------------------------------------
case 'fileManager':
// 仅当有活动会话时才返回实际 props,否则返回空对象
if (!currentActiveSession) return {};
@@ -208,50 +169,26 @@ const componentProps = computed(() => {
activeTabId: props.activeEditorTabId, // 从 WorkspaceView 传入
sessionId: props.activeSessionId,
class: 'pane-content',
// 绑定内部处理器以转发事件 (恢复正确的编辑器事件)
onCloseTab: (tabId: string) => emit('closeEditorTab', tabId),
onActivateTab: (tabId: string) => emit('activateEditorTab', tabId),
'onUpdate:content': (payload: { tabId: string; content: string }) => emit('updateEditorContent', payload), // 注意事件名
onRequestSave: (tabId: string) => emit('saveEditorTab', tabId),
// +++ 添加:转发 change-encoding 事件 +++
onChangeEncoding: (payload: { tabId: string; encoding: string }) => emit('change-encoding', payload),
// +++ 添加:转发其他关闭事件 +++
onCloseOtherTabs: (tabId: string) => emit('close-other-tabs', tabId),
onCloseTabsToRight: (tabId: string) => emit('close-tabs-to-right', tabId),
onCloseTabsToLeft: (tabId: string) => emit('close-tabs-to-left', tabId),
// --- 移除事件转发 ---
};
case 'commandBar':
// CommandInputBar 需要转发 send-command 事件
// searchResultCount 和 currentSearchResultIndex 将在模板中直接从 terminalManager 绑定
return {
class: 'pane-content',
onSendCommand: (command: string) => emit('sendCommand', command),
// 转发搜索事件
onSearch: (term: string) => emit('search', term),
onFindNext: () => emit('find-next'),
onFindPrevious: () => emit('find-previous'),
onCloseSearch: () => emit('close-search'),
onClearTerminal: () => emit('clear-terminal'), // --- 移除日志 ---
// --- 移除事件转发 ---
};
case 'connections':
// WorkspaceConnectionList 需要转发 connect-request 等事件
return {
class: 'pane-content',
// 绑定内部处理器以转发事件 (除了 request-add-connection)
onConnectRequest: (id: number) => emit('connect-request', id),
onOpenNewSession: (id: number) => emit('open-new-session', id),
// onRequestAddConnection: () => { ... }, // 移除,将在模板中处理
onRequestEditConnection: (conn: any) => emit('request-edit-connection', conn),
// --- 移除事件转发 ---
};
case 'commandHistory':
case 'quickCommands':
// 这两个视图需要转发 execute-command 事件
return {
class: 'flex flex-col flex-grow h-full overflow-auto', // 移除 pane-content,保留填充类
onExecuteCommand: (command: string) => emit('sendCommand', command), // 复用 sendCommand 事件
// --- 移除事件转发 ---
};
case 'dockerManager':
// DockerManager 可能不需要 session 信息,但需要转发事件
// DockerManager 可能不需要 session 信息
return {
class: 'flex-grow h-full overflow-hidden', // <-- 修改:添加 flex-grow 和 h-full,并保留 overflow-hidden
// 假设 DockerManager 会发出 'docker-command' 事件
@@ -277,30 +214,12 @@ const sidebarProps = computed(() => (paneName: PaneName | null, side: 'left' | '
tabs: editorTabsFromStore.value, // Access .value for refs from storeToRefs
activeTabId: activeEditorTabIdFromStore.value, // Access .value
sessionId: props.activeSessionId,
// Event forwarding
onCloseTab: (tabId: string) => emit('closeEditorTab', tabId),
onActivateTab: (tabId: string) => emit('activateEditorTab', tabId),
'onUpdate:content': (payload: { tabId: string; content: string }) => emit('updateEditorContent', payload),
onRequestSave: (tabId: string) => emit('saveEditorTab', tabId),
// --- 移除事件转发 ---
};
case 'connections':
return {
...baseProps,
// Event forwarding
onConnectRequest: (id: number) => emit('connect-request', id),
onOpenNewSession: (id: number) => {
console.log(`[LayoutRenderer Sidebar] Forwarding 'open-new-session' for ID: ${id}`);
emit('open-new-session', id);
},
onRequestEditConnection: (conn: any) => {
console.log(`[LayoutRenderer Sidebar] Forwarding 'request-edit-connection'`);
emit('request-edit-connection', conn);
},
// Forward 'request-add-connection' from sidebar context
onRequestAddConnection: () => {
console.log(`[LayoutRenderer Sidebar] Forwarding 'request-add-connection'`);
emit('request-add-connection');
},
// --- 移除事件转发 ---
};
case 'fileManager':
// Only provide props if there's an active session
@@ -508,27 +427,6 @@ onMounted(() => {
:active-session-id="activeSessionId"
:editor-tabs="editorTabs"
:active-editor-tab-id="activeEditorTabId"
@send-command="emit('sendCommand', $event)"
@terminal-input="emit('terminalInput', $event)"
@terminal-resize="emit('terminalResize', $event)"
@terminal-ready="emit('terminal-ready', $event)"
@close-editor-tab="emit('closeEditorTab', $event)"
@activate-editor-tab="emit('activateEditorTab', $event)"
@update-editor-content="emit('updateEditorContent', $event)"
@save-editor-tab="emit('saveEditorTab', $event)"
@connect-request="emit('connect-request', $event)"
@open-new-session="emit('open-new-session', $event)"
@request-add-connection="() => emit('request-add-connection')"
@request-edit-connection="emit('request-edit-connection', $event)"
@search="emit('search', $event)"
@find-next="emit('find-next')"
@find-previous="emit('find-previous')"
@close-search="emit('close-search')"
@clear-terminal="() => emit('clear-terminal')"
@change-encoding="emit('change-encoding', $event)"
@close-other-tabs="emit('close-other-tabs', $event)"
@close-tabs-to-right="emit('close-tabs-to-right', $event)"
@close-tabs-to-left="emit('close-tabs-to-left', $event)"
class="flex-grow overflow-auto"
/>
</pane>
@@ -597,7 +495,6 @@ onMounted(() => {
v-if="layoutNode.component === 'connections'"
:is="currentMainComponent"
v-bind="componentProps"
@request-add-connection="() => emit('request-add-connection')"
class="flex-grow overflow-auto"
/>
<component
+17 -19
View File
@@ -1,13 +1,14 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
import { useAppearanceStore } from '../stores/appearance.store';
import { useSettingsStore } from '../stores/settings.store';
import { storeToRefs } from 'pinia';
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
import { useAppearanceStore } from '../stores/appearance.store';
import { useSettingsStore } from '../stores/settings.store';
import { storeToRefs } from 'pinia';
import { FitAddon } from '@xterm/addon-fit'; // Updated import path
import { WebLinksAddon } from 'xterm-addon-web-links';
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search';
import 'xterm/css/xterm.css';
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search';
import 'xterm/css/xterm.css';
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
// 定义 props 和 emits
@@ -18,12 +19,9 @@ const props = defineProps<{
options?: object; // xterm 的配置选项
}>();
const emit = defineEmits<{
(e: 'data', data: string): void; // 用户输入事件
(e: 'resize', dimensions: { cols: number; rows: number }): void; // 终端大小调整事件
// *** 更新 ready 事件 payload,包含 searchAddon ***
(e: 'ready', payload: { sessionId: string; terminal: Terminal; searchAddon: SearchAddon | null }): void;
}>();
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
const terminalRef = ref<HTMLElement | null>(null); // 终端容器的引用
let terminal: Terminal | null = null;
@@ -70,7 +68,7 @@ const debouncedEmitResize = debounce((term: Terminal) => {
if (term && props.isActive) { // 仅当标签仍处于活动状态时才发送防抖后的 resize
const dimensions = { cols: term.cols, rows: term.rows };
console.log(`[Terminal ${props.sessionId}] Debounced resize emit (from ResizeObserver):`, dimensions);
emit('resize', dimensions);
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
// *** 新增:尝试在发送 resize 后强制刷新终端显示 ***
try {
term.refresh(0, term.rows - 1); // Refresh entire viewport
@@ -92,7 +90,7 @@ const fitAndEmitResizeNow = (term: Terminal) => {
fitAddon?.fit();
const dimensions = { cols: term.cols, rows: term.rows };
console.log(`[Terminal ${props.sessionId}] Immediate resize emit:`, dimensions);
emit('resize', dimensions);
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
// *** 恢复:仅使用 nextTick 触发 window resize ***
// 使用 nextTick 确保 fit() 的效果已反映,再触发 resize
@@ -162,11 +160,11 @@ onMounted(() => {
// 适应容器大小
fitAddon.fit();
emit('resize', { cols: terminal.cols, rows: terminal.rows }); // 触发初始 resize 事件
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: { cols: terminal.cols, rows: terminal.rows } }); // 触发初始 resize 事件
// 监听用户输入
terminal.onData((data) => {
emit('data', data);
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data });
});
// 监听终端大小变化 (通过 ResizeObserver) - 主要处理浏览器窗口大小变化等
@@ -244,7 +242,7 @@ onMounted(() => {
// 触发 ready 事件,传递 sessionId, terminal 和 searchAddon 实例
if (terminal) {
emit('ready', { sessionId: props.sessionId, terminal: terminal, searchAddon: searchAddon });
emitWorkspaceEvent('terminal:ready', { sessionId: props.sessionId, terminal: terminal, searchAddon: searchAddon });
}
// --- 监听并处理选中即复制 ---
@@ -359,7 +357,7 @@ onMounted(() => {
const text = await navigator.clipboard.readText();
if (text) {
// 将粘贴的文本发送到后端,模拟用户输入
emit('data', text);
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data: text });
console.log('[Terminal] Pasted via Ctrl+Shift+V');
}
} catch (err) {
@@ -380,7 +378,7 @@ onMounted(() => {
const text = await navigator.clipboard.readText();
if (text && terminal) {
// 将粘贴的文本发送到后端
emit('data', text);
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data: text });
console.log('[Terminal] Pasted via Right Click');
}
} catch (err) {
@@ -9,11 +9,13 @@ import TabBarContextMenu from './TabBarContextMenu.vue';
import { useSessionStore } from '../stores/session.store';
import { useConnectionsStore, type ConnectionInfo } from '../stores/connections.store';
import { useLayoutStore, type PaneName } from '../stores/layout.store';
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ +++
import type { SessionTabInfoWithStatus } from '../stores/session.store';
const { t } = useI18n(); // i18n
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ +++
const layoutStore = useLayoutStore(); // store
const connectionsStore = useConnectionsStore();
const { isHeaderVisible } = storeToRefs(layoutStore); // layout store
@@ -37,30 +39,21 @@ const props = defineProps({
},
});
// (使)
// ( update:sessions v-model)
const emit = defineEmits<{
(e: 'activate-session', sessionId: string): void;
(e: 'close-session', sessionId: string): void;
(e: 'open-layout-configurator'): void;
(e: 'request-add-connection-from-popup'): void;
(e: 'request-edit-connection-from-popup', connection: any): void; // any 使 ConnectionInfo
// +
(e: 'close-other-sessions', sessionId: string): void;
(e: 'close-sessions-to-right', sessionId: string): void;
(e: 'close-sessions-to-left', sessionId: string): void;
(e: 'update:sessions', newSessions: SessionTabInfoWithStatus[]): void; // + Add event for reordering
(e: 'update:sessions', newSessions: SessionTabInfoWithStatus[]): void;
}>();
const activateSession = (sessionId: string) => {
if (sessionId !== props.activeSessionId) {
emit('activate-session', sessionId);
emitWorkspaceEvent('session:activate', { sessionId });
}
};
const closeSession = (event: MouseEvent, sessionId: string) => {
event.stopPropagation(); //
emit('close-session', sessionId);
emitWorkspaceEvent('session:close', { sessionId });
};
// --- ---
@@ -109,15 +102,15 @@ const handlePopupConnect = (connectionId: number) => {
const handleRequestAddFromPopup = () => {
console.log('[TabBar] Received request-add-connection from popup component.');
showConnectionListPopup.value = false; //
emit('request-add-connection-from-popup'); //
emitWorkspaceEvent('connection:requestAdd'); //
};
//
const handleRequestEditFromPopup = (connection: any) => { // WorkspaceConnectionList
const handleRequestEditFromPopup = (connection: ConnectionInfo) => { // WorkspaceConnectionList
console.log('[TabBar] Received request-edit-connection from popup component for connection:', connection);
showConnectionListPopup.value = false; //
//
emit('request-edit-connection-from-popup', connection);
emitWorkspaceEvent('connection:requestEdit', { connectionInfo: connection });
};
// --- handleRequestRdpFromPopup ---
@@ -166,17 +159,17 @@ const handleContextMenuAction = (payload: { action: string; targetId: string | n
switch (action) {
case 'close':
emit('close-session', targetId);
emitWorkspaceEvent('session:close', { sessionId: targetId });
break;
case 'close-others':
emit('close-other-sessions', targetId);
emitWorkspaceEvent('session:closeOthers', { targetSessionId: targetId });
break;
case 'close-right':
emit('close-sessions-to-right', targetId);
emitWorkspaceEvent('session:closeToRight', { targetSessionId: targetId });
break;
case 'close-left':
//
emit('close-sessions-to-left', targetId);
emitWorkspaceEvent('session:closeToLeft', { targetSessionId: targetId });
break;
default:
console.warn(`[TabBar] Unknown context menu action: ${action}`);
@@ -214,7 +207,7 @@ const contextMenuItems = computed(() => {
//
const openLayoutConfigurator = () => {
console.log('[TabBar] Emitting open-layout-configurator event');
emit('open-layout-configurator'); //
emitWorkspaceEvent('ui:openLayoutConfigurator'); //
};
// --- Header Visibility Logic ---
@@ -10,15 +10,11 @@ import { useSessionStore } from '../stores/session.store';
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // +++ +++
import { useSettingsStore } from '../stores/settings.store'; // store
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ +++
//
const emit = defineEmits([
'connect-request', // -
// 'open-new-session', // - ()
'request-add-connection', // -
'request-edit-connection' // -
]);
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ +++
const { t } = useI18n();
// const router = useRouter(); //
@@ -307,7 +303,7 @@ const handleConnect = (connectionId: number, event?: MouseEvent | KeyboardEvent)
closeContextMenu(); //
// connect-request sessionStore.handleConnectRequest
emit('connect-request', connectionId);
emitWorkspaceEvent('connection:connect', { connectionId });
};
// --- closeRdpModal ---
@@ -346,11 +342,11 @@ const handleMenuAction = (action: 'add' | 'edit' | 'delete' | 'clone') => { //
if (action === 'add') {
console.log('[WorkspaceConnectionList] handleMenuAction called with action: add. Emitting request-add-connection...'); //
// router.push('/connections/add'); //
emit('request-add-connection');
emitWorkspaceEvent('connection:requestAdd');
} else if (conn) {
if (action === 'edit') {
// router.push(`/connections/edit/${conn.id}`); //
emit('request-edit-connection', conn); //
emitWorkspaceEvent('connection:requestEdit', { connectionInfo: conn }); //
} else if (action === 'delete') {
if (confirm(t('connections.prompts.confirmDelete', { name: conn.name || conn.host }))) {
connectionsStore.deleteConnection(conn.id);