🔨 重构主机模块.
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<!-- 主机列表 -->
|
||||
<host-list-view class="host-list"
|
||||
:hostList="hostList"
|
||||
empty-value="当前分组内无授权主机/主机未启用 SSH 配置!" />
|
||||
empty-value="当前分组内无授权主机!" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
const emptyMessage = computed(() => {
|
||||
if (props.newConnectionType === NewConnectionType.LIST) {
|
||||
// 列表
|
||||
return '无授权主机/主机未启用 SSH 配置!';
|
||||
return '无授权主机!';
|
||||
} else if (props.newConnectionType === NewConnectionType.FAVORITE) {
|
||||
// 收藏
|
||||
return '无收藏记录, 快去点击主机右侧的⭐进行收藏吧!';
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
const { toOptions } = useDictStore();
|
||||
|
||||
const formModel = ref<LabelExtraSettingModel>({
|
||||
color: ''
|
||||
color: '',
|
||||
});
|
||||
|
||||
// 渲染表单
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
selectedFiles: Array<string>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'download', 'setLoading']);
|
||||
const emits = defineEmits(['loadFile', 'download', 'deleteFile', 'setLoading']);
|
||||
|
||||
const showHiddenFile = ref(false);
|
||||
const analysisPaths = ref<Array<PathAnalysis>>([]);
|
||||
@@ -295,9 +295,12 @@
|
||||
|
||||
// 删除选中文件
|
||||
const deleteSelectFiles = () => {
|
||||
if (props.selectedFiles?.length) {
|
||||
props.session?.remove(props.selectedFiles);
|
||||
}
|
||||
emits('deleteFile', [...props.selectedFiles]);
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
const downloadFile = () => {
|
||||
emits('download', [...props.selectedFiles], true);
|
||||
};
|
||||
|
||||
// 重新连接
|
||||
@@ -309,12 +312,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
const downloadFile = () => {
|
||||
emits('download', [...props.selectedFiles]);
|
||||
emits('update:selectedFiles', []);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
selectedFiles: Array<string>;
|
||||
}>();
|
||||
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile', 'download']);
|
||||
const emits = defineEmits(['update:selectedFiles', 'loadFile', 'editFile', 'deleteFile', 'download']);
|
||||
|
||||
const openSftpMoveModal = inject(openSftpMoveModalKey) as (sessionId: string, path: string) => void;
|
||||
const openSftpChmodModal = inject(openSftpChmodModalKey) as (sessionId: string, path: string, permission: number) => void;
|
||||
@@ -239,7 +239,7 @@
|
||||
if (!props.session?.connected) {
|
||||
return;
|
||||
}
|
||||
props.session?.remove([path]);
|
||||
emits('deleteFile', [path]);
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
@@ -248,7 +248,7 @@
|
||||
if (!props.session?.connected) {
|
||||
return;
|
||||
}
|
||||
emits('download', [path]);
|
||||
emits('download', [path], false);
|
||||
};
|
||||
|
||||
// 移动文件
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
title-align="start"
|
||||
title="文件上传"
|
||||
ok-text="上传"
|
||||
:body-style="{ padding: '20px' }"
|
||||
:body-style="{ padding: 0 }"
|
||||
:align-center="false"
|
||||
:mask-closable="false"
|
||||
:unmount-on-close="true"
|
||||
:on-before-ok="handlerOk"
|
||||
@cancel="handleClose">
|
||||
<div class="upload-container">
|
||||
<div class="parent-wrapper mb16">
|
||||
<span class="parent-label">上传至文件夹:</span>
|
||||
<a-input class="parent-input"
|
||||
<!-- 上传目录 -->
|
||||
<div class="item-wrapper">
|
||||
<div class="form-item">
|
||||
<span class="item-label">上传至文件夹</span>
|
||||
<a-input class="item-input"
|
||||
v-model="parentPath"
|
||||
placeholder="上传目录" />
|
||||
</div>
|
||||
@@ -80,8 +81,8 @@
|
||||
import { ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { getFileSize } from '@/utils/file';
|
||||
import useVisible from '@/hooks/visible';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { transferManager } = useTerminalStore();
|
||||
@@ -100,7 +101,7 @@
|
||||
defineExpose({ open });
|
||||
|
||||
// 确定
|
||||
const handlerOk = () => {
|
||||
const handlerOk = async () => {
|
||||
if (!parentPath.value) {
|
||||
Message.error('请输入上传目录');
|
||||
return false;
|
||||
@@ -109,8 +110,9 @@
|
||||
Message.error('请选择文件');
|
||||
return false;
|
||||
}
|
||||
// 添加到上传列表
|
||||
// 获取上传的文件
|
||||
const files = fileList.value.map(s => s.file as File);
|
||||
// 上传
|
||||
transferManager.addUpload(hostId.value, parentPath.value, files);
|
||||
Message.success('已开始上传, 点击右侧传输列表查看进度');
|
||||
// 清空
|
||||
@@ -132,23 +134,35 @@
|
||||
|
||||
<style lang="less" scoped>
|
||||
@file-size-width: 82px;
|
||||
@item-label: 104px;
|
||||
|
||||
.upload-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.parent-wrapper {
|
||||
.item-wrapper {
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.parent-label {
|
||||
width: 98px;
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.parent-input {
|
||||
width: 386px;
|
||||
.item-label {
|
||||
width: @item-label;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
user-select: none;
|
||||
|
||||
&:after {
|
||||
content: ':';
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.item-input {
|
||||
width: 376px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.file-list-uploader {
|
||||
@@ -160,7 +174,7 @@
|
||||
|
||||
:deep(.arco-upload-list) {
|
||||
padding: 0 12px 0 0;
|
||||
max-height: calc(100vh - 386px);
|
||||
max-height: calc(100vh - 496px);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
:hide-icon="true">
|
||||
<!-- 表头 -->
|
||||
<sftp-table-header class="sftp-table-header"
|
||||
v-model:selected-files="selectFiles"
|
||||
:selected-files="selectFiles"
|
||||
:close-message="closeMessage"
|
||||
:current-path="currentPath"
|
||||
:session="session"
|
||||
@load-file="loadFiles"
|
||||
@download="downloadFiles"
|
||||
@set-loading="setTableLoading" />
|
||||
@set-loading="setTableLoading"
|
||||
@delete-file="deleteFile"
|
||||
@download="downloadFiles" />
|
||||
<!-- 表格 -->
|
||||
<sftp-table class="sftp-table-wrapper"
|
||||
v-model:selected-files="selectFiles"
|
||||
@@ -26,6 +27,7 @@
|
||||
:loading="tableLoading"
|
||||
@load-file="loadFiles"
|
||||
@edit-file="editFile"
|
||||
@delete-file="deleteFile"
|
||||
@download="downloadFiles" />
|
||||
</a-spin>
|
||||
</template>
|
||||
@@ -145,17 +147,30 @@
|
||||
editorFilePath.value = '';
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
const downloadFiles = (paths: Array<string>) => {
|
||||
// 删除文件
|
||||
const deleteFile = (paths: Array<string>) => {
|
||||
if (!paths.length) {
|
||||
return;
|
||||
}
|
||||
// 删除
|
||||
selectFiles.value = [];
|
||||
session.value?.remove(paths);
|
||||
};
|
||||
|
||||
// 下载文件
|
||||
const downloadFiles = (paths: Array<string>, clear: boolean) => {
|
||||
if (!paths.length) {
|
||||
return;
|
||||
}
|
||||
Message.success('已开始下载, 点击右侧传输列表查看进度');
|
||||
// 映射为文件
|
||||
const files = fileList.value.filter(s => paths.includes(s.path))
|
||||
.map(s => {
|
||||
return { ...s };
|
||||
});
|
||||
if (clear) {
|
||||
selectFiles.value = [];
|
||||
}
|
||||
Message.success('已开始下载, 点击右侧传输列表查看进度');
|
||||
// 添加普通文件到下载队列
|
||||
const normalFiles = files.filter(s => !s.isDir);
|
||||
transferManager.addDownload(props.tab.hostId as number, currentPath.value, normalFiles);
|
||||
@@ -193,9 +208,9 @@
|
||||
};
|
||||
|
||||
// 接收列表回调
|
||||
const resolveList = (result: string, path: string, list: Array<SftpFile>) => {
|
||||
const resolveList = (path: string, result: string, msg: string, list: Array<SftpFile>) => {
|
||||
setTableLoading(false);
|
||||
if (!checkResult(result, '查询失败')) {
|
||||
if (!checkResult(result, msg)) {
|
||||
return;
|
||||
}
|
||||
currentPath.value = path;
|
||||
@@ -216,11 +231,11 @@
|
||||
};
|
||||
|
||||
// 接收获取文件内容响应
|
||||
const resolveSftpGetContent = (path: string, result: string, content: string) => {
|
||||
const resolveSftpGetContent = (path: string, result: string, msg: string, content: string) => {
|
||||
setTableLoading(false);
|
||||
setEditorLoading(false);
|
||||
// 检查结果
|
||||
if (!checkResult(result, '加载失败')) {
|
||||
if (!checkResult(result, msg)) {
|
||||
return;
|
||||
}
|
||||
editorRef.value?.setValue(content);
|
||||
@@ -237,8 +252,12 @@
|
||||
};
|
||||
|
||||
// 接收下载文件夹展开文件响应
|
||||
const resolveDownloadFlatDirectory = (currentPath: string, list: Array<SftpFile>) => {
|
||||
const resolveDownloadFlatDirectory = (currentPath: string, result: string, msg: string, list: Array<SftpFile>) => {
|
||||
setTableLoading(false);
|
||||
// 检查结果
|
||||
if (!checkResult(result, msg)) {
|
||||
return;
|
||||
}
|
||||
transferManager.addDownload(props.tab.hostId as number, currentPath, list);
|
||||
};
|
||||
|
||||
@@ -273,7 +292,7 @@
|
||||
|
||||
.sftp-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--header-height) - var(--panel-nav-height));
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.split-view {
|
||||
|
||||
@@ -40,7 +40,7 @@ const columns = [
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
width: 234,
|
||||
width: 264,
|
||||
cellClass: 'action-cell',
|
||||
},
|
||||
] as TableColumnData[];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ISftpSession, ISftpSessionResolver, ITerminalChannel, TerminalPanelTabItem } from '../types/terminal.type';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
||||
import { PanelSessionType } from '../types/terminal.const';
|
||||
import { Modal } from '@arco-design/web-vue';
|
||||
import BaseSession from './base-session';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SftpTransferItem } from '../types/terminal.type';
|
||||
import { TransferStatus, TransferType } from '../types/terminal.const';
|
||||
import { TransferStatus } from '../types/terminal.const';
|
||||
import { getFileName, openDownloadFile } from '@/utils/file';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { getDownloadTransferUrl } from '@/api/asset/host-sftp';
|
||||
@@ -8,8 +8,8 @@ import SftpTransferHandler from './sftp-transfer-handler';
|
||||
// sftp 下载器实现
|
||||
export default class SftpTransferDownloader extends SftpTransferHandler {
|
||||
|
||||
constructor(item: SftpTransferItem, client: WebSocket) {
|
||||
super(TransferType.DOWNLOAD, item, client);
|
||||
constructor(type: string, item: SftpTransferItem, client: WebSocket) {
|
||||
super(type, item, client);
|
||||
}
|
||||
|
||||
// 开始回调
|
||||
|
||||
@@ -27,7 +27,7 @@ export default abstract class SftpTransferHandler implements ISftpTransferHandle
|
||||
operator: TransferOperator.START,
|
||||
type: this.type,
|
||||
path: getPath(this.item.parentPath + '/' + this.item.name),
|
||||
hostId: this.item.hostId
|
||||
hostId: this.item.hostId,
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -81,9 +81,14 @@ export default abstract class SftpTransferHandler implements ISftpTransferHandle
|
||||
};
|
||||
|
||||
// 进度回调
|
||||
onProgress(size: number) {
|
||||
if (this.item && size) {
|
||||
this.item.currentSize = size;
|
||||
onProgress(totalSize: number | undefined, currentSize: number | undefined) {
|
||||
if (this.item) {
|
||||
if (totalSize) {
|
||||
this.item.totalSize = totalSize;
|
||||
}
|
||||
if (currentSize) {
|
||||
this.item.currentSize = currentSize;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
|
||||
private run: boolean;
|
||||
|
||||
private progressIntervalId?: number;
|
||||
private progressIntervalId?: any;
|
||||
|
||||
private currentItem?: SftpTransferItem;
|
||||
|
||||
@@ -45,9 +45,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
});
|
||||
this.transferList.push(...items);
|
||||
// 开始传输
|
||||
if (!this.run) {
|
||||
this.openClient();
|
||||
}
|
||||
this.startTransfer(items);
|
||||
}
|
||||
|
||||
// 添加下载任务
|
||||
@@ -67,6 +65,12 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
status: TransferStatus.WAITING,
|
||||
};
|
||||
}) as Array<SftpTransferItem>;
|
||||
// 开始传输
|
||||
this.startTransfer(items);
|
||||
}
|
||||
|
||||
// 开始传输
|
||||
startTransfer(items: Array<SftpTransferItem>) {
|
||||
this.transferList.push(...items);
|
||||
// 开始传输
|
||||
if (!this.run) {
|
||||
@@ -154,13 +158,8 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
// 获取任务
|
||||
this.currentItem = this.transferList.find(s => s.status === TransferStatus.WAITING);
|
||||
if (this.currentItem) {
|
||||
if (this.currentItem.type === TransferType.UPLOAD) {
|
||||
// 上传
|
||||
this.currentTransfer = new SftpTransferUploader(this.currentItem, this.client as WebSocket);
|
||||
} else {
|
||||
// 下载
|
||||
this.currentTransfer = new SftpTransferDownloader(this.currentItem, this.client as WebSocket);
|
||||
}
|
||||
// 创建传输器
|
||||
this.currentTransfer = this.createTransfer();
|
||||
// 开始
|
||||
this.currentTransfer?.start();
|
||||
} else {
|
||||
@@ -169,6 +168,20 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建传输器
|
||||
private createTransfer(): ISftpTransferHandler | undefined {
|
||||
if (!this.currentItem) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.currentItem.type === TransferType.UPLOAD) {
|
||||
// 上传
|
||||
return new SftpTransferUploader(TransferType.UPLOAD, this.currentItem, this.client as WebSocket);
|
||||
} else if (this.currentItem.type === TransferType.DOWNLOAD) {
|
||||
// 下载
|
||||
return new SftpTransferDownloader(TransferType.DOWNLOAD, this.currentItem, this.client as WebSocket);
|
||||
}
|
||||
}
|
||||
|
||||
// 接收消息
|
||||
private async resolveMessage(message: MessageEvent) {
|
||||
// 文本消息
|
||||
@@ -181,7 +194,7 @@ export default class SftpTransferManager implements ISftpTransferManager {
|
||||
this.currentTransfer?.onStart(data.channelId as string, data.transferToken as string);
|
||||
} else if (data.type === TransferReceiver.PROGRESS) {
|
||||
// 进度回调
|
||||
this.currentTransfer?.onProgress(data.currentSize as number);
|
||||
this.currentTransfer?.onProgress(data.totalSize, data.currentSize);
|
||||
} else if (data.type === TransferReceiver.FINISH) {
|
||||
// 完成回调
|
||||
this.currentTransfer?.onFinish();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { SftpTransferItem } from '../types/terminal.type';
|
||||
import { TransferType } from '../types/terminal.const';
|
||||
import SftpTransferHandler from './sftp-transfer-handler';
|
||||
|
||||
// 512 KB
|
||||
@@ -12,8 +11,8 @@ export default class SftpTransferUploader extends SftpTransferHandler {
|
||||
private readonly totalPart: number;
|
||||
private file: File;
|
||||
|
||||
constructor(item: SftpTransferItem, client: WebSocket) {
|
||||
super(TransferType.UPLOAD, item, client);
|
||||
constructor(type: string, item: SftpTransferItem, client: WebSocket) {
|
||||
super(type, item, client);
|
||||
this.file = item.file;
|
||||
this.currentPart = 0;
|
||||
this.totalPart = Math.ceil(item.file.size / PART_SIZE);
|
||||
|
||||
@@ -2,10 +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, XtermAddons, XtermDomRef } from '../types/terminal.type';
|
||||
import type { ISshSession, ISshSessionHandler, ITerminalChannel, TerminalPanelTabItem, XtermDomRef } from '../types/terminal.type';
|
||||
import type { XtermAddons } from '@/types/xterm';
|
||||
import { defaultFontFamily } from '@/types/xterm';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { fontFamilySuffix, PanelSessionType, TerminalShortcutType, TerminalStatus, } from '../types/terminal.const';
|
||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
||||
import { PanelSessionType, TerminalShortcutType, TerminalStatus } from '../types/terminal.const';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
@@ -44,6 +46,7 @@ export default class SshSession extends BaseSession implements ISshSession {
|
||||
// 初始化
|
||||
init(domRef: XtermDomRef): void {
|
||||
const { preference } = useTerminalStore();
|
||||
const fontFamily = preference.displaySetting.fontFamily;
|
||||
// 初始化实例
|
||||
this.inst = new Terminal({
|
||||
...(preference.displaySetting as any),
|
||||
@@ -52,7 +55,7 @@ export default class SshSession extends BaseSession implements ISshSession {
|
||||
altClickMovesCursor: !!preference.interactSetting.altClickMovesCursor,
|
||||
rightClickSelectsWord: !!preference.interactSetting.rightClickSelectsWord,
|
||||
wordSeparator: preference.interactSetting.wordSeparator,
|
||||
fontFamily: preference.displaySetting.fontFamily + fontFamilySuffix,
|
||||
fontFamily: fontFamily === '_' ? defaultFontFamily : `${fontFamily}, ${defaultFontFamily}`,
|
||||
scrollback: preference.sessionSetting.scrollBackLine,
|
||||
allowProposedApi: true,
|
||||
});
|
||||
@@ -219,6 +222,11 @@ export default class SshSession extends BaseSession implements ISshSession {
|
||||
this.inst.write(value);
|
||||
}
|
||||
|
||||
// 修改大小
|
||||
resize(cols: number, rows: number): void {
|
||||
this.inst.resize(cols, rows);
|
||||
}
|
||||
|
||||
// 聚焦
|
||||
focus(): void {
|
||||
this.inst.focus();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { InputPayload, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, Protocol, } from '../types/terminal.type';
|
||||
import { format, OutputProtocol, parse } from '../types/terminal.protocol';
|
||||
import type { ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager } from '../types/terminal.type';
|
||||
import type { InputPayload, Protocol } from '@/types/protocol/terminal.protocol';
|
||||
import { format, OutputProtocol, parse } from '@/types/protocol/terminal.protocol';
|
||||
import { sessionCloseMsg } from '../types/terminal.const';
|
||||
import { getTerminalAccessToken, openHostTerminalChannel } from '@/api/asset/host-terminal';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import type {
|
||||
ISftpSession,
|
||||
ISshSession,
|
||||
ITerminalChannel,
|
||||
ITerminalOutputProcessor,
|
||||
ITerminalSession,
|
||||
ITerminalSessionManager,
|
||||
OutputPayload
|
||||
} from '../types/terminal.type';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import type { ISftpSession, ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSession, ITerminalSessionManager } from '../types/terminal.type';
|
||||
import type { OutputPayload } from '@/types/protocol/terminal.protocol';
|
||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
||||
import { PanelSessionType, TerminalStatus } from '../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
@@ -43,7 +36,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
});
|
||||
} else {
|
||||
// 未成功展示错误信息
|
||||
ssh.write(`[91m${msg || ''}\r\n输入回车重新连接...[0m\r\n\r\n`);
|
||||
ssh.write(`[91m${msg || ''}[0m\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
||||
ssh.status = TerminalStatus.CLOSED;
|
||||
}
|
||||
}, sftp => {
|
||||
@@ -109,7 +102,7 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
// ssh 拼接关闭消息
|
||||
ssh.write(`\r\n\r\n[91m${msg || ''}[0m\r\n`);
|
||||
if (!isForceClose) {
|
||||
ssh.write(`[91m${msg || ''}[0m\r\n[91m输入回车重新连接...[0m\r\n\r\n`);
|
||||
ssh.write('[91m输入回车重新连接...[0m\r\n\r\n');
|
||||
}
|
||||
// 设置状态
|
||||
ssh.status = TerminalStatus.CLOSED;
|
||||
@@ -135,10 +128,10 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
}
|
||||
|
||||
// 处理 SFTP 文件列表
|
||||
processSftpList({ sessionId, result, path, body }: OutputPayload): void {
|
||||
processSftpList({ sessionId, result, path, msg, body }: OutputPayload): void {
|
||||
// 获取会话
|
||||
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
|
||||
session && session.resolver.resolveList(result, path, JSON.parse(body));
|
||||
session && session.resolver.resolveList(path, result, msg, JSON.parse(body));
|
||||
}
|
||||
|
||||
// 处理 SFTP 创建文件夹
|
||||
@@ -177,17 +170,17 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
}
|
||||
|
||||
// 处理 SFTP 下载文件夹展开文件
|
||||
processDownloadFlatDirectory({ sessionId, currentPath, body }: OutputPayload): void {
|
||||
processDownloadFlatDirectory({ sessionId, currentPath, result, msg, body }: OutputPayload): void {
|
||||
// 获取会话
|
||||
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
|
||||
session && session.resolver.resolveDownloadFlatDirectory(currentPath, JSON.parse(body));
|
||||
session && session.resolver.resolveDownloadFlatDirectory(currentPath, result, msg, JSON.parse(body));
|
||||
}
|
||||
|
||||
// 处理 SFTP 获取文件内容
|
||||
processSftpGetContent({ sessionId, path, result, content }: OutputPayload): void {
|
||||
processSftpGetContent({ sessionId, path, result, msg, content }: OutputPayload): void {
|
||||
// 获取会话
|
||||
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
|
||||
session && session.resolver.resolveSftpGetContent(path, result, content);
|
||||
session && session.resolver.resolveSftpGetContent(path, result, msg, content);
|
||||
}
|
||||
|
||||
// 处理 SFTP 修改文件内容
|
||||
|
||||
@@ -4,10 +4,9 @@ import TerminalTabManager from '../handler/terminal-tab-manager';
|
||||
// 终端面板管理器实现
|
||||
export default class TerminalPanelManager implements ITerminalPanelManager {
|
||||
|
||||
// 当前面板
|
||||
active: number;
|
||||
// 面板列表
|
||||
panels: Array<TerminalTabManager<TerminalPanelTabItem>>;
|
||||
public active: number;
|
||||
|
||||
public panels: Array<TerminalTabManager<TerminalPanelTabItem>>;
|
||||
|
||||
constructor() {
|
||||
this.active = 0;
|
||||
@@ -31,17 +30,18 @@ export default class TerminalPanelManager implements ITerminalPanelManager {
|
||||
|
||||
// 移除面板
|
||||
removePanel(index: number) {
|
||||
this.panels.splice(index, 1);
|
||||
this.panels[index].clear();
|
||||
this.active = index >= this.panels.length ? this.panels.length - 1 : index;
|
||||
};
|
||||
|
||||
// 重置
|
||||
reset() {
|
||||
for (let panel of this.panels) {
|
||||
panel.clear();
|
||||
}
|
||||
this.active = 0;
|
||||
this.panels = [new TerminalTabManager()];
|
||||
if (this.panels) {
|
||||
for (let panel of this.panels) {
|
||||
panel.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
XtermDomRef
|
||||
} from '../types/terminal.type';
|
||||
import { sleep } from '@/utils';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { InputProtocol } from '@/types/protocol/terminal.protocol';
|
||||
import { PanelSessionType } from '../types/terminal.const';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { addEventListen, removeEventListen } from '@/utils/event';
|
||||
@@ -132,7 +132,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
}
|
||||
|
||||
// 调度重置大小
|
||||
private dispatchResize() {
|
||||
dispatchResize() {
|
||||
// 对所有已连接的会话重置大小
|
||||
Object.values(this.sessions)
|
||||
.filter(s => s?.type === PanelSessionType.SSH.type)
|
||||
|
||||
@@ -56,8 +56,8 @@ export const NewConnectionType = {
|
||||
|
||||
// 主机额外配置项
|
||||
export const ExtraSettingItems = {
|
||||
SSH: 'ssh',
|
||||
LABEL: 'label',
|
||||
SSH: 'SSH',
|
||||
LABEL: 'LABEL',
|
||||
};
|
||||
|
||||
// 主机额外配置 ssh 认证方式
|
||||
@@ -324,7 +324,7 @@ export const TransferStatus = {
|
||||
// 传输类型
|
||||
export const TransferType = {
|
||||
UPLOAD: 'upload',
|
||||
DOWNLOAD: 'download'
|
||||
DOWNLOAD: 'download',
|
||||
};
|
||||
|
||||
// 传输操作
|
||||
@@ -363,9 +363,6 @@ export const openSftpChmodModalKey = Symbol();
|
||||
// 打开 sftpUploadModal key
|
||||
export const openSftpUploadModalKey = Symbol();
|
||||
|
||||
// 字体后缀 兜底
|
||||
export const fontFamilySuffix = ', Courier New, Monaco, courier, monospace';
|
||||
|
||||
// 终端字体样式
|
||||
export const fontFamilyKey = 'terminalFontFamily';
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import type { Terminal } from '@xterm/xterm';
|
||||
import type { FitAddon } from '@xterm/addon-fit';
|
||||
import type { CanvasAddon } from '@xterm/addon-canvas';
|
||||
import type { WebglAddon } from '@xterm/addon-webgl';
|
||||
import type { WebLinksAddon } from '@xterm/addon-web-links';
|
||||
import type { ISearchOptions, SearchAddon } from '@xterm/addon-search';
|
||||
import type { ImageAddon } from '@xterm/addon-image';
|
||||
import type { Unicode11Addon } from '@xterm/addon-unicode11';
|
||||
import type { ISearchOptions } from '@xterm/addon-search';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import type { HostQueryResponse } from '@/api/asset/host';
|
||||
import type { InputPayload, OutputPayload, Protocol } from '@/types/protocol/terminal.protocol';
|
||||
|
||||
// 终端 tab 元素
|
||||
export interface TerminalTabItem {
|
||||
@@ -81,30 +76,6 @@ export interface PanelSessionTabType {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// 终端协议
|
||||
export interface Protocol {
|
||||
type: string;
|
||||
template: string[];
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// 终端输入消息内容
|
||||
export interface InputPayload {
|
||||
type?: string;
|
||||
sessionId?: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
// 终端输出消息内容
|
||||
export interface OutputPayload {
|
||||
type: string;
|
||||
sessionId: string;
|
||||
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
// 终端 tab 管理器定义
|
||||
export interface ITerminalTabManager<T extends TerminalTabItem = TerminalTabItem> {
|
||||
// 当前 tab
|
||||
@@ -166,6 +137,8 @@ export interface ITerminalSessionManager {
|
||||
getSession: <T extends ITerminalSession>(sessionId: string) => T;
|
||||
// 关闭终端会话
|
||||
closeSession: (sessionId: string) => void;
|
||||
// 重置大小
|
||||
dispatchResize: () => void;
|
||||
// 重置
|
||||
reset: () => void;
|
||||
}
|
||||
@@ -221,17 +194,6 @@ export interface XtermDomRef {
|
||||
editorModal: any;
|
||||
}
|
||||
|
||||
// xterm 插件
|
||||
export interface XtermAddons {
|
||||
fit: FitAddon;
|
||||
webgl: WebglAddon;
|
||||
canvas: CanvasAddon;
|
||||
weblink: WebLinksAddon;
|
||||
search: SearchAddon;
|
||||
image: ImageAddon;
|
||||
unicode: Unicode11Addon;
|
||||
}
|
||||
|
||||
// 终端会话定义
|
||||
export interface ITerminalSession {
|
||||
type: string;
|
||||
@@ -269,6 +231,8 @@ export interface ISshSession extends ITerminalSession {
|
||||
init: (domRef: XtermDomRef) => void;
|
||||
// 写入数据
|
||||
write: (value: string) => void;
|
||||
// 修改大小
|
||||
resize: (cols: number, rows: number) => void;
|
||||
// 聚焦
|
||||
focus: () => void;
|
||||
// 失焦
|
||||
@@ -368,7 +332,7 @@ export interface ISftpSessionResolver {
|
||||
// 关闭回调
|
||||
onClose: (forceClose: boolean, msg: string) => void;
|
||||
// 接受文件列表响应
|
||||
resolveList: (result: string, path: string, list: Array<SftpFile>) => void;
|
||||
resolveList: (path: string, result: string, msg: string, list: Array<SftpFile>) => void;
|
||||
// 接收创建文件夹响应
|
||||
resolveSftpMkdir: (result: string, msg: string) => void;
|
||||
// 接收创建文件响应
|
||||
@@ -380,9 +344,9 @@ export interface ISftpSessionResolver {
|
||||
// 接收修改文件权限响应
|
||||
resolveSftpChmod: (result: string, msg: string) => void;
|
||||
// 接收下载文件夹展开文件响应
|
||||
resolveDownloadFlatDirectory: (currentPath: string, list: Array<SftpFile>) => void;
|
||||
resolveDownloadFlatDirectory: (currentPath: string, result: string, msg: string, list: Array<SftpFile>) => void;
|
||||
// 接收获取文件内容响应
|
||||
resolveSftpGetContent: (path: string, result: string, content: string) => void;
|
||||
resolveSftpGetContent: (path: string, result: string, msg: string, content: string) => void;
|
||||
// 接收修改文件内容响应
|
||||
resolveSftpSetContent: (result: string, msg: string) => void;
|
||||
}
|
||||
@@ -414,7 +378,6 @@ export interface ISftpTransferManager {
|
||||
cancelAllTransfer: () => void;
|
||||
}
|
||||
|
||||
|
||||
// sftp 传输处理回调定义
|
||||
export interface ISftpTransferCallback {
|
||||
// 下一分片回调
|
||||
@@ -422,7 +385,7 @@ export interface ISftpTransferCallback {
|
||||
// 开始回调
|
||||
onStart: (channelId: string, token: string) => void;
|
||||
// 进度回调
|
||||
onProgress: (size: number) => void;
|
||||
onProgress: (totalSize: number | undefined, currentSize: number | undefined) => void;
|
||||
// 失败回调
|
||||
onError: (msg: string | undefined) => void;
|
||||
// 完成回调
|
||||
@@ -475,4 +438,5 @@ export interface TransferOperatorResponse {
|
||||
transferToken?: string;
|
||||
success: boolean;
|
||||
msg?: string;
|
||||
totalSize?: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user