feat: 终端布局.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-layout class="host-layout">
|
||||
<a-layout class="full-layout">
|
||||
<!-- 页面 -->
|
||||
<a-layout-content>
|
||||
<!-- 水印 -->
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'host-layout'
|
||||
name: 'full-layout'
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.host-layout {
|
||||
.full-layout {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import {
|
||||
DEFAULT_ROUTE_FULL_PATH,
|
||||
REDIRECT_ROUTE_NAME,
|
||||
LOGIN_ROUTE_NAME,
|
||||
FORBIDDEN_ROUTER_NAME,
|
||||
NOT_FOUND_ROUTER_NAME,
|
||||
} from '@/router/constants';
|
||||
import { DEFAULT_ROUTE_FULL_PATH, FORBIDDEN_ROUTER_NAME, LOGIN_ROUTE_NAME, NOT_FOUND_ROUTER_NAME, REDIRECT_ROUTE_NAME, } from '@/router/constants';
|
||||
|
||||
// 默认布局
|
||||
export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue');
|
||||
|
||||
// 全屏布局
|
||||
export const FULL_LAYOUT = () => import('@/layout/full-layout.vue');
|
||||
|
||||
// 根页面
|
||||
export const ROOT_ROUTER: RouteRecordRaw = {
|
||||
path: '/',
|
||||
|
||||
17
orion-ops-ui/src/router/routes/modules/host-ops.ts
Normal file
17
orion-ops-ui/src/router/routes/modules/host-ops.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
import { FULL_LAYOUT } from '../base';
|
||||
|
||||
const HOST_OPS: AppRouteRecordRaw = {
|
||||
name: 'hostOps',
|
||||
path: '/host',
|
||||
component: FULL_LAYOUT,
|
||||
children: [
|
||||
{
|
||||
name: 'hostTerminal',
|
||||
path: '/host/terminal',
|
||||
component: () => import('@/views/host-ops/terminal/index.vue'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default HOST_OPS;
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { AppRouteRecordRaw } from '../types';
|
||||
|
||||
const DASHBOARD: AppRouteRecordRaw = {
|
||||
name: 'hostWorkspace',
|
||||
path: '/host-workspace',
|
||||
component: () => import('@/layout/host-workspace-layout.vue'),
|
||||
children: [
|
||||
{
|
||||
name: 'hostTerminal',
|
||||
path: '/host-workspace/terminal',
|
||||
component: () => import('@/views/host-workspace/terminal/index.vue'),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default DASHBOARD;
|
||||
@@ -0,0 +1,184 @@
|
||||
// 布局常量
|
||||
.host-layout {
|
||||
--header-height: 44px;
|
||||
--sidebar-width: 44px;
|
||||
--sidebar-icon-wrapper-size: var(--header-height);
|
||||
--sidebar-icon-size: 32px;
|
||||
--sidebar-icon-font-size: 22px;
|
||||
}
|
||||
|
||||
// 亮色主题配色常量
|
||||
body[terminal-theme='light'] {
|
||||
--color-bg-header: #292B31;
|
||||
--color-bg-sidebar: #F2F3F5;
|
||||
--color-bg-content: #FEFEFE;
|
||||
--color-sidebar-icon: #737070;
|
||||
--color-sidebar-icon-bg: #D7D8DB;
|
||||
--color-sidebar-tooltip-text: rgba(255, 255, 255, .9);
|
||||
--color-sidebar-tooltip-bg: rgb(var(--gray-10));
|
||||
}
|
||||
|
||||
// 暗色主题配色常量
|
||||
body[terminal-theme='dark'] {
|
||||
--color-bg-header: #2C2E31;
|
||||
--color-bg-sidebar: #2C2E31;
|
||||
--color-bg-content: #1A1B1F;
|
||||
--color-sidebar-icon: #C3C8CE;
|
||||
--color-sidebar-icon-bg: #43444C;
|
||||
--color-sidebar-tooltip-text: rgba(255, 255, 255, .9);
|
||||
--color-sidebar-tooltip-bg: var(--color-sidebar-icon-bg);
|
||||
}
|
||||
|
||||
// 侧栏图标 wrapper
|
||||
.terminal-sidebar-icon-wrapper {
|
||||
width: var(--sidebar-icon-wrapper-size);
|
||||
height: var(--sidebar-icon-wrapper-size);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 侧栏图标
|
||||
.terminal-sidebar-icon {
|
||||
width: var(--sidebar-icon-size);
|
||||
height: var(--sidebar-icon-size);
|
||||
font-size: var(--sidebar-icon-font-size);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-sidebar-icon);
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
transition: 0.1s cubic-bezier(0, 0, 1, 1);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-sidebar-icon-bg);
|
||||
}
|
||||
}
|
||||
|
||||
// 侧栏 tooltip 内容
|
||||
.terminal-sidebar-tooltip-content {
|
||||
color: var(--color-sidebar-tooltip-text);
|
||||
background: var(--color-sidebar-tooltip-bg);
|
||||
}
|
||||
|
||||
// 侧栏 tooltip 箭头
|
||||
.terminal-sidebar-tooltip-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.host-layout.light {
|
||||
--color-white: #FFFFFF;
|
||||
--color-black: #000000;
|
||||
--color-border: rgb(var(--gray-3));
|
||||
--color-bg-popup: var(--color-bg-5);
|
||||
--color-bg-1: #FFFFFF;
|
||||
--color-bg-2: #FFFFFF;
|
||||
--color-bg-3: #FFFFFF;
|
||||
--color-bg-4: #FFFFFF;
|
||||
--color-bg-5: #FFFFFF;
|
||||
--color-bg-white: #FFFFFF;
|
||||
--color-neutral-1: rgb(var(--gray-1));
|
||||
--color-neutral-2: rgb(var(--gray-2));
|
||||
--color-neutral-3: rgb(var(--gray-3));
|
||||
--color-neutral-4: rgb(var(--gray-4));
|
||||
--color-neutral-5: rgb(var(--gray-5));
|
||||
--color-neutral-6: rgb(var(--gray-6));
|
||||
--color-neutral-7: rgb(var(--gray-7));
|
||||
--color-neutral-8: rgb(var(--gray-8));
|
||||
--color-neutral-9: rgb(var(--gray-9));
|
||||
--color-neutral-10: rgb(var(--gray-10));
|
||||
--color-text-1: var(--color-neutral-10);
|
||||
--color-text-2: var(--color-neutral-8);
|
||||
--color-text-3: var(--color-neutral-6);
|
||||
--color-text-4: var(--color-neutral-4);
|
||||
--color-border-1: var(--color-neutral-2);
|
||||
--color-border-2: var(--color-neutral-3);
|
||||
--color-border-3: var(--color-neutral-4);
|
||||
--color-border-4: var(--color-neutral-6);
|
||||
--color-fill-1: var(--color-neutral-1);
|
||||
--color-fill-2: var(--color-neutral-2);
|
||||
--color-fill-3: var(--color-neutral-3);
|
||||
--color-fill-4: var(--color-neutral-4);
|
||||
--color-primary-light-1: rgb(var(--primary-1));
|
||||
--color-primary-light-2: rgb(var(--primary-2));
|
||||
--color-primary-light-3: rgb(var(--primary-3));
|
||||
--color-primary-light-4: rgb(var(--primary-4));
|
||||
--color-link-light-1: rgb(var(--link-1));
|
||||
--color-link-light-2: rgb(var(--link-2));
|
||||
--color-link-light-3: rgb(var(--link-3));
|
||||
--color-link-light-4: rgb(var(--link-4));
|
||||
--color-secondary: var(--color-neutral-2);
|
||||
--color-secondary-hover: var(--color-neutral-3);
|
||||
--color-secondary-active: var(--color-neutral-4);
|
||||
--color-secondary-disabled: var(--color-neutral-1);
|
||||
--color-danger-light-1: rgb(var(--danger-1));
|
||||
--color-danger-light-2: rgb(var(--danger-2));
|
||||
--color-danger-light-3: rgb(var(--danger-3));
|
||||
--color-danger-light-4: rgb(var(--danger-4));
|
||||
--color-success-light-1: rgb(var(--success-1));
|
||||
--color-success-light-2: rgb(var(--success-2));
|
||||
--color-success-light-3: rgb(var(--success-3));
|
||||
--color-success-light-4: rgb(var(--success-4));
|
||||
--color-warning-light-1: rgb(var(--warning-1));
|
||||
--color-warning-light-2: rgb(var(--warning-2));
|
||||
--color-warning-light-3: rgb(var(--warning-3));
|
||||
--color-warning-light-4: rgb(var(--warning-4));
|
||||
--color-sidebar-tooltip-bg: rgb(var(--gray-10));
|
||||
--color-spin-layer-bg: rgba(255, 255, 255, 0.6);
|
||||
--color-menu-dark-bg: #232324;
|
||||
--color-menu-light-bg: #FFFFFF;
|
||||
--color-menu-dark-hover: rgba(255, 255, 255, 0.04);
|
||||
--color-mask-bg: rgba(29, 33, 41, 0.6);
|
||||
}
|
||||
|
||||
.host-layout.dark {
|
||||
--color-white: rgba(255, 255, 255, 0.9);
|
||||
--color-black: #000000;
|
||||
--color-border: #333335;
|
||||
--color-bg-1: #17171A;
|
||||
--color-bg-2: #232324;
|
||||
--color-bg-3: #2A2A2B;
|
||||
--color-bg-4: #313132;
|
||||
--color-bg-5: #373739;
|
||||
--color-bg-white: #F6F6F6;
|
||||
--color-text-1: rgba(255, 255, 255, 0.9);
|
||||
--color-text-2: rgba(255, 255, 255, 0.7);
|
||||
--color-text-3: rgba(255, 255, 255, 0.5);
|
||||
--color-text-4: rgba(255, 255, 255, 0.3);
|
||||
--color-fill-1: rgba(255, 255, 255, 0.04);
|
||||
--color-fill-2: rgba(255, 255, 255, 0.08);
|
||||
--color-fill-3: rgba(255, 255, 255, 0.12);
|
||||
--color-fill-4: rgba(255, 255, 255, 0.16);
|
||||
--color-primary-light-1: rgba(var(--primary-6), 0.2);
|
||||
--color-primary-light-2: rgba(var(--primary-6), 0.35);
|
||||
--color-primary-light-3: rgba(var(--primary-6), 0.5);
|
||||
--color-primary-light-4: rgba(var(--primary-6), 0.65);
|
||||
--color-secondary: rgba(var(--gray-9), 0.08);
|
||||
--color-secondary-hover: rgba(var(--gray-8), 0.16);
|
||||
--color-secondary-active: rgba(var(--gray-7), 0.24);
|
||||
--color-secondary-disabled: rgba(var(--gray-9), 0.08);
|
||||
--color-danger-light-1: rgba(var(--danger-6), 0.2);
|
||||
--color-danger-light-2: rgba(var(--danger-6), 0.35);
|
||||
--color-danger-light-3: rgba(var(--danger-6), 0.5);
|
||||
--color-danger-light-4: rgba(var(--danger-6), 0.65);
|
||||
--color-success-light-1: rgb(var(--success-6), 0.2);
|
||||
--color-success-light-2: rgb(var(--success-6), 0.35);
|
||||
--color-success-light-3: rgb(var(--success-6), 0.5);
|
||||
--color-success-light-4: rgb(var(--success-6), 0.65);
|
||||
--color-warning-light-1: rgb(var(--warning-6), 0.2);
|
||||
--color-warning-light-2: rgb(var(--warning-6), 0.35);
|
||||
--color-warning-light-3: rgb(var(--warning-6), 0.5);
|
||||
--color-warning-light-4: rgb(var(--warning-6), 0.65);
|
||||
--color-link-light-1: rgb(var(--link-6), 0.2);
|
||||
--color-link-light-2: rgb(var(--link-6), 0.35);
|
||||
--color-link-light-3: rgb(var(--link-6), 0.5);
|
||||
--color-link-light-4: rgb(var(--link-6), 0.65);
|
||||
--color-sidebar-tooltip-bg: #373739;
|
||||
--color-spin-layer-bg: rgba(51, 51, 51, 0.6);
|
||||
--color-menu-dark-bg: #232324;
|
||||
--color-menu-light-bg: #232324;
|
||||
--color-menu-dark-hover: var(--color-fill-2);
|
||||
--color-mask-bg: rgba(23, 23, 26, 0.6);
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div class="terminal-header">
|
||||
tabs > > > 全屏 > > > 传输列表
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'index'
|
||||
name: 'TerminalHeader'
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -17,5 +15,7 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.terminal-header {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="terminal-left-sidebar">
|
||||
<!-- 操作按钮 -->
|
||||
<a-tooltip v-for="(action, index) in actions"
|
||||
:key="index"
|
||||
position="right"
|
||||
:mini="true"
|
||||
content-class="terminal-sidebar-tooltip-content"
|
||||
arrow-class="terminal-sidebar-tooltip-arrow"
|
||||
:content="action.content">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="action.event">
|
||||
<component :is="action.icon" />
|
||||
</div>
|
||||
</div>
|
||||
外观
|
||||
主题
|
||||
快捷键
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'TerminalLeftSidebar'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const emits = defineEmits(['openAdd', 'openSetting']);
|
||||
|
||||
// 操作
|
||||
const actions = [
|
||||
{
|
||||
icon: 'icon-plus',
|
||||
content: '新建连接',
|
||||
event: () => emits('openAdd')
|
||||
},
|
||||
{
|
||||
icon: 'icon-settings',
|
||||
content: '设置',
|
||||
event: () => emits('openSetting')
|
||||
}
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.terminal-left-sidebar {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="terminal-right-sidebar">
|
||||
<!-- 操作按钮 -->
|
||||
<a-tooltip v-for="(action, index) in actions"
|
||||
:key="index"
|
||||
position="left"
|
||||
:mini="true"
|
||||
content-class="terminal-sidebar-tooltip-content"
|
||||
arrow-class="terminal-sidebar-tooltip-arrow"
|
||||
:content="action.content">
|
||||
<div class="terminal-sidebar-icon-wrapper">
|
||||
<div class="terminal-sidebar-icon" @click="action.event">
|
||||
<component :is="action.icon" />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'TerminalRightSidebar'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const emits = defineEmits(['openSnippet']);
|
||||
|
||||
// 操作
|
||||
const actions = [
|
||||
{
|
||||
icon: 'icon-code-block',
|
||||
content: '打开命令片段',
|
||||
event: () => emits('openSnippet')
|
||||
},
|
||||
];
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.terminal-right-sidebar {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div class="terminal-content">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'TerminalContent'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.terminal-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
96
orion-ops-ui/src/views/host-ops/terminal/index.vue
Normal file
96
orion-ops-ui/src/views/host-ops/terminal/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="host-layout">
|
||||
<!-- 头部区域 -->
|
||||
<header class="host-layout-header">
|
||||
<terminal-header />
|
||||
</header>
|
||||
<!-- 主体区域 -->
|
||||
<main class="host-layout-main">
|
||||
<!-- 左侧操作栏 -->
|
||||
<div class="host-layout-left">
|
||||
<terminal-left-sidebar />
|
||||
</div>
|
||||
<!-- 内容区域 -->
|
||||
<div class="host-layout-content">
|
||||
<terminal-content>
|
||||
<div class="my16 mx16">
|
||||
<a-button @click="changeTheme">
|
||||
{{ darkTheme }}
|
||||
</a-button>
|
||||
</div>
|
||||
</terminal-content>
|
||||
</div>
|
||||
<!-- 右侧操作栏 -->
|
||||
<div class="host-layout-right">
|
||||
<terminal-right-sidebar />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'hostTerminal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TerminalHeader from './components/layout/terminal-header.vue';
|
||||
import TerminalLeftSidebar from './components/layout/terminal-left-sidebar.vue';
|
||||
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
||||
import TerminalContent from './components/terminal-content.vue';
|
||||
import { useDark } from '@vueuse/core';
|
||||
import './assets/styles/layout.less';
|
||||
|
||||
// 主题
|
||||
const darkTheme = useDark({
|
||||
selector: 'body',
|
||||
attribute: 'terminal-theme',
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light',
|
||||
initialValue: 'dark',
|
||||
storageKey: null
|
||||
});
|
||||
|
||||
const changeTheme = () => {
|
||||
console.log('current', darkTheme.value);
|
||||
darkTheme.value = !darkTheme.value;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.host-layout {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
|
||||
&-header {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background: var(--color-bg-header);
|
||||
}
|
||||
|
||||
&-main {
|
||||
width: 100%;
|
||||
height: calc(100% - var(--sidebar-width));
|
||||
overflow: hidden;
|
||||
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);
|
||||
}
|
||||
|
||||
&-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-bg-content);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -160,7 +160,7 @@
|
||||
type: MenuType.PARENT_MENU,
|
||||
permission: undefined,
|
||||
sort: undefined,
|
||||
visible: MenuVisible.SHOW,
|
||||
visible: EnabledStatus.ENABLED,
|
||||
cache: EnabledStatus.ENABLED,
|
||||
newWindow: EnabledStatus.DISABLED,
|
||||
icon: undefined,
|
||||
|
||||
Reference in New Issue
Block a user