feat: 移动端适配挂起会话管理器
This commit is contained in:
@@ -8,6 +8,7 @@ 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 SuspendedSshSessionsModal from './SuspendedSshSessionsModal.vue'; // +++ Import the new modal +++
|
||||||
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 新增导入 +++
|
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)
|
||||||
@@ -46,6 +47,7 @@ const props = defineProps<{
|
|||||||
const isSearching = ref(false);
|
const isSearching = ref(false);
|
||||||
const searchTerm = ref('');
|
const searchTerm = ref('');
|
||||||
const showQuickCommands = ref(false); // +++ Add state for modal visibility +++
|
const showQuickCommands = ref(false); // +++ Add state for modal visibility +++
|
||||||
|
const showSuspendedSshSessionsModal = ref(false); // +++ Add state for suspended SSH sessions modal +++
|
||||||
// *** 移除本地的搜索结果 ref ***
|
// *** 移除本地的搜索结果 ref ***
|
||||||
// const searchResultCount = ref(0);
|
// const searchResultCount = ref(0);
|
||||||
// const currentSearchResultIndex = ref(0);
|
// const currentSearchResultIndex = ref(0);
|
||||||
@@ -280,6 +282,15 @@ const closeQuickCommandsModal = () => {
|
|||||||
showQuickCommands.value = false;
|
showQuickCommands.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// +++ Functions to control the suspended SSH sessions modal +++
|
||||||
|
const openSuspendedSshSessionsModal = () => {
|
||||||
|
showSuspendedSshSessionsModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeSuspendedSshSessionsModal = () => {
|
||||||
|
showSuspendedSshSessionsModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
// +++ 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}`);
|
||||||
@@ -362,6 +373,15 @@ const handleQuickCommandExecute = (command: string) => {
|
|||||||
>
|
>
|
||||||
<i class="fas fa-keyboard text-base" :class="{ 'opacity-50': !props.isVirtualKeyboardVisible }"></i>
|
<i class="fas fa-keyboard text-base" :class="{ 'opacity-50': !props.isVirtualKeyboardVisible }"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<!-- +++ Suspended SSH Sessions Button (Mobile only, moved here) +++ -->
|
||||||
|
<button
|
||||||
|
v-if="props.isMobile"
|
||||||
|
@click="openSuspendedSshSessionsModal"
|
||||||
|
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('suspendedSshSessions.title', '挂起会话')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-pause-circle text-base"></i>
|
||||||
|
</button>
|
||||||
<!-- Search Toggle Button -->
|
<!-- Search Toggle Button -->
|
||||||
<button
|
<button
|
||||||
v-if="!props.isMobile"
|
v-if="!props.isMobile"
|
||||||
@@ -401,6 +421,11 @@ const handleQuickCommandExecute = (command: string) => {
|
|||||||
@close="closeQuickCommandsModal"
|
@close="closeQuickCommandsModal"
|
||||||
@execute-command="handleQuickCommandExecute"
|
@execute-command="handleQuickCommandExecute"
|
||||||
/>
|
/>
|
||||||
|
<!-- +++ Suspended SSH Sessions Modal Instance +++ -->
|
||||||
|
<SuspendedSshSessionsModal
|
||||||
|
:is-visible="showSuspendedSshSessionsModal"
|
||||||
|
@close="closeSuspendedSshSessionsModal"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits, watch, onMounted, onUnmounted } from 'vue';
|
||||||
|
import SuspendedSshSessionsView from '../views/SuspendedSshSessionsView.vue'; // 导入视图
|
||||||
|
import { useWorkspaceEventSubscriber, useWorkspaceEventOff } from '../composables/workspaceEvents'; // 导入事件订阅器和取消订阅器
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
isVisible: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'close'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 键盘监听 Esc 关闭
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(() => props.isVisible, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
document.addEventListener('keydown', handleKeydown);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const onWorkspaceEvent = useWorkspaceEventSubscriber();
|
||||||
|
const offWorkspaceEvent = useWorkspaceEventOff(); // 获取取消订阅函数
|
||||||
|
|
||||||
|
// 定义事件处理函数
|
||||||
|
const handleSuspendedSessionActionCompleted = () => {
|
||||||
|
console.log('[SuspendedSshSessionsModal] Received suspendedSession:actionCompleted event, closing modal.');
|
||||||
|
closeModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 监听 suspendedSession:actionCompleted 事件以关闭模态框
|
||||||
|
onWorkspaceEvent('suspendedSession:actionCompleted', handleSuspendedSessionActionCompleted);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
|
// 组件卸载时取消订阅
|
||||||
|
offWorkspaceEvent('suspendedSession:actionCompleted', handleSuspendedSessionActionCompleted);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="isVisible" class="fixed inset-0 bg-overlay flex justify-center items-center z-50 p-4" @click.self="closeModal">
|
||||||
|
<div class="bg-background text-foreground p-4 rounded-lg shadow-xl border border-border w-full max-w-2xl max-h-[85vh] flex flex-col relative">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="absolute top-2 right-2 p-1 text-text-secondary hover:text-foreground z-10" @click="closeModal" :title="t('close', '关闭')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<!-- Title -->
|
||||||
|
<h3 class="text-lg font-semibold text-center mb-3 flex-shrink-0">{{ t('suspendedSshSessions.modalTitle', '挂起的 SSH 会话') }}</h3>
|
||||||
|
<!-- Suspended SSH Sessions View Embedded -->
|
||||||
|
<div class="flex-grow overflow-y-auto border border-border rounded">
|
||||||
|
<SuspendedSshSessionsView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bg-overlay {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -44,6 +44,9 @@ export type WorkspaceEventPayloads = {
|
|||||||
// UI Interaction Events
|
// UI Interaction Events
|
||||||
'ui:openLayoutConfigurator': void;
|
'ui:openLayoutConfigurator': void;
|
||||||
// 'ui:toggleVirtualKeyboard': void; // 如果决定迁移 CommandInputBar 的这个事件
|
// 'ui:toggleVirtualKeyboard': void; // 如果决定迁移 CommandInputBar 的这个事件
|
||||||
|
|
||||||
|
// Suspended SSH Session Events
|
||||||
|
'suspendedSession:actionCompleted': void; // Emitted when a resume/remove action is completed
|
||||||
};
|
};
|
||||||
|
|
||||||
// 创建 mitt 事件发射器实例
|
// 创建 mitt 事件发射器实例
|
||||||
|
|||||||
@@ -112,8 +112,10 @@ import { useI18n } from 'vue-i18n';
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useSessionStore } from '../stores/session.store';
|
import { useSessionStore } from '../stores/session.store';
|
||||||
import type { SuspendedSshSession } from '../types/ssh-suspend.types';
|
import type { SuspendedSshSession } from '../types/ssh-suspend.types';
|
||||||
|
import { useWorkspaceEventEmitter } from '../composables/workspaceEvents'; // +++ 导入事件发射器 +++
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const emitWorkspaceEvent = useWorkspaceEventEmitter(); // +++ 获取事件发射器 +++
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
const { suspendedSshSessions: storeSuspendedSshSessions, isLoadingSuspendedSessions: isLoading } = storeToRefs(sessionStore);
|
const { suspendedSshSessions: storeSuspendedSshSessions, isLoadingSuspendedSessions: isLoading } = storeToRefs(sessionStore);
|
||||||
|
|
||||||
@@ -233,6 +235,9 @@ const resumeSession = async (session: SuspendedSshSession) => { // 参数类型
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[SuspendedSshSessionsView] Error during resumeSession for ${session.suspendSessionId}:`, error);
|
console.error(`[SuspendedSshSessionsView] Error during resumeSession for ${session.suspendSessionId}:`, error);
|
||||||
}
|
}
|
||||||
|
// 无论成功与否(或者仅在成功时,取决于需求),都可能需要通知模态框关闭
|
||||||
|
// 为了简化,这里假设操作已发起,具体成功状态由 store 或后端处理
|
||||||
|
emitWorkspaceEvent('suspendedSession:actionCompleted');
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSession = (session: SuspendedSshSession) => { // 参数类型改为 SuspendedSshSession
|
const removeSession = (session: SuspendedSshSession) => { // 参数类型改为 SuspendedSshSession
|
||||||
@@ -241,6 +246,7 @@ const removeSession = (session: SuspendedSshSession) => { // 参数类型改为
|
|||||||
} else if (session.backendSshStatus === 'disconnected_by_backend') {
|
} else if (session.backendSshStatus === 'disconnected_by_backend') {
|
||||||
sessionStore.removeSshSessionEntry(session.suspendSessionId);
|
sessionStore.removeSshSessionEntry(session.suspendSessionId);
|
||||||
}
|
}
|
||||||
|
emitWorkspaceEvent('suspendedSession:actionCompleted');
|
||||||
};
|
};
|
||||||
|
|
||||||
let fetchIntervalId: number | undefined;
|
let fetchIntervalId: number | undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user