update
This commit is contained in:
@@ -171,13 +171,20 @@ const handleContextMenuAction = (payload: { action: string; targetId: string | n
|
||||
// 注意:关闭左侧通常不包括当前标签本身
|
||||
emitWorkspaceEvent('session:closeToLeft', { targetSessionId: targetId });
|
||||
break;
|
||||
case 'suspend-session': // 新增处理 suspend-session 动作
|
||||
// 确保 targetId 是字符串类型的 sessionId
|
||||
case 'mark-for-suspend': // +++ 修改 action 名称 +++
|
||||
if (typeof targetId === 'string') {
|
||||
console.log(`[TabBar] Context menu action 'suspend-session' requested for session ID: ${targetId}`);
|
||||
sessionStore.requestStartSshSuspend(targetId);
|
||||
console.log(`[TabBar] Context menu action 'mark-for-suspend' requested for session ID: ${targetId}`);
|
||||
sessionStore.requestStartSshSuspend(targetId); // 这个 action 现在是标记
|
||||
} else {
|
||||
console.warn(`[TabBar] 'suspend-session' action called with invalid targetId:`, targetId);
|
||||
console.warn(`[TabBar] 'mark-for-suspend' action called with invalid targetId:`, targetId);
|
||||
}
|
||||
break;
|
||||
case 'unmark-for-suspend': // +++ 新增 case +++
|
||||
if (typeof targetId === 'string') {
|
||||
console.log(`[TabBar] Context menu action 'unmark-for-suspend' requested for session ID: ${targetId}`);
|
||||
sessionStore.requestUnmarkSshSuspend(targetId);
|
||||
} else {
|
||||
console.warn(`[TabBar] 'unmark-for-suspend' action called with invalid targetId:`, targetId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -201,14 +208,17 @@ const contextMenuItems = computed(() => {
|
||||
const currentIndex = props.sessions.findIndex(s => s.sessionId === targetSessionIdValue);
|
||||
const totalTabs = props.sessions.length;
|
||||
|
||||
// 添加挂起会话菜单项(如果适用)
|
||||
// 添加标记/取消标记挂起会话菜单项(如果适用)
|
||||
if (connectionInfo && connectionInfo.type === 'SSH') {
|
||||
// 仅对活动的SSH会话显示 (可以进一步判断会话是否真的已连接等)
|
||||
const isActiveSession = targetSessionState.wsManager.isConnected.value; // 检查 WebSocket 是否连接
|
||||
if (isActiveSession) {
|
||||
items.push({ label: 'tabs.contextMenu.suspendSession', action: 'suspend-session' });
|
||||
// 为分隔符提供空的 label 和 action 以满足 MenuItem 类型
|
||||
items.push({ label: '', action: '', isSeparator: true });
|
||||
const isActiveSession = targetSessionState.wsManager.isConnected.value;
|
||||
if (isActiveSession) { // 只对活动的SSH会话显示相关操作
|
||||
if (targetSessionState.isMarkedForSuspend) {
|
||||
items.push({ label: 'tabs.contextMenu.unmarkForSuspend', action: 'unmark-for-suspend' });
|
||||
} else {
|
||||
// 当未标记时,显示原来的“挂起”文本,但 action 触发新的标记流程
|
||||
items.push({ label: 'tabs.contextMenu.suspendSession', action: 'mark-for-suspend' });
|
||||
}
|
||||
items.push({ label: '', action: '', isSeparator: true }); // 分隔符
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1137,7 +1137,8 @@
|
||||
"closeOthers": "Close Other Tabs",
|
||||
"closeRight": "Close Tabs to the Right",
|
||||
"closeLeft": "Close Tabs to the Left",
|
||||
"suspendSession": "Suspend Session"
|
||||
"suspendSession": "Suspend Session",
|
||||
"unmarkForSuspend": "Unmark Suspend"
|
||||
},
|
||||
"closeTabTooltip": "Close Tab",
|
||||
"newTabTooltip": "New Connection Tab"
|
||||
|
||||
@@ -1139,7 +1139,8 @@
|
||||
"closeOthers": "关闭其他标签页",
|
||||
"closeRight": "关闭右侧标签页",
|
||||
"closeLeft": "关闭左侧标签页",
|
||||
"suspendSession": "挂起会话"
|
||||
"suspendSession": "挂起会话",
|
||||
"unmarkForSuspend": "取消挂起"
|
||||
},
|
||||
"closeTabTooltip": "关闭标签页",
|
||||
"newTabTooltip": "新建连接标签页"
|
||||
|
||||
@@ -3,13 +3,15 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { sessions, suspendedSshSessions, isLoadingSuspendedSessions, activeSessionId } from '../state';
|
||||
import type {
|
||||
MessagePayload,
|
||||
SshMarkForSuspendReqMessage, // +++ 修改:导入新的请求类型 +++
|
||||
SshMarkForSuspendReqMessage,
|
||||
SshUnmarkForSuspendReqMessage, // +++ 新增导入 +++
|
||||
SshSuspendResumeReqMessage,
|
||||
SshSuspendTerminateReqMessage,
|
||||
SshSuspendRemoveEntryReqMessage,
|
||||
SshSuspendEditNameReqMessage,
|
||||
// S2C Payloads
|
||||
SshMarkedForSuspendAckPayload, // +++ 新增:导入新的响应类型 +++
|
||||
SshMarkedForSuspendAckPayload,
|
||||
SshUnmarkedForSuspendAckPayload, // +++ 新增导入 +++
|
||||
SshSuspendListResponsePayload,
|
||||
SshSuspendResumedNotifPayload,
|
||||
SshOutputCachedChunkPayload,
|
||||
@@ -77,14 +79,28 @@ export const requestStartSshSuspend = (sessionId: string): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 不再需要获取 initialBuffer
|
||||
let initialBuffer = ''; // +++ 恢复 initialBuffer 的获取 +++
|
||||
if (session.terminalManager && session.terminalManager.terminalInstance && session.terminalManager.terminalInstance.value) {
|
||||
const term = session.terminalManager.terminalInstance.value;
|
||||
const buffer = term.buffer.active;
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
initialBuffer += (buffer.getLine(i)?.translateToString(true) || '') + '\n';
|
||||
}
|
||||
// 移除可能多余的最后一个换行符
|
||||
if (initialBuffer.endsWith('\n')) {
|
||||
initialBuffer = initialBuffer.slice(0, -1);
|
||||
}
|
||||
console.log(`[${t('term.sshSuspend')}] 已获取会话 ${sessionId} 的初始屏幕缓冲区内容,长度: ${initialBuffer.length}`);
|
||||
} else {
|
||||
console.warn(`[${t('term.sshSuspend')}] 未能获取会话 ${sessionId} 的终端实例以提取初始缓冲区。`);
|
||||
}
|
||||
|
||||
const message: SshMarkForSuspendReqMessage = { // +++ 修改:使用新的消息类型 +++
|
||||
const message: SshMarkForSuspendReqMessage = {
|
||||
type: 'SSH_MARK_FOR_SUSPEND',
|
||||
payload: { sessionId },
|
||||
payload: { sessionId, initialBuffer: initialBuffer || undefined }, // +++ 将 initialBuffer 添加到 payload +++
|
||||
};
|
||||
session.wsManager.sendMessage(message);
|
||||
console.log(`[${t('term.sshSuspend')}] 已发送 SSH_MARK_FOR_SUSPEND 请求 (会话 ID: ${sessionId})`);
|
||||
console.log(`[${t('term.sshSuspend')}] 已发送 SSH_MARK_FOR_SUSPEND 请求 (会话 ID: ${sessionId}, 包含初始缓冲区: ${!!initialBuffer})`);
|
||||
// 前端在发送此请求后,会话应保持活动状态,直到用户关闭标签页或网络断开。
|
||||
// 后端会在 WebSocket 关闭时处理实际的挂起。
|
||||
// 用户界面上可以给一个提示,表明“此会话已标记,关闭后将尝试挂起”。
|
||||
@@ -100,6 +116,38 @@ export const requestStartSshSuspend = (sessionId: string): void => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 请求取消标记一个会话为待挂起
|
||||
* @param sessionId 要取消标记的活动会话 ID
|
||||
*/
|
||||
export const requestUnmarkSshSuspend = (sessionId: string): void => {
|
||||
const session = sessions.value.get(sessionId);
|
||||
if (session && session.wsManager) {
|
||||
if (!session.wsManager.isConnected.value) {
|
||||
console.warn(`[${t('term.sshSuspend')}] WebSocket 未连接,无法请求取消标记挂起 (会话 ID: ${sessionId})。`);
|
||||
useUiNotificationsStore().addNotification({ type: 'error', message: t('sshSuspend.notifications.wsNotConnectedError') });
|
||||
return;
|
||||
}
|
||||
if (!session.isMarkedForSuspend) {
|
||||
console.warn(`[${t('term.sshSuspend')}] 会话 ${sessionId} 并未被标记为待挂起,无需取消。`);
|
||||
// 可以选择不发送请求或发送一个让后端确认的请求
|
||||
// 为保持简单,如果前端状态已经是未标记,则不执行操作或仅给用户提示
|
||||
useUiNotificationsStore().addNotification({ type: 'info', message: t('sshSuspend.notifications.notMarkedWarning') });
|
||||
return;
|
||||
}
|
||||
|
||||
const message: SshUnmarkForSuspendReqMessage = {
|
||||
type: 'SSH_UNMARK_FOR_SUSPEND',
|
||||
payload: { sessionId },
|
||||
};
|
||||
session.wsManager.sendMessage(message);
|
||||
console.log(`[${t('term.sshSuspend')}] 已发送 SSH_UNMARK_FOR_SUSPEND 请求 (会话 ID: ${sessionId})`);
|
||||
} else {
|
||||
console.warn(`[${t('term.sshSuspend')}] 未找到会话或 WebSocket 管理器 (会话 ID: ${sessionId}),无法请求取消标记挂起。`);
|
||||
useUiNotificationsStore().addNotification({ type: 'error', message: t('sshSuspend.notifications.sessionNotFoundError') });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取挂起的 SSH 会话列表 (通过 HTTP API)
|
||||
*/
|
||||
@@ -331,6 +379,32 @@ const handleSshMarkedForSuspendAck = (payload: SshMarkedForSuspendAckPayload): v
|
||||
}
|
||||
};
|
||||
|
||||
const handleSshUnmarkedForSuspendAck = (payload: SshUnmarkedForSuspendAckPayload): void => {
|
||||
const uiNotificationsStore = useUiNotificationsStore();
|
||||
console.log(`[${t('term.sshSuspend')}] 接到 SSH_UNMARKED_FOR_SUSPEND_ACK:`, payload);
|
||||
const session = sessions.value.get(payload.sessionId);
|
||||
|
||||
if (payload.success) {
|
||||
if (session) {
|
||||
session.isMarkedForSuspend = false;
|
||||
}
|
||||
uiNotificationsStore.addNotification({
|
||||
type: 'success',
|
||||
message: t('sshSuspend.notifications.unmarkedSuccess', { id: payload.sessionId.slice(0,8) }),
|
||||
});
|
||||
} else {
|
||||
// 即便后端失败,如果前端之前是标记状态,也最好保持一致或提示用户检查
|
||||
// 但通常后端失败意味着前端状态可能与后端不一致,提示错误让用户知晓
|
||||
uiNotificationsStore.addNotification({
|
||||
type: 'error',
|
||||
message: t('sshSuspend.notifications.unmarkError', { error: payload.error || t('term.unknownError') }),
|
||||
});
|
||||
console.error(`[${t('term.sshSuspend')}] 取消标记会话 ${payload.sessionId} 失败: ${payload.error}`);
|
||||
// 此处不自动回滚前端的 isMarkedForSuspend 状态,因为后端是权威源。
|
||||
// 如果后端说操作失败,那么会话可能仍然被后端认为是标记的(尽管这不应该发生,因为后端会先清除标记)。
|
||||
}
|
||||
};
|
||||
|
||||
const handleSshSuspendListResponse = (payload: SshSuspendListResponsePayload): void => {
|
||||
console.log(`[${t('term.sshSuspend')}] 接到 SSH_SUSPEND_LIST_RESPONSE,数量: ${payload.suspendSessions.length}`);
|
||||
suspendedSshSessions.value = payload.suspendSessions;
|
||||
@@ -536,8 +610,9 @@ export const registerSshSuspendHandlers = (wsManager: WsManagerInstance): void =
|
||||
|
||||
// 注意:wsManager.onMessage 返回一个注销函数,如果需要,可以收集它们并在会话关闭时调用。
|
||||
// 但通常这些处理器会随 wsManager 实例的生命周期一起存在。
|
||||
// wsManager.onMessage('SSH_SUSPEND_STARTED_RESP', (p: MessagePayload) => handleSshSuspendStartedResp(p as SshSuspendStartedRespPayload)); // 已移除
|
||||
wsManager.onMessage('SSH_MARKED_FOR_SUSPEND_ACK', (p: MessagePayload) => handleSshMarkedForSuspendAck(p as SshMarkedForSuspendAckPayload)); // +++ 新增处理器 +++
|
||||
// wsManager.onMessage('SSH_SUSPEND_STARTED_RESP', (p: MessagePayload) => handleSshSuspendStartedResp(p as SshSuspendStartedRespPayload));
|
||||
wsManager.onMessage('SSH_MARKED_FOR_SUSPEND_ACK', (p: MessagePayload) => handleSshMarkedForSuspendAck(p as SshMarkedForSuspendAckPayload));
|
||||
wsManager.onMessage('SSH_UNMARKED_FOR_SUSPEND_ACK', (p: MessagePayload) => handleSshUnmarkedForSuspendAck(p as SshUnmarkedForSuspendAckPayload)); // +++ 新增处理器 +++
|
||||
wsManager.onMessage('SSH_SUSPEND_LIST_RESPONSE', (p: MessagePayload) => handleSshSuspendListResponse(p as SshSuspendListResponsePayload));
|
||||
wsManager.onMessage('SSH_SUSPEND_RESUMED_NOTIF', (p: MessagePayload) => handleSshSuspendResumedNotif(p as SshSuspendResumedNotifPayload));
|
||||
wsManager.onMessage('SSH_OUTPUT_CACHED_CHUNK', (p: MessagePayload) => handleSshOutputCachedChunk(p as SshOutputCachedChunkPayload));
|
||||
|
||||
@@ -45,12 +45,23 @@ export interface SshSuspendEditNameReqPayload {
|
||||
customName: string;
|
||||
}
|
||||
|
||||
export interface SshMarkForSuspendReqPayload { // +++ 新增 +++
|
||||
export interface SshMarkForSuspendReqPayload {
|
||||
sessionId: string;
|
||||
initialBuffer?: string; // +++ 新增:可选的初始屏幕缓冲区内容 +++
|
||||
}
|
||||
|
||||
export interface SshUnmarkForSuspendReqPayload {
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
// --- Server to Client (S2C) Message Payloads ---
|
||||
export interface SshMarkedForSuspendAckPayload { // +++ 新增 +++
|
||||
export interface SshMarkedForSuspendAckPayload {
|
||||
sessionId: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface SshUnmarkedForSuspendAckPayload { // +++ 新增 +++
|
||||
sessionId: string;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
@@ -135,17 +146,27 @@ export interface SshSuspendEditNameReqMessage extends WebSocketMessage {
|
||||
payload: SshSuspendEditNameReqPayload;
|
||||
}
|
||||
|
||||
export interface SshMarkForSuspendReqMessage extends WebSocketMessage { // +++ 新增 +++
|
||||
export interface SshMarkForSuspendReqMessage extends WebSocketMessage {
|
||||
type: 'SSH_MARK_FOR_SUSPEND';
|
||||
payload: SshMarkForSuspendReqPayload;
|
||||
}
|
||||
|
||||
export interface SshUnmarkForSuspendReqMessage extends WebSocketMessage { // +++ 新增 +++
|
||||
type: 'SSH_UNMARK_FOR_SUSPEND';
|
||||
payload: SshUnmarkForSuspendReqPayload;
|
||||
}
|
||||
|
||||
// --- Specific S2C Message Interfaces ---
|
||||
export interface SshMarkedForSuspendAckMessage extends WebSocketMessage { // +++ 新增 +++
|
||||
export interface SshMarkedForSuspendAckMessage extends WebSocketMessage {
|
||||
type: 'SSH_MARKED_FOR_SUSPEND_ACK';
|
||||
payload: SshMarkedForSuspendAckPayload;
|
||||
}
|
||||
|
||||
export interface SshUnmarkedForSuspendAckMessage extends WebSocketMessage { // +++ 新增 +++
|
||||
type: 'SSH_UNMARKED_FOR_SUSPEND_ACK';
|
||||
payload: SshUnmarkedForSuspendAckPayload;
|
||||
}
|
||||
|
||||
export interface SshSuspendStartedRespMessage extends WebSocketMessage {
|
||||
type: 'SSH_SUSPEND_STARTED';
|
||||
payload: SshSuspendStartedRespPayload;
|
||||
@@ -194,10 +215,12 @@ export type SshSuspendC2SMessage =
|
||||
| SshSuspendTerminateReqMessage
|
||||
| SshSuspendRemoveEntryReqMessage
|
||||
| SshSuspendEditNameReqMessage
|
||||
| SshMarkForSuspendReqMessage; // +++ 新增 +++
|
||||
| SshMarkForSuspendReqMessage
|
||||
| SshUnmarkForSuspendReqMessage; // +++ 新增 +++
|
||||
|
||||
export type SshSuspendS2CMessage =
|
||||
| SshMarkedForSuspendAckMessage // +++ 新增 +++
|
||||
| SshMarkedForSuspendAckMessage
|
||||
| SshUnmarkedForSuspendAckMessage // +++ 新增 +++
|
||||
| SshSuspendStartedRespMessage
|
||||
| SshSuspendListResponseMessage
|
||||
| SshSuspendResumedNotifMessage
|
||||
|
||||
Reference in New Issue
Block a user