回车重连.

This commit is contained in:
lijiahangmax
2024-04-23 09:15:22 +08:00
parent c66e62623e
commit c8c947d8a8
17 changed files with 247 additions and 123 deletions

View File

@@ -8,7 +8,7 @@ import type {
TerminalShortcutSetting,
TerminalState
} from './types';
import type { ISshSession, PanelSessionTab, TerminalPanelTabItem } from '@/views/host/terminal/types/terminal.type';
import type { ISshSession, PanelSessionTabType, TerminalPanelTabItem } from '@/views/host/terminal/types/terminal.type';
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
import type { HostQueryResponse } from '@/api/asset/host';
@@ -135,7 +135,7 @@ export default defineStore('terminal', {
},
// 打开会话
openSession(record: HostQueryResponse, type: PanelSessionTab, panelIndex: number = 0) {
openSession(record: HostQueryResponse, type: PanelSessionTabType, panelIndex: number = 0) {
// 添加到最近连接
this.hosts.latestHosts = [...new Set([record.id, ...this.hosts.latestHosts])];
// 切换到终端面板页面
@@ -151,8 +151,10 @@ export default defineStore('terminal', {
? Math.max(...seqArr) + 1
: 1;
// 打开 tab
const sessionId = nextId(10);
this.panelManager.getPanel(panelIndex).openTab({
key: nextId(10),
key: sessionId,
sessionId,
seq: nextSeq,
title: `(${nextSeq}) ${record.alias || record.name}`,
hostId: record.id,
@@ -163,20 +165,18 @@ export default defineStore('terminal', {
});
},
// 重新打开 terminal 会话
async reOpenTerminal(hostId: number, sessionId: string, panelIndex: number = 0) {
console.log('rec');
// 添加到最近连接
this.hosts.latestHosts = [...new Set([hostId, ...this.hosts.latestHosts])];
// 重新打开会话
async reOpenSession(sessionId: string, panelIndex: number = 0) {
// 切换到终端面板页面
this.tabManager.openTab(TerminalTabs.TERMINAL_PANEL);
// 获取当前面板并且分配新的 sessionId
const panel = this.panelManager.getPanel(panelIndex);
const tab = panel.getTab(sessionId);
const newSessionId = nextId(10);
tab.key = newSessionId;
// 重新打开 ssh
await this.sessionManager.reOpenSsh(sessionId, newSessionId);
const newSessionId = tab.sessionId = nextId(10);
// 添加到最近连接
this.hosts.latestHosts = [...new Set([tab.hostId, ...this.hosts.latestHosts])];
// 重新打开会话
await this.sessionManager.reOpenSession(tab.type, sessionId, newSessionId);
},
// 复制并且打开会话
@@ -220,7 +220,7 @@ export default defineStore('terminal', {
return;
}
// 获取会话
return this.sessionManager.getSession<ISshSession>(sessionTab.key);
return this.sessionManager.getSession<ISshSession>(sessionTab.sessionId);
},
},

View File

@@ -16,9 +16,8 @@
</span>
</a-space>
</template>
<!-- 终端面板 FIXME -->
<a-tab-pane v-for="tab in panel.items"
:key="tab.key">
<!-- 终端面板 这里的 key 只能使用 key 字段, 否则重连有问题 -->
<a-tab-pane v-for="tab in panel.items" :key="tab.key">
<!-- 标题 -->
<template #title>
<span class="tab-title-wrapper usn"
@@ -64,6 +63,9 @@
// 监听 tab 切换
watch(() => props.panel.active, (active, before) => {
console.log(active);
console.log(before);
console.log(props.panel);
// 失焦自动终端
if (before) {
const beforeTab = props.panel.items.find(s => s.key === before);
@@ -78,7 +80,7 @@
sessionManager.getSession<ISshSession>(active)?.focus();
}
}
// 无终端自动关闭
// 无终端自动关闭 FIXME
if (!props.panel.items.length) {
close();
}

View File

@@ -80,7 +80,7 @@
</script>
<script lang="ts" setup>
import type { PanelSessionTab } from '../../types/terminal.type';
import type { PanelSessionTabType } from '../../types/terminal.type';
import type { HostQueryResponse } from '@/api/asset/host';
import { computed, ref } from 'vue';
import { useTerminalStore } from '@/store';
@@ -129,7 +129,7 @@
defineExpose({ open });
// 打开终端
const clickHost = (item: HostQueryResponse, tab: PanelSessionTab) => {
const clickHost = (item: HostQueryResponse, tab: PanelSessionTabType) => {
openSession(item, tab, panelIndex.value);
setVisible(false);
};

View File

@@ -45,12 +45,27 @@
</div>
</div>
<!-- 已关闭-右侧操作 -->
<div v-if="closed" class="sftp-table-header-right">
<div v-if="session?.connected === false && closeMessage !== undefined"
class="sftp-table-header-right">
<!-- 错误信息 -->
<a-tag class="close-message"
color="red"
:title="closeMessage">
已断开: {{ closeMessage }}
</a-tag>
<!-- 重连 FIXME -->
<a-tooltip position="top"
:mini="true"
:overlay-inverse="true"
:auto-fix-position="false"
content-class="terminal-tooltip-content"
arrow-class="terminal-tooltip-content"
content="重连">
<span class="click-icon-wrapper header-action-icon ml8"
@click="reConnect">
<icon-refresh />
</span>
</a-tooltip>
</div>
<!-- 路径编辑模式-右侧操作 -->
<a-space v-else-if="pathEditable" class="sftp-table-header-right">
@@ -191,16 +206,16 @@
import { inject, nextTick, ref, watch } from 'vue';
import { getParentPath, getPathAnalysis } from '@/utils/file';
import { openSftpCreateModalKey, openSftpUploadModalKey } from '../../types/terminal.const';
import { useTerminalStore } from '@/store';
const props = defineProps<{
closed: boolean;
closeMessage?: string;
currentPath: string;
session?: ISftpSession;
selectedFiles: Array<string>;
}>();
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'download']);
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'download', 'setLoading']);
const showHiddenFile = ref(false);
const analysisPaths = ref<Array<PathAnalysis>>([]);
@@ -229,7 +244,7 @@
// 设置命令编辑模式
const setPathEditable = (editable: boolean) => {
// 检查是否断开
if (editable && props.closed) {
if (editable && !props.session?.connected) {
return;
}
pathEditable.value = editable;
@@ -251,7 +266,7 @@
// 加载文件列表
const loadFileList = (path: string = props.currentPath) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
emits('loadFile', path);
@@ -284,6 +299,15 @@
}
};
// 重新连接
const reConnect = () => {
if (props.session) {
emits('setLoading', true);
// 重新连接
useTerminalStore().reOpenSession(props.session.sessionId);
}
};
// 下载文件
const downloadFile = () => {
emits('download', [...props.selectedFiles]);

View File

@@ -158,7 +158,6 @@
session?: ISftpSession;
list: Array<SftpFile>;
loading: boolean;
closed: boolean;
selectedFiles: Array<string>;
}>();
@@ -214,7 +213,7 @@
const clickFilename = (record: TableData) => {
if (record.isDir) {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
// 进入文件夹
@@ -227,7 +226,7 @@
// 编辑文件
const editFile = (record: TableData) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
emits('editFile', record.name, record.path);
@@ -237,7 +236,7 @@
// 删除文件
const deleteFile = (path: string) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
props.session?.remove([path]);
@@ -246,7 +245,7 @@
// 下载文件
const downloadFile = (path: string) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
emits('download', [path]);
@@ -255,7 +254,7 @@
// 移动文件
const moveFile = (path: string) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
openSftpMoveModal(props.session?.sessionId as string, path);
@@ -264,7 +263,7 @@
// 文件提权
const chmodFile = (path: string, permission: number) => {
// 检查是否断开
if (props.closed) {
if (!props.session?.connected) {
return;
}
openSftpChmodModal(props.session?.sessionId as string, path, permission);

View File

@@ -12,17 +12,16 @@
<!-- 表头 -->
<sftp-table-header class="sftp-table-header"
v-model:selected-files="selectFiles"
:closed="closed"
:close-message="closeMessage"
:current-path="currentPath"
:session="session"
@load-file="loadFiles"
@download="downloadFiles" />
@download="downloadFiles"
@set-loading="setTableLoading" />
<!-- 表格 -->
<sftp-table class="sftp-table-wrapper"
v-model:selected-files="selectFiles"
:session="session"
:closed="closed"
:list="fileList"
:loading="tableLoading"
@load-file="loadFiles"
@@ -63,7 +62,7 @@
</script>
<script lang="ts" setup>
import type { ISftpSession, SftpFile, TerminalTabItem } from '../../types/terminal.type';
import type { ISftpSession, SftpFile, TerminalPanelTabItem } from '../../types/terminal.type';
import { onMounted, onUnmounted, provide, ref } from 'vue';
import { useTerminalStore } from '@/store';
import { Message } from '@arco-design/web-vue';
@@ -79,7 +78,7 @@
import SftpUploadModal from './sftp-upload-modal.vue';
const props = defineProps<{
tab: TerminalTabItem;
tab: TerminalPanelTabItem;
}>();
const { preference, sessionManager, transferManager } = useTerminalStore();
@@ -91,8 +90,7 @@
const fileList = ref<Array<SftpFile>>([]);
const selectFiles = ref<Array<string>>([]);
const splitSize = ref(1);
const closed = ref(false);
const closeMessage = ref('');
const closeMessage = ref<string>();
const editorView = ref(false);
const editorRef = ref();
const editorFileName = ref('');
@@ -170,7 +168,8 @@
// 连接成功回调
const connectCallback = () => {
loadFiles('~');
// FIXME TEST
loadFiles(currentPath.value || '~');
};
// 加载文件列表
@@ -189,8 +188,7 @@
};
// 关闭回调
const onClose = (forceClose: string, msg: string) => {
closed.value = true;
const onClose = (forceClose: boolean, msg: string) => {
closeMessage.value = msg;
setTableLoading(false);
setEditorLoading(false);
@@ -267,7 +265,7 @@
// 关闭会话
onUnmounted(() => {
sessionManager.closeSession(props.tab.key);
sessionManager.closeSession(props.tab.sessionId);
});
</script>

View File

@@ -68,7 +68,7 @@
</script>
<script lang="ts" setup>
import type { ISshSession, TerminalTabItem, SidebarAction } from '../../types/terminal.type';
import type { ISshSession, TerminalPanelTabItem, SidebarAction } from '../../types/terminal.type';
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useDictStore, useTerminalStore } from '@/store';
import { copy } from '@/hooks/copy';
@@ -79,7 +79,7 @@
import XtermSearchModal from '@/components/xtrem/search-modal/index.vue';
const props = defineProps<{
tab: TerminalTabItem;
tab: TerminalPanelTabItem;
}>();
const { getDictValue } = useDictStore();
@@ -137,7 +137,6 @@
// 初始化会话
onMounted(async () => {
console.log('onMounted', props.tab.key);
// 创建终端处理器
session.value = await sessionManager.openSsh(props.tab, {
el: terminalRef.value,
@@ -148,7 +147,7 @@
// 关闭会话
onUnmounted(() => {
sessionManager.closeSession(props.tab.key);
sessionManager.closeSession(props.tab.sessionId);
});
</script>

View File

@@ -11,6 +11,8 @@ export default class SftpSession implements ISftpSession {
public connected: boolean;
public canReconnect: boolean;
public resolver: ISftpSessionResolver;
private showHiddenFile: boolean;
@@ -24,6 +26,7 @@ export default class SftpSession implements ISftpSession {
this.sessionId = sessionId;
this.channel = channel;
this.connected = false;
this.canReconnect = false;
this.showHiddenFile = false;
this.resolver = undefined as unknown as ISftpSessionResolver;
}

View File

@@ -26,6 +26,8 @@ export default class SshSession implements ISshSession {
public connected: boolean;
public canReconnect: boolean;
public canWrite: boolean;
public status: number;
@@ -43,6 +45,7 @@ export default class SshSession implements ISshSession {
this.sessionId = sessionId;
this.channel = channel;
this.connected = false;
this.canReconnect = false;
this.canWrite = false;
this.status = TerminalStatus.CONNECTING;
this.inst = undefined as unknown as Terminal;
@@ -85,7 +88,6 @@ export default class SshSession implements ISshSession {
if (e.type !== 'keydown') {
return true;
}
console.log(e);
// 检测是否为内置快捷键
if (this.handler.checkIsBuiltin(e)) {
return true;
@@ -94,11 +96,12 @@ export default class SshSession implements ISshSession {
if (this.handler.checkPreventDefault(e)) {
e.preventDefault();
}
if (e.key === 'Enter') {
console.log("enter start");
// TODO 回车 重新连接
useTerminalStore().reOpenTerminal(this.hostId, this.sessionId);
console.log("enter end");
// 重新连接
if (!this.connected && this.canReconnect && e.key === 'Enter') {
setTimeout(async () => {
await useTerminalStore().reOpenSession(this.sessionId);
}, 50);
return true;
}
// 自定义快捷键
if (preference.shortcutSetting.enabled && preference.shortcutSetting.keys.length) {

View File

@@ -42,7 +42,6 @@ export default class TerminalChannel implements ITerminalChannel {
// 发送消息
send(protocol: Protocol, payload: InputPayload): void {
console.log('send', payload);
// 检查是否连接
if (!this.isConnected()) {
return;

View File

@@ -22,6 +22,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;
if (session instanceof SshSession) {
// ssh 会话
if (success) {
@@ -35,7 +36,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
});
} else {
// 未成功展示错误信息
session.write(`${msg || ''}`);
session.write(`${msg || ''}\r\n输入回车重新连接...\r\n\r\n`);
session.status = TerminalStatus.CLOSED;
}
} else if (session instanceof SftpSession) {
@@ -47,7 +48,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
});
} else {
// 未成功提示错误信息
session.resolver?.onClose('0', msg);
session.resolver?.onClose(false, msg);
Message.error(msg || '建立 SFTP 失败');
}
}
@@ -57,6 +58,7 @@ 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;
if (session instanceof SshSession) {
// ssh 会话
if (success) {
@@ -66,7 +68,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
session.connect();
} else {
// 未成功展示错误信息
session.write(`${msg || ''}`);
session.write(`${msg || ''}\r\n输入回车重新连接...\r\n\r\n`);
session.status = TerminalStatus.CLOSED;
}
} else if (session instanceof SftpSession) {
@@ -76,7 +78,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
session.connect();
} else {
// 未成功提示错误信息
session.resolver?.onClose('0', msg);
session.resolver?.onClose(false, msg);
Message.error(msg || '打开 SFTP 失败');
}
}
@@ -89,18 +91,22 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
if (!session) {
return;
}
const isForceClose = !!Number.parseInt(forceClose);
session.connected = false;
session.canReconnect = !isForceClose;
if (session instanceof SshSession) {
// ssh 拼接关闭消息
session.write(`\r\n\r\n${msg || ''}\r\n\r\n`);
session.write(`\r\n\r\n${msg || ''}\r\n`);
if (!isForceClose) {
session.write('输入回车重新连接...\r\n\r\n');
}
// 设置状态
session.status = TerminalStatus.CLOSED;
session.connected = false;
// 设置不可写
session.setCanWrite(false);
} else if (session instanceof SftpSession) {
// sftp 设置状态
session.connected = false;
session.resolver?.onClose(forceClose, msg);
session.resolver?.onClose(isForceClose, msg);
}
}

View File

@@ -1,21 +1,21 @@
import type { ITerminalPanelManager, TerminalPanelTabItem } from '../types/terminal.type';
import TerminalTabManager from '../handler/terminal-tab-manager';
import type { ITerminalPanelManager } from '../types/terminal.type';
import TerminalPanelTabManager from '../handler/terminal-panel-tab-manager';
// 终端面板管理器实现
export default class TerminalPanelManager<T extends TerminalPanelTabItem = TerminalPanelTabItem> implements ITerminalPanelManager<T> {
export default class TerminalPanelManager implements ITerminalPanelManager {
// 当前面板
active: number;
// 面板列表
panels: Array<TerminalTabManager<T>>;
panels: Array<TerminalPanelTabManager>;
constructor() {
this.active = 0;
this.panels = [new TerminalTabManager()];
this.panels = [new TerminalPanelTabManager()];
}
// 获取当前面板
getCurrentPanel(): TerminalTabManager<T> {
getCurrentPanel(): TerminalPanelTabManager {
return this.panels[this.active];
}
@@ -25,7 +25,7 @@ export default class TerminalPanelManager<T extends TerminalPanelTabItem = Termi
};
// 获取面板
getPanel(index: number): TerminalTabManager<T> {
getPanel(index: number): TerminalPanelTabManager {
return this.panels[index];
};
@@ -41,7 +41,7 @@ export default class TerminalPanelManager<T extends TerminalPanelTabItem = Termi
panel.clear();
}
this.active = 0;
this.panels = [new TerminalTabManager()];
this.panels = [new TerminalPanelTabManager()];
};
}

View File

@@ -0,0 +1,91 @@
import type { ITerminalTabManager, TerminalPanelTabItem } from '../types/terminal.type';
// 终端面板 tab 管理器实现
export default class TerminalPanelTabManager implements ITerminalTabManager<TerminalPanelTabItem> {
public active: string;
public items: Array<TerminalPanelTabItem>;
constructor(def: TerminalPanelTabItem | undefined = undefined) {
if (def) {
this.active = def.sessionId;
this.items = [def];
} else {
this.active = undefined as unknown as string;
this.items = [];
}
}
// 获取当前 tab
getCurrentTab() {
if (!this.active) {
return undefined;
}
return this.items.find(s => s.sessionId === this.active);
}
// 获取 tab
getTab(sessionId: string): TerminalPanelTabItem {
return this.items.find(s => s.sessionId === sessionId) as TerminalPanelTabItem;
}
// 点击 tab
clickTab(sessionId: string): void {
this.active = sessionId;
}
// 删除 tab
deleteTab(sessionId: string): void {
// 获取当前 tab
const tabIndex = this.items.findIndex(s => s.sessionId === sessionId);
// 删除 tab
this.items.splice(tabIndex, 1);
if (sessionId === this.active && this.items.length !== 0) {
// 切换为前一个 tab
this.active = this.items[Math.max(tabIndex - 1, 0)].sessionId;
} else {
this.active = undefined as unknown as string;
}
}
// 打开 tab
openTab(tab: TerminalPanelTabItem): void {
// 不存在则创建 tab
if (!this.items.find(s => s.sessionId === tab.sessionId)) {
this.items.push(tab);
}
this.active = tab.sessionId;
}
// 切换到前一个 tab
changeToPrevTab() {
this.changeToIndex(this.getCurrentTabIndex() - 1);
}
// 切换到后一个 tab
changeToNextTab() {
this.changeToIndex(this.getCurrentTabIndex() + 1);
}
// 切换索引 tab
changeToIndex(index: number) {
if (index < 0 || index >= this.items.length) {
return;
}
// 切换 tab
this.active = this.items[index].sessionId;
}
// 获取当前索引
private getCurrentTabIndex(): number {
return this.items.findIndex(s => s.sessionId === this.active);
}
// 清空
clear() {
this.active = undefined as unknown as string;
this.items = [];
}
}

View File

@@ -4,10 +4,9 @@ import type {
ITerminalChannel,
ITerminalSession,
ITerminalSessionManager,
TerminalTabItem,
TerminalPanelTabItem,
XtermDomRef
} from '../types/terminal.type';
import type { ISshSession } from '../types/terminal.type';
import { sleep } from '@/utils';
import { InputProtocol } from '../types/terminal.protocol';
import { PanelSessionType } from '../types/terminal.const';
@@ -35,8 +34,8 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
}
// 打开 ssh 会话
async openSsh(tab: TerminalTabItem, domRef: XtermDomRef) {
const sessionId = tab.key;
async openSsh(tab: TerminalPanelTabItem, domRef: XtermDomRef) {
const sessionId = tab.sessionId;
const hostId = tab.hostId as number;
// 初始化客户端
await this.initChannel();
@@ -61,28 +60,9 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
return session;
}
// 重新打开 ssh 会话
async reOpenSsh(sessionId: string, newSessionId: string): Promise<void> {
console.log('sessionId', sessionId, 'newSessionId', newSessionId);
// 初始化客户端
await this.initChannel();
// 获取会话并且重新设置 sessionId
const session = this.sessions[sessionId] as ISshSession;
session.sessionId = newSessionId;
this.sessions[sessionId] = undefined as unknown as ISshSession;
this.sessions[newSessionId] = session;
console.log('ckckck');
// 发送会话初始化请求
this.channel.send(InputProtocol.CHECK, {
sessionId: newSessionId,
hostId: session.hostId,
connectType: PanelSessionType.SSH.type
});
}
// 打开 sftp 会话
async openSftp(tab: TerminalTabItem, resolver: ISftpSessionResolver): Promise<ISftpSession> {
const sessionId = tab.key;
async openSftp(tab: TerminalPanelTabItem, resolver: ISftpSessionResolver): Promise<ISftpSession> {
const sessionId = tab.sessionId;
const hostId = tab.hostId as number;
// 初始化客户端
await this.initChannel();
@@ -105,6 +85,23 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
return session;
}
// 重新打开会话
async reOpenSession(type: string, sessionId: string, newSessionId: string): Promise<void> {
// 初始化客户端
await this.initChannel();
// 获取会话并且重新设置 sessionId
const session = this.sessions[sessionId] as ITerminalSession;
session.sessionId = newSessionId;
this.sessions[sessionId] = undefined as unknown as ITerminalSession;
this.sessions[newSessionId] = session;
// 发送会话初始化请求
this.channel.send(InputProtocol.CHECK, {
sessionId: newSessionId,
hostId: session.hostId,
connectType: type,
});
}
// 获取终端会话
getSession<T>(sessionId: string): T {
return this.sessions[sessionId] as T;

View File

@@ -1,13 +1,13 @@
import type { ITerminalTabManager, TerminalTabItem } from '../types/terminal.type';
// 终端 tab 管理器实现
export default class TerminalTabManager<T extends TerminalTabItem = TerminalTabItem> implements ITerminalTabManager<T> {
export default class TerminalTabManager implements ITerminalTabManager {
public active: string;
public items: Array<T>;
public items: Array<TerminalTabItem>;
constructor(def: T | undefined = undefined) {
constructor(def: TerminalTabItem | undefined = undefined) {
if (def) {
this.active = def.key;
this.items = [def];
@@ -26,8 +26,8 @@ export default class TerminalTabManager<T extends TerminalTabItem = TerminalTabI
}
// 获取 tab
getTab(key: string): T {
return this.items.find(s => s.key === key) as T;
getTab(key: string): TerminalTabItem {
return this.items.find(s => s.key === key) as TerminalTabItem;
}
// 点击 tab
@@ -50,7 +50,7 @@ export default class TerminalTabManager<T extends TerminalTabItem = TerminalTabI
}
// 打开 tab
openTab(tab: T): void {
openTab(tab: TerminalTabItem): void {
// 不存在则创建 tab
if (!this.items.find(s => s.key === tab.key)) {
this.items.push(tab);

View File

@@ -34,6 +34,18 @@ export const TerminalTabs = {
},
};
// 面板会话 tab 类型
export const PanelSessionType = {
SSH: {
type: 'SSH',
icon: 'icon-desktop'
},
SFTP: {
type: 'SFTP',
icon: 'icon-folder'
},
};
// 新建连接类型
export const NewConnectionType = {
GROUP: 'group',
@@ -97,18 +109,6 @@ export const FILE_TYPE = {
}
};
// 面板会话 tab 类型
export const PanelSessionType = {
SSH: {
type: 'SSH',
icon: 'icon-desktop'
},
SFTP: {
type: 'SFTP',
icon: 'icon-folder'
},
};
// 终端状态
export const TerminalStatus = {
// 连接中

View File

@@ -19,10 +19,11 @@ export interface TerminalTabItem {
// 终端面板 tab 元素
export interface TerminalPanelTabItem extends TerminalTabItem {
type: string;
sessionId: string;
seq: number;
hostId: number;
address: string;
type: string;
color?: string;
}
@@ -74,7 +75,7 @@ export interface LabelExtraSettingModel {
}
// session tab
export interface PanelSessionTab {
export interface PanelSessionTabType {
type: string;
icon: string;
}
@@ -131,18 +132,18 @@ export interface ITerminalTabManager<T extends TerminalTabItem = TerminalTabItem
}
// 终端面板管理器定义
export interface ITerminalPanelManager<T extends TerminalPanelTabItem = TerminalPanelTabItem> {
export interface ITerminalPanelManager {
// 当前面板
active: number;
// 面板列表
panels: Array<ITerminalTabManager<T>>;
panels: Array<ITerminalTabManager<TerminalPanelTabItem>>;
// 获取当前面板
getCurrentPanel: () => ITerminalTabManager<T>;
getCurrentPanel: () => ITerminalTabManager<TerminalPanelTabItem>;
// 设置当前面板
setCurrentPanel: (active: number) => void;
// 获取面板
getPanel: (index: number) => ITerminalTabManager<T>;
getPanel: (index: number) => ITerminalTabManager<TerminalPanelTabItem>;
// 移除面板
removePanel: (index: number) => void;
// 重置
@@ -152,11 +153,11 @@ export interface ITerminalPanelManager<T extends TerminalPanelTabItem = Terminal
// 终端会话管理器定义
export interface ITerminalSessionManager {
// 打开 ssh 会话
openSsh: (tab: TerminalTabItem, domRef: XtermDomRef) => Promise<ISshSession>;
// 重新打开 ssh 会话
reOpenSsh: (sessionId: string, newSessionId: string) => Promise<void>;
openSsh: (tab: TerminalPanelTabItem, domRef: XtermDomRef) => Promise<ISshSession>;
// 打开 sftp 会话
openSftp: (tab: TerminalTabItem, resolver: ISftpSessionResolver) => Promise<ISftpSession>;
openSftp: (tab: TerminalPanelTabItem, resolver: ISftpSessionResolver) => Promise<ISftpSession>;
// 重新打开会话
reOpenSession: (type: string, sessionId: string, newSessionId: string) => Promise<void>;
// 获取终端会话
getSession: <T extends ITerminalSession>(sessionId: string) => T;
// 关闭终端会话
@@ -232,6 +233,8 @@ export interface ITerminalSession {
sessionId: string;
// 是否已连接
connected: boolean;
// 是否可以重新连接
canReconnect: boolean;
// 连接
connect: () => void;
@@ -355,7 +358,7 @@ export interface ISftpSessionResolver {
// 连接后回调
connectCallback: () => void;
// 关闭回调
onClose: (forceClose: string, msg: string) => void;
onClose: (forceClose: boolean, msg: string) => void;
// 接受文件列表响应
resolveList: (result: string, path: string, list: Array<SftpFile>) => void;
// 接收创建文件夹响应