⚡ 优化终端会话管理器.
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 { HostQueryResponse } from '@/api/asset/host';
|
||||||
import type { TerminalTheme, TerminalThemeSchema } from '@/api/asset/terminal';
|
import type { TerminalTheme, TerminalThemeSchema } from '@/api/asset/terminal';
|
||||||
import { getTerminalThemes } from '@/api/asset/terminal';
|
import { getTerminalThemes } from '@/api/asset/terminal';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { getPreference, updatePreference } from '@/api/user/preference';
|
import { getPreference, updatePreference } from '@/api/user/preference';
|
||||||
import { getLatestConnectHostId } from '@/api/asset/terminal-connect-log';
|
import { getLatestConnectHostId } from '@/api/asset/terminal-connect-log';
|
||||||
@@ -73,7 +74,7 @@ export default defineStore('terminal', {
|
|||||||
hosts: {} as AuthorizedHostQueryResponse,
|
hosts: {} as AuthorizedHostQueryResponse,
|
||||||
tabManager: new TerminalTabManager(),
|
tabManager: new TerminalTabManager(),
|
||||||
panelManager: new TerminalPanelManager(),
|
panelManager: new TerminalPanelManager(),
|
||||||
sessionManager: new TerminalSessionManager(),
|
sessionManager: markRaw(new TerminalSessionManager()),
|
||||||
transferManager: new SftpTransferManager(),
|
transferManager: new SftpTransferManager(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 已关闭-右侧操作 -->
|
<!-- 已关闭-右侧操作 -->
|
||||||
<div v-if="session?.connected === false && closeMessage !== undefined"
|
<div v-if="session?.status.connected === false && closeMessage !== undefined"
|
||||||
class="sftp-table-header-right">
|
class="sftp-table-header-right">
|
||||||
<!-- 错误信息 -->
|
<!-- 错误信息 -->
|
||||||
<a-tag class="close-message"
|
<a-tag class="close-message"
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
已断开: {{ closeMessage }}
|
已断开: {{ closeMessage }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<!-- 重连 -->
|
<!-- 重连 -->
|
||||||
<a-tooltip v-if="session?.canReconnect"
|
<a-tooltip v-if="session?.status.canReconnect"
|
||||||
position="top"
|
position="top"
|
||||||
:mini="true"
|
:mini="true"
|
||||||
:overlay-inverse="true"
|
:overlay-inverse="true"
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
// 设置命令编辑模式
|
// 设置命令编辑模式
|
||||||
const setPathEditable = (editable: boolean) => {
|
const setPathEditable = (editable: boolean) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (editable && !props.session?.connected) {
|
if (editable && !props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pathEditable.value = editable;
|
pathEditable.value = editable;
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
// 加载文件列表
|
// 加载文件列表
|
||||||
const loadFileList = (path: string = props.currentPath) => {
|
const loadFileList = (path: string = props.currentPath) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emits('loadFile', path);
|
emits('loadFile', path);
|
||||||
|
|||||||
@@ -205,7 +205,7 @@
|
|||||||
const clickFilename = (record: TableData) => {
|
const clickFilename = (record: TableData) => {
|
||||||
if (record.isDir) {
|
if (record.isDir) {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 进入文件夹
|
// 进入文件夹
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
// 编辑文件
|
// 编辑文件
|
||||||
const editFile = (record: TableData) => {
|
const editFile = (record: TableData) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emits('editFile', record.name, record.path);
|
emits('editFile', record.name, record.path);
|
||||||
@@ -228,7 +228,7 @@
|
|||||||
// 删除文件
|
// 删除文件
|
||||||
const deleteFile = (path: string) => {
|
const deleteFile = (path: string) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emits('deleteFile', [path]);
|
emits('deleteFile', [path]);
|
||||||
@@ -237,7 +237,7 @@
|
|||||||
// 下载文件
|
// 下载文件
|
||||||
const downloadFile = (path: string) => {
|
const downloadFile = (path: string) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emits('download', [path], false);
|
emits('download', [path], false);
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
// 移动文件
|
// 移动文件
|
||||||
const moveFile = (path: string) => {
|
const moveFile = (path: string) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openSftpMoveModal(props.session?.sessionId as string, path);
|
openSftpMoveModal(props.session?.sessionId as string, path);
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
// 文件提权
|
// 文件提权
|
||||||
const chmodFile = (path: string, permission: number) => {
|
const chmodFile = (path: string, permission: number) => {
|
||||||
// 检查是否断开
|
// 检查是否断开
|
||||||
if (!props.session?.connected) {
|
if (!props.session?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openSftpChmodModal(props.session?.sessionId as string, path, permission);
|
openSftpChmodModal(props.session?.sessionId as string, path, permission);
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
<!-- 连接状态 -->
|
<!-- 连接状态 -->
|
||||||
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
||||||
class="status-bridge"
|
class="status-bridge"
|
||||||
:status="getDictValue(sessionStatusKey, session ? session.status : 0, 'status')"
|
:status="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0, 'status')"
|
||||||
:text="getDictValue(sessionStatusKey, session ? session.status : 0)" />
|
:text="getDictValue(sessionStatusKey, session ? session.status.connectStatus : 0)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
// 发送命令
|
// 发送命令
|
||||||
const writeCommand = (value: string) => {
|
const writeCommand = (value: string) => {
|
||||||
if (session.value?.canWrite) {
|
if (session.value?.status.canWrite) {
|
||||||
session.value?.handler.pasteTrimEnd(value);
|
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 readonly type: string;
|
||||||
public hostId: number;
|
public readonly hostId: number;
|
||||||
public title: string;
|
public readonly title: string;
|
||||||
public address: string;
|
public readonly address: string;
|
||||||
|
public readonly status: Reactive<Status>;
|
||||||
public sessionId: string;
|
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.type = type;
|
||||||
this.hostId = tab.hostId;
|
this.hostId = tab.hostId;
|
||||||
this.title = tab.title;
|
this.title = tab.title;
|
||||||
this.address = tab.address;
|
this.address = tab.address;
|
||||||
this.sessionId = tab.sessionId;
|
this.sessionId = tab.sessionId;
|
||||||
this.connected = false;
|
this.status = reactive({
|
||||||
this.canWrite = false;
|
connectStatus: TerminalSessionStatus.CONNECTING,
|
||||||
this.canReconnect = false;
|
connected: false,
|
||||||
}
|
canWrite: false,
|
||||||
|
canReconnect: false,
|
||||||
// 设置是否可写
|
...status,
|
||||||
setCanWrite(canWrite: boolean): void {
|
} as Status);
|
||||||
this.canWrite = canWrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置已连接
|
|
||||||
setConnected(): void {
|
|
||||||
this.connected = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 连接会话
|
// 连接会话
|
||||||
connect(): void {
|
connect(): void {
|
||||||
|
this.status.connectStatus = TerminalSessionStatus.CONNECTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
this.connected = false;
|
// 设置已关闭
|
||||||
|
this.setClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭
|
// 关闭
|
||||||
close(): void {
|
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 { h } from 'vue';
|
||||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
||||||
import { PanelSessionType } from '../types/const';
|
import { PanelSessionType } from '../types/const';
|
||||||
@@ -6,7 +6,7 @@ import { Modal } from '@arco-design/web-vue';
|
|||||||
import BaseSession from './base-session';
|
import BaseSession from './base-session';
|
||||||
|
|
||||||
// sftp 会话实现
|
// sftp 会话实现
|
||||||
export default class SftpSession extends BaseSession implements ISftpSession {
|
export default class SftpSession extends BaseSession<TerminalStatus> implements ISftpSession {
|
||||||
|
|
||||||
public resolver: ISftpSessionResolver;
|
public resolver: ISftpSessionResolver;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export default class SftpSession extends BaseSession implements ISftpSession {
|
|||||||
|
|
||||||
constructor(tab: TerminalPanelTabItem,
|
constructor(tab: TerminalPanelTabItem,
|
||||||
channel: ITerminalChannel) {
|
channel: ITerminalChannel) {
|
||||||
super(PanelSessionType.SFTP.type, tab);
|
super(PanelSessionType.SFTP.type, tab, {});
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.showHiddenFile = false;
|
this.showHiddenFile = false;
|
||||||
this.resolver = undefined as unknown as ISftpSessionResolver;
|
this.resolver = undefined as unknown as ISftpSessionResolver;
|
||||||
@@ -27,13 +27,6 @@ export default class SftpSession extends BaseSession implements ISftpSession {
|
|||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置已连接
|
|
||||||
setConnected(): void {
|
|
||||||
super.setConnected();
|
|
||||||
// 连接回调
|
|
||||||
this.resolver.connectCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 连接会话
|
// 连接会话
|
||||||
connect(): void {
|
connect(): void {
|
||||||
super.connect();
|
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 'openSftp':
|
||||||
case 'uploadFile':
|
case 'uploadFile':
|
||||||
case 'checkAppendMissing':
|
case 'checkAppendMissing':
|
||||||
return this.session.canWrite;
|
return this.session.status.canWrite;
|
||||||
case 'disconnect':
|
case 'disconnect':
|
||||||
return this.session.connected;
|
return this.session.status.connected;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ export default class SshSessionHandler implements ISshSessionHandler {
|
|||||||
// 字号增加
|
// 字号增加
|
||||||
private fontSizeAdd(addSize: number) {
|
private fontSizeAdd(addSize: number) {
|
||||||
this.inst.options['fontSize'] = this.inst.options['fontSize'] as number + addSize;
|
this.inst.options['fontSize'] = this.inst.options['fontSize'] as number + addSize;
|
||||||
if (this.session.connected) {
|
if (this.session.status.connected) {
|
||||||
this.session.fit();
|
this.session.fit();
|
||||||
this.inst.focus();
|
this.inst.focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import type { UnwrapRef } from 'vue';
|
|||||||
import type { ISearchOptions } from '@xterm/addon-search';
|
import type { ISearchOptions } from '@xterm/addon-search';
|
||||||
import { SearchAddon } from '@xterm/addon-search';
|
import { SearchAddon } from '@xterm/addon-search';
|
||||||
import type { TerminalPreference } from '@/store/modules/terminal/types';
|
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 type { XtermAddons } from '@/types/xterm';
|
||||||
import { defaultFontFamily } from '@/types/xterm';
|
import { defaultFontFamily } from '@/types/xterm';
|
||||||
import { useTerminalStore } from '@/store';
|
import { useTerminalStore } from '@/store';
|
||||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
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 { Terminal } from '@xterm/xterm';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||||
@@ -21,12 +21,10 @@ import SshSessionHandler from './ssh-session-handler';
|
|||||||
import BaseSession from './base-session';
|
import BaseSession from './base-session';
|
||||||
|
|
||||||
// ssh 会话实现
|
// ssh 会话实现
|
||||||
export default class SshSession extends BaseSession implements ISshSession {
|
export default class SshSession extends BaseSession<TerminalStatus> implements ISshSession {
|
||||||
|
|
||||||
public inst: Terminal;
|
public inst: Terminal;
|
||||||
|
|
||||||
public status: number;
|
|
||||||
|
|
||||||
public handler: ISshSessionHandler;
|
public handler: ISshSessionHandler;
|
||||||
|
|
||||||
private readonly channel: ITerminalChannel;
|
private readonly channel: ITerminalChannel;
|
||||||
@@ -38,10 +36,9 @@ export default class SshSession extends BaseSession implements ISshSession {
|
|||||||
constructor(tab: TerminalPanelTabItem,
|
constructor(tab: TerminalPanelTabItem,
|
||||||
channel: ITerminalChannel,
|
channel: ITerminalChannel,
|
||||||
canUseWebgl: boolean) {
|
canUseWebgl: boolean) {
|
||||||
super(PanelSessionType.SSH.type, tab);
|
super(PanelSessionType.SSH.type, tab, {});
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.canUseWebgl = canUseWebgl;
|
this.canUseWebgl = canUseWebgl;
|
||||||
this.status = TerminalSessionStatus.CONNECTING;
|
|
||||||
this.inst = undefined as unknown as Terminal;
|
this.inst = undefined as unknown as Terminal;
|
||||||
this.handler = undefined as unknown as ISshSessionHandler;
|
this.handler = undefined as unknown as ISshSessionHandler;
|
||||||
this.addons = {} as XtermAddons;
|
this.addons = {} as XtermAddons;
|
||||||
@@ -93,9 +90,9 @@ export default class SshSession extends BaseSession implements ISshSession {
|
|||||||
e.preventDefault();
|
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 () => {
|
setTimeout(async () => {
|
||||||
await useTerminalStore().reOpenSession(this.sessionId);
|
await useTerminalStore().reOpenSession(this.sessionId);
|
||||||
@@ -120,7 +117,7 @@ export default class SshSession extends BaseSession implements ISshSession {
|
|||||||
private registerEvent(dom: HTMLElement, preference: UnwrapRef<TerminalPreference>) {
|
private registerEvent(dom: HTMLElement, preference: UnwrapRef<TerminalPreference>) {
|
||||||
// 注册输入事件
|
// 注册输入事件
|
||||||
this.inst.onData(s => {
|
this.inst.onData(s => {
|
||||||
if (!this.canWrite || !this.connected) {
|
if (!this.status.canWrite || !this.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 输入
|
// 输入
|
||||||
@@ -145,7 +142,7 @@ export default class SshSession extends BaseSession implements ISshSession {
|
|||||||
}
|
}
|
||||||
// 注册 resize 事件
|
// 注册 resize 事件
|
||||||
this.inst.onResize(({ cols, rows }) => {
|
this.inst.onResize(({ cols, rows }) => {
|
||||||
if (!this.connected) {
|
if (!this.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.channel.send(InputProtocol.SSH_RESIZE, {
|
this.channel.send(InputProtocol.SSH_RESIZE, {
|
||||||
@@ -158,7 +155,7 @@ export default class SshSession extends BaseSession implements ISshSession {
|
|||||||
addEventListen(dom, 'contextmenu', async () => {
|
addEventListen(dom, 'contextmenu', async () => {
|
||||||
// 右键粘贴逻辑
|
// 右键粘贴逻辑
|
||||||
if (preference.interactSetting.rightClickPaste) {
|
if (preference.interactSetting.rightClickPaste) {
|
||||||
if (!this.canWrite || !this.connected) {
|
if (!this.status.canWrite || !this.status.connected) {
|
||||||
return;
|
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 {
|
connect(): void {
|
||||||
super.connect();
|
super.connect();
|
||||||
// 设置状态
|
|
||||||
this.status = TerminalSessionStatus.CONNECTING;
|
|
||||||
// 发送会话初始化请求
|
// 发送会话初始化请求
|
||||||
this.channel.send(InputProtocol.CHECK, {
|
this.channel.send(InputProtocol.CHECK, {
|
||||||
sessionId: this.sessionId,
|
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 {
|
private closeCallback(): void {
|
||||||
// 关闭时将手动触发 close 消息, 有可能是其他原因关闭的, 没有接收到 close 消息, 导致已断开是终端还是显示已连接
|
// 关闭时将手动触发 close 消息, 有可能是其他原因关闭的, 没有接收到 close 消息, 导致已断开是终端还是显示已连接
|
||||||
Object.values(this.sessionManager.sessions).forEach(s => {
|
Object.values(this.sessionManager.sessions).forEach(s => {
|
||||||
if (!s?.connected) {
|
if (!s?.status.connected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// close 消息
|
// close 消息
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { ISftpSession, ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSession, ITerminalSessionManager } from '../types/define';
|
import type { ISftpSession, ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSession, ITerminalSessionManager } from '../types/define';
|
||||||
import type { OutputPayload } from '@/types/protocol/terminal.protocol';
|
import type { OutputPayload } from '@/types/protocol/terminal.protocol';
|
||||||
import { InputProtocol } 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 { useTerminalStore } from '@/store';
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
processCheck({ sessionId, result, msg }: OutputPayload): void {
|
processCheck({ sessionId, result, msg }: OutputPayload): void {
|
||||||
const success = !!Number.parseInt(result);
|
const success = !!Number.parseInt(result);
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
const session = this.sessionManager.getSession(sessionId);
|
||||||
session.canReconnect = !success;
|
session.status.canReconnect = !success;
|
||||||
// 处理
|
// 处理
|
||||||
this.processWithType(session, ssh => {
|
this.processWithType(session, ssh => {
|
||||||
// ssh 会话
|
// ssh 会话
|
||||||
@@ -35,9 +35,10 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
rows: ssh.inst.rows
|
rows: ssh.inst.rows
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 设置已关闭
|
||||||
|
session.setClosed();
|
||||||
// 未成功展示错误信息
|
// 未成功展示错误信息
|
||||||
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
||||||
ssh.status = TerminalSessionStatus.CLOSED;
|
|
||||||
}
|
}
|
||||||
}, sftp => {
|
}, sftp => {
|
||||||
// sftp 会话
|
// sftp 会话
|
||||||
@@ -47,6 +48,8 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
sessionId,
|
sessionId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// 设置已关闭
|
||||||
|
session.setClosed();
|
||||||
// 未成功提示错误信息
|
// 未成功提示错误信息
|
||||||
sftp.resolver?.onClose(false, msg);
|
sftp.resolver?.onClose(false, msg);
|
||||||
Message.error(msg || '建立 SFTP 失败');
|
Message.error(msg || '建立 SFTP 失败');
|
||||||
@@ -58,29 +61,25 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
processConnect({ sessionId, result, msg }: OutputPayload): void {
|
processConnect({ sessionId, result, msg }: OutputPayload): void {
|
||||||
const success = !!Number.parseInt(result);
|
const success = !!Number.parseInt(result);
|
||||||
const session = this.sessionManager.getSession(sessionId);
|
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 => {
|
this.processWithType(session, ssh => {
|
||||||
// ssh 会话
|
if (!success) {
|
||||||
if (success) {
|
// ssh 会话 未成功展示错误信息
|
||||||
// 设置可写
|
|
||||||
ssh.setCanWrite(true);
|
|
||||||
// 设置已连接
|
|
||||||
ssh.setConnected();
|
|
||||||
} else {
|
|
||||||
// 未成功展示错误信息
|
|
||||||
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
ssh.write(`[91m${msg || ''}[0m\r\n\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
||||||
ssh.status = TerminalSessionStatus.CLOSED;
|
|
||||||
}
|
}
|
||||||
}, sftp => {
|
}, sftp => {
|
||||||
// sftp 会话
|
if (!success) {
|
||||||
if (success) {
|
// sftp 会话 未成功提示错误信息
|
||||||
// 设置可写
|
|
||||||
sftp.setCanWrite(true);
|
|
||||||
// 设置已连接
|
|
||||||
sftp.setConnected();
|
|
||||||
} else {
|
|
||||||
// 未成功提示错误信息
|
|
||||||
sftp.resolver?.onClose(false, msg);
|
sftp.resolver?.onClose(false, msg);
|
||||||
Message.error(msg || '打开 SFTP 失败');
|
Message.error(msg || '打开 SFTP 失败');
|
||||||
}
|
}
|
||||||
@@ -95,8 +94,9 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isForceClose = !!Number.parseInt(forceClose);
|
const isForceClose = !!Number.parseInt(forceClose);
|
||||||
session.connected = false;
|
session.status.canReconnect = !isForceClose;
|
||||||
session.canReconnect = !isForceClose;
|
// 设置已关闭
|
||||||
|
session.setClosed();
|
||||||
// 处理
|
// 处理
|
||||||
this.processWithType(session, ssh => {
|
this.processWithType(session, ssh => {
|
||||||
// ssh 拼接关闭消息
|
// ssh 拼接关闭消息
|
||||||
@@ -104,13 +104,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
|||||||
if (!isForceClose) {
|
if (!isForceClose) {
|
||||||
ssh.write('[91m输入回车重新连接...[0m\r\n\r\n');
|
ssh.write('[91m输入回车重新连接...[0m\r\n\r\n');
|
||||||
}
|
}
|
||||||
// 设置状态
|
|
||||||
ssh.status = TerminalSessionStatus.CLOSED;
|
|
||||||
// 设置不可写
|
|
||||||
ssh.setCanWrite(false);
|
|
||||||
}, sftp => {
|
}, sftp => {
|
||||||
// 设置不可写
|
|
||||||
sftp.setCanWrite(false);
|
|
||||||
// sftp 设置状态
|
// sftp 设置状态
|
||||||
sftp.resolver?.onClose(isForceClose, msg);
|
sftp.resolver?.onClose(isForceClose, msg);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -104,7 +104,10 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
// 移除 session
|
// 移除 session
|
||||||
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
|
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
|
||||||
// session 全部关闭后 关闭 channel
|
// 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();
|
this.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Terminal } from '@xterm/xterm';
|
import type { Terminal } from '@xterm/xterm';
|
||||||
import type { ISearchOptions } from '@xterm/addon-search';
|
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 { HostQueryResponse } from '@/api/asset/host';
|
||||||
import type { InputPayload, OutputPayload, Protocol } from '@/types/protocol/terminal.protocol';
|
import type { InputPayload, OutputPayload, Protocol } from '@/types/protocol/terminal.protocol';
|
||||||
|
|
||||||
@@ -196,38 +196,47 @@ export interface XtermDomRef {
|
|||||||
uploadModal: any;
|
uploadModal: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端会话定义
|
// 终端状态
|
||||||
export interface ITerminalSession {
|
export interface TerminalStatus {
|
||||||
type: string;
|
// 连接状态
|
||||||
title: string;
|
connectStatus: number;
|
||||||
address: string;
|
|
||||||
hostId: number;
|
|
||||||
sessionId: string;
|
|
||||||
// 是否已连接
|
// 是否已连接
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
// 是否可以重新连接
|
|
||||||
canReconnect: boolean;
|
|
||||||
// 是否可写
|
// 是否可写
|
||||||
canWrite: 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;
|
connect: () => void;
|
||||||
// 断开连接
|
// 断开连接
|
||||||
disconnect: () => void;
|
disconnect: () => void;
|
||||||
// 关闭
|
// 关闭
|
||||||
close: () => void;
|
close: () => void;
|
||||||
|
|
||||||
|
// 设置是否可写
|
||||||
|
setCanWrite: (canWrite: boolean) => void;
|
||||||
|
// 设置已连接
|
||||||
|
setConnected: () => void;
|
||||||
|
// 设置已关闭
|
||||||
|
setClosed: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssh 会话定义
|
// ssh 会话定义
|
||||||
export interface ISshSession extends ITerminalSession {
|
export interface ISshSession extends ITerminalSession {
|
||||||
// terminal 实例
|
// terminal 实例
|
||||||
inst: Terminal;
|
inst: Terminal;
|
||||||
// 状态
|
|
||||||
status: number;
|
|
||||||
// 处理器
|
// 处理器
|
||||||
handler: ISshSessionHandler;
|
handler: ISshSessionHandler;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user