fix: 轻量版终端固定omc本机
This commit is contained in:
339
src/views/tool/terminal-more/index.vue
Normal file
339
src/views/tool/terminal-more/index.vue
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { Modal } from 'ant-design-vue/es';
|
||||||
|
import { useFullscreen } from '@vueuse/core';
|
||||||
|
import { defineAsyncComponent, reactive, ref } from 'vue';
|
||||||
|
import { parseDuration } from '@/utils/date-utils';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const TerminalSSH = defineAsyncComponent(
|
||||||
|
() => import('@/components/TerminalSSH/index.vue')
|
||||||
|
);
|
||||||
|
const TerminalTelnet = defineAsyncComponent(
|
||||||
|
() => import('@/components/TerminalTelnet/index.vue')
|
||||||
|
);
|
||||||
|
const TerminalRedis = defineAsyncComponent(
|
||||||
|
() => import('@/components/TerminalRedis/index.vue')
|
||||||
|
);
|
||||||
|
const HostList = defineAsyncComponent(
|
||||||
|
() => import('./components/hostList.vue')
|
||||||
|
);
|
||||||
|
|
||||||
|
// 全屏
|
||||||
|
const terminalCard = ref<HTMLElement | null>(null);
|
||||||
|
const { isFullscreen, toggle } = useFullscreen(terminalCard);
|
||||||
|
|
||||||
|
/**主机对象信息状态类型 */
|
||||||
|
type HostStateType = {
|
||||||
|
/**显示主机列表 */
|
||||||
|
show: boolean;
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**查询参数 */
|
||||||
|
params: {
|
||||||
|
pageNum: number;
|
||||||
|
pageSize: number;
|
||||||
|
};
|
||||||
|
/**数据总数 */
|
||||||
|
total: number;
|
||||||
|
data: Record<string, any>[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**主机对象信息状态 */
|
||||||
|
const hostState: HostStateType = reactive({
|
||||||
|
show: false,
|
||||||
|
loading: false,
|
||||||
|
params: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
total: 0,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**连接主机 */
|
||||||
|
function fnConnectHost(data: Record<string, any>) {
|
||||||
|
const id = `${Date.now()}`;
|
||||||
|
tabState.panes.push({
|
||||||
|
id,
|
||||||
|
status: false,
|
||||||
|
host: data,
|
||||||
|
});
|
||||||
|
tabState.activeKey = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**标签对象信息状态类型 */
|
||||||
|
type TabStateType = {
|
||||||
|
/**激活选中 */
|
||||||
|
activeKey: string;
|
||||||
|
/**页签数据 */
|
||||||
|
panes: {
|
||||||
|
id: string;
|
||||||
|
status: boolean;
|
||||||
|
host: Record<string, any>;
|
||||||
|
connectStamp?: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**标签对象信息状态 */
|
||||||
|
const tabState: TabStateType = reactive({
|
||||||
|
activeKey: '0',
|
||||||
|
panes: [
|
||||||
|
{
|
||||||
|
id: '0',
|
||||||
|
host: {
|
||||||
|
id: 0,
|
||||||
|
title: t('views.tool.terminal.start'),
|
||||||
|
type: '0',
|
||||||
|
},
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端连接状态
|
||||||
|
* @param data 主机连接结果
|
||||||
|
*/
|
||||||
|
function fnTerminalConnect(data: Record<string, any>) {
|
||||||
|
const { id, timeStamp } = data;
|
||||||
|
const seconds = timeStamp / 1000;
|
||||||
|
// 获取当前项下标
|
||||||
|
const tab = tabState.panes.find(item => item.id === id);
|
||||||
|
if (tab) {
|
||||||
|
tab.status = true;
|
||||||
|
tab.connectStamp = parseDuration(seconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终端关闭状态
|
||||||
|
* @param data 主机连接结果
|
||||||
|
*/
|
||||||
|
function fnTerminalClose(data: Record<string, any>) {
|
||||||
|
const { id } = data;
|
||||||
|
// 获取当前项下标
|
||||||
|
const tab = tabState.panes.find(item => item.id === id);
|
||||||
|
if (tab) {
|
||||||
|
tab.status = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签更多菜单项
|
||||||
|
* @param key 菜单key
|
||||||
|
*/
|
||||||
|
function fnTabMenu(key: string | number) {
|
||||||
|
// 刷新当前
|
||||||
|
if (key === 'reload') {
|
||||||
|
const tabIndex = tabState.panes.findIndex(
|
||||||
|
item => item.id === tabState.activeKey
|
||||||
|
);
|
||||||
|
if (tabIndex) {
|
||||||
|
const tab = tabState.panes[tabIndex];
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.tool.terminal.reloadTip', {
|
||||||
|
num: `${tab.host.hostType} - ${tab.host.title}`,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
tabState.panes.splice(tabIndex, 1);
|
||||||
|
tab.host && fnConnectHost(tab.host);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关闭当前
|
||||||
|
if (key === 'current') {
|
||||||
|
fnTabClose(tabState.activeKey);
|
||||||
|
}
|
||||||
|
// 关闭其他
|
||||||
|
if (key === 'other') {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.tool.terminal.otherTip'),
|
||||||
|
onOk() {
|
||||||
|
hostState.show = false;
|
||||||
|
tabState.panes = tabState.panes.filter(
|
||||||
|
tab => tab.id === '0' || tab.id === tabState.activeKey
|
||||||
|
);
|
||||||
|
tabState.activeKey = tabState.activeKey;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 关闭全部
|
||||||
|
if (key === 'all') {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.tool.terminal.allTip'),
|
||||||
|
onOk() {
|
||||||
|
hostState.show = false;
|
||||||
|
tabState.panes.splice(1);
|
||||||
|
tabState.activeKey = '0';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航标签关闭
|
||||||
|
* @param id 标签的key
|
||||||
|
*/
|
||||||
|
function fnTabClose(id: string) {
|
||||||
|
if (isFullscreen.value) toggle();
|
||||||
|
|
||||||
|
// 获取当前项下标
|
||||||
|
const tabIndex = tabState.panes.findIndex(tab => tab.id === id);
|
||||||
|
if (tabIndex === -1) return;
|
||||||
|
const item = tabState.panes[tabIndex];
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.tool.terminal.closeTip', {
|
||||||
|
num: `${item.host.hostType.toUpperCase()} - ${item.host.title}`,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
tabState.panes.splice(tabIndex, 1);
|
||||||
|
// 激活前一项标签
|
||||||
|
tabState.activeKey = tabState.panes[tabIndex - 1].id;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
:body-style="{ padding: '12px' }"
|
||||||
|
ref="terminalCard"
|
||||||
|
>
|
||||||
|
<a-tabs
|
||||||
|
class="terminal-tabs"
|
||||||
|
hide-add
|
||||||
|
size="small"
|
||||||
|
tab-position="top"
|
||||||
|
type="editable-card"
|
||||||
|
:tab-bar-gutter="8"
|
||||||
|
:tab-bar-style="{ margin: '0' }"
|
||||||
|
v-model:activeKey="tabState.activeKey"
|
||||||
|
@edit="(id:any) => fnTabClose(id)"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
v-for="pane in tabState.panes"
|
||||||
|
:key="pane.id"
|
||||||
|
:closable="pane.id !== '0'"
|
||||||
|
>
|
||||||
|
<template #tab>
|
||||||
|
<a-badge
|
||||||
|
:status="pane.status ? 'success' : 'error'"
|
||||||
|
:text="pane.host.title"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="pane-box">
|
||||||
|
<!-- 非开始页的ssh主机 -->
|
||||||
|
<TerminalSSH
|
||||||
|
v-if="pane.id !== '0' && pane.host.hostType === 'ssh'"
|
||||||
|
:id="pane.id"
|
||||||
|
:hostId="pane.host.id"
|
||||||
|
@connect="fnTerminalConnect"
|
||||||
|
@close="fnTerminalClose"
|
||||||
|
>
|
||||||
|
</TerminalSSH>
|
||||||
|
|
||||||
|
<!-- 非开始页的telnet主机 -->
|
||||||
|
<TerminalTelnet
|
||||||
|
v-if="pane.id !== '0' && pane.host.hostType === 'telnet'"
|
||||||
|
:id="pane.id"
|
||||||
|
:hostId="pane.host.id"
|
||||||
|
init-cmd="help"
|
||||||
|
:disable="true"
|
||||||
|
@connect="fnTerminalConnect"
|
||||||
|
@close="fnTerminalClose"
|
||||||
|
>
|
||||||
|
</TerminalTelnet>
|
||||||
|
|
||||||
|
<!-- 非开始页的redis主机 -->
|
||||||
|
<TerminalRedis
|
||||||
|
v-if="pane.id !== '0' && pane.host.hostType === 'redis'"
|
||||||
|
:id="pane.id"
|
||||||
|
:hostId="pane.host.id"
|
||||||
|
@connect="fnTerminalConnect"
|
||||||
|
@close="fnTerminalClose"
|
||||||
|
>
|
||||||
|
</TerminalRedis>
|
||||||
|
|
||||||
|
<!-- 开始页 -->
|
||||||
|
<div v-if="pane.id === '0'">
|
||||||
|
<!-- 主机列表 -->
|
||||||
|
<HostList
|
||||||
|
v-show="tabState.activeKey === '0'"
|
||||||
|
@modal="() => (isFullscreen ? toggle() : null)"
|
||||||
|
@link="fnConnectHost"
|
||||||
|
></HostList>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<template #rightExtra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{ t('loayouts.rightContent.fullscreen') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
size="small"
|
||||||
|
style="color: inherit"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||||
|
<FullscreenOutlined v-else />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<!-- 非开始页的更多操作 -->
|
||||||
|
<div v-show="tabState.activeKey !== '0'">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.tool.terminal.more') }}
|
||||||
|
</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="ghost" shape="circle" size="small">
|
||||||
|
<template #icon><EllipsisOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu @click="({ key }:any) => fnTabMenu(key)">
|
||||||
|
<a-menu-item key="reload">
|
||||||
|
{{ t('views.tool.terminal.reload') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="current">
|
||||||
|
{{ t('views.tool.terminal.current') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="other">
|
||||||
|
{{ t('views.tool.terminal.other') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="all">
|
||||||
|
{{ t('views.tool.terminal.all') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-tabs>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.pane-box {
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,203 +1,76 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { Modal } from 'ant-design-vue/es';
|
import { listNeHost } from '@/api/ne/neHost';
|
||||||
import { useFullscreen } from '@vueuse/core';
|
import { defineAsyncComponent, onMounted, reactive } from 'vue';
|
||||||
import { defineAsyncComponent, reactive, ref } from 'vue';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { parseDuration } from '@/utils/date-utils';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
|
||||||
const { t } = useI18n();
|
|
||||||
const TerminalSSH = defineAsyncComponent(
|
const TerminalSSH = defineAsyncComponent(
|
||||||
() => import('@/components/TerminalSSH/index.vue')
|
() => import('@/components/TerminalSSH/index.vue')
|
||||||
);
|
);
|
||||||
const TerminalTelnet = defineAsyncComponent(
|
|
||||||
() => import('@/components/TerminalTelnet/index.vue')
|
|
||||||
);
|
|
||||||
const TerminalRedis = defineAsyncComponent(
|
|
||||||
() => import('@/components/TerminalRedis/index.vue')
|
|
||||||
);
|
|
||||||
const HostList = defineAsyncComponent(
|
|
||||||
() => import('./components/hostList.vue')
|
|
||||||
);
|
|
||||||
|
|
||||||
// 全屏
|
|
||||||
const terminalCard = ref<HTMLElement | null>(null);
|
|
||||||
const { isFullscreen, toggle } = useFullscreen(terminalCard);
|
|
||||||
|
|
||||||
/**主机对象信息状态类型 */
|
/**主机对象信息状态类型 */
|
||||||
type HostStateType = {
|
type HostStateType = {
|
||||||
/**显示主机列表 */
|
|
||||||
show: boolean;
|
|
||||||
/**加载等待 */
|
/**加载等待 */
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
params: {
|
params: {
|
||||||
pageNum: number;
|
/**主机类型 */
|
||||||
pageSize: number;
|
hostType: 'ssh';
|
||||||
|
/**分组 */
|
||||||
|
groupId: '1';
|
||||||
|
/**名称 */
|
||||||
|
title: 'OMC';
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1;
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10;
|
||||||
|
sortField: 'createTime';
|
||||||
|
sortOrder: 'desc';
|
||||||
};
|
};
|
||||||
/**数据总数 */
|
/**OMC主机 */
|
||||||
total: number;
|
host: Record<string, any>;
|
||||||
data: Record<string, any>[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**主机对象信息状态 */
|
/**主机对象信息状态 */
|
||||||
const hostState: HostStateType = reactive({
|
const hostState: HostStateType = reactive({
|
||||||
show: false,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
params: {
|
params: {
|
||||||
|
/**主机类型 */
|
||||||
|
hostType: 'ssh',
|
||||||
|
/**分组 */
|
||||||
|
groupId: '1',
|
||||||
|
/**名称 */
|
||||||
|
title: 'OMC',
|
||||||
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
sortField: 'createTime',
|
||||||
|
sortOrder: 'desc',
|
||||||
},
|
},
|
||||||
total: 0,
|
host: {},
|
||||||
data: [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**连接主机 */
|
/**查询信息列表, pageNum初始页数 */
|
||||||
function fnConnectHost(data: Record<string, any>) {
|
function fnGetList() {
|
||||||
const id = `${Date.now()}`;
|
hostState.loading = true;
|
||||||
tabState.panes.push({
|
listNeHost(hostState.params)
|
||||||
id,
|
.then(res => {
|
||||||
status: false,
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
host: data,
|
const { total, rows } = res.data;
|
||||||
});
|
if (total > 0) {
|
||||||
tabState.activeKey = id;
|
hostState.host = rows[0];
|
||||||
}
|
|
||||||
|
|
||||||
/**标签对象信息状态类型 */
|
|
||||||
type TabStateType = {
|
|
||||||
/**激活选中 */
|
|
||||||
activeKey: string;
|
|
||||||
/**页签数据 */
|
|
||||||
panes: {
|
|
||||||
id: string;
|
|
||||||
status: boolean;
|
|
||||||
host: Record<string, any>;
|
|
||||||
connectStamp?: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**标签对象信息状态 */
|
|
||||||
const tabState: TabStateType = reactive({
|
|
||||||
activeKey: '0',
|
|
||||||
panes: [
|
|
||||||
{
|
|
||||||
id: '0',
|
|
||||||
host: {
|
|
||||||
id: 0,
|
|
||||||
title: t('views.tool.terminal.start'),
|
|
||||||
type: '0',
|
|
||||||
},
|
|
||||||
status: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 终端连接状态
|
|
||||||
* @param data 主机连接结果
|
|
||||||
*/
|
|
||||||
function fnTerminalConnect(data: Record<string, any>) {
|
|
||||||
const { id, timeStamp } = data;
|
|
||||||
const seconds = timeStamp / 1000;
|
|
||||||
// 获取当前项下标
|
|
||||||
const tab = tabState.panes.find(item => item.id === id);
|
|
||||||
if (tab) {
|
|
||||||
tab.status = true;
|
|
||||||
tab.connectStamp = parseDuration(seconds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
/**
|
.finally(() => {
|
||||||
* 终端关闭状态
|
hostState.loading = false;
|
||||||
* @param data 主机连接结果
|
|
||||||
*/
|
|
||||||
function fnTerminalClose(data: Record<string, any>) {
|
|
||||||
const { id } = data;
|
|
||||||
// 获取当前项下标
|
|
||||||
const tab = tabState.panes.find(item => item.id === id);
|
|
||||||
if (tab) {
|
|
||||||
tab.status = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标签更多菜单项
|
|
||||||
* @param key 菜单key
|
|
||||||
*/
|
|
||||||
function fnTabMenu(key: string | number) {
|
|
||||||
// 刷新当前
|
|
||||||
if (key === 'reload') {
|
|
||||||
const tabIndex = tabState.panes.findIndex(
|
|
||||||
item => item.id === tabState.activeKey
|
|
||||||
);
|
|
||||||
if (tabIndex) {
|
|
||||||
const tab = tabState.panes[tabIndex];
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.tool.terminal.reloadTip', {
|
|
||||||
num: `${tab.host.hostType} - ${tab.host.title}`,
|
|
||||||
}),
|
|
||||||
onOk() {
|
|
||||||
tabState.panes.splice(tabIndex, 1);
|
|
||||||
tab.host && fnConnectHost(tab.host);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 关闭当前
|
|
||||||
if (key === 'current') {
|
|
||||||
fnTabClose(tabState.activeKey);
|
|
||||||
}
|
|
||||||
// 关闭其他
|
|
||||||
if (key === 'other') {
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.tool.terminal.otherTip'),
|
|
||||||
onOk() {
|
|
||||||
hostState.show = false;
|
|
||||||
tabState.panes = tabState.panes.filter(
|
|
||||||
tab => tab.id === '0' || tab.id === tabState.activeKey
|
|
||||||
);
|
|
||||||
tabState.activeKey = tabState.activeKey;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 关闭全部
|
|
||||||
if (key === 'all') {
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.tool.terminal.allTip'),
|
|
||||||
onOk() {
|
|
||||||
hostState.show = false;
|
|
||||||
tabState.panes.splice(1);
|
|
||||||
tabState.activeKey = '0';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
onMounted(() => {
|
||||||
* 导航标签关闭
|
// 获取列表数据
|
||||||
* @param id 标签的key
|
fnGetList();
|
||||||
*/
|
|
||||||
function fnTabClose(id: string) {
|
|
||||||
if (isFullscreen.value) toggle();
|
|
||||||
|
|
||||||
// 获取当前项下标
|
|
||||||
const tabIndex = tabState.panes.findIndex(tab => tab.id === id);
|
|
||||||
if (tabIndex === -1) return;
|
|
||||||
const item = tabState.panes[tabIndex];
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.tool.terminal.closeTip', {
|
|
||||||
num: `${item.host.hostType.toUpperCase()} - ${item.host.title}`,
|
|
||||||
}),
|
|
||||||
onOk() {
|
|
||||||
tabState.panes.splice(tabIndex, 1);
|
|
||||||
// 激活前一项标签
|
|
||||||
tabState.activeKey = tabState.panes[tabIndex - 1].id;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -205,135 +78,13 @@ function fnTabClose(id: string) {
|
|||||||
<a-card
|
<a-card
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
size="small"
|
size="small"
|
||||||
:body-style="{ padding: '12px' }"
|
:body-style="{ padding: '12px', height: 'calc(100vh - 200px)' }"
|
||||||
ref="terminalCard"
|
:loading="hostState.loading"
|
||||||
>
|
|
||||||
<a-tabs
|
|
||||||
class="terminal-tabs"
|
|
||||||
hide-add
|
|
||||||
size="small"
|
|
||||||
tab-position="top"
|
|
||||||
type="editable-card"
|
|
||||||
:tab-bar-gutter="8"
|
|
||||||
:tab-bar-style="{ margin: '0' }"
|
|
||||||
v-model:activeKey="tabState.activeKey"
|
|
||||||
@edit="(id:any) => fnTabClose(id)"
|
|
||||||
>
|
|
||||||
<a-tab-pane
|
|
||||||
v-for="pane in tabState.panes"
|
|
||||||
:key="pane.id"
|
|
||||||
:closable="pane.id !== '0'"
|
|
||||||
>
|
|
||||||
<template #tab>
|
|
||||||
<a-badge
|
|
||||||
:status="pane.status ? 'success' : 'error'"
|
|
||||||
:text="pane.host.title"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="pane-box">
|
|
||||||
<!-- 非开始页的ssh主机 -->
|
|
||||||
<TerminalSSH
|
|
||||||
v-if="pane.id !== '0' && pane.host.hostType === 'ssh'"
|
|
||||||
:id="pane.id"
|
|
||||||
:hostId="pane.host.id"
|
|
||||||
@connect="fnTerminalConnect"
|
|
||||||
@close="fnTerminalClose"
|
|
||||||
>
|
>
|
||||||
|
<TerminalSSH :id="hostState.host.title" :hostId="hostState.host.id">
|
||||||
</TerminalSSH>
|
</TerminalSSH>
|
||||||
|
|
||||||
<!-- 非开始页的telnet主机 -->
|
|
||||||
<TerminalTelnet
|
|
||||||
v-if="pane.id !== '0' && pane.host.hostType === 'telnet'"
|
|
||||||
:id="pane.id"
|
|
||||||
:hostId="pane.host.id"
|
|
||||||
init-cmd="help"
|
|
||||||
:disable="true"
|
|
||||||
@connect="fnTerminalConnect"
|
|
||||||
@close="fnTerminalClose"
|
|
||||||
>
|
|
||||||
</TerminalTelnet>
|
|
||||||
|
|
||||||
<!-- 非开始页的redis主机 -->
|
|
||||||
<TerminalRedis
|
|
||||||
v-if="pane.id !== '0' && pane.host.hostType === 'redis'"
|
|
||||||
:id="pane.id"
|
|
||||||
:hostId="pane.host.id"
|
|
||||||
@connect="fnTerminalConnect"
|
|
||||||
@close="fnTerminalClose"
|
|
||||||
>
|
|
||||||
</TerminalRedis>
|
|
||||||
|
|
||||||
<!-- 开始页 -->
|
|
||||||
<div v-if="pane.id === '0'">
|
|
||||||
<!-- 主机列表 -->
|
|
||||||
<HostList
|
|
||||||
v-show="tabState.activeKey === '0'"
|
|
||||||
@modal="() => (isFullscreen ? toggle() : null)"
|
|
||||||
@link="fnConnectHost"
|
|
||||||
></HostList>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<template #rightExtra>
|
|
||||||
<a-space :size="8" align="center">
|
|
||||||
<a-tooltip placement="topRight">
|
|
||||||
<template #title>
|
|
||||||
{{ t('loayouts.rightContent.fullscreen') }}
|
|
||||||
</template>
|
|
||||||
<a-button
|
|
||||||
type="default"
|
|
||||||
shape="circle"
|
|
||||||
size="small"
|
|
||||||
style="color: inherit"
|
|
||||||
@click="toggle"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<FullscreenExitOutlined v-if="isFullscreen" />
|
|
||||||
<FullscreenOutlined v-else />
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<!-- 非开始页的更多操作 -->
|
|
||||||
<div v-show="tabState.activeKey !== '0'">
|
|
||||||
<a-tooltip placement="topRight">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.tool.terminal.more') }}
|
|
||||||
</template>
|
|
||||||
<a-dropdown trigger="click" placement="bottomRight">
|
|
||||||
<a-button type="ghost" shape="circle" size="small">
|
|
||||||
<template #icon><EllipsisOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
<template #overlay>
|
|
||||||
<a-menu @click="({ key }:any) => fnTabMenu(key)">
|
|
||||||
<a-menu-item key="reload">
|
|
||||||
{{ t('views.tool.terminal.reload') }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="current">
|
|
||||||
{{ t('views.tool.terminal.current') }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="other">
|
|
||||||
{{ t('views.tool.terminal.other') }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="all">
|
|
||||||
{{ t('views.tool.terminal.all') }}
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</a-tooltip>
|
|
||||||
</div>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</a-tabs>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped></style>
|
||||||
.pane-box {
|
|
||||||
height: calc(100vh - 200px);
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user