From 7943deb92449f7d5fb3101f6a8d78bfcc6622e4c Mon Sep 17 00:00:00 2001 From: lijiahang Date: Wed, 5 Mar 2025 10:20:20 +0800 Subject: [PATCH] =?UTF-8?q?:zap:=20=E4=BC=98=E5=8C=96=E7=BB=88=E7=AB=AF?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E7=AE=A1=E7=90=86=E5=99=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/store/modules/terminal/index.ts | 3 +- .../components/sftp/sftp-table-header.vue | 8 +-- .../terminal/components/sftp/sftp-table.vue | 12 ++-- .../terminal/components/ssh/ssh-header.vue | 4 +- .../host/terminal/components/ssh/ssh-view.vue | 2 +- .../host/terminal/handler/base-session.ts | 66 ++++++++++++------- .../host/terminal/handler/sftp-session.ts | 20 +++--- .../terminal/handler/ssh-session-handler.ts | 6 +- .../host/terminal/handler/ssh-session.ts | 57 +++++++--------- .../host/terminal/handler/terminal-channel.ts | 2 +- .../handler/terminal-output-processor.ts | 52 +++++++-------- .../handler/terminal-session-manager.ts | 5 +- .../src/views/host/terminal/types/define.ts | 41 +++++++----- 13 files changed, 147 insertions(+), 131 deletions(-) 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 @@ -
- { // 检查是否断开 - 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); diff --git a/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table.vue b/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table.vue index 5e295e9d..5232851a 100644 --- a/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table.vue +++ b/orion-visor-ui/src/views/host/terminal/components/sftp/sftp-table.vue @@ -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); diff --git a/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-header.vue b/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-header.vue index bcc40c85..83811390 100644 --- a/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-header.vue +++ b/orion-visor-ui/src/views/host/terminal/components/ssh/ssh-header.vue @@ -23,8 +23,8 @@ + :status="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0, 'status')" + :text="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0)" />
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(`${msg || ''}\r\n\r\n输入回车重新连接...\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(`${msg || ''}\r\n\r\n输入回车重新连接...\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('输入回车重新连接...\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;