🔨 sftp 表格.
This commit is contained in:
457
orion-ops-ui/src/views/host/terminal/components/sftp/data.ts
Normal file
457
orion-ops-ui/src/views/host/terminal/components/sftp/data.ts
Normal file
@@ -0,0 +1,457 @@
|
||||
export default [{
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1639399737000,
|
||||
'name': '04',
|
||||
'path': '/root/04',
|
||||
'permission': 644,
|
||||
'size': '34.2 MB',
|
||||
'sizeByte': 35845278,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1662630786000,
|
||||
'name': 'j3',
|
||||
'path': '/root/j3',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1658895079000,
|
||||
'name': '123',
|
||||
'path': '/root/123',
|
||||
'permission': 644,
|
||||
'size': '13 B',
|
||||
'sizeByte': 13,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--rwx',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1664427351000,
|
||||
'name': 'bug.txt',
|
||||
'path': '/root/bug.txt',
|
||||
'permission': 647,
|
||||
'size': '50.3 KB',
|
||||
'sizeByte': 51491,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1659888021000,
|
||||
'name': 'ops-monitor-agent',
|
||||
'path': '/root/ops-monitor-agent',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1635143381000,
|
||||
'name': '04_体验一下面试官对于消息队列的7个连环炮.zip',
|
||||
'path': '/root/04_体验一下面试官对于消息队列的7个连环炮.zip',
|
||||
'permission': 644,
|
||||
'size': '34.2 MB',
|
||||
'sizeByte': 35845278,
|
||||
'suffix': 'zip',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1637921002000,
|
||||
'name': '一个超长的文件一个超长的文件一个超长的文件一个超长的文件一个超长的文件一个超长的文件',
|
||||
'path': '/root/一个超长的文件一个超长的文件一个超长的文件一个超长的文件一个超长的文件一个超长的文件',
|
||||
'permission': 644,
|
||||
'size': '4 B',
|
||||
'sizeByte': 4,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1662627298000,
|
||||
'name': 'WebStorm-2021.2.2.exe',
|
||||
'path': '/root/WebStorm-2021.2.2.exe',
|
||||
'permission': 644,
|
||||
'size': '366.2 MB',
|
||||
'sizeByte': 383998600,
|
||||
'suffix': 'exe',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxrwxr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1557936457000,
|
||||
'name': 'redis-5.0.5',
|
||||
'path': '/root/redis-5.0.5',
|
||||
'permission': 775,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '5',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1643003400000,
|
||||
'name': 'pic',
|
||||
'path': '/root/pic',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1635127278000,
|
||||
'name': 'pvzβv6.25-R4.7z',
|
||||
'path': '/root/pvzβv6.25-R4.7z',
|
||||
'permission': 644,
|
||||
'size': '37.2 MB',
|
||||
'sizeByte': 38978762,
|
||||
'suffix': '7z',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1652947847000,
|
||||
'name': '1.txt',
|
||||
'path': '/root/1.txt',
|
||||
'permission': 644,
|
||||
'size': '133 B',
|
||||
'sizeByte': 133,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1653015555000,
|
||||
'name': 'write',
|
||||
'path': '/root/write',
|
||||
'permission': 644,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1660552691000,
|
||||
'name': 'k.pub',
|
||||
'path': '/root/k.pub',
|
||||
'permission': 644,
|
||||
'size': '401 B',
|
||||
'sizeByte': 401,
|
||||
'suffix': 'pub',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1625065314000,
|
||||
'name': 'ideaIU-2021.1.1.exe',
|
||||
'path': '/root/ideaIU-2021.1.1.exe',
|
||||
'permission': 644,
|
||||
'size': '729.3 MB',
|
||||
'sizeByte': 764705864,
|
||||
'suffix': 'exe',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1636525624000,
|
||||
'name': '文本.txt',
|
||||
'path': '/root/文本.txt',
|
||||
'permission': 644,
|
||||
'size': '0 B',
|
||||
'sizeByte': 0,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1635090233000,
|
||||
'name': '数据头.txt',
|
||||
'path': '/root/数据头.txt',
|
||||
'permission': 644,
|
||||
'size': '4.3 KB',
|
||||
'sizeByte': 4376,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1662627659000,
|
||||
'name': 'j1',
|
||||
'path': '/root/j1',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1642956894000,
|
||||
'name': 'PowerShell-7.1.4-win-x64.msi',
|
||||
'path': '/root/PowerShell-7.1.4-win-x64.msi',
|
||||
'permission': 644,
|
||||
'size': '94.9 MB',
|
||||
'sizeByte': 99524608,
|
||||
'suffix': 'msi',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-------',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1660552691000,
|
||||
'name': 'k',
|
||||
'path': '/root/k',
|
||||
'permission': 600,
|
||||
'size': '1.7 KB',
|
||||
'sizeByte': 1766,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1652942938000,
|
||||
'name': 'bbb',
|
||||
'path': '/root/bbb',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1675653580000,
|
||||
'name': 'swapper',
|
||||
'path': '/root/swapper',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1639443147000,
|
||||
'name': 'tmp2.txt',
|
||||
'path': '/root/tmp2.txt',
|
||||
'permission': 644,
|
||||
'size': '184 B',
|
||||
'sizeByte': 184,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1653277297000,
|
||||
'name': 'test',
|
||||
'path': '/root/test',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1625064193000,
|
||||
'name': 'a1.rar',
|
||||
'path': '/root/a1.rar',
|
||||
'permission': 644,
|
||||
'size': '52 MB',
|
||||
'sizeByte': 54528512,
|
||||
'suffix': 'rar',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rwxrwxrwx',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1704289282000,
|
||||
'name': 'bridge.sh',
|
||||
'path': '/root/bridge.sh',
|
||||
'permission': 777,
|
||||
'size': '396 B',
|
||||
'sizeByte': 396,
|
||||
'suffix': 'sh',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1663829274000,
|
||||
'name': 'orion-ops',
|
||||
'path': '/root/orion-ops',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1668680082000,
|
||||
'name': 'ub.txt',
|
||||
'path': '/root/ub.txt',
|
||||
'permission': 644,
|
||||
'size': '71.7 MB',
|
||||
'sizeByte': 75167744,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rwxrwxrwx',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1668594989000,
|
||||
'name': 'osx.d',
|
||||
'path': '/root/osx.d',
|
||||
'permission': 777,
|
||||
'size': '170 B',
|
||||
'sizeByte': 170,
|
||||
'suffix': 'd',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1636337848000,
|
||||
'name': 'temp',
|
||||
'path': '/root/temp',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1659524667000,
|
||||
'name': '46746619-175F-41c0-8D0E-0007E9271507.png',
|
||||
'path': '/root/46746619-175F-41c0-8D0E-0007E9271507.png',
|
||||
'permission': 644,
|
||||
'size': '158.1 KB',
|
||||
'sizeByte': 161927,
|
||||
'suffix': 'png',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1646190836000,
|
||||
'name': 'video',
|
||||
'path': '/root/video',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1625136042000,
|
||||
'name': 'sql1.txt',
|
||||
'path': '/root/sql1.txt',
|
||||
'permission': 644,
|
||||
'size': '16.9 KB',
|
||||
'sizeByte': 17334,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1665545226000,
|
||||
'name': 'ts.txt',
|
||||
'path': '/root/ts.txt',
|
||||
'permission': 644,
|
||||
'size': '101.6 KB',
|
||||
'sizeByte': 104004,
|
||||
'suffix': 'txt',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1635090505000,
|
||||
'name': 'shop.xlsx',
|
||||
'path': '/root/shop.xlsx',
|
||||
'permission': 644,
|
||||
'size': '87.9 KB',
|
||||
'sizeByte': 89993,
|
||||
'suffix': 'xlsx',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1635127741000,
|
||||
'name': 'Java 8实战.pdf',
|
||||
'path': '/root/Java 8实战.pdf',
|
||||
'permission': 644,
|
||||
'size': '12.9 MB',
|
||||
'sizeByte': 13490536,
|
||||
'suffix': 'pdf',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': '-rw-r--r--',
|
||||
'gid': 0,
|
||||
'isDir': false,
|
||||
'modifyTime': 1662626999000,
|
||||
'name': 'Postman-win64-6.5.3-Setup.exe',
|
||||
'path': '/root/Postman-win64-6.5.3-Setup.exe',
|
||||
'permission': 644,
|
||||
'size': '68.1 MB',
|
||||
'sizeByte': 71401080,
|
||||
'suffix': 'exe',
|
||||
'uid': 0
|
||||
}, {
|
||||
'attr': 'drwxr-xr-x',
|
||||
'gid': 0,
|
||||
'isDir': true,
|
||||
'modifyTime': 1662627183000,
|
||||
'name': 'jar',
|
||||
'path': '/root/jar',
|
||||
'permission': 755,
|
||||
'size': '4 KB',
|
||||
'sizeByte': 4096,
|
||||
'suffix': '',
|
||||
'uid': 0
|
||||
}];
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<a-table row-key="name"
|
||||
class="sftp-table"
|
||||
label-align="left"
|
||||
:columns="columns"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
:row-selection="rowSelection"
|
||||
:sticky-header="true"
|
||||
:data="list"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
@cell-mouse-enter="setEditable"
|
||||
@cell-mouse-leave="unsetEditable">
|
||||
<!-- 文件搜索框 -->
|
||||
<template #nameFilter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset}">
|
||||
<div class="name-filter">
|
||||
<a-space direction="vertical">
|
||||
<!-- 过滤输入框 -->
|
||||
<a-input size="small"
|
||||
:model-value="filterValue[0]"
|
||||
@input="(value) => setFilterValue([value])" />
|
||||
<!-- 按钮 -->
|
||||
<div class="name-filter-footer">
|
||||
<a-button size="small" @click="handleFilterConfirm">过滤</a-button>
|
||||
<a-button size="small" @click="handleFilterReset">重置</a-button>
|
||||
</div>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 文件大小 -->
|
||||
<template #size="{ record }">
|
||||
<span v-if="editRecord.name === record.name">操作</span>
|
||||
<span v-else>{{ record.size }}</span>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'sftpTable'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TableData } from '@arco-design/web-vue/es/table/interface';
|
||||
import type { SftpFile } from '../../types/terminal.type';
|
||||
import { ref } from 'vue';
|
||||
import { useRowSelection } from '@/types/table';
|
||||
import columns from './types/table.columns';
|
||||
|
||||
const props = defineProps<{
|
||||
list: Array<SftpFile>;
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
const rowSelection = useRowSelection();
|
||||
const selectedKeys = ref<Array<string>>([]);
|
||||
const editRecord = ref<TableData>({});
|
||||
|
||||
// 设置选中状态
|
||||
const setEditable = (record: TableData) => {
|
||||
editRecord.value = record;
|
||||
record.hover = true;
|
||||
};
|
||||
|
||||
// 设置未选中状态
|
||||
const unsetEditable = (record: TableData) => {
|
||||
setTimeout(() => {
|
||||
if (record.name === editRecord.value.name && !record.hover) {
|
||||
editRecord.value = {};
|
||||
}
|
||||
}, 20);
|
||||
record.hover = false;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sftp-table {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.name-filter {
|
||||
padding: 12px;
|
||||
background: var(--color-bg-5);
|
||||
border: 1px solid var(--color-neutral-3);
|
||||
border-radius: var(--border-radius-medium);
|
||||
box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
|
||||
|
||||
&-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,8 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ tab }}
|
||||
<div>header</div>
|
||||
<div>table</div>
|
||||
<div class="sftp-container">
|
||||
<!-- 头部 -->
|
||||
<div class="sftp-header">
|
||||
<!-- 左侧操作 -->
|
||||
<div class="sftp-header-left">
|
||||
home input
|
||||
</div>
|
||||
<!-- 右侧操作 -->
|
||||
<div class="sftp-header-right">
|
||||
上传 下载 删除 刷新 copy touch mk
|
||||
</div>
|
||||
</div>
|
||||
<a-split class="split-view"
|
||||
v-model:size="splitSize"
|
||||
:min="0.3"
|
||||
:disabled="!editView">
|
||||
<!-- 表格 -->
|
||||
<template #first>
|
||||
<sftp-table :list="list"
|
||||
:loading="loading" />
|
||||
</template>
|
||||
<template #second v-if="editView">
|
||||
<div>editor</div>
|
||||
</template>
|
||||
</a-split>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,14 +34,75 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TerminalTabItem } from '../../types/terminal.type';
|
||||
import type { ISftpSession, SftpFile, TerminalTabItem } from '../../types/terminal.type';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import data from './data';
|
||||
import SftpTable from '@/views/host/terminal/components/sftp/sftp-table.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
tab: TerminalTabItem
|
||||
}>();
|
||||
|
||||
const { preference, sessionManager } = useTerminalStore();
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
|
||||
const session = ref<ISftpSession>();
|
||||
const currentPath = ref<string>('');
|
||||
const list = ref<Array<SftpFile>>(data);
|
||||
const splitSize = ref(1);
|
||||
const editView = ref(true);
|
||||
|
||||
// 初始化会话
|
||||
onMounted(async () => {
|
||||
// 创建终端处理器
|
||||
session.value = await sessionManager.openSftp(props.tab, {
|
||||
list,
|
||||
currentPath,
|
||||
setLoading
|
||||
});
|
||||
});
|
||||
|
||||
// 关闭会话
|
||||
onUnmounted(() => {
|
||||
sessionManager.closeSession(props.tab.key);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@sftp-header-height: 36px;
|
||||
|
||||
.sftp-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - var(--header-height) - var(--panel-nav-height));
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.split-view {
|
||||
width: 100%;
|
||||
height: calc(100% - @sftp-header-height);
|
||||
}
|
||||
|
||||
.sftp-header {
|
||||
width: 100%;
|
||||
height: @sftp-header-height;
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: var(--color-bg-panel-bar);
|
||||
|
||||
&-left, &-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { dateFormat } from '@/utils';
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
fixed: 'left',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
filterable: {
|
||||
filter: (value, record) => record.name.includes(value),
|
||||
slotName: 'nameFilter',
|
||||
}
|
||||
}, {
|
||||
title: '大小',
|
||||
dataIndex: 'sizeByte',
|
||||
slotName: 'size',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
}, {
|
||||
title: '属性',
|
||||
dataIndex: 'attr',
|
||||
slotName: 'attr',
|
||||
}, {
|
||||
title: '修改时间',
|
||||
dataIndex: 'modifyTime',
|
||||
slotName: 'modifyTime',
|
||||
align: 'center',
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
},
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.modifyTime));
|
||||
},
|
||||
}, {
|
||||
title: '操作',
|
||||
dataIndex: 'actions',
|
||||
slotName: 'actions',
|
||||
align: 'left',
|
||||
fixed: 'right',
|
||||
},
|
||||
] as TableColumnData[];
|
||||
|
||||
export default columns;
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { ISftpSession, ISftpSessionResolver, SftpDataRef, SftpFile } from '../types/terminal.type';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
// sftp 会话接收器实现
|
||||
export default class SftpSessionResolver implements ISftpSessionResolver {
|
||||
|
||||
private readonly dataRef: SftpDataRef;
|
||||
|
||||
private readonly session: ISftpSession;
|
||||
|
||||
constructor(session: ISftpSession,
|
||||
dataRef: SftpDataRef) {
|
||||
this.session = session;
|
||||
this.dataRef = dataRef;
|
||||
}
|
||||
|
||||
// 接受文件列表响应
|
||||
resolveList(result: string, path: string, list: Array<SftpFile>) {
|
||||
const success = !!Number.parseInt(result);
|
||||
this.dataRef.setLoading(false);
|
||||
if (!success) {
|
||||
Message.error('查询失败');
|
||||
return;
|
||||
}
|
||||
this.dataRef.currentPath = path;
|
||||
this.dataRef.list = list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ISftpSession, ITerminalChannel } from '../types/terminal.type';
|
||||
import type { ISftpSession, ISftpSessionResolver, ITerminalChannel, SftpDataRef } from '../types/terminal.type';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import SftpSessionResolver from './sftp-session-resolver';
|
||||
|
||||
// sftp 会话实现
|
||||
export default class SftpSession implements ISftpSession {
|
||||
@@ -10,6 +11,10 @@ export default class SftpSession implements ISftpSession {
|
||||
|
||||
public connected: boolean;
|
||||
|
||||
public resolver: ISftpSessionResolver;
|
||||
|
||||
private dataRef: SftpDataRef;
|
||||
|
||||
private readonly channel: ITerminalChannel;
|
||||
|
||||
constructor(hostId: number,
|
||||
@@ -19,13 +24,33 @@ export default class SftpSession implements ISftpSession {
|
||||
this.sessionId = sessionId;
|
||||
this.channel = channel;
|
||||
this.connected = false;
|
||||
this.dataRef = undefined as unknown as SftpDataRef;
|
||||
this.resolver = undefined as unknown as ISftpSessionResolver;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
init(dataRef: SftpDataRef): void {
|
||||
this.dataRef = dataRef;
|
||||
// 处理器
|
||||
this.resolver = new SftpSessionResolver(this, dataRef);
|
||||
}
|
||||
|
||||
// 设置已连接
|
||||
connect(): void {
|
||||
this.connected = true;
|
||||
// 加载 home 目录文件数据
|
||||
this.list(undefined);
|
||||
}
|
||||
|
||||
// 查询文件列表
|
||||
list(path: string | undefined) {
|
||||
this.dataRef.setLoading(true);
|
||||
this.channel.send(InputProtocol.SFTP_LIST, {
|
||||
sessionId: this.sessionId,
|
||||
path
|
||||
});
|
||||
};
|
||||
|
||||
// 断开连接
|
||||
disconnect(): void {
|
||||
// 发送关闭消息
|
||||
|
||||
@@ -110,7 +110,7 @@ export default class SshSession implements ISshSession {
|
||||
return;
|
||||
}
|
||||
// 输入
|
||||
this.channel.send(InputProtocol.INPUT, {
|
||||
this.channel.send(InputProtocol.SSH_INPUT, {
|
||||
sessionId: this.sessionId,
|
||||
command: s
|
||||
});
|
||||
@@ -134,7 +134,7 @@ export default class SshSession implements ISshSession {
|
||||
if (!this.connected) {
|
||||
return;
|
||||
}
|
||||
this.channel.send(InputProtocol.RESIZE, {
|
||||
this.channel.send(InputProtocol.SSH_RESIZE, {
|
||||
sessionId: this.sessionId,
|
||||
cols,
|
||||
rows
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload } from '../types/terminal.type';
|
||||
import { ISftpSession, ISshSession, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload } from '../types/terminal.type';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { TerminalStatus } from '../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
@@ -42,8 +42,9 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
// sftp 会话
|
||||
if (success) {
|
||||
// 检查成功发送 connect 命令
|
||||
// TODO
|
||||
|
||||
this.channel.send(InputProtocol.CONNECT, {
|
||||
sessionId,
|
||||
});
|
||||
} else {
|
||||
// 未成功提示错误信息
|
||||
Message.error(msg || '建立 SFTP 失败');
|
||||
@@ -105,10 +106,17 @@ export default class TerminalOutputProcessor implements ITerminalOutputProcessor
|
||||
// console.log('pong');
|
||||
}
|
||||
|
||||
// 处理输出消息
|
||||
processOutput({ sessionId, body }: OutputPayload): void {
|
||||
// 处理 SSH 输出消息
|
||||
processSshOutput({ sessionId, body }: OutputPayload): void {
|
||||
const session = this.sessionManager.getSession<ISshSession>(sessionId);
|
||||
session && session.write(body);
|
||||
}
|
||||
|
||||
// 处理 SFTP 文件列表
|
||||
processSftpList({ sessionId, result, path, body }: OutputPayload): void {
|
||||
// 获取会话
|
||||
const session = this.sessionManager.getSession<ISftpSession>(sessionId);
|
||||
session && session.resolver.resolveList(result, path, JSON.parse(body));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import type { ISftpSession, ITerminalChannel, ITerminalSession, ITerminalSessionManager, TerminalTabItem, XtermDomRef } from '../types/terminal.type';
|
||||
import type {
|
||||
ISftpSession,
|
||||
ITerminalChannel,
|
||||
ITerminalSession,
|
||||
ITerminalSessionManager,
|
||||
SftpDataRef,
|
||||
TerminalTabItem,
|
||||
XtermDomRef
|
||||
} from '../types/terminal.type';
|
||||
import { sleep } from '@/utils';
|
||||
import { InputProtocol } from '../types/terminal.protocol';
|
||||
import { PanelSessionType } from '../types/terminal.const';
|
||||
@@ -54,7 +62,7 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
}
|
||||
|
||||
// 打开 sftp 会话
|
||||
async openSftp(tab: TerminalTabItem): Promise<ISftpSession> {
|
||||
async openSftp(tab: TerminalTabItem, dataRef: SftpDataRef): Promise<ISftpSession> {
|
||||
const sessionId = tab.key;
|
||||
const hostId = tab.hostId as number;
|
||||
// 初始化客户端
|
||||
@@ -65,6 +73,8 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
||||
sessionId,
|
||||
this.channel
|
||||
);
|
||||
// 初始化
|
||||
session.init(dataRef);
|
||||
// 添加会话
|
||||
this.sessions[sessionId] = session;
|
||||
// 发送会话初始化请求
|
||||
|
||||
@@ -55,11 +55,11 @@ export const ExtraSshAuthType = {
|
||||
// 面板会话 tab 类型
|
||||
export const PanelSessionType = {
|
||||
SSH: {
|
||||
type: 'ssh',
|
||||
type: 'SSH',
|
||||
icon: 'icon-desktop'
|
||||
},
|
||||
SFTP: {
|
||||
type: 'sftp',
|
||||
type: 'SFTP',
|
||||
icon: 'icon-folder'
|
||||
},
|
||||
};
|
||||
|
||||
@@ -20,16 +20,21 @@ export const InputProtocol = {
|
||||
type: 'p',
|
||||
template: ['type']
|
||||
},
|
||||
// 修改大小
|
||||
RESIZE: {
|
||||
// SSH 修改大小
|
||||
SSH_RESIZE: {
|
||||
type: 'rs',
|
||||
template: ['type', 'sessionId', 'cols', 'rows']
|
||||
},
|
||||
// 输入
|
||||
INPUT: {
|
||||
// SSH 输入
|
||||
SSH_INPUT: {
|
||||
type: 'i',
|
||||
template: ['type', 'sessionId', 'command']
|
||||
}
|
||||
},
|
||||
// SFTP 文件列表
|
||||
SFTP_LIST: {
|
||||
type: 'ls',
|
||||
template: ['type', 'sessionId', 'path']
|
||||
},
|
||||
};
|
||||
|
||||
// 输出协议
|
||||
@@ -58,10 +63,16 @@ export const OutputProtocol = {
|
||||
template: ['type'],
|
||||
processMethod: 'processPong'
|
||||
},
|
||||
// 输出
|
||||
OUTPUT: {
|
||||
// SSH 输出
|
||||
SSH_OUTPUT: {
|
||||
type: 'o',
|
||||
template: ['type', 'sessionId', 'body'],
|
||||
processMethod: 'processOutput'
|
||||
processMethod: 'processSshOutput'
|
||||
},
|
||||
// SFTP 文件列表
|
||||
SFTP_LIST: {
|
||||
type: 'ls',
|
||||
template: ['type', 'sessionId', 'result', 'path', 'body'],
|
||||
processMethod: 'processSftpList'
|
||||
},
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ export interface ITerminalSessionManager {
|
||||
// 打开 ssh 会话
|
||||
openSsh: (tab: TerminalTabItem, domRef: XtermDomRef) => Promise<ISshSession>;
|
||||
// 打开 sftp 会话
|
||||
openSftp: (tab: TerminalTabItem) => Promise<ISftpSession>;
|
||||
openSftp: (tab: TerminalTabItem, dataRef: SftpDataRef) => Promise<ISftpSession>;
|
||||
// 获取终端会话
|
||||
getSession: <T extends ITerminalSession>(sessionId: string) => T;
|
||||
// 关闭终端会话
|
||||
@@ -176,18 +176,20 @@ export interface ITerminalOutputProcessor {
|
||||
processClose: (payload: OutputPayload) => void;
|
||||
// 处理 pong 消息
|
||||
processPong: (payload: OutputPayload) => void;
|
||||
// 处理输出消息
|
||||
processOutput: (payload: OutputPayload) => void;
|
||||
// 处理 SSH 输出消息
|
||||
processSshOutput: (payload: OutputPayload) => void;
|
||||
// 处理 SFTP 文件列表
|
||||
processSftpList: (payload: OutputPayload) => void;
|
||||
}
|
||||
|
||||
// 终端 dom 元素引用
|
||||
// xterm dom 元素引用
|
||||
export interface XtermDomRef {
|
||||
el: HTMLElement;
|
||||
searchModal: any;
|
||||
editorModal: any;
|
||||
}
|
||||
|
||||
// 终端插件
|
||||
// xterm 插件
|
||||
export interface XtermAddons {
|
||||
fit: FitAddon;
|
||||
webgl: WebglAddon;
|
||||
@@ -197,6 +199,16 @@ export interface XtermAddons {
|
||||
image: ImageAddon;
|
||||
}
|
||||
|
||||
// sftp 数据引用
|
||||
export interface SftpDataRef {
|
||||
// 文件列表
|
||||
list: any;
|
||||
// 当前路径
|
||||
currentPath: any;
|
||||
// 设置加载状态
|
||||
setLoading: (loading: boolean) => void;
|
||||
}
|
||||
|
||||
// 终端会话定义
|
||||
export interface ITerminalSession {
|
||||
hostId: number;
|
||||
@@ -288,5 +300,32 @@ export interface ISshSessionHandler {
|
||||
|
||||
// sftp 会话定义
|
||||
export interface ISftpSession extends ITerminalSession {
|
||||
// 接收器
|
||||
resolver: ISftpSessionResolver;
|
||||
|
||||
// 初始化
|
||||
init: (dataRef: SftpDataRef) => void;
|
||||
// 查询文件列表
|
||||
list: (path: string | undefined) => void;
|
||||
}
|
||||
|
||||
// sftp 会话接收器定义
|
||||
export interface ISftpSessionResolver {
|
||||
// 接受文件列表响应
|
||||
resolveList: (result: string, path: string, list: Array<SftpFile>) => void;
|
||||
}
|
||||
|
||||
// sftp 文件
|
||||
export interface SftpFile {
|
||||
name: string;
|
||||
path: string;
|
||||
suffix: string;
|
||||
size: string;
|
||||
sizeByte: number;
|
||||
attr: string;
|
||||
isDir: boolean;
|
||||
permission: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
modifyTime: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user