diff --git a/orion-visor-ui/src/store/modules/terminal/index.ts b/orion-visor-ui/src/store/modules/terminal/index.ts
index 73ca7c09..b9286daa 100644
--- a/orion-visor-ui/src/store/modules/terminal/index.ts
+++ b/orion-visor-ui/src/store/modules/terminal/index.ts
@@ -14,6 +14,7 @@ import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
import type { HostQueryResponse } from '@/api/asset/host';
import type { TerminalTheme, TerminalThemeSchema } from '@/api/asset/terminal';
import { getTerminalThemes } from '@/api/asset/terminal';
+import { markRaw } from 'vue';
import { defineStore } from 'pinia';
import { getPreference, updatePreference } from '@/api/user/preference';
import { getLatestConnectHostId } from '@/api/asset/terminal-connect-log';
@@ -73,7 +74,7 @@ export default defineStore('terminal', {
hosts: {} as AuthorizedHostQueryResponse,
tabManager: new TerminalTabManager(),
panelManager: new TerminalPanelManager(),
- sessionManager: new TerminalSessionManager(),
+ sessionManager: markRaw(new TerminalSessionManager()),
transferManager: new SftpTransferManager(),
}),
diff --git a/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table-header.vue b/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table-header.vue
index 62cb5890..f05e7b52 100644
--- a/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table-header.vue
+++ b/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table-header.vue
@@ -45,7 +45,7 @@
-
diff --git a/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-view.vue b/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-view.vue
index c3fb8164..817769ae 100644
--- a/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-view.vue
+++ b/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-view.vue
@@ -64,7 +64,7 @@
// 发送命令
const writeCommand = (value: string) => {
- if (session.value?.canWrite) {
+ if (session.value?.status.canWrite) {
session.value?.handler.pasteTrimEnd(value);
}
};
diff --git a/orion-visor-ui/src/views/host/terminal/handler/base-session.ts b/orion-visor-ui/src/views/host/terminal/handler/base-session.ts
index 28406f3f..7c55e19a 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/base-session.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/base-session.ts
@@ -1,50 +1,66 @@
-import type { ITerminalSession, TerminalPanelTabItem } from '../types/define';
+import type { ITerminalSession, TerminalPanelTabItem, TerminalStatus } from '../types/define';
+import type { Reactive } from 'vue';
+import { reactive } from 'vue';
+import { TerminalSessionStatus } from '@/views/host/terminal/types/const';
// 会话基类
-export default abstract class BaseSession implements ITerminalSession {
+export default abstract class BaseSession implements ITerminalSession {
- public type: string;
- public hostId: number;
- public title: string;
- public address: string;
+ public readonly type: string;
+ public readonly hostId: number;
+ public readonly title: string;
+ public readonly address: string;
+ public readonly status: Reactive;
public sessionId: string;
- public connected: boolean;
- public canReconnect: boolean;
- public canWrite: boolean;
- protected constructor(type: string, tab: TerminalPanelTabItem) {
+ protected constructor(type: string, tab: TerminalPanelTabItem, status: Partial) {
this.type = type;
this.hostId = tab.hostId;
this.title = tab.title;
this.address = tab.address;
this.sessionId = tab.sessionId;
- this.connected = false;
- this.canWrite = false;
- this.canReconnect = false;
- }
-
- // 设置是否可写
- setCanWrite(canWrite: boolean): void {
- this.canWrite = canWrite;
- }
-
- // 设置已连接
- setConnected(): void {
- this.connected = true;
+ this.status = reactive({
+ connectStatus: TerminalSessionStatus.CONNECTING,
+ connected: false,
+ canWrite: false,
+ canReconnect: false,
+ ...status,
+ } as Status);
}
// 连接会话
connect(): void {
+ this.status.connectStatus = TerminalSessionStatus.CONNECTING;
}
// 断开连接
disconnect(): void {
- this.connected = false;
+ // 设置已关闭
+ this.setClosed();
}
// 关闭
close(): void {
- this.connected = false;
+ // 设置已关闭
+ this.setClosed();
+ }
+
+ // 设置是否可写
+ setCanWrite(canWrite: boolean): void {
+ this.status.canWrite = canWrite;
+ }
+
+ // 设置已连接
+ setConnected(): void {
+ this.status.connected = true;
+ this.status.connectStatus = TerminalSessionStatus.CONNECTED;
+ }
+
+ // 设置已关闭
+ setClosed(): void {
+ this.status.connected = false;
+ this.status.canWrite = false;
+ this.status.connectStatus = TerminalSessionStatus.CLOSED;
}
}
diff --git a/orion-visor-ui/src/views/host/terminal/handler/sftp-session.ts b/orion-visor-ui/src/views/host/terminal/handler/sftp-session.ts
index b2e5cff6..42eba81b 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/sftp-session.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/sftp-session.ts
@@ -1,4 +1,4 @@
-import type { ISftpSession, ISftpSessionResolver, ITerminalChannel, TerminalPanelTabItem } from '../types/define';
+import type { ISftpSession, ISftpSessionResolver, ITerminalChannel, TerminalPanelTabItem, TerminalStatus } from '../types/define';
import { h } from 'vue';
import { InputProtocol } from '@/types/protocol/terminal.protocol';
import { PanelSessionType } from '../types/const';
@@ -6,7 +6,7 @@ import { Modal } from '@arco-design/web-vue';
import BaseSession from './base-session';
// sftp 会话实现
-export default class SftpSession extends BaseSession implements ISftpSession {
+export default class SftpSession extends BaseSession implements ISftpSession {
public resolver: ISftpSessionResolver;
@@ -16,7 +16,7 @@ export default class SftpSession extends BaseSession implements ISftpSession {
constructor(tab: TerminalPanelTabItem,
channel: ITerminalChannel) {
- super(PanelSessionType.SFTP.type, tab);
+ super(PanelSessionType.SFTP.type, tab, {});
this.channel = channel;
this.showHiddenFile = false;
this.resolver = undefined as unknown as ISftpSessionResolver;
@@ -27,13 +27,6 @@ export default class SftpSession extends BaseSession implements ISftpSession {
this.resolver = resolver;
}
- // 设置已连接
- setConnected(): void {
- super.setConnected();
- // 连接回调
- this.resolver.connectCallback();
- }
-
// 连接会话
connect(): void {
super.connect();
@@ -171,4 +164,11 @@ export default class SftpSession extends BaseSession implements ISftpSession {
});
}
+ // 设置已连接
+ setConnected(): void {
+ super.setConnected();
+ // 连接回调
+ this.resolver.connectCallback();
+ }
+
}
diff --git a/orion-visor-ui/src/views/host/terminal/handler/ssh-session-handler.ts b/orion-visor-ui/src/views/host/terminal/handler/ssh-session-handler.ts
index cef0a795..67de5031 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/ssh-session-handler.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/ssh-session-handler.ts
@@ -91,9 +91,9 @@ export default class SshSessionHandler implements ISshSessionHandler {
case 'openSftp':
case 'uploadFile':
case 'checkAppendMissing':
- return this.session.canWrite;
+ return this.session.status.canWrite;
case 'disconnect':
- return this.session.connected;
+ return this.session.status.connected;
default:
return true;
}
@@ -197,7 +197,7 @@ export default class SshSessionHandler implements ISshSessionHandler {
// 字号增加
private fontSizeAdd(addSize: number) {
this.inst.options['fontSize'] = this.inst.options['fontSize'] as number + addSize;
- if (this.session.connected) {
+ if (this.session.status.connected) {
this.session.fit();
this.inst.focus();
}
diff --git a/orion-visor-ui/src/views/host/terminal/handler/ssh-session.ts b/orion-visor-ui/src/views/host/terminal/handler/ssh-session.ts
index 79c38480..1f9e7e1f 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/ssh-session.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/ssh-session.ts
@@ -2,12 +2,12 @@ import type { UnwrapRef } from 'vue';
import type { ISearchOptions } from '@xterm/addon-search';
import { SearchAddon } from '@xterm/addon-search';
import type { TerminalPreference } from '@/store/modules/terminal/types';
-import type { ISshSession, ISshSessionHandler, ITerminalChannel, TerminalPanelTabItem, XtermDomRef } from '../types/define';
+import type { ISshSession, ISshSessionHandler, ITerminalChannel, TerminalPanelTabItem, TerminalStatus, XtermDomRef } from '../types/define';
import type { XtermAddons } from '@/types/xterm';
import { defaultFontFamily } from '@/types/xterm';
import { useTerminalStore } from '@/store';
import { InputProtocol } from '@/types/protocol/terminal.protocol';
-import { PanelSessionType, TerminalSessionStatus, TerminalShortcutType } from '../types/const';
+import { PanelSessionType, TerminalShortcutType } from '../types/const';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { WebLinksAddon } from '@xterm/addon-web-links';
@@ -21,12 +21,10 @@ import SshSessionHandler from './ssh-session-handler';
import BaseSession from './base-session';
// ssh 会话实现
-export default class SshSession extends BaseSession implements ISshSession {
+export default class SshSession extends BaseSession implements ISshSession {
public inst: Terminal;
- public status: number;
-
public handler: ISshSessionHandler;
private readonly channel: ITerminalChannel;
@@ -38,10 +36,9 @@ export default class SshSession extends BaseSession implements ISshSession {
constructor(tab: TerminalPanelTabItem,
channel: ITerminalChannel,
canUseWebgl: boolean) {
- super(PanelSessionType.SSH.type, tab);
+ super(PanelSessionType.SSH.type, tab, {});
this.channel = channel;
this.canUseWebgl = canUseWebgl;
- this.status = TerminalSessionStatus.CONNECTING;
this.inst = undefined as unknown as Terminal;
this.handler = undefined as unknown as ISshSessionHandler;
this.addons = {} as XtermAddons;
@@ -93,9 +90,9 @@ export default class SshSession extends BaseSession implements ISshSession {
e.preventDefault();
}
// 检查重新连接
- if (!this.connected && this.canReconnect && e.key === 'Enter') {
+ if (!this.status.connected && this.status.canReconnect && e.key === 'Enter') {
// 防止重复回车
- this.canReconnect = false;
+ this.status.canReconnect = false;
// 异步作用域重新连接
setTimeout(async () => {
await useTerminalStore().reOpenSession(this.sessionId);
@@ -120,7 +117,7 @@ export default class SshSession extends BaseSession implements ISshSession {
private registerEvent(dom: HTMLElement, preference: UnwrapRef) {
// 注册输入事件
this.inst.onData(s => {
- if (!this.canWrite || !this.connected) {
+ if (!this.status.canWrite || !this.status.connected) {
return;
}
// 输入
@@ -145,7 +142,7 @@ export default class SshSession extends BaseSession implements ISshSession {
}
// 注册 resize 事件
this.inst.onResize(({ cols, rows }) => {
- if (!this.connected) {
+ if (!this.status.connected) {
return;
}
this.channel.send(InputProtocol.SSH_RESIZE, {
@@ -158,7 +155,7 @@ export default class SshSession extends BaseSession implements ISshSession {
addEventListen(dom, 'contextmenu', async () => {
// 右键粘贴逻辑
if (preference.interactSetting.rightClickPaste) {
- if (!this.canWrite || !this.connected) {
+ if (!this.status.canWrite || !this.status.connected) {
return;
}
// 未开启右键选中 || 开启并无选中的内容则粘贴
@@ -204,29 +201,9 @@ export default class SshSession extends BaseSession implements ISshSession {
}
}
- // 设置已连接
- setConnected(): void {
- super.setConnected();
- // 设置状态
- this.status = TerminalSessionStatus.CONNECTED;
- this.inst.focus();
- }
-
- // 设置是否可写
- setCanWrite(canWrite: boolean): void {
- super.setCanWrite(canWrite);
- if (canWrite) {
- this.inst.options.cursorBlink = useTerminalStore().preference.displaySetting.cursorBlink;
- } else {
- this.inst.options.cursorBlink = false;
- }
- }
-
// 连接会话
connect(): void {
super.connect();
- // 设置状态
- this.status = TerminalSessionStatus.CONNECTING;
// 发送会话初始化请求
this.channel.send(InputProtocol.CHECK, {
sessionId: this.sessionId,
@@ -295,4 +272,20 @@ export default class SshSession extends BaseSession implements ISshSession {
}
}
+ // 设置已连接
+ setConnected(): void {
+ super.setConnected();
+ this.inst.focus();
+ }
+
+ // 设置是否可写
+ setCanWrite(canWrite: boolean): void {
+ super.setCanWrite(canWrite);
+ if (canWrite) {
+ this.inst.options.cursorBlink = useTerminalStore().preference.displaySetting.cursorBlink;
+ } else {
+ this.inst.options.cursorBlink = false;
+ }
+ }
+
}
diff --git a/orion-visor-ui/src/views/host/terminal/handler/terminal-channel.ts b/orion-visor-ui/src/views/host/terminal/handler/terminal-channel.ts
index 8b635132..b584a299 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/terminal-channel.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/terminal-channel.ts
@@ -77,7 +77,7 @@ export default class TerminalChannel implements ITerminalChannel {
private closeCallback(): void {
// 关闭时将手动触发 close 消息, 有可能是其他原因关闭的, 没有接收到 close 消息, 导致已断开是终端还是显示已连接
Object.values(this.sessionManager.sessions).forEach(s => {
- if (!s?.connected) {
+ if (!s?.status.connected) {
return;
}
// close 消息
diff --git a/orion-visor-ui/src/views/host/terminal/handler/terminal-output-processor.ts b/orion-visor-ui/src/views/host/terminal/handler/terminal-output-processor.ts
index be1252a6..3dc8c51d 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/terminal-output-processor.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/terminal-output-processor.ts
@@ -1,7 +1,7 @@
import type { ISftpSession, ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSession, ITerminalSessionManager } from '../types/define';
import type { OutputPayload } from '@/types/protocol/terminal.protocol';
import { InputProtocol } from '@/types/protocol/terminal.protocol';
-import { PanelSessionType, TerminalSessionStatus } from '../types/const';
+import { PanelSessionType } from '../types/const';
import { useTerminalStore } from '@/store';
import { Message } from '@arco-design/web-vue';
@@ -21,7 +21,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
processCheck({ sessionId, result, msg }: OutputPayload): void {
const success = !!Number.parseInt(result);
const session = this.sessionManager.getSession(sessionId);
- session.canReconnect = !success;
+ session.status.canReconnect = !success;
// 处理
this.processWithType(session, ssh => {
// ssh 会话
@@ -35,9 +35,10 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
rows: ssh.inst.rows
});
} else {
+ // 设置已关闭
+ session.setClosed();
// 未成功展示错误信息
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
- ssh.status = TerminalSessionStatus.CLOSED;
}
}, sftp => {
// sftp 会话
@@ -47,6 +48,8 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
sessionId,
});
} else {
+ // 设置已关闭
+ session.setClosed();
// 未成功提示错误信息
sftp.resolver?.onClose(false, msg);
Message.error(msg || '建立 SFTP 失败');
@@ -58,29 +61,25 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
processConnect({ sessionId, result, msg }: OutputPayload): void {
const success = !!Number.parseInt(result);
const session = this.sessionManager.getSession(sessionId);
- session.canReconnect = !success;
+ session.status.canReconnect = !success;
+ if (success) {
+ // 设置可写
+ session.setCanWrite(true);
+ // 设置已连接
+ session.setConnected();
+ } else {
+ // 设置已关闭
+ session.setClosed();
+ }
// 处理
this.processWithType(session, ssh => {
- // ssh 会话
- if (success) {
- // 设置可写
- ssh.setCanWrite(true);
- // 设置已连接
- ssh.setConnected();
- } else {
- // 未成功展示错误信息
+ if (!success) {
+ // ssh 会话 未成功展示错误信息
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
- ssh.status = TerminalSessionStatus.CLOSED;
}
}, sftp => {
- // sftp 会话
- if (success) {
- // 设置可写
- sftp.setCanWrite(true);
- // 设置已连接
- sftp.setConnected();
- } else {
- // 未成功提示错误信息
+ if (!success) {
+ // sftp 会话 未成功提示错误信息
sftp.resolver?.onClose(false, msg);
Message.error(msg || '打开 SFTP 失败');
}
@@ -95,8 +94,9 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
return;
}
const isForceClose = !!Number.parseInt(forceClose);
- session.connected = false;
- session.canReconnect = !isForceClose;
+ session.status.canReconnect = !isForceClose;
+ // 设置已关闭
+ session.setClosed();
// 处理
this.processWithType(session, ssh => {
// ssh 拼接关闭消息
@@ -104,13 +104,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
if (!isForceClose) {
ssh.write('[91m输入回车重新连接...[0m\r\n\r\n');
}
- // 设置状态
- ssh.status = TerminalSessionStatus.CLOSED;
- // 设置不可写
- ssh.setCanWrite(false);
}, sftp => {
- // 设置不可写
- sftp.setCanWrite(false);
// sftp 设置状态
sftp.resolver?.onClose(isForceClose, msg);
});
diff --git a/orion-visor-ui/src/views/host/terminal/handler/terminal-session-manager.ts b/orion-visor-ui/src/views/host/terminal/handler/terminal-session-manager.ts
index ec87ab74..8983d368 100644
--- a/orion-visor-ui/src/views/host/terminal/handler/terminal-session-manager.ts
+++ b/orion-visor-ui/src/views/host/terminal/handler/terminal-session-manager.ts
@@ -104,7 +104,10 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
// 移除 session
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
// session 全部关闭后 关闭 channel
- if (Object.values(this.sessions).filter(Boolean).every(s => !s?.connected)) {
+ const allClosed = Object.values(this.sessions)
+ .filter(Boolean)
+ .every(s => !s?.status.connected);
+ if (allClosed) {
this.reset();
}
}
diff --git a/orion-visor-ui/src/views/host/terminal/types/define.ts b/orion-visor-ui/src/views/host/terminal/types/define.ts
index 9c6f58a4..f5cbe993 100644
--- a/orion-visor-ui/src/views/host/terminal/types/define.ts
+++ b/orion-visor-ui/src/views/host/terminal/types/define.ts
@@ -1,6 +1,6 @@
import type { Terminal } from '@xterm/xterm';
import type { ISearchOptions } from '@xterm/addon-search';
-import type { CSSProperties } from 'vue';
+import type { CSSProperties, Reactive } from 'vue';
import type { HostQueryResponse } from '@/api/asset/host';
import type { InputPayload, OutputPayload, Protocol } from '@/types/protocol/terminal.protocol';
@@ -196,38 +196,47 @@ export interface XtermDomRef {
uploadModal: any;
}
-// 终端会话定义
-export interface ITerminalSession {
- type: string;
- title: string;
- address: string;
- hostId: number;
- sessionId: string;
+// 终端状态
+export interface TerminalStatus {
+ // 连接状态
+ connectStatus: number;
// 是否已连接
connected: boolean;
- // 是否可以重新连接
- canReconnect: boolean;
// 是否可写
canWrite: boolean;
+ // 是否可以重新连接
+ canReconnect: boolean;
+}
+
+// 终端会话定义
+export interface ITerminalSession {
+ readonly type: string;
+ readonly title: string;
+ readonly address: string;
+ readonly hostId: number;
+ // 终端状态
+ readonly status: Reactive;
+ sessionId: string;
- // 设置是否可写
- setCanWrite: (canWrite: boolean) => void;
- // 设置已连接
- setConnected: () => void;
// 连接会话
connect: () => void;
// 断开连接
disconnect: () => void;
// 关闭
close: () => void;
+
+ // 设置是否可写
+ setCanWrite: (canWrite: boolean) => void;
+ // 设置已连接
+ setConnected: () => void;
+ // 设置已关闭
+ setClosed: () => void;
}
// ssh 会话定义
export interface ISshSession extends ITerminalSession {
// terminal 实例
inst: Terminal;
- // 状态
- status: number;
// 处理器
handler: ISshSessionHandler;