推翻重来.

This commit is contained in:
lijiahang
2024-01-31 19:02:43 +08:00
parent e9d338f66e
commit 66ab7a7637
15 changed files with 464 additions and 34 deletions

View File

@@ -22,7 +22,7 @@ body {
}
// 暗色主题配色常量
body[terminal-theme='dark'] {
body[host-space-theme='dark'] {
--color-bg-header: #232323;
--color-bg-sidebar: #2C2E31;
--color-bg-content: #1A1B1C;
@@ -45,7 +45,7 @@ body[terminal-theme='dark'] {
}
// 布局常量
.host-layout {
.host-space-layout {
--header-height: 44px;
--sidebar-width: 44px;
--sidebar-icon-wrapper-size: var(--header-height);
@@ -62,7 +62,7 @@ body[terminal-theme='dark'] {
}
// arco 亮色配色
body .host-layout, .arco-modal-container {
body .host-space-layout, .arco-modal-container {
--color-white: #ffffff;
--color-black: #000000;
--color-border: rgb(var(--gray-3));
@@ -133,9 +133,9 @@ body .host-layout, .arco-modal-container {
}
// arco 暗色配色
body[terminal-theme='dark'],
body[terminal-theme='dark'] .host-layout,
body[terminal-theme='dark'] .arco-modal-container {
body[host-space-theme='dark'],
body[host-space-theme='dark'] .host-space-layout,
body[host-space-theme='dark'] .arco-modal-container {
--color-white: rgba(255, 255, 255, 0.9);
--color-black: #000000;
--color-border: #333335;

View File

@@ -0,0 +1,18 @@
import type { AppRouteRecordRaw } from '../types';
import { DEFAULT_LAYOUT } from '../base';
const HOST_AUDIT: AppRouteRecordRaw =
{
name: 'hostAudit',
path: '/host-audit',
component: DEFAULT_LAYOUT,
children: [
{
name: 'hostAuditConnectLog',
path: '/host-audit/connect-log',
component: () => import('@/views/host-audit/connect-log/index.vue'),
},
],
};
export default HOST_AUDIT;

View File

@@ -1,18 +0,0 @@
import type { AppRouteRecordRaw } from '../types';
import { DEFAULT_LAYOUT } from '../base';
const HOST_REVIEW: AppRouteRecordRaw =
{
name: 'hostReview',
path: '/host-review',
component: DEFAULT_LAYOUT,
children: [
{
name: 'hostReviewConnectLog',
path: '/host-review/connect-log',
component: () => import('@/views/host-review/connect-log/index.vue'),
},
],
};
export default HOST_REVIEW;

View File

@@ -7,9 +7,9 @@ const HOST: AppRouteRecordRaw = {
component: FULL_LAYOUT,
children: [
{
name: 'hostTerminal',
path: '/host/terminal',
component: () => import('@/views/host/terminal/index.vue'),
name: 'hostSpace',
path: '/host/space',
component: () => import('@/views/host/space/index.vue'),
meta: {
noAffix: true
}

View File

@@ -97,7 +97,7 @@
<script lang="ts">
export default {
name: 'hostReviewConnectLogTable'
name: 'hostAuditConnectLogTable'
};
</script>

View File

@@ -7,7 +7,7 @@
<script lang="ts">
export default {
name: 'hostReviewConnectLog'
name: 'hostAuditConnectLog'
};
</script>

View File

@@ -0,0 +1,46 @@
<template>
<div>
<a-tooltip v-for="(action, index) in actions"
:key="index"
:position="position as any"
:mini="true"
:show-arrow="false"
content-class="terminal-tooltip-content"
:auto-fix-position="false"
:content="action.content">
<div class="terminal-sidebar-icon-wrapper" v-if="action.visible !== false">
<div class="terminal-sidebar-icon"
:class="[
iconClass,
action.disabled === true ? 'disabled-item' : '',
action.checked === true ? 'checked-item' : '',
]"
@click="action.disabled === true ? false : action.click()">
<component :is="action.icon" :style="action?.iconStyle" />
</div>
</div>
</a-tooltip>
</div>
</template>
<script lang="ts">
export default {
name: 'iconActions'
};
</script>
<script lang="ts" setup>
import type { SidebarAction } from '../../terminal/types/terminal.type';
import type { PropType } from 'vue';
defineProps({
actions: Array as PropType<Array<SidebarAction>>,
position: String,
iconClass: String,
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,231 @@
<template>
<div class="layout-header">
<!-- 左侧 logo -->
<!-- FIXME -->
<div class="layout-header-left">
<img alt="logo"
class="layout-header-logo-img"
draggable="false"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image" />
<h5 class="layout-header-logo-text">Orion Ops Pro</h5>
</div>
<!-- 左侧 tabs -->
<div class="layout-header-tabs">
<a-tabs v-model:active-key="tabManager.active"
:editable="true"
:hide-content="true"
:auto-switch="true"
@tab-click="k => tabManager.clickTab(k as string)"
@delete="k => tabManager.deleteTab(k as string)">
<a-tab-pane v-for="tab in tabManager.items"
:key="tab.key"
:title="tab.title" />
</a-tabs>
</div>
<!-- 右侧操作 -->
<div class="layout-header-right">
<!-- 操作按钮 -->
<icon-actions class="layout-header-right-actions"
:actions="actions"
position="br"
icon-class="layout-header-icon" />
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'layoutHeader'
};
</script>
<script lang="ts" setup>
import type { SidebarAction } from '../../terminal/types/terminal.type';
import { useFullscreen } from '@vueuse/core';
import { computed } from 'vue';
import { useTerminalStore } from '@/store';
import IconActions from './icon-actions.vue';
const { isFullscreen, toggle: toggleFullScreen } = useFullscreen();
const { tabManager } = useTerminalStore();
// 顶部操作
const actions = computed<Array<SidebarAction>>(() => [
{
icon: isFullscreen.value ? 'icon-fullscreen-exit' : 'icon-fullscreen',
content: isFullscreen.value ? '点击退出全屏模式' : '点击切换全屏模式',
click: toggleFullScreen
},
]);
</script>
<style lang="less" scoped>
.layout-header {
--logo-width: 168px;
}
.layout-header {
height: 100%;
color: var(--color-header-text-2);
display: flex;
user-select: none;
&-left {
width: var(--logo-width);
display: flex;
align-items: center;
justify-content: flex-start;
}
&-logo-img {
padding-left: 6px;
width: 36px;
height: 36px;
}
&-logo-text {
margin: 0;
display: flex;
align-items: center;
padding: 0 8px;
font-size: 16px;
overflow: hidden;
white-space: nowrap;
}
&-tabs {
width: calc(100% - var(--logo-width) - var(--sidebar-icon-wrapper-size));
display: flex;
}
&-right {
width: var(--sidebar-icon-wrapper-size);
display: flex;
justify-content: flex-end;
&-actions {
width: var(--sidebar-icon-wrapper-size);
display: flex;
justify-content: flex-end;
}
}
:deep(&-icon) {
color: var(--color-header-text-2) !important;
&:hover {
background: var(--color-bg-header-icon-1) !important;
}
}
}
:deep(.arco-tabs-nav) {
height: 100%;
&::before {
display: none;
}
&-tab {
height: 100%;
}
&-ink {
display: none;
}
&-button .arco-icon-hover:hover {
color: var(--color-header-text-2);
&::before {
background: var(--color-bg-header-icon-1);
}
}
}
:deep(.arco-tabs-nav-type-line .arco-tabs-tab:hover .arco-tabs-tab-title::before) {
background: transparent;
}
:deep(.arco-tabs-tab) {
height: 100%;
margin: 0;
padding: 0;
color: var(--color-header-text-1);
background: var(--color-header-tabs-bg);
position: relative;
&:hover {
color: var(--color-header-text-2);
transition: .2s;
}
&::after {
content: '';
width: 54px;
height: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
position: absolute;
right: 0;
top: 0;
}
&:hover::after {
background: linear-gradient(270deg, var(--color-gradient-start) 45%, var(--color-gradient-end) 120%);
}
.arco-tabs-tab-title {
padding: 11px 18px;
background: var(--color-header-tabs-bg);
font-size: 12px;
display: flex;
align-items: center;
&::before {
display: none;
}
}
&:hover .arco-tabs-tab-close-btn {
display: unset;
}
&-close-btn {
margin: 0 8px 0 0;
padding: 4px;
border-radius: 4px;
position: absolute;
right: 0;
z-index: 4;
display: none;
color: var(--color-header-text-2);
&:hover {
transition: .2s;
background: var(--color-bg-header-icon-1);
}
&::before {
display: none;
}
}
}
:deep(.arco-tabs-tab-active) {
background: var(--color-header-tabs-bg-hover);
color: var(--color-header-text-2) !important;
.arco-tabs-tab-title {
background: var(--color-header-tabs-bg-hover);
}
&:hover::after {
background: linear-gradient(270deg, var(--color-gradient-start) 45%, var(--color-gradient-end) 120%);
}
}
</style>

View File

@@ -0,0 +1,153 @@
<template>
<div class="host-space-layout" v-if="render">
<!-- 头部区域 -->
<header class="host-space-layout-header">
<layout-header />
</header>
<!-- 主体区域 -->
<main class="host-space-layout-main">
<!-- 左侧操作栏 -->
<div class="host-space-layout-left">
<terminal-left-sidebar />
</div>
<!-- 内容区域 -->
<div class="host-space-layout-content">
<!-- 主机加载中骨架 -->
<loading-skeleton v-if="contentLoading" />
<!-- 终端内容区域 -->
<terminal-content v-else />
</div>
<!-- 右侧操作栏 -->
<div class="host-space-layout-right">
<terminal-right-sidebar />
</div>
</main>
</div>
</template>
<script lang="ts">
export default {
name: 'hostSpace'
};
</script>
<script lang="ts" setup>
import { ref, onBeforeMount, onUnmounted, onMounted } from 'vue';
import { dictKeys, InnerTabs } from '../terminal/types/terminal.const';
import { useCacheStore, useDictStore, useTerminalStore } from '@/store';
import useLoading from '@/hooks/loading';
import TerminalLeftSidebar from '../terminal/components/layout/terminal-left-sidebar.vue';
import TerminalRightSidebar from '../terminal/components/layout/terminal-right-sidebar.vue';
import TerminalContent from '../terminal/components/layout/terminal-content.vue';
import LoadingSkeleton from '../terminal/components/layout/loading-skeleton.vue';
import '@/assets/style/host-space-layout.less';
import 'xterm/css/xterm.css';
import LayoutHeader from './components/layout-header.vue';
const terminalStore = useTerminalStore();
const dictStore = useDictStore();
const cacheStore = useCacheStore();
const { loading: contentLoading, setLoading: setContentLoading } = useLoading(true);
const originTitle = document.title;
const render = ref(false);
// 关闭视口处理
const handleBeforeUnload = (event: any) => {
event.preventDefault();
event.returnValue = confirm('系统可能不会保存您所做的更改');
};
// 加载用户终端偏好
onBeforeMount(async () => {
await terminalStore.fetchPreference();
// 设置系统主题配色
const dark = terminalStore.preference.theme.dark;
document.body.setAttribute('host-space-theme', dark ? 'dark' : 'light');
render.value = true;
});
// 加载字典值
onBeforeMount(async () => {
await dictStore.loadKeys([...dictKeys]);
});
// 加载主机信息
onMounted(async () => {
try {
await terminalStore.loadHosts();
} catch (e) {
} finally {
setContentLoading(false);
}
});
// 事件处理
onMounted(() => {
// 默认标题
document.title = InnerTabs.NEW_CONNECTION.title;
// 注册关闭视口事件
// FIXME 开发阶段
// window.addEventListener('beforeunload', handleBeforeUnload);
});
onUnmounted(() => {
// 卸载时清除 cache
cacheStore.reset('authorizedHostKeys', 'authorizedHostIdentities', 'commandSnippetGroups');
// 移除关闭视口事件
window.removeEventListener('beforeunload', handleBeforeUnload);
// 去除 body style
document.body.removeAttribute('host-space-theme');
// 重置 title
document.title = originTitle;
});
</script>
<style lang="less" scoped>
.host-space-layout {
width: 100%;
height: 100vh;
position: relative;
color: var(--color-content-text-2);
&-header {
width: 100%;
height: var(--header-height);
background: var(--color-bg-header);
position: relative;
}
&-main {
width: 100%;
height: calc(100% - var(--sidebar-width));
position: relative;
display: flex;
justify-content: space-between;
}
&-left, &-right {
width: var(--sidebar-width);
height: 100%;
background: var(--color-bg-sidebar);
border-top: 1px solid var(--color-bg-content);
overflow: hidden;
}
&-left {
border-right: 1px solid var(--color-bg-content);
}
&-right {
border-left: 1px solid var(--color-bg-content);
}
&-content {
width: calc(100% - var(--sidebar-width) * 2);
height: 100%;
background: var(--color-bg-content);
overflow: auto;
}
}
</style>

View File

@@ -9,7 +9,7 @@
<!-- 内容区域 -->
<div class="terminal-setting-body setting-body">
<!-- 提示 -->
<a-alert class="mb16">点击保存按钮后需要刷新页面生效 (恢复默认配置后也需要点击保存按钮) (设置时需要避免使用浏览器内置快捷键)</a-alert>
<a-alert class="mb16">点击保存按钮后需要刷新页面生效 (恢复默认配置后也需要点击保存按钮) (设置时需要避免浏览器内置快捷键冲突)</a-alert>
<a-space class="action-container" size="mini">
<!-- 是否启用 -->
<a-switch v-model="value"

View File

@@ -41,7 +41,7 @@
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
import TerminalContent from './components/layout/terminal-content.vue';
import LoadingSkeleton from './components/layout/loading-skeleton.vue';
import './assets/styles/layout.less';
import '@/assets/style/host-space-layout.less';
import 'xterm/css/xterm.css';
const terminalStore = useTerminalStore();
@@ -63,7 +63,7 @@
await terminalStore.fetchPreference();
// 设置系统主题配色
const dark = terminalStore.preference.theme.dark;
document.body.setAttribute('terminal-theme', dark ? 'dark' : 'light');
document.body.setAttribute('host-space-theme', dark ? 'dark' : 'light');
render.value = true;
});
@@ -97,7 +97,7 @@
// 移除关闭视口事件
window.removeEventListener('beforeunload', handleBeforeUnload);
// 去除 body style
document.body.removeAttribute('terminal-theme');
document.body.removeAttribute('host-space-theme');
// 重置 title
document.title = originTitle;
});

View File

@@ -1,5 +1,5 @@
// tab 类型
import { ShortcutKeyItem } from '@/views/host/terminal/types/terminal.type';
import { ShortcutKeyItem } from '@/views/host/space/types/terminal.type';
export const TerminalTabType = {
SETTING: 'setting',