⚡ 优化终端会话管理器.
This commit is contained in:
@@ -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(),
|
||||
}),
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- 已关闭-右侧操作 -->
|
||||
<div v-if="session?.connected === false && closeMessage !== undefined"
|
||||
<div v-if="session?.status.connected === false && closeMessage !== undefined"
|
||||
class="sftp-table-header-right">
|
||||
<!-- 错误信息 -->
|
||||
<a-tag class="close-message"
|
||||
@@ -54,7 +54,7 @@
|
||||
已断开: {{ closeMessage }}
|
||||
</a-tag>
|
||||
<!-- 重连 -->
|
||||
<a-tooltip v-if="session?.canReconnect"
|
||||
<a-tooltip v-if="session?.status.canReconnect"
|
||||
position="top"
|
||||
:mini="true"
|
||||
:overlay-inverse="true"
|
||||
@@ -245,7 +245,7 @@
|
||||
// 设置命令编辑模式
|
||||
const setPathEditable = (editable: boolean) => {
|
||||
// 检查是否断开
|
||||
if (editable && !props.session?.connected) {
|
||||
if (editable && !props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
pathEditable.value = editable;
|
||||
@@ -267,7 +267,7 @@
|
||||
// 加载文件列表
|
||||
const loadFileList = (path: string = props.currentPath) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
emits('loadFile', path);
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
const clickFilename = (record: TableData) => {
|
||||
if (record.isDir) {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
// 进入文件夹
|
||||
@@ -218,7 +218,7 @@
|
||||
// 编辑文件
|
||||
const editFile = (record: TableData) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
emits('editFile', record.name, record.path);
|
||||
@@ -228,7 +228,7 @@
|
||||
// 删除文件
|
||||
const deleteFile = (path: string) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
emits('deleteFile', [path]);
|
||||
@@ -237,7 +237,7 @@
|
||||
// 下载文件
|
||||
const downloadFile = (path: string) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
emits('download', [path], false);
|
||||
@@ -246,7 +246,7 @@
|
||||
// 移动文件
|
||||
const moveFile = (path: string) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
openSftpMoveModal(props.session?.sessionId as string, path);
|
||||
@@ -255,7 +255,7 @@
|
||||
// 文件提权
|
||||
const chmodFile = (path: string, permission: number) => {
|
||||
// 检查是否断开
|
||||
if (!props.session?.connected) {
|
||||
if (!props.session?.status.connected) {
|
||||
return;
|
||||
}
|
||||
openSftpChmodModal(props.session?.sessionId as string, path, permission);
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
<!-- 连接状态 -->
|
||||
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
||||
class="status-bridge"
|
||||
:status="getDictValue(sessionStatusKey, session ? session.status : 0, 'status')"
|
||||
:text="getDictValue(sessionStatusKey, session ? session.status : 0)" />
|
||||
:status="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0, 'status')"
|
||||
:text="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
// 发送命令
|
||||
const writeCommand = (value: string) => {
|
||||
if (session.value?.canWrite) {
|
||||
if (session.value?.status.canWrite) {
|
||||
session.value?.handler.pasteTrimEnd(value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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<Status extends TerminalStatus> implements ITerminalSession<Status> {
|
||||
|
||||
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<Status>;
|
||||
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<Status>) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<TerminalStatus> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<TerminalStatus> 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<TerminalPreference>) {
|
||||
// 注册输入事件
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 消息
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Status extends TerminalStatus = TerminalStatus> {
|
||||
readonly type: string;
|
||||
readonly title: string;
|
||||
readonly address: string;
|
||||
readonly hostId: number;
|
||||
// 终端状态
|
||||
readonly status: Reactive<Status>;
|
||||
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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user