feat: 主机分组显示
This commit is contained in:
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
// 垂直滚动
|
// 垂直滚动
|
||||||
.arco-scrollbar-track-direction-vertical {
|
.arco-scrollbar-track-direction-vertical {
|
||||||
width: 9px;
|
width: 6px;
|
||||||
|
|
||||||
.arco-scrollbar-thumb-bar {
|
.arco-scrollbar-thumb-bar {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
// 水平滚动
|
// 水平滚动
|
||||||
.arco-scrollbar-track-direction-horizontal {
|
.arco-scrollbar-track-direction-horizontal {
|
||||||
height: 9px;
|
height: 6px;
|
||||||
|
|
||||||
.arco-scrollbar-thumb-bar {
|
.arco-scrollbar-thumb-bar {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -81,31 +81,42 @@
|
|||||||
|
|
||||||
// 块状树
|
// 块状树
|
||||||
.block-tree {
|
.block-tree {
|
||||||
|
@tree-node-hover-color: var(--color-fill-1);
|
||||||
|
@tree-node-selected-color: var(--color-fill-2);
|
||||||
|
@tree-node-selected-hover-color: var(--color-fill-2);
|
||||||
|
|
||||||
.arco-tree-node {
|
.arco-tree-node {
|
||||||
cursor: unset;
|
cursor: unset;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
|
||||||
&-switcher {
|
&-switcher {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-fill-1);
|
background-color: @tree-node-hover-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-selected {
|
&-selected {
|
||||||
background-color: var(--color-fill-2);
|
background-color: @tree-node-selected-color;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-fill-1);
|
background-color: @tree-node-selected-hover-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-selected .arco-tree-node-title:hover {
|
||||||
|
background-color: unset;
|
||||||
|
//background-color: @tree-node-selected-hover-color;
|
||||||
|
}
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
padding: 0;
|
padding: 0 16px 0 0;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-fill-1);
|
background-color: unset;
|
||||||
|
//background-color: @tree-node-hover-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-text {
|
&-text {
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ body {
|
|||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
height: 4px;
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 数据 -->
|
<!-- 数据 -->
|
||||||
<template #item="{ item }">
|
<template #item="{ item }">
|
||||||
<a-list-item :title="`${item.name}(${item.code}) - ${ item.address}`">
|
<a-list-item :title="`${item.name}(${item.code}) - ${item.address}`">
|
||||||
<icon-desktop class="host-list-icon" />
|
<icon-desktop class="host-list-icon" />
|
||||||
<span>{{ `${item.name}(${item.code}) - ` }}</span>
|
<span>{{ `${item.name}(${item.code}) - ` }}</span>
|
||||||
<span class="span-blue">{{ item.address }}</span>
|
<span class="span-blue">{{ item.address }}</span>
|
||||||
|
|||||||
@@ -192,7 +192,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 44px);
|
height: calc(100% - 44px);
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 亮色主题配色常量
|
// 亮色主题配色常量
|
||||||
body {
|
body {
|
||||||
--color-bg-header: #232323;
|
--color-bg-header: #232323;
|
||||||
--color-bg-sidebar: #F2F3F5;
|
--color-bg-sidebar: #F2F3F4;
|
||||||
--color-bg-content: #FEFEFE;
|
--color-bg-content: #FEFEFE;
|
||||||
--color-sidebar-icon: #737070;
|
--color-sidebar-icon: #737070;
|
||||||
--color-sidebar-icon-bg: #D7D8DB;
|
--color-sidebar-icon-bg: #D7D8DB;
|
||||||
@@ -16,7 +16,7 @@ body {
|
|||||||
body[terminal-theme='dark'] {
|
body[terminal-theme='dark'] {
|
||||||
--color-bg-header: #232323;
|
--color-bg-header: #232323;
|
||||||
--color-bg-sidebar: #2C2E31;
|
--color-bg-sidebar: #2C2E31;
|
||||||
--color-bg-content: #1A1B1F;
|
--color-bg-content: #1A1B1C;
|
||||||
--color-sidebar-icon: #C3C8CE;
|
--color-sidebar-icon: #C3C8CE;
|
||||||
--color-sidebar-icon-bg: #43444C;
|
--color-sidebar-icon-bg: #43444C;
|
||||||
--color-sidebar-tooltip-text: rgba(255, 255, 255, .9);
|
--color-sidebar-tooltip-text: rgba(255, 255, 255, .9);
|
||||||
@@ -213,6 +213,7 @@ body[terminal-theme='dark'] .host-layout {
|
|||||||
|
|
||||||
.terminal-setting-wrapper {
|
.terminal-setting-wrapper {
|
||||||
min-width: 932px;
|
min-width: 932px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-setting-title {
|
.terminal-setting-title {
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
import { useFullscreen } from '@vueuse/core';
|
import { useFullscreen } from '@vueuse/core';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import IconActions from '../layout/icon-actions.vue';
|
import IconActions from '../layout/icon-actions.vue';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@@ -67,24 +68,25 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(['update:modelValue', 'clickTab', 'deleteTab', 'split', 'share']);
|
const emits = defineEmits(['update:modelValue', 'clickTab', 'deleteTab', 'share']);
|
||||||
|
|
||||||
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
|
||||||
|
const terminalStore = useTerminalStore();
|
||||||
|
|
||||||
// 顶部操作
|
// 顶部操作
|
||||||
const actions = computed<Array<SidebarAction>>(() => [
|
const actions = computed<Array<SidebarAction>>(() => [
|
||||||
{
|
|
||||||
icon: 'icon-interaction',
|
|
||||||
content: '分屏',
|
|
||||||
visible: false,
|
|
||||||
click: () => emits('split')
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: 'icon-share-alt',
|
icon: 'icon-share-alt',
|
||||||
content: '分享链接',
|
content: '分享链接',
|
||||||
visible: false,
|
visible: false,
|
||||||
click: () => emits('share')
|
click: () => emits('share')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// FIXME 持久化
|
||||||
|
icon: terminalStore.isDarkTheme ? 'icon-sun-fill' : 'icon-moon-fill',
|
||||||
|
content: terminalStore.isDarkTheme ? '点击切换为亮色模式' : '点击切换为暗色模式',
|
||||||
|
click: () => terminalStore.changeDarkTheme(!terminalStore.isDarkTheme)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: isFullscreen.value ? 'icon-fullscreen-exit' : 'icon-fullscreen',
|
icon: isFullscreen.value ? 'icon-fullscreen-exit' : 'icon-fullscreen',
|
||||||
content: isFullscreen.value ? '点击退出全屏模式' : '点击切换全屏模式',
|
content: isFullscreen.value ? '点击退出全屏模式' : '点击切换全屏模式',
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<div class="group-view-container">
|
||||||
|
<!-- 主机分组 -->
|
||||||
|
<a-scrollbar>
|
||||||
|
<a-tree v-model:selected-keys="selectedGroup"
|
||||||
|
:data="hosts.groupTree"
|
||||||
|
:blockNode="true"
|
||||||
|
class="host-tree block-tree"
|
||||||
|
@select="chooseGroup">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<template #extra="node">
|
||||||
|
<span class="node-host-count span-blue">{{ hosts?.treeNodes[node.key]?.length || 0 }}</span>
|
||||||
|
</template>
|
||||||
|
</a-tree>
|
||||||
|
</a-scrollbar>
|
||||||
|
<!-- 主机列表 -->
|
||||||
|
<div class="host-list">
|
||||||
|
<a-list size="large"
|
||||||
|
max-height="100%"
|
||||||
|
:hoverable="true"
|
||||||
|
:data="[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]">
|
||||||
|
<!-- 空数据 -->
|
||||||
|
<template #empty>
|
||||||
|
<span class="host-list-empty">当前分组未配置主机</span>
|
||||||
|
</template>
|
||||||
|
<!-- 数据 -->
|
||||||
|
<template #item="{ item }">
|
||||||
|
<a-list-item :title="`${item.name}(${item.code}) - ${item.address}`">
|
||||||
|
{{ hosts?.treeNodes[selectedGroup[0]] }}
|
||||||
|
<icon-desktop class="host-list-icon" />
|
||||||
|
<span>{{ `${item.name}(${item.code}) - ` }}</span>
|
||||||
|
<span class="span-blue">{{ item.address }}</span>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'hostGroupView'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
hosts: AuthorizedHostQueryResponse
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const selectedGroup = ref([0]);
|
||||||
|
|
||||||
|
const chooseGroup = () => {
|
||||||
|
console.log(selectedGroup.value[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@tree-width: 298px;
|
||||||
|
@tree-gap: 32px;
|
||||||
|
|
||||||
|
.group-view-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
max-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.host-tree {
|
||||||
|
min-width: 100%;
|
||||||
|
width: max-content;
|
||||||
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.node-host-count {
|
||||||
|
margin-right: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.host-list {
|
||||||
|
width: calc(100% - @tree-width - @tree-gap);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-scrollbar) {
|
||||||
|
width: @tree-width;
|
||||||
|
margin-right: @tree-gap;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&-container {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -5,16 +5,20 @@
|
|||||||
<h2 class="terminal-setting-title">新建连接</h2>
|
<h2 class="terminal-setting-title">新建连接</h2>
|
||||||
<!-- 操作栏 -->
|
<!-- 操作栏 -->
|
||||||
<div class="terminal-setting-block header-actions">
|
<div class="terminal-setting-block header-actions">
|
||||||
<a-radio-group type="button">
|
<!-- 视图类型 -->
|
||||||
<a-radio value="1">分组</a-radio>
|
<a-radio-group v-model="newConnectionType"
|
||||||
<a-radio value="2">列表</a-radio>
|
type="button"
|
||||||
<a-radio value="3">最近连接</a-radio>
|
class="usn"
|
||||||
</a-radio-group>
|
:options="toOptions(NewConnectionTypeKey)"
|
||||||
<a-input-search class="host-filter"
|
@change="changeConnectionType" />
|
||||||
placeholder="输入名称/编码/IP 进行过滤" />
|
<!-- 过滤 -->
|
||||||
|
<a-input-search v-model="filterValue"
|
||||||
|
class="host-filter"
|
||||||
|
placeholder="输入名称/编码/IP @标签"
|
||||||
|
:allow-clear="true" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 授权主机 -->
|
<!-- 授权主机 -->
|
||||||
<div class="terminal-setting-block">
|
<div class="terminal-setting-block" style="margin: 0;">
|
||||||
<!-- 顶部 -->
|
<!-- 顶部 -->
|
||||||
<div class="terminal-setting-subtitle-wrapper">
|
<div class="terminal-setting-subtitle-wrapper">
|
||||||
<h3 class="terminal-setting-subtitle">
|
<h3 class="terminal-setting-subtitle">
|
||||||
@@ -22,17 +26,33 @@
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<div class="terminal-setting-body hosts-container">
|
<div class="terminal-setting-body body-container">
|
||||||
<div class="host-tree">
|
<!-- 加载中 -->
|
||||||
<a-tree :data="hosts.groupTree"
|
<a-skeleton v-if="loading"
|
||||||
:blockNode="true"
|
class="hosts-skeleton"
|
||||||
>
|
:animation="true">
|
||||||
|
<a-skeleton-line :rows="6"
|
||||||
|
:line-height="40"
|
||||||
|
:line-spacing="20" />
|
||||||
|
</a-skeleton>
|
||||||
|
<!-- 无数据 -->
|
||||||
|
<a-empty v-else-if="!hosts.hostList?.length">
|
||||||
|
<template #image>
|
||||||
|
<icon-desktop />
|
||||||
|
</template>
|
||||||
|
Oops! 无授权主机 请联系管理员授权后重试!
|
||||||
|
</a-empty>
|
||||||
|
<!-- 主机列表 -->
|
||||||
|
<div v-else class="host-view-container">
|
||||||
|
<!-- 分组视图列表 -->
|
||||||
|
<host-group-view v-if="NewConnectionType.GROUP === newConnectionType"
|
||||||
|
:hosts="hosts" />
|
||||||
|
<!-- 列表视图 -->
|
||||||
|
|
||||||
|
<!-- 我的收藏 -->
|
||||||
|
|
||||||
|
<!-- 最近连接 -->
|
||||||
|
|
||||||
</a-tree>
|
|
||||||
{{ }}
|
|
||||||
</div>
|
|
||||||
<div class="host-list">
|
|
||||||
{{ hosts.treeNodes }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,13 +69,33 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||||
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onBeforeMount, ref } from 'vue';
|
||||||
|
import { NewConnectionType, NewConnectionTypeKey } from '../../types/terminal.const';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
import HostGroupView from './host-group-view.vue';
|
||||||
|
import { useDictStore } from '@/store';
|
||||||
|
|
||||||
|
const { loading, setLoading } = useLoading();
|
||||||
|
const { toOptions } = useDictStore();
|
||||||
|
|
||||||
|
const newConnectionType = ref(NewConnectionType.GROUP);
|
||||||
|
const filterValue = ref();
|
||||||
const hosts = ref<AuthorizedHostQueryResponse>({} as AuthorizedHostQueryResponse);
|
const hosts = ref<AuthorizedHostQueryResponse>({} as AuthorizedHostQueryResponse);
|
||||||
|
|
||||||
onMounted(async () => {
|
// 修改连接类型
|
||||||
const { data } = await getCurrentAuthorizedHost();
|
const changeConnectionType = () => {
|
||||||
hosts.value = data;
|
// FIXME 持久化
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载主机信息
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { data } = await getCurrentAuthorizedHost();
|
||||||
|
hosts.value = data;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -66,20 +106,21 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.host-filter {
|
.host-filter {
|
||||||
width: 40%;
|
width: 36%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hosts-container {
|
.body-container {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.host-tree {
|
.hosts-skeleton {
|
||||||
margin-right: 16px;
|
width: 100%;
|
||||||
width: 350px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.host-list {
|
.host-view-container {
|
||||||
width: 490px;
|
width: 100%;
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ export interface SidebarAction {
|
|||||||
click: () => void;
|
click: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tab 元素
|
||||||
|
export interface TabItem {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
// tab 类型
|
// tab 类型
|
||||||
export const TabType = {
|
export const TabType = {
|
||||||
SETTING: 'setting',
|
SETTING: 'setting',
|
||||||
@@ -34,14 +43,13 @@ export const InnerTabs = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// tab 元素
|
// 新建连接类型
|
||||||
export interface TabItem {
|
export const NewConnectionType = {
|
||||||
key: string;
|
GROUP: 'group',
|
||||||
title: string;
|
LIST: 'list',
|
||||||
type: string;
|
FAVORITE: 'favorite',
|
||||||
|
LATEST: 'latest'
|
||||||
[key: string]: unknown;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// 字体后缀 兜底
|
// 字体后缀 兜底
|
||||||
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
export const fontFamilySuffix = ',courier-new, courier, monospace';
|
||||||
@@ -61,5 +69,12 @@ export const fontWeightKey = 'terminalFontWeight';
|
|||||||
// 终端光标样式
|
// 终端光标样式
|
||||||
export const cursorStyleKey = 'terminalCursorStyle';
|
export const cursorStyleKey = 'terminalCursorStyle';
|
||||||
|
|
||||||
|
// 终端新建连接类型
|
||||||
|
export const NewConnectionTypeKey = 'terminalNewConnectionType';
|
||||||
|
|
||||||
// 加载的字典值
|
// 加载的字典值
|
||||||
export const dictKeys = [darkThemeKey, fontFamilyKey, fontSizeKey, fontWeightKey, cursorStyleKey];
|
export const dictKeys = [
|
||||||
|
darkThemeKey, fontFamilyKey,
|
||||||
|
fontSizeKey, fontWeightKey,
|
||||||
|
cursorStyleKey, NewConnectionTypeKey
|
||||||
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user