refactor: 引入 workspaceEvents 并迁移核心组件事件处理
This commit is contained in:
Generated
+1
@@ -9042,6 +9042,7 @@
|
|||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"guacamole-common-js": "^1.5.0",
|
"guacamole-common-js": "^1.5.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||||
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
||||||
|
"@simplewebauthn/browser": "^9.0.1",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@vscode/iconv-lite-umd": "^0.7.0",
|
"@vscode/iconv-lite-umd": "^0.7.0",
|
||||||
"@vueuse/core": "^13.1.0",
|
"@vueuse/core": "^13.1.0",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"guacamole-common-js": "^1.5.0",
|
"guacamole-common-js": "^1.5.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
@@ -33,8 +35,7 @@
|
|||||||
"vue3-recaptcha2": "^1.8.0",
|
"vue3-recaptcha2": "^1.8.0",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"xterm": "^5.3.0",
|
"xterm": "^5.3.0",
|
||||||
"xterm-addon-web-links": "^0.9.0",
|
"xterm-addon-web-links": "^0.9.0"
|
||||||
"@simplewebauthn/browser": "^9.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|||||||
@@ -8,19 +8,14 @@ import { useSettingsStore } from '../stores/settings.store';
|
|||||||
import { useQuickCommandsStore } from '../stores/quickCommands.store';
|
import { useQuickCommandsStore } from '../stores/quickCommands.store';
|
||||||
import { useCommandHistoryStore } from '../stores/commandHistory.store';
|
import { useCommandHistoryStore } from '../stores/commandHistory.store';
|
||||||
import QuickCommandsModal from './QuickCommandsModal.vue'; // +++ Import the modal component +++
|
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)
|
// Disable attribute inheritance as this component has multiple root nodes (div + modal)
|
||||||
defineOptions({ inheritAttrs: false });
|
defineOptions({ inheritAttrs: false });
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
'send-command',
|
const emit = defineEmits(['toggle-virtual-keyboard']);
|
||||||
'search',
|
|
||||||
'find-next',
|
|
||||||
'find-previous',
|
|
||||||
'close-search',
|
|
||||||
'clear-terminal',
|
|
||||||
'toggle-virtual-keyboard' // +++ Add new emit +++
|
|
||||||
]);
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const focusSwitcherStore = useFocusSwitcherStore();
|
const focusSwitcherStore = useFocusSwitcherStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
@@ -72,7 +67,7 @@ const currentSessionCommandInput = computed({
|
|||||||
const sendCommand = () => {
|
const sendCommand = () => {
|
||||||
const command = currentSessionCommandInput.value; // 使用计算属性获取值
|
const command = currentSessionCommandInput.value; // 使用计算属性获取值
|
||||||
console.log(`[CommandInputBar] Sending command: ${command || '<Enter>'} `);
|
console.log(`[CommandInputBar] Sending command: ${command || '<Enter>'} `);
|
||||||
emit('send-command', command);
|
emitWorkspaceEvent('terminal:sendCommand', { command });
|
||||||
// 清空 store 中的值
|
// 清空 store 中的值
|
||||||
if (activeSessionId.value) {
|
if (activeSessionId.value) {
|
||||||
updateSessionCommandInput(activeSessionId.value, '');
|
updateSessionCommandInput(activeSessionId.value, '');
|
||||||
@@ -83,7 +78,7 @@ const toggleSearch = () => {
|
|||||||
isSearching.value = !isSearching.value;
|
isSearching.value = !isSearching.value;
|
||||||
if (!isSearching.value) {
|
if (!isSearching.value) {
|
||||||
searchTerm.value = ''; // 关闭搜索时清空
|
searchTerm.value = ''; // 关闭搜索时清空
|
||||||
emit('close-search'); // 通知父组件关闭搜索
|
emitWorkspaceEvent('search:close'); // 通知父组件关闭搜索
|
||||||
} else {
|
} else {
|
||||||
// 可以在这里聚焦搜索输入框
|
// 可以在这里聚焦搜索输入框
|
||||||
// nextTick(() => searchInputRef.value?.focus());
|
// nextTick(() => searchInputRef.value?.focus());
|
||||||
@@ -91,16 +86,16 @@ const toggleSearch = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const performSearch = () => {
|
const performSearch = () => {
|
||||||
emit('search', searchTerm.value);
|
emitWorkspaceEvent('search:start', { term: searchTerm.value });
|
||||||
// 实际的计数更新逻辑应该由父组件通过 props 或事件传递回来
|
// 实际的计数更新逻辑应该由父组件通过 props 或事件传递回来
|
||||||
};
|
};
|
||||||
|
|
||||||
const findNext = () => {
|
const findNext = () => {
|
||||||
emit('find-next');
|
emitWorkspaceEvent('search:findNext');
|
||||||
};
|
};
|
||||||
|
|
||||||
const findPrevious = () => {
|
const findPrevious = () => {
|
||||||
emit('find-previous');
|
emitWorkspaceEvent('search:findPrevious');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听搜索词变化,执行搜索
|
// 监听搜索词变化,执行搜索
|
||||||
@@ -151,7 +146,7 @@ const handleCommandInputKeydown = (event: KeyboardEvent) => {
|
|||||||
if (selectedCommand !== undefined) {
|
if (selectedCommand !== undefined) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log(`[CommandInputBar] Enter detected with selection. Sending selected command: ${selectedCommand}`);
|
console.log(`[CommandInputBar] Enter detected with selection. Sending selected command: ${selectedCommand}`);
|
||||||
emit('send-command', selectedCommand); // 发送选中命令 (移除多余的 \n)
|
emitWorkspaceEvent('terminal:sendCommand', { command: selectedCommand }); // 发送选中命令
|
||||||
if (activeSessionId.value) {
|
if (activeSessionId.value) {
|
||||||
updateSessionCommandInput(activeSessionId.value, ''); // 清空输入框
|
updateSessionCommandInput(activeSessionId.value, ''); // 清空输入框
|
||||||
}
|
}
|
||||||
@@ -190,7 +185,7 @@ const handleCommandInputKeydown = (event: KeyboardEvent) => {
|
|||||||
// Handle Ctrl+C when input is empty
|
// Handle Ctrl+C when input is empty
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
console.log('[CommandInputBar] Ctrl+C detected with empty input. Sending SIGINT.');
|
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') {
|
} else if (!event.altKey && event.key === 'Enter') {
|
||||||
// Handle regular Enter key press - send current input (empty or not)
|
// Handle regular Enter key press - send current input (empty or not)
|
||||||
event.preventDefault(); // Prevent default if needed, e.g., form submission
|
event.preventDefault(); // Prevent default if needed, e.g., form submission
|
||||||
@@ -288,7 +283,7 @@ const closeQuickCommandsModal = () => {
|
|||||||
// +++ Handler for command execution from the modal +++
|
// +++ Handler for command execution from the modal +++
|
||||||
const handleQuickCommandExecute = (command: string) => {
|
const handleQuickCommandExecute = (command: string) => {
|
||||||
console.log(`[CommandInputBar] Executing quick command: ${command}`);
|
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
|
closeQuickCommandsModal(); // Close the modal after selection
|
||||||
};
|
};
|
||||||
</script>
|
</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 -->
|
<div class="flex-grow flex items-center bg-transparent relative gap-1 px-2"> <!-- Added px-2 here -->
|
||||||
<!-- Clear Terminal Button -->
|
<!-- Clear Terminal Button -->
|
||||||
<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"
|
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', '清空终端')"
|
:title="t('commandInputBar.clearTerminal', '清空终端')"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++
|
|||||||
import { useSessionStore } from '../stores/session.store'; // +++ 导入会话 Store +++
|
import { useSessionStore } from '../stores/session.store'; // +++ 导入会话 Store +++
|
||||||
import { useSettingsStore } from '../stores/settings.store'; // +++ 导入设置 Store +++
|
import { useSettingsStore } from '../stores/settings.store'; // +++ 导入设置 Store +++
|
||||||
import { storeToRefs } from 'pinia'; // +++ 导入 storeToRefs +++
|
import { storeToRefs } from 'pinia'; // +++ 导入 storeToRefs +++
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
const sessionStore = useSessionStore(); // +++ 实例化会话 Store +++
|
const sessionStore = useSessionStore(); // +++ 实例化会话 Store +++
|
||||||
const settingsStore = useSettingsStore(); // +++ 实例化设置 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) {
|
if (activeTab.value && newContent !== activeTab.value.content) {
|
||||||
// console.log(`[EditorContainer] Emitting update:content for tab ${activeTab.value.id}`);
|
// 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 计算
|
// 注意:isModified 状态应该由 Store 根据 content 和 originalContent 计算
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -199,7 +190,7 @@ const encodingOptions = ref([
|
|||||||
// --- 事件处理 ---
|
// --- 事件处理 ---
|
||||||
const handleSaveRequest = () => {
|
const handleSaveRequest = () => {
|
||||||
if (activeTab.value) {
|
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;
|
const newEncoding = target.value;
|
||||||
if (activeTab.value && newEncoding && newEncoding !== currentSelectedEncoding.value) {
|
if (activeTab.value && newEncoding && newEncoding !== currentSelectedEncoding.value) {
|
||||||
console.log(`[EditorContainer] Encoding changed to ${newEncoding} for tab ${activeTab.value.id}`);
|
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;
|
const nextTabId = props.tabs[nextIndex]?.id;
|
||||||
if (nextTabId) {
|
if (nextTabId) {
|
||||||
emit('activate-tab', nextTabId);
|
emitWorkspaceEvent('editor:activateTab', { tabId: nextTabId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -292,11 +283,11 @@ const handleKeyDown = (event: KeyboardEvent) => {
|
|||||||
<FileEditorTabs
|
<FileEditorTabs
|
||||||
:tabs="orderedTabs"
|
:tabs="orderedTabs"
|
||||||
:active-tab-id="props.activeTabId"
|
:active-tab-id="props.activeTabId"
|
||||||
@activate-tab="(tabId: string) => emit('activate-tab', tabId)"
|
@activate-tab="(tabId: string) => emitWorkspaceEvent('editor:activateTab', { tabId })"
|
||||||
@close-tab="(tabId: string) => emit('close-tab', tabId)"
|
@close-tab="(tabId: string) => emitWorkspaceEvent('editor:closeTab', { tabId })"
|
||||||
@close-other-tabs="(tabId: string) => emit('close-other-tabs', tabId)"
|
@close-other-tabs="(tabId: string) => emitWorkspaceEvent('editor:closeOtherTabs', { tabId })"
|
||||||
@close-tabs-to-right="(tabId: string) => emit('close-tabs-to-right', tabId)"
|
@close-tabs-to-right="(tabId: string) => emitWorkspaceEvent('editor:closeTabsToRight', { tabId })"
|
||||||
@close-tabs-to-left="(tabId: string) => emit('close-tabs-to-left', tabId)"
|
@close-tabs-to-left="(tabId: string) => emitWorkspaceEvent('editor:closeTabsToLeft', { tabId })"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 2. 编辑器头部 (显示当前激活标签信息) -->
|
<!-- 2. 编辑器头部 (显示当前激活标签信息) -->
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useFileEditorStore } from '../stores/fileEditor.store'; // <-- Import F
|
|||||||
import { useSettingsStore } from '../stores/settings.store'; // +++ Import SettingsStore +++
|
import { useSettingsStore } from '../stores/settings.store'; // +++ Import SettingsStore +++
|
||||||
import { useSidebarResize } from '../composables/useSidebarResize'; // +++ Import useSidebarResize +++
|
import { useSidebarResize } from '../composables/useSidebarResize'; // +++ Import useSidebarResize +++
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { defineEmits } from 'vue';
|
// import { defineEmits } from 'vue'; // --- 移除 ---
|
||||||
|
|
||||||
// --- Props ---
|
// --- Props ---
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -44,39 +44,9 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
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 ---
|
// --- Setup ---
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
@@ -160,17 +130,8 @@ const componentProps = computed(() => {
|
|||||||
return {
|
return {
|
||||||
sessionId: props.activeSessionId ?? '', // 如果 activeSessionId 为 null,则传递空字符串
|
sessionId: props.activeSessionId ?? '', // 如果 activeSessionId 为 null,则传递空字符串
|
||||||
isActive: true,
|
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':
|
case 'fileManager':
|
||||||
// 仅当有活动会话时才返回实际 props,否则返回空对象
|
// 仅当有活动会话时才返回实际 props,否则返回空对象
|
||||||
if (!currentActiveSession) return {};
|
if (!currentActiveSession) return {};
|
||||||
@@ -208,50 +169,26 @@ const componentProps = computed(() => {
|
|||||||
activeTabId: props.activeEditorTabId, // 从 WorkspaceView 传入
|
activeTabId: props.activeEditorTabId, // 从 WorkspaceView 传入
|
||||||
sessionId: props.activeSessionId,
|
sessionId: props.activeSessionId,
|
||||||
class: 'pane-content',
|
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':
|
case 'commandBar':
|
||||||
// CommandInputBar 需要转发 send-command 事件
|
|
||||||
// searchResultCount 和 currentSearchResultIndex 将在模板中直接从 terminalManager 绑定
|
|
||||||
return {
|
return {
|
||||||
class: 'pane-content',
|
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':
|
case 'connections':
|
||||||
// WorkspaceConnectionList 需要转发 connect-request 等事件
|
|
||||||
return {
|
return {
|
||||||
class: 'pane-content',
|
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 'commandHistory':
|
||||||
case 'quickCommands':
|
case 'quickCommands':
|
||||||
// 这两个视图需要转发 execute-command 事件
|
|
||||||
return {
|
return {
|
||||||
class: 'flex flex-col flex-grow h-full overflow-auto', // 移除 pane-content,保留填充类
|
class: 'flex flex-col flex-grow h-full overflow-auto', // 移除 pane-content,保留填充类
|
||||||
onExecuteCommand: (command: string) => emit('sendCommand', command), // 复用 sendCommand 事件
|
// --- 移除事件转发 ---
|
||||||
};
|
};
|
||||||
case 'dockerManager':
|
case 'dockerManager':
|
||||||
// DockerManager 可能不需要 session 信息,但需要转发事件
|
// DockerManager 可能不需要 session 信息
|
||||||
return {
|
return {
|
||||||
class: 'flex-grow h-full overflow-hidden', // <-- 修改:添加 flex-grow 和 h-full,并保留 overflow-hidden
|
class: 'flex-grow h-full overflow-hidden', // <-- 修改:添加 flex-grow 和 h-full,并保留 overflow-hidden
|
||||||
// 假设 DockerManager 会发出 'docker-command' 事件
|
// 假设 DockerManager 会发出 'docker-command' 事件
|
||||||
@@ -277,30 +214,12 @@ const sidebarProps = computed(() => (paneName: PaneName | null, side: 'left' | '
|
|||||||
tabs: editorTabsFromStore.value, // Access .value for refs from storeToRefs
|
tabs: editorTabsFromStore.value, // Access .value for refs from storeToRefs
|
||||||
activeTabId: activeEditorTabIdFromStore.value, // Access .value
|
activeTabId: activeEditorTabIdFromStore.value, // Access .value
|
||||||
sessionId: props.activeSessionId,
|
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':
|
case 'connections':
|
||||||
return {
|
return {
|
||||||
...baseProps,
|
...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':
|
case 'fileManager':
|
||||||
// Only provide props if there's an active session
|
// Only provide props if there's an active session
|
||||||
@@ -508,27 +427,6 @@ onMounted(() => {
|
|||||||
:active-session-id="activeSessionId"
|
:active-session-id="activeSessionId"
|
||||||
:editor-tabs="editorTabs"
|
:editor-tabs="editorTabs"
|
||||||
:active-editor-tab-id="activeEditorTabId"
|
: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"
|
class="flex-grow overflow-auto"
|
||||||
/>
|
/>
|
||||||
</pane>
|
</pane>
|
||||||
@@ -597,7 +495,6 @@ onMounted(() => {
|
|||||||
v-if="layoutNode.component === 'connections'"
|
v-if="layoutNode.component === 'connections'"
|
||||||
:is="currentMainComponent"
|
:is="currentMainComponent"
|
||||||
v-bind="componentProps"
|
v-bind="componentProps"
|
||||||
@request-add-connection="() => emit('request-add-connection')"
|
|
||||||
class="flex-grow overflow-auto"
|
class="flex-grow overflow-auto"
|
||||||
/>
|
/>
|
||||||
<component
|
<component
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
||||||
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
|
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
|
||||||
import { useAppearanceStore } from '../stores/appearance.store';
|
import { useAppearanceStore } from '../stores/appearance.store';
|
||||||
import { useSettingsStore } from '../stores/settings.store';
|
import { useSettingsStore } from '../stores/settings.store';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { FitAddon } from '@xterm/addon-fit'; // Updated import path
|
import { FitAddon } from '@xterm/addon-fit'; // Updated import path
|
||||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||||
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search';
|
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search';
|
||||||
import 'xterm/css/xterm.css';
|
import 'xterm/css/xterm.css';
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
|
|
||||||
// 定义 props 和 emits
|
// 定义 props 和 emits
|
||||||
@@ -18,12 +19,9 @@ const props = defineProps<{
|
|||||||
options?: object; // xterm 的配置选项
|
options?: object; // xterm 的配置选项
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'data', data: string): void; // 用户输入事件
|
|
||||||
(e: 'resize', dimensions: { cols: number; rows: number }): void; // 终端大小调整事件
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
// *** 更新 ready 事件 payload,包含 searchAddon ***
|
|
||||||
(e: 'ready', payload: { sessionId: string; terminal: Terminal; searchAddon: SearchAddon | null }): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const terminalRef = ref<HTMLElement | null>(null); // 终端容器的引用
|
const terminalRef = ref<HTMLElement | null>(null); // 终端容器的引用
|
||||||
let terminal: Terminal | null = null;
|
let terminal: Terminal | null = null;
|
||||||
@@ -70,7 +68,7 @@ const debouncedEmitResize = debounce((term: Terminal) => {
|
|||||||
if (term && props.isActive) { // 仅当标签仍处于活动状态时才发送防抖后的 resize
|
if (term && props.isActive) { // 仅当标签仍处于活动状态时才发送防抖后的 resize
|
||||||
const dimensions = { cols: term.cols, rows: term.rows };
|
const dimensions = { cols: term.cols, rows: term.rows };
|
||||||
console.log(`[Terminal ${props.sessionId}] Debounced resize emit (from ResizeObserver):`, dimensions);
|
console.log(`[Terminal ${props.sessionId}] Debounced resize emit (from ResizeObserver):`, dimensions);
|
||||||
emit('resize', dimensions);
|
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
|
||||||
// *** 新增:尝试在发送 resize 后强制刷新终端显示 ***
|
// *** 新增:尝试在发送 resize 后强制刷新终端显示 ***
|
||||||
try {
|
try {
|
||||||
term.refresh(0, term.rows - 1); // Refresh entire viewport
|
term.refresh(0, term.rows - 1); // Refresh entire viewport
|
||||||
@@ -92,7 +90,7 @@ const fitAndEmitResizeNow = (term: Terminal) => {
|
|||||||
fitAddon?.fit();
|
fitAddon?.fit();
|
||||||
const dimensions = { cols: term.cols, rows: term.rows };
|
const dimensions = { cols: term.cols, rows: term.rows };
|
||||||
console.log(`[Terminal ${props.sessionId}] Immediate resize emit:`, dimensions);
|
console.log(`[Terminal ${props.sessionId}] Immediate resize emit:`, dimensions);
|
||||||
emit('resize', dimensions);
|
emitWorkspaceEvent('terminal:resize', { sessionId: props.sessionId, dims: dimensions });
|
||||||
|
|
||||||
// *** 恢复:仅使用 nextTick 触发 window resize ***
|
// *** 恢复:仅使用 nextTick 触发 window resize ***
|
||||||
// 使用 nextTick 确保 fit() 的效果已反映,再触发 resize
|
// 使用 nextTick 确保 fit() 的效果已反映,再触发 resize
|
||||||
@@ -162,11 +160,11 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 适应容器大小
|
// 适应容器大小
|
||||||
fitAddon.fit();
|
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) => {
|
terminal.onData((data) => {
|
||||||
emit('data', data);
|
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听终端大小变化 (通过 ResizeObserver) - 主要处理浏览器窗口大小变化等
|
// 监听终端大小变化 (通过 ResizeObserver) - 主要处理浏览器窗口大小变化等
|
||||||
@@ -244,7 +242,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 触发 ready 事件,传递 sessionId, terminal 和 searchAddon 实例
|
// 触发 ready 事件,传递 sessionId, terminal 和 searchAddon 实例
|
||||||
if (terminal) {
|
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();
|
const text = await navigator.clipboard.readText();
|
||||||
if (text) {
|
if (text) {
|
||||||
// 将粘贴的文本发送到后端,模拟用户输入
|
// 将粘贴的文本发送到后端,模拟用户输入
|
||||||
emit('data', text);
|
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data: text });
|
||||||
console.log('[Terminal] Pasted via Ctrl+Shift+V');
|
console.log('[Terminal] Pasted via Ctrl+Shift+V');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -380,7 +378,7 @@ onMounted(() => {
|
|||||||
const text = await navigator.clipboard.readText();
|
const text = await navigator.clipboard.readText();
|
||||||
if (text && terminal) {
|
if (text && terminal) {
|
||||||
// 将粘贴的文本发送到后端
|
// 将粘贴的文本发送到后端
|
||||||
emit('data', text);
|
emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data: text });
|
||||||
console.log('[Terminal] Pasted via Right Click');
|
console.log('[Terminal] Pasted via Right Click');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import TabBarContextMenu from './TabBarContextMenu.vue';
|
|||||||
import { useSessionStore } from '../stores/session.store';
|
import { useSessionStore } from '../stores/session.store';
|
||||||
import { useConnectionsStore, type ConnectionInfo } from '../stores/connections.store';
|
import { useConnectionsStore, type ConnectionInfo } from '../stores/connections.store';
|
||||||
import { useLayoutStore, type PaneName } from '../stores/layout.store';
|
import { useLayoutStore, type PaneName } from '../stores/layout.store';
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
import type { SessionTabInfoWithStatus } from '../stores/session.store';
|
import type { SessionTabInfoWithStatus } from '../stores/session.store';
|
||||||
|
|
||||||
|
|
||||||
const { t } = useI18n(); // 初始化 i18n
|
const { t } = useI18n(); // 初始化 i18n
|
||||||
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
const layoutStore = useLayoutStore(); // 初始化布局 store
|
const layoutStore = useLayoutStore(); // 初始化布局 store
|
||||||
const connectionsStore = useConnectionsStore();
|
const connectionsStore = useConnectionsStore();
|
||||||
const { isHeaderVisible } = storeToRefs(layoutStore); // 从 layout store 获取主导航栏可见状态
|
const { isHeaderVisible } = storeToRefs(layoutStore); // 从 layout store 获取主导航栏可见状态
|
||||||
@@ -37,30 +39,21 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义事件 (使用对象语法修复类型)
|
// 定义事件 (保留 update:sessions 用于 v-model)
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'activate-session', sessionId: string): void;
|
(e: 'update:sessions', newSessions: SessionTabInfoWithStatus[]): 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
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
||||||
const activateSession = (sessionId: string) => {
|
const activateSession = (sessionId: string) => {
|
||||||
if (sessionId !== props.activeSessionId) {
|
if (sessionId !== props.activeSessionId) {
|
||||||
emit('activate-session', sessionId);
|
emitWorkspaceEvent('session:activate', { sessionId });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeSession = (event: MouseEvent, sessionId: string) => {
|
const closeSession = (event: MouseEvent, sessionId: string) => {
|
||||||
event.stopPropagation(); // 阻止事件冒泡到标签点击事件
|
event.stopPropagation(); // 阻止事件冒泡到标签点击事件
|
||||||
emit('close-session', sessionId);
|
emitWorkspaceEvent('session:close', { sessionId });
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 本地状态 ---
|
// --- 本地状态 ---
|
||||||
@@ -109,15 +102,15 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
const handleRequestAddFromPopup = () => {
|
const handleRequestAddFromPopup = () => {
|
||||||
console.log('[TabBar] Received request-add-connection from popup component.');
|
console.log('[TabBar] Received request-add-connection from popup component.');
|
||||||
showConnectionListPopup.value = false; // 关闭弹窗
|
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);
|
console.log('[TabBar] Received request-edit-connection from popup component for connection:', connection);
|
||||||
showConnectionListPopup.value = false; // 关闭弹窗
|
showConnectionListPopup.value = false; // 关闭弹窗
|
||||||
// 向上发出事件,并携带连接信息
|
// 向上发出事件,并携带连接信息
|
||||||
emit('request-edit-connection-from-popup', connection);
|
emitWorkspaceEvent('connection:requestEdit', { connectionInfo: connection });
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 移除 handleRequestRdpFromPopup 方法 ---
|
// --- 移除 handleRequestRdpFromPopup 方法 ---
|
||||||
@@ -166,17 +159,17 @@ const handleContextMenuAction = (payload: { action: string; targetId: string | n
|
|||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'close':
|
case 'close':
|
||||||
emit('close-session', targetId);
|
emitWorkspaceEvent('session:close', { sessionId: targetId });
|
||||||
break;
|
break;
|
||||||
case 'close-others':
|
case 'close-others':
|
||||||
emit('close-other-sessions', targetId);
|
emitWorkspaceEvent('session:closeOthers', { targetSessionId: targetId });
|
||||||
break;
|
break;
|
||||||
case 'close-right':
|
case 'close-right':
|
||||||
emit('close-sessions-to-right', targetId);
|
emitWorkspaceEvent('session:closeToRight', { targetSessionId: targetId });
|
||||||
break;
|
break;
|
||||||
case 'close-left':
|
case 'close-left':
|
||||||
// 注意:关闭左侧通常不包括当前标签本身
|
// 注意:关闭左侧通常不包括当前标签本身
|
||||||
emit('close-sessions-to-left', targetId);
|
emitWorkspaceEvent('session:closeToLeft', { targetSessionId: targetId });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn(`[TabBar] Unknown context menu action: ${action}`);
|
console.warn(`[TabBar] Unknown context menu action: ${action}`);
|
||||||
@@ -214,7 +207,7 @@ const contextMenuItems = computed(() => {
|
|||||||
// 新增:处理打开布局配置器的事件
|
// 新增:处理打开布局配置器的事件
|
||||||
const openLayoutConfigurator = () => {
|
const openLayoutConfigurator = () => {
|
||||||
console.log('[TabBar] Emitting open-layout-configurator event');
|
console.log('[TabBar] Emitting open-layout-configurator event');
|
||||||
emit('open-layout-configurator'); // 发出事件
|
emitWorkspaceEvent('ui:openLayoutConfigurator'); // 发出事件
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Header Visibility Logic ---
|
// --- Header Visibility Logic ---
|
||||||
|
|||||||
@@ -10,15 +10,11 @@ import { useSessionStore } from '../stores/session.store';
|
|||||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
|
||||||
import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // +++ 修正导入大小写 +++
|
import { useUiNotificationsStore } from '../stores/uiNotifications.store'; // +++ 修正导入大小写 +++
|
||||||
import { useSettingsStore } from '../stores/settings.store'; // 新增:导入设置 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 { t } = useI18n();
|
||||||
// const router = useRouter(); // 不再需要
|
// const router = useRouter(); // 不再需要
|
||||||
@@ -307,7 +303,7 @@ const handleConnect = (connectionId: number, event?: MouseEvent | KeyboardEvent)
|
|||||||
closeContextMenu(); // 关闭右键菜单
|
closeContextMenu(); // 关闭右键菜单
|
||||||
|
|
||||||
// 统一发出 connect-request 事件,让 sessionStore.handleConnectRequest 处理模态框和会话
|
// 统一发出 connect-request 事件,让 sessionStore.handleConnectRequest 处理模态框和会话
|
||||||
emit('connect-request', connectionId);
|
emitWorkspaceEvent('connection:connect', { connectionId });
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 移除 closeRdpModal 方法 ---
|
// --- 移除 closeRdpModal 方法 ---
|
||||||
@@ -346,11 +342,11 @@ const handleMenuAction = (action: 'add' | 'edit' | 'delete' | 'clone') => { //
|
|||||||
if (action === 'add') {
|
if (action === 'add') {
|
||||||
console.log('[WorkspaceConnectionList] handleMenuAction called with action: add. Emitting request-add-connection...'); // 添加日志
|
console.log('[WorkspaceConnectionList] handleMenuAction called with action: add. Emitting request-add-connection...'); // 添加日志
|
||||||
// router.push('/connections/add'); // 改为触发事件
|
// router.push('/connections/add'); // 改为触发事件
|
||||||
emit('request-add-connection');
|
emitWorkspaceEvent('connection:requestAdd');
|
||||||
} else if (conn) {
|
} else if (conn) {
|
||||||
if (action === 'edit') {
|
if (action === 'edit') {
|
||||||
// router.push(`/connections/edit/${conn.id}`); // 改为触发事件
|
// router.push(`/connections/edit/${conn.id}`); // 改为触发事件
|
||||||
emit('request-edit-connection', conn); // 传递整个连接对象
|
emitWorkspaceEvent('connection:requestEdit', { connectionInfo: conn }); // 传递整个连接对象
|
||||||
} else if (action === 'delete') {
|
} else if (action === 'delete') {
|
||||||
if (confirm(t('connections.prompts.confirmDelete', { name: conn.name || conn.host }))) {
|
if (confirm(t('connections.prompts.confirmDelete', { name: conn.name || conn.host }))) {
|
||||||
connectionsStore.deleteConnection(conn.id);
|
connectionsStore.deleteConnection(conn.id);
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// packages/frontend/src/composables/workspaceEvents.ts
|
||||||
|
import mitt from 'mitt';
|
||||||
|
import type { ConnectionInfo } from '../stores/connections.store';
|
||||||
|
import type { Terminal as XtermTerminal } from 'xterm';
|
||||||
|
|
||||||
|
// 定义事件载荷类型
|
||||||
|
export type WorkspaceEventPayloads = {
|
||||||
|
// Terminal Events
|
||||||
|
'terminal:input': { sessionId: string; data: string };
|
||||||
|
'terminal:resize': { sessionId: string; dims: { cols: number; rows: number } };
|
||||||
|
'terminal:ready': { sessionId: string; terminal: XtermTerminal; searchAddon: any };
|
||||||
|
'terminal:sendCommand': { command: string; sessionId?: string }; // sessionId 可选,用于指定目标,默认为 active
|
||||||
|
'terminal:clear': void; // sessionId 可选,默认为 active
|
||||||
|
|
||||||
|
// Editor Events
|
||||||
|
'editor:closeTab': { tabId: string };
|
||||||
|
'editor:activateTab': { tabId: string };
|
||||||
|
'editor:updateContent': { tabId: string; content: string };
|
||||||
|
'editor:saveTab': { tabId: string };
|
||||||
|
'editor:changeEncoding': { tabId: string; encoding: string };
|
||||||
|
'editor:closeOtherTabs': { tabId: string };
|
||||||
|
'editor:closeTabsToRight': { tabId: string };
|
||||||
|
'editor:closeTabsToLeft': { tabId: string };
|
||||||
|
|
||||||
|
// Connection Events
|
||||||
|
'connection:connect': { connectionId: number }; // 来自 WorkspaceConnectionList 或其他地方
|
||||||
|
'connection:openNewSession': { connectionId: number }; // 来自 WorkspaceConnectionList
|
||||||
|
'connection:requestAdd': void; // 来自 WorkspaceConnectionList 或 TerminalTabBar
|
||||||
|
'connection:requestEdit': { connectionInfo: ConnectionInfo }; // 来自 WorkspaceConnectionList 或 TerminalTabBar
|
||||||
|
|
||||||
|
// Search Events (主要由 CommandInputBar 或 PaneTitleBar 发出)
|
||||||
|
'search:start': { term: string; sessionId?: string }; // sessionId 可选,用于指定搜索目标终端
|
||||||
|
'search:findNext': void;
|
||||||
|
'search:findPrevious': void;
|
||||||
|
'search:close': void;
|
||||||
|
|
||||||
|
// Session Management Events (主要由 TerminalTabBar 发出)
|
||||||
|
'session:activate': { sessionId: string };
|
||||||
|
'session:close': { sessionId: string };
|
||||||
|
'session:closeOthers': { targetSessionId: string };
|
||||||
|
'session:closeToRight': { targetSessionId: string };
|
||||||
|
'session:closeToLeft': { targetSessionId: string };
|
||||||
|
|
||||||
|
// UI Interaction Events
|
||||||
|
'ui:openLayoutConfigurator': void;
|
||||||
|
// 'ui:toggleVirtualKeyboard': void; // 如果决定迁移 CommandInputBar 的这个事件
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建 mitt 事件发射器实例
|
||||||
|
export const workspaceEmitter = mitt<WorkspaceEventPayloads>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to get the workspace event emitter function.
|
||||||
|
* @returns The emit function from the mitt instance.
|
||||||
|
*/
|
||||||
|
export function useWorkspaceEventEmitter() {
|
||||||
|
return workspaceEmitter.emit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to get the workspace event subscriber function.
|
||||||
|
* @returns The 'on' function from the mitt instance for subscribing to events.
|
||||||
|
*/
|
||||||
|
export function useWorkspaceEventSubscriber() {
|
||||||
|
return workspaceEmitter.on;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to get the workspace event unsubscriber function.
|
||||||
|
* @returns The 'off' function from the mitt instance for unsubscribing from events.
|
||||||
|
*/
|
||||||
|
export function useWorkspaceEventOff() {
|
||||||
|
return workspaceEmitter.off;
|
||||||
|
}
|
||||||
@@ -68,13 +68,14 @@ import { storeToRefs } from 'pinia'; // Import storeToRefs
|
|||||||
import { useCommandHistoryStore, CommandHistoryEntryFE } from '../stores/commandHistory.store';
|
import { useCommandHistoryStore, CommandHistoryEntryFE } from '../stores/commandHistory.store';
|
||||||
import { useUiNotificationsStore } from '../stores/uiNotifications.store';
|
import { useUiNotificationsStore } from '../stores/uiNotifications.store';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import PaneTitleBar from '../components/PaneTitleBar.vue'; // 导入标题栏
|
|
||||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
|
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
const commandHistoryStore = useCommandHistoryStore();
|
const commandHistoryStore = useCommandHistoryStore();
|
||||||
const uiNotificationsStore = useUiNotificationsStore();
|
const uiNotificationsStore = useUiNotificationsStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
const hoveredItemId = ref<number | null>(null);
|
const hoveredItemId = ref<number | null>(null);
|
||||||
// const selectedIndex = ref<number>(-1); // REMOVED: Use store's selectedIndex
|
// const selectedIndex = ref<number>(-1); // REMOVED: Use store's selectedIndex
|
||||||
const historyListRef = ref<HTMLUListElement | null>(null); // Ref for the history list UL
|
const historyListRef = ref<HTMLUListElement | null>(null); // Ref for the history list UL
|
||||||
@@ -88,11 +89,6 @@ const filteredHistory = computed(() => commandHistoryStore.filteredHistory);
|
|||||||
const isLoading = computed(() => commandHistoryStore.isLoading);
|
const isLoading = computed(() => commandHistoryStore.isLoading);
|
||||||
const { selectedIndex: storeSelectedIndex } = storeToRefs(commandHistoryStore); // Get selectedIndex reactively
|
const { selectedIndex: storeSelectedIndex } = storeToRefs(commandHistoryStore); // Get selectedIndex reactively
|
||||||
|
|
||||||
// --- 事件定义 ---
|
|
||||||
// 定义组件发出的事件
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'execute-command', command: string): void; // 定义新事件
|
|
||||||
}>();
|
|
||||||
|
|
||||||
// --- 生命周期钩子 ---
|
// --- 生命周期钩子 ---
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -207,7 +203,7 @@ const deleteSingleCommand = (id: number) => {
|
|||||||
|
|
||||||
// 新增:执行命令 (发出事件)
|
// 新增:执行命令 (发出事件)
|
||||||
const executeCommand = (command: string) => {
|
const executeCommand = (command: string) => {
|
||||||
emit('execute-command', command);
|
emitWorkspaceEvent('terminal:sendCommand', { command });
|
||||||
// Optionally reset selection after execution
|
// Optionally reset selection after execution
|
||||||
// selectedIndex.value = -1; // REMOVED: Store handles index
|
// selectedIndex.value = -1; // REMOVED: Store handles index
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ import { useI18n } from 'vue-i18n';
|
|||||||
import AddEditQuickCommandForm from '../components/AddEditQuickCommandForm.vue'; // 导入表单组件
|
import AddEditQuickCommandForm from '../components/AddEditQuickCommandForm.vue'; // 导入表单组件
|
||||||
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
|
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 导入焦点切换 Store +++
|
||||||
import { useSettingsStore } from '../stores/settings.store'; // 新增:导入设置 store
|
import { useSettingsStore } from '../stores/settings.store'; // 新增:导入设置 store
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
const quickCommandsStore = useQuickCommandsStore();
|
const quickCommandsStore = useQuickCommandsStore();
|
||||||
const quickCommandTagsStore = useQuickCommandTagsStore(); // +++ Instantiate the new tag store +++
|
const quickCommandTagsStore = useQuickCommandTagsStore(); // +++ Instantiate the new tag store +++
|
||||||
@@ -173,6 +174,7 @@ const uiNotificationsStore = useUiNotificationsStore(); // 如果需要显示通
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
const settingsStore = useSettingsStore(); // 新增:实例化设置 store
|
const settingsStore = useSettingsStore(); // 新增:实例化设置 store
|
||||||
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
|
|
||||||
const hoveredItemId = ref<number | null>(null);
|
const hoveredItemId = ref<number | null>(null);
|
||||||
const isFormVisible = ref(false);
|
const isFormVisible = ref(false);
|
||||||
@@ -213,10 +215,7 @@ const isCommandSelected = (commandId: number): boolean => {
|
|||||||
return flatVisibleCommands.value[storeSelectedIndex.value].id === commandId;
|
return flatVisibleCommands.value[storeSelectedIndex.value].id === commandId;
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 事件定义 ---
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'execute-command', command: string): void; // 用于通知 WorkspaceView 执行命令
|
|
||||||
}>();
|
|
||||||
|
|
||||||
// --- 生命周期钩子 ---
|
// --- 生命周期钩子 ---
|
||||||
onMounted(async () => { // Make onMounted async
|
onMounted(async () => { // Make onMounted async
|
||||||
@@ -399,7 +398,7 @@ const executeCommand = (command: QuickCommandFE) => {
|
|||||||
// 1. 增加使用次数 (后台操作,不阻塞执行)
|
// 1. 增加使用次数 (后台操作,不阻塞执行)
|
||||||
quickCommandsStore.incrementUsage(command.id);
|
quickCommandsStore.incrementUsage(command.id);
|
||||||
// 2. 发出执行事件给父组件
|
// 2. 发出执行事件给父组件
|
||||||
emit('execute-command', command.command);
|
emitWorkspaceEvent('terminal:sendCommand', { command: command.command });
|
||||||
// Optionally reset selection after execution
|
// Optionally reset selection after execution
|
||||||
// selectedIndex.value = -1; // REMOVED: Store handles index
|
// selectedIndex.value = -1; // REMOVED: Store handles index
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ import { useFileEditorStore, type FileTab } from '../stores/fileEditor.store';
|
|||||||
import { useCommandHistoryStore } from '../stores/commandHistory.store';
|
import { useCommandHistoryStore } from '../stores/commandHistory.store';
|
||||||
import type { Terminal as XtermTerminal } from 'xterm'; // --- 重命名避免冲突 ---
|
import type { Terminal as XtermTerminal } from 'xterm'; // --- 重命名避免冲突 ---
|
||||||
import type { ISearchOptions } from '@xterm/addon-search';
|
import type { ISearchOptions } from '@xterm/addon-search';
|
||||||
|
import {
|
||||||
|
useWorkspaceEventSubscriber,
|
||||||
|
useWorkspaceEventOff,
|
||||||
|
type WorkspaceEventPayloads
|
||||||
|
} from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
||||||
|
|
||||||
// --- Setup ---
|
// --- Setup ---
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -112,6 +117,40 @@ onMounted(() => {
|
|||||||
// 添加键盘事件监听器
|
// 添加键盘事件监听器
|
||||||
window.addEventListener('keydown', handleGlobalKeyDown);
|
window.addEventListener('keydown', handleGlobalKeyDown);
|
||||||
// 确保布局已初始化 (layoutStore 内部会处理)
|
// 确保布局已初始化 (layoutStore 内部会处理)
|
||||||
|
|
||||||
|
// +++ 订阅工作区事件 +++
|
||||||
|
subscribeToWorkspaceEvents('terminal:sendCommand', (payload) => handleSendCommand(payload.command));
|
||||||
|
subscribeToWorkspaceEvents('terminal:input', handleTerminalInput);
|
||||||
|
subscribeToWorkspaceEvents('terminal:resize', handleTerminalResize);
|
||||||
|
subscribeToWorkspaceEvents('terminal:ready', handleTerminalReady);
|
||||||
|
subscribeToWorkspaceEvents('terminal:clear', handleClearTerminal);
|
||||||
|
|
||||||
|
subscribeToWorkspaceEvents('editor:closeTab', (payload) => handleCloseEditorTab(payload.tabId));
|
||||||
|
subscribeToWorkspaceEvents('editor:activateTab', (payload) => handleActivateEditorTab(payload.tabId));
|
||||||
|
subscribeToWorkspaceEvents('editor:updateContent', handleUpdateEditorContent);
|
||||||
|
subscribeToWorkspaceEvents('editor:saveTab', (payload) => handleSaveEditorTab(payload.tabId));
|
||||||
|
subscribeToWorkspaceEvents('editor:changeEncoding', handleChangeEncoding);
|
||||||
|
subscribeToWorkspaceEvents('editor:closeOtherTabs', (payload) => handleCloseOtherEditorTabs(payload.tabId));
|
||||||
|
subscribeToWorkspaceEvents('editor:closeTabsToRight', (payload) => handleCloseEditorTabsToRight(payload.tabId));
|
||||||
|
subscribeToWorkspaceEvents('editor:closeTabsToLeft', (payload) => handleCloseEditorTabsToLeft(payload.tabId));
|
||||||
|
|
||||||
|
subscribeToWorkspaceEvents('connection:connect', (payload) => handleConnectRequest(payload.connectionId));
|
||||||
|
subscribeToWorkspaceEvents('connection:openNewSession', (payload) => handleOpenNewSession(payload.connectionId));
|
||||||
|
subscribeToWorkspaceEvents('connection:requestAdd', handleRequestAddConnection);
|
||||||
|
subscribeToWorkspaceEvents('connection:requestEdit', (payload) => handleRequestEditConnection(payload.connectionInfo));
|
||||||
|
|
||||||
|
subscribeToWorkspaceEvents('search:start', (payload) => handleSearch(payload.term));
|
||||||
|
subscribeToWorkspaceEvents('search:findNext', handleFindNext);
|
||||||
|
subscribeToWorkspaceEvents('search:findPrevious', handleFindPrevious);
|
||||||
|
subscribeToWorkspaceEvents('search:close', handleCloseSearch);
|
||||||
|
|
||||||
|
// 来自 TerminalTabBar 的事件
|
||||||
|
subscribeToWorkspaceEvents('session:activate', (payload) => sessionStore.activateSession(payload.sessionId));
|
||||||
|
subscribeToWorkspaceEvents('session:close', (payload) => sessionStore.closeSession(payload.sessionId));
|
||||||
|
subscribeToWorkspaceEvents('session:closeOthers', (payload) => handleCloseOtherSessions(payload.targetSessionId));
|
||||||
|
subscribeToWorkspaceEvents('session:closeToRight', (payload) => handleCloseSessionsToRight(payload.targetSessionId));
|
||||||
|
subscribeToWorkspaceEvents('session:closeToLeft', (payload) => handleCloseSessionsToLeft(payload.targetSessionId));
|
||||||
|
subscribeToWorkspaceEvents('ui:openLayoutConfigurator', handleOpenLayoutConfigurator);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@@ -119,8 +158,44 @@ onBeforeUnmount(() => {
|
|||||||
// 移除键盘事件监听器
|
// 移除键盘事件监听器
|
||||||
window.removeEventListener('keydown', handleGlobalKeyDown);
|
window.removeEventListener('keydown', handleGlobalKeyDown);
|
||||||
sessionStore.cleanupAllSessions();
|
sessionStore.cleanupAllSessions();
|
||||||
|
|
||||||
|
// +++ 取消订阅工作区事件 +++
|
||||||
|
unsubscribeFromWorkspaceEvents('terminal:sendCommand', (payload) => handleSendCommand(payload.command));
|
||||||
|
unsubscribeFromWorkspaceEvents('terminal:input', handleTerminalInput);
|
||||||
|
unsubscribeFromWorkspaceEvents('terminal:resize', handleTerminalResize);
|
||||||
|
unsubscribeFromWorkspaceEvents('terminal:ready', handleTerminalReady);
|
||||||
|
unsubscribeFromWorkspaceEvents('terminal:clear', handleClearTerminal);
|
||||||
|
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:closeTab', (payload) => handleCloseEditorTab(payload.tabId));
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:activateTab', (payload) => handleActivateEditorTab(payload.tabId));
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:updateContent', handleUpdateEditorContent);
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:saveTab', (payload) => handleSaveEditorTab(payload.tabId));
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:changeEncoding', handleChangeEncoding);
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:closeOtherTabs', (payload) => handleCloseOtherEditorTabs(payload.tabId));
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:closeTabsToRight', (payload) => handleCloseEditorTabsToRight(payload.tabId));
|
||||||
|
unsubscribeFromWorkspaceEvents('editor:closeTabsToLeft', (payload) => handleCloseEditorTabsToLeft(payload.tabId));
|
||||||
|
|
||||||
|
unsubscribeFromWorkspaceEvents('connection:connect', (payload) => handleConnectRequest(payload.connectionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('connection:openNewSession', (payload) => handleOpenNewSession(payload.connectionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('connection:requestAdd', handleRequestAddConnection);
|
||||||
|
unsubscribeFromWorkspaceEvents('connection:requestEdit', (payload) => handleRequestEditConnection(payload.connectionInfo));
|
||||||
|
|
||||||
|
unsubscribeFromWorkspaceEvents('search:start', (payload) => handleSearch(payload.term));
|
||||||
|
unsubscribeFromWorkspaceEvents('search:findNext', handleFindNext);
|
||||||
|
unsubscribeFromWorkspaceEvents('search:findPrevious', handleFindPrevious);
|
||||||
|
unsubscribeFromWorkspaceEvents('search:close', handleCloseSearch);
|
||||||
|
|
||||||
|
unsubscribeFromWorkspaceEvents('session:activate', (payload) => sessionStore.activateSession(payload.sessionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('session:close', (payload) => sessionStore.closeSession(payload.sessionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('session:closeOthers', (payload) => handleCloseOtherSessions(payload.targetSessionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('session:closeToRight', (payload) => handleCloseSessionsToRight(payload.targetSessionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('session:closeToLeft', (payload) => handleCloseSessionsToLeft(payload.targetSessionId));
|
||||||
|
unsubscribeFromWorkspaceEvents('ui:openLayoutConfigurator', handleOpenLayoutConfigurator);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const subscribeToWorkspaceEvents = useWorkspaceEventSubscriber(); // +++ 定义订阅和取消订阅函数 +++
|
||||||
|
const unsubscribeFromWorkspaceEvents = useWorkspaceEventOff();
|
||||||
|
|
||||||
// --- 本地方法 (仅处理 UI 状态) ---
|
// --- 本地方法 (仅处理 UI 状态) ---
|
||||||
const handleRequestAddConnection = () => {
|
const handleRequestAddConnection = () => {
|
||||||
console.log('[WorkspaceView] handleRequestAddConnection 被调用!'); // 添加日志确认事件到达
|
console.log('[WorkspaceView] handleRequestAddConnection 被调用!'); // 添加日志确认事件到达
|
||||||
@@ -571,27 +646,6 @@ const toggleVirtualKeyboard = () => {
|
|||||||
class="layout-renderer-wrapper"
|
class="layout-renderer-wrapper"
|
||||||
:editor-tabs="editorTabs"
|
:editor-tabs="editorTabs"
|
||||||
:active-editor-tab-id="activeEditorTabId"
|
:active-editor-tab-id="activeEditorTabId"
|
||||||
@send-command="handleSendCommand"
|
|
||||||
@terminal-input="handleTerminalInput"
|
|
||||||
@terminal-resize="handleTerminalResize"
|
|
||||||
@terminal-ready="handleTerminalReady"
|
|
||||||
@close-editor-tab="handleCloseEditorTab"
|
|
||||||
@activate-editor-tab="handleActivateEditorTab"
|
|
||||||
@update-editor-content="handleUpdateEditorContent"
|
|
||||||
@save-editor-tab="handleSaveEditorTab"
|
|
||||||
@connect-request="handleConnectRequest"
|
|
||||||
@open-new-session="handleOpenNewSession"
|
|
||||||
@request-add-connection="handleRequestAddConnection"
|
|
||||||
@request-edit-connection="handleRequestEditConnection"
|
|
||||||
@search="handleSearch"
|
|
||||||
@find-next="handleFindNext"
|
|
||||||
@find-previous="handleFindPrevious"
|
|
||||||
@close-search="handleCloseSearch"
|
|
||||||
@clear-terminal="handleClearTerminal"
|
|
||||||
@change-encoding="handleChangeEncoding"
|
|
||||||
@close-other-tabs="handleCloseOtherEditorTabs"
|
|
||||||
@close-tabs-to-right="handleCloseEditorTabsToRight"
|
|
||||||
@close-tabs-to-left="handleCloseEditorTabsToLeft"
|
|
||||||
></LayoutRenderer>
|
></LayoutRenderer>
|
||||||
<div v-else class="pane-placeholder">
|
<div v-else class="pane-placeholder">
|
||||||
{{ t('layout.loading', '加载布局中...') }}
|
{{ t('layout.loading', '加载布局中...') }}
|
||||||
|
|||||||
Reference in New Issue
Block a user