♻️ 终端面板开发完成
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
|
||||
// 垂直滚动
|
||||
.arco-scrollbar-track-direction-vertical {
|
||||
width: 6px;
|
||||
width: 10px;
|
||||
|
||||
.arco-scrollbar-thumb-bar {
|
||||
margin: 0;
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
// 水平滚动
|
||||
.arco-scrollbar-track-direction-horizontal {
|
||||
height: 6px;
|
||||
width: 10px;
|
||||
|
||||
.arco-scrollbar-thumb-bar {
|
||||
margin: 0;
|
||||
|
||||
@@ -45,7 +45,7 @@ body[terminal-theme='dark'] {
|
||||
--color-content-text-2: rgba(255, 255, 255, .85);
|
||||
--color-content-text-3: rgba(255, 255, 255, .95);
|
||||
--color-bg-panel-tabs: var(--color-bg-panel);
|
||||
--color-bg-panel-tabs-active: #434343;
|
||||
--color-bg-panel-tabs-active: #383838;
|
||||
--color-bg-panel-icon-1: var(--color-bg-panel-tabs-active);
|
||||
--color-bg-panel-bar: #323538;
|
||||
--color-panel-text-1: var(--color-content-text-1);
|
||||
|
||||
@@ -130,10 +130,10 @@
|
||||
|
||||
.tab-title-wrapper {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
|
||||
.tab-title-icon {
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,7 @@
|
||||
}
|
||||
|
||||
.arco-tabs-tab-title {
|
||||
padding: 11px 18px;
|
||||
padding: 11px 18px 11px 14px;
|
||||
background: var(--color-bg-header-tabs);
|
||||
font-size: 14px;
|
||||
height: var(--header-height);
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<template>
|
||||
<div class="terminal-panel-container">
|
||||
<!-- 终端 tab -->
|
||||
<a-tabs v-model:active-key="panel.active"
|
||||
:editable="true"
|
||||
:auto-switch="true"
|
||||
:show-add-button="true"
|
||||
@add="openHostModal"
|
||||
@tab-click="k => panel.clickTab(k as string)"
|
||||
@delete="k => panel.deleteTab(k as string)">
|
||||
<!-- 右侧按钮 -->
|
||||
<template #extra>
|
||||
<a-button>Action</a-button>
|
||||
</template>
|
||||
<!-- 终端面板 -->
|
||||
<a-tab-pane v-for="tab in panel.items"
|
||||
:key="tab.key">
|
||||
<!-- 标题 -->
|
||||
@@ -20,6 +28,9 @@
|
||||
<terminal-view :tab="tab" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<!-- 新建连接模态框 -->
|
||||
<host-list-modal ref="hostModal"
|
||||
@choose="item => openTerminal(item, index)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -31,13 +42,27 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { ITerminalTabManager } from '../../types/terminal.type';
|
||||
import TerminalView from '@/views/host/terminal/components/xterm/terminal-view.vue';
|
||||
import TerminalView from '../xterm/terminal-view.vue';
|
||||
import HostListModal from '../new-connection/host-list-modal.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
|
||||
const props = defineProps<{
|
||||
panel: ITerminalTabManager
|
||||
index: number,
|
||||
panel: ITerminalTabManager,
|
||||
}>();
|
||||
|
||||
const { openTerminal } = useTerminalStore();
|
||||
|
||||
const hostModal = ref();
|
||||
|
||||
// 打开主机模态框
|
||||
const openHostModal = () => {
|
||||
hostModal.value.open();
|
||||
};
|
||||
|
||||
// FIXME 全部关闭展示新增
|
||||
onMounted(openHostModal);
|
||||
|
||||
</script>
|
||||
|
||||
@@ -49,10 +74,10 @@
|
||||
|
||||
.tab-title-wrapper {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
|
||||
.tab-title-icon {
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
@@ -89,6 +114,14 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-add-btn {
|
||||
padding: 0 14px;
|
||||
|
||||
.arco-icon-hover {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&-button .arco-icon-hover:hover {
|
||||
color: var(--color-panel-text-2);
|
||||
|
||||
@@ -96,6 +129,7 @@
|
||||
background: var(--color-bg-panel-icon-1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
:deep(.arco-tabs-nav-type-line .arco-tabs-tab:hover .arco-tabs-tab-title::before) {
|
||||
@@ -132,7 +166,7 @@
|
||||
}
|
||||
|
||||
.arco-tabs-tab-title {
|
||||
padding: 11px 18px;
|
||||
padding: 11px 18px 11px 14px;
|
||||
background: var(--color-bg-panel-tabs);
|
||||
font-size: 14px;
|
||||
height: var(--panel-nav-height);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<!-- 面板 -->
|
||||
<terminal-panel v-for="panelIndex in panelManager.panels.length"
|
||||
:key="panelIndex"
|
||||
:index="panelIndex - 1"
|
||||
:panel="panelManager.panels[panelIndex - 1]" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible"
|
||||
title-align="start"
|
||||
:top="128"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:unmount-on-close="true"
|
||||
:body-style="{ padding: 0 }"
|
||||
:hide-title="true"
|
||||
:footer="false">
|
||||
<div class="host-list-wrapper">
|
||||
<!-- 主机搜索框 -->
|
||||
<div class="host-filter-wrapper">
|
||||
<span class="host-label">主机列表</span>
|
||||
<a-input v-model="filterValue"
|
||||
class="host-filter"
|
||||
placeholder="别名/名称/编码/IP" />
|
||||
</div>
|
||||
<!-- 主机列表 -->
|
||||
<a-list class="host-list"
|
||||
max-height="50vh"
|
||||
:hoverable="true"
|
||||
:split="false"
|
||||
:bordered="false"
|
||||
:data="hostList">
|
||||
<template #item="{ item }">
|
||||
<a-list-item class="host-item-wrapper" @click="clickHost(item)">
|
||||
<div class="host-item">
|
||||
<!-- 图标 -->
|
||||
<span class="host-item-icon">
|
||||
<icon-desktop />
|
||||
</span>
|
||||
<!-- 名称 -->
|
||||
<span class="host-item-name">
|
||||
{{ `${item.alias || item.name} (${item.address})` }}
|
||||
</span>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'hostListModal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { HostQueryResponse } from '@/api/asset/host';
|
||||
|
||||
const emits = defineEmits(['choose']);
|
||||
|
||||
const { hosts } = useTerminalStore();
|
||||
const { visible, setVisible } = useVisible();
|
||||
|
||||
const filterValue = ref('');
|
||||
const hostList = computed(() => {
|
||||
const filterVal = filterValue.value.toLowerCase();
|
||||
let list;
|
||||
// 过滤
|
||||
if (filterVal) {
|
||||
list = hosts.hostList.filter(item => {
|
||||
return (item.name as string)?.toLowerCase().indexOf(filterVal) > -1
|
||||
|| (item.code as string)?.toLowerCase().indexOf(filterVal) > -1
|
||||
|| (item.alias as string)?.toLowerCase().indexOf(filterVal) > -1
|
||||
|| (item.address as string)?.toLowerCase().indexOf(filterVal) > -1;
|
||||
});
|
||||
} else {
|
||||
list = hosts.hostList;
|
||||
}
|
||||
// 排序
|
||||
return list.sort((o1, o2) => {
|
||||
if (o1.favorite || o2.favorite) {
|
||||
if (o1.favorite && o2.favorite) {
|
||||
return o2.id < o1.id ? 1 : -1;
|
||||
}
|
||||
return o2.favorite ? 1 : -1;
|
||||
} else {
|
||||
return o2.id < o1.id ? 1 : -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 打开配置
|
||||
const open = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 打开终端
|
||||
const clickHost = (item: HostQueryResponse) => {
|
||||
emits('choose', item);
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.host-list-wrapper {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.host-filter-wrapper {
|
||||
height: 32px;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.host-label {
|
||||
margin-left: 8px;
|
||||
user-select: none;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.host-filter {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
.host-item-wrapper {
|
||||
padding: 12px 16px !important;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-fill-2);
|
||||
}
|
||||
}
|
||||
|
||||
.host-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,30 +1,30 @@
|
||||
<template>
|
||||
<div class="hosts-list-container">
|
||||
<a-list size="large"
|
||||
max-height="100%"
|
||||
:hoverable="true"
|
||||
:data="hostList">
|
||||
<!-- 空数据 -->
|
||||
<template #empty>
|
||||
<a-empty>
|
||||
<template #image>
|
||||
<icon-desktop />
|
||||
</template>
|
||||
{{ emptyValue }}
|
||||
</a-empty>
|
||||
</template>
|
||||
<!-- 数据 -->
|
||||
<template #item="{ item }">
|
||||
<a-list-item class="host-item-wrapper">
|
||||
<div class="host-item">
|
||||
<!-- 左侧图标-名称 -->
|
||||
<div class="flex-center host-item-left">
|
||||
<!-- 图标 -->
|
||||
<span class="host-item-left-icon" @click="openTerminal(item)">
|
||||
<a-list class="hosts-list-container"
|
||||
size="large"
|
||||
max-height="100%"
|
||||
:hoverable="true"
|
||||
:data="hostList">
|
||||
<!-- 空数据 -->
|
||||
<template #empty>
|
||||
<a-empty>
|
||||
<template #image>
|
||||
<icon-desktop />
|
||||
</template>
|
||||
{{ emptyValue }}
|
||||
</a-empty>
|
||||
</template>
|
||||
<!-- 数据 -->
|
||||
<template #item="{ item }">
|
||||
<a-list-item class="host-item-wrapper">
|
||||
<div class="host-item">
|
||||
<!-- 左侧图标-名称 -->
|
||||
<div class="flex-center host-item-left">
|
||||
<!-- 图标 -->
|
||||
<span class="host-item-left-icon" @click="openTerminal(item)">
|
||||
<icon-desktop />
|
||||
</span>
|
||||
<!-- 名称 -->
|
||||
<span class="host-item-left-name">
|
||||
<!-- 名称 -->
|
||||
<span class="host-item-left-name">
|
||||
<!-- 名称文本 -->
|
||||
<template v-if="!item.editable">
|
||||
<!-- 文本 -->
|
||||
@@ -53,7 +53,7 @@
|
||||
@click="clickEditAlias(item)" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<!-- 名称输入框 -->
|
||||
<!-- 名称输入框 -->
|
||||
<template v-else>
|
||||
<a-input v-model="item.alias"
|
||||
ref="aliasNameInput"
|
||||
@@ -77,87 +77,86 @@
|
||||
</a-input>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<!-- 中间ip -->
|
||||
<div class="flex-center host-item-center">
|
||||
<!-- ip -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
:content="item.address">
|
||||
<span class="host-item-text host-item-center-address">
|
||||
{{ item.address }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 右侧tag-操作 -->
|
||||
<div class="flex-center host-item-right">
|
||||
<!-- tags -->
|
||||
<div class="host-item-right-tags">
|
||||
<template v-if="item.tags?.length">
|
||||
<a-tag v-for="(tag, i) in item.tags"
|
||||
class="host-item-text"
|
||||
:key="tag.id"
|
||||
:style="{
|
||||
maxWidth: `calc(${100 / item.tags.length}% - ${i !== item.tags.length - 1 ? '8px' : '0px'})`,
|
||||
marginRight: `${i !== item.tags.length - 1 ? '8px' : '0'}`,
|
||||
}"
|
||||
:color="dataColor(tag.name, tagColor)">
|
||||
{{ tag.name }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 中间ip -->
|
||||
<div class="flex-center host-item-center">
|
||||
<!-- ip -->
|
||||
<!-- 操作 -->
|
||||
<div class="host-item-right-actions">
|
||||
<!-- 连接主机 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
:content="item.address">
|
||||
<span class="host-item-text host-item-center-address">
|
||||
{{ item.address }}
|
||||
</span>
|
||||
content="连接主机">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="openTerminal(item)">
|
||||
<icon-thunderbolt />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<!-- 连接设置 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="连接设置">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="openSetting(item)">
|
||||
<icon-settings />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<!-- 收藏 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="收藏">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="setFavorite(item)">
|
||||
<icon-star-fill class="favorite" v-if="item.favorite" />
|
||||
<icon-star v-else />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<!-- 右侧tag-操作 -->
|
||||
<div class="flex-center host-item-right">
|
||||
<!-- tags -->
|
||||
<div class="host-item-right-tags">
|
||||
<template v-if="item.tags?.length">
|
||||
<a-tag v-for="(tag, i) in item.tags"
|
||||
class="host-item-text"
|
||||
:key="tag.id"
|
||||
:style="{
|
||||
maxWidth: `calc(${100 / item.tags.length}% - ${i !== item.tags.length - 1 ? '8px' : '0px'})`,
|
||||
marginRight: `${i !== item.tags.length - 1 ? '8px' : '0'}`,
|
||||
}"
|
||||
:color="dataColor(tag.name, tagColor)">
|
||||
{{ tag.name }}
|
||||
</a-tag>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 操作 -->
|
||||
<div class="host-item-right-actions">
|
||||
<!-- 连接主机 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="连接主机">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="openTerminal(item)">
|
||||
<icon-thunderbolt />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<!-- 连接设置 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="连接设置">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="openSetting(item)">
|
||||
<icon-settings />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
<!-- 收藏 -->
|
||||
<a-tooltip position="top"
|
||||
:mini="true"
|
||||
:auto-fix-position="false"
|
||||
content-class="terminal-tooltip-content"
|
||||
arrow-class="terminal-tooltip-content"
|
||||
content="收藏">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="setFavorite(item)">
|
||||
<icon-star-fill class="favorite" v-if="item.favorite" />
|
||||
<icon-star v-else />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -234,24 +233,32 @@
|
||||
<style lang="less" scoped>
|
||||
@host-item-height: 56px;
|
||||
|
||||
:deep(.arco-list-bordered) {
|
||||
border: 1px solid var(--color-fill-3);
|
||||
.hosts-list-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.arco-empty {
|
||||
padding: 16px 0;
|
||||
flex-direction: column;
|
||||
:deep(.arco-scrollbar) {
|
||||
height: 100%;
|
||||
|
||||
.arco-empty-image {
|
||||
margin-bottom: 0;
|
||||
.arco-list-bordered {
|
||||
border: 1px solid var(--color-fill-3);
|
||||
|
||||
.arco-empty {
|
||||
padding: 16px 0;
|
||||
flex-direction: column;
|
||||
|
||||
.arco-empty-image {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arco-list-item:not(:last-child) {
|
||||
border-bottom: 1px solid var(--color-fill-3);
|
||||
}
|
||||
.arco-list-item:not(:last-child) {
|
||||
border-bottom: 1px solid var(--color-fill-3);
|
||||
}
|
||||
|
||||
.arco-list-item:hover {
|
||||
background-color: var(--color-fill-2);
|
||||
.arco-list-item:hover {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ITerminalTabManager, TerminalTabItem } from '../types/terminal.type';
|
||||
|
||||
// 终端 tab 管理器实现
|
||||
export default class TerminalTabManagerm<T extends TerminalTabItem = TerminalTabItem> implements ITerminalTabManager<T> {
|
||||
export default class TerminalTabManager<T extends TerminalTabItem = TerminalTabItem> implements ITerminalTabManager<T> {
|
||||
|
||||
public active: string;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user