feat: 新建连接.
This commit is contained in:
@@ -33,4 +33,7 @@ public class AuthorizedHostWrapperVO {
|
||||
@Schema(description = "分组树节点映射 'groupId':hostIdList")
|
||||
private Map<String, Set<Long>> treeNodes;
|
||||
|
||||
// TODO 我的收藏
|
||||
// TODO 最近连接
|
||||
|
||||
}
|
||||
|
||||
@@ -33,12 +33,6 @@
|
||||
"@dangojs/a-query-header": "^0.0.31",
|
||||
"@sanqi377/arco-vue-icon-picker": "^1.0.7",
|
||||
"@vueuse/core": "^9.3.0",
|
||||
"@xterm/addon-canvas": "0.6.0-beta.14",
|
||||
"@xterm/addon-fit": "0.9.0-beta.14",
|
||||
"@xterm/addon-search": "0.14.0-beta.14",
|
||||
"@xterm/addon-web-links": "0.10.0-beta.1",
|
||||
"@xterm/addon-webgl": "0.17.0-beta.14",
|
||||
"@xterm/xterm": "5.4.0-beta.14",
|
||||
"axios": "^0.24.0",
|
||||
"dayjs": "^1.11.5",
|
||||
"echarts": "^5.4.0",
|
||||
@@ -53,7 +47,14 @@
|
||||
"vue": "^3.2.40",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.0.14"
|
||||
"vue-router": "^4.0.14",
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-canvas": "^0.5.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
"xterm-addon-image": "^0.5.0",
|
||||
"xterm-addon-search": "^0.13.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"xterm-addon-webgl": "^0.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arco-plugins/vite-vue": "^1.4.5",
|
||||
|
||||
11399
orion-ops-ui/pnpm-lock.yaml
generated
11399
orion-ops-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -76,7 +76,8 @@ body {
|
||||
.sticky-list {
|
||||
.arco-list-header {
|
||||
position: sticky;
|
||||
background: var(--color-bg-2);
|
||||
background: var(--color-fill-2);
|
||||
border-bottom: none;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ const HOST_OPS: AppRouteRecordRaw = {
|
||||
name: 'hostTerminal',
|
||||
path: '/host/terminal',
|
||||
component: () => import('@/views/host-ops/terminal/index.vue'),
|
||||
meta: {
|
||||
noAffix: true
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -20,9 +20,9 @@ export interface TerminalThemeSchema {
|
||||
foreground: string;
|
||||
cursor: string;
|
||||
cursorAccent?: string;
|
||||
selectionInactiveBackground?: string;
|
||||
selectionBackground?: string;
|
||||
selectionForeground?: string;
|
||||
selectionInactiveBackground?: string;
|
||||
black: string;
|
||||
red: string;
|
||||
green: string;
|
||||
|
||||
@@ -205,21 +205,26 @@ body[terminal-theme='dark'] .host-layout {
|
||||
|
||||
// 终端设置容器
|
||||
.terminal-setting-container {
|
||||
padding: 32px 16px;
|
||||
width: max-content;
|
||||
padding: 32px 16px 16px 16px;
|
||||
width: fit-content;
|
||||
margin: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.terminal-setting-wrapper {
|
||||
min-width: 932px;
|
||||
}
|
||||
|
||||
.terminal-setting-title {
|
||||
margin: 0 0 24px 0;
|
||||
user-select: none;
|
||||
font-size: 1.65em;
|
||||
color: var(--color-content-text-3);
|
||||
}
|
||||
|
||||
.terminal-setting-block {
|
||||
color: var(--color-content-text-2);
|
||||
margin-bottom: 18px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.terminal-setting-subtitle-wrapper {
|
||||
@@ -229,13 +234,13 @@ body[terminal-theme='dark'] .host-layout {
|
||||
}
|
||||
|
||||
.terminal-setting-subtitle {
|
||||
margin: 0;
|
||||
margin: 0 0 16px 0;
|
||||
user-select: none;
|
||||
color: var(--color-content-text-3);
|
||||
}
|
||||
|
||||
.terminal-setting-body {
|
||||
margin: 16px 0 24px 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
arrow-class="terminal-tooltip-arrow"
|
||||
:content="action.content">
|
||||
<div class="terminal-sidebar-icon-wrapper"
|
||||
v-if="action.visible !== false"
|
||||
:style="action?.style">
|
||||
v-if="action.visible !== false">
|
||||
<div class="terminal-sidebar-icon"
|
||||
:class="iconClass"
|
||||
@click="action.click">
|
||||
<component :is="action.icon" />
|
||||
<component :is="action.icon" :style="action?.iconStyle" />
|
||||
</div>
|
||||
</div>
|
||||
</a-tooltip>
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
:title="tab.title">
|
||||
<!-- 设置 -->
|
||||
<template v-if="tab.type === TabType.SETTING">
|
||||
<!-- 新建连接 -->
|
||||
<new-connection-view v-if="tab.key === InnerTabs.NEW_CONNECTION.key" />
|
||||
<!-- 显示设置 -->
|
||||
<terminal-view-setting v-if="tab.key === InnerTabs.THEME_SETTING.key" />
|
||||
<terminal-view-setting v-else-if="tab.key === InnerTabs.THEME_SETTING.key" />
|
||||
<span v-else>
|
||||
{{ tab.key }}
|
||||
{{ tab.title }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -37,6 +40,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { TabType, InnerTabs } from '../../types/terminal.const';
|
||||
import TerminalViewSetting from '../view-setting/terminal-view-setting.vue';
|
||||
import NewConnectionView from '@/views/host-ops/terminal/components/new-connection/new-connection-view.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
<div class="terminal-header">
|
||||
<!-- 左侧 logo -->
|
||||
<div class="terminal-header-left">
|
||||
<img alt="logo"
|
||||
class="terminal-header-logo-img"
|
||||
draggable="false"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image" />
|
||||
<h5 class="terminal-header-logo-text">Orion Ops Pro</h5>
|
||||
</div>
|
||||
<!-- 左侧 tabs -->
|
||||
@@ -105,7 +109,7 @@
|
||||
|
||||
<style lang="less" scoped>
|
||||
.terminal-header {
|
||||
--logo-width: 150px;
|
||||
--logo-width: 168px;
|
||||
--right-avatar-width: calc(28px * 5 - 7px * 4);
|
||||
--right-action-width: calc(var(--header-height) * 3);
|
||||
}
|
||||
@@ -118,6 +122,15 @@
|
||||
|
||||
&-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 {
|
||||
@@ -125,7 +138,7 @@
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 16px;
|
||||
padding: 0 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{
|
||||
icon: 'icon-plus',
|
||||
content: '新建连接',
|
||||
click: () => emits('switchTab', InnerTabs.HOST_LIST)
|
||||
click: () => emits('switchTab', InnerTabs.NEW_CONNECTION)
|
||||
},
|
||||
{
|
||||
icon: 'icon-copy',
|
||||
|
||||
@@ -28,19 +28,17 @@
|
||||
{
|
||||
icon: 'icon-code-block',
|
||||
content: '打开命令片段',
|
||||
style: {},
|
||||
click: () => emits('openSnippet')
|
||||
},
|
||||
{
|
||||
icon: 'icon-folder',
|
||||
content: '打开 SFTP',
|
||||
style: {},
|
||||
click: () => emits('openSftp')
|
||||
},
|
||||
{
|
||||
icon: 'icon-swap',
|
||||
content: '文件传输列表',
|
||||
style: {
|
||||
iconStyle: {
|
||||
transform: 'rotate(90deg)'
|
||||
},
|
||||
click: () => emits('openTransfer')
|
||||
@@ -58,7 +56,6 @@
|
||||
{
|
||||
icon: 'icon-camera',
|
||||
content: '截图',
|
||||
style: {},
|
||||
click: () => emits('screenshot')
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="terminal-setting-container">
|
||||
<div class="terminal-setting-wrapper">
|
||||
<!-- 主标题 -->
|
||||
<h2 class="terminal-setting-title">新建连接</h2>
|
||||
<!-- 操作栏 -->
|
||||
<div class="terminal-setting-block header-actions">
|
||||
<a-radio-group type="button">
|
||||
<a-radio value="1">分组</a-radio>
|
||||
<a-radio value="2">列表</a-radio>
|
||||
<a-radio value="3">最近连接</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search class="host-filter"
|
||||
placeholder="输入名称/编码/IP 进行过滤" />
|
||||
</div>
|
||||
<!-- 授权主机 -->
|
||||
<div class="terminal-setting-block">
|
||||
<!-- 顶部 -->
|
||||
<div class="terminal-setting-subtitle-wrapper">
|
||||
<h3 class="terminal-setting-subtitle">
|
||||
授权主机
|
||||
</h3>
|
||||
</div>
|
||||
<!-- 内容区域 -->
|
||||
<div class="terminal-setting-body hosts-container">
|
||||
<div class="host-tree">
|
||||
<a-tree :data="hosts.groupTree"
|
||||
:blockNode="true"
|
||||
>
|
||||
|
||||
</a-tree>
|
||||
{{ }}
|
||||
</div>
|
||||
<div class="host-list">
|
||||
{{ hosts.treeNodes }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'newConnectionView'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { AuthorizedHostQueryResponse } from '@/api/asset/asset-authorized-data';
|
||||
import { getCurrentAuthorizedHost } from '@/api/asset/asset-authorized-data';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const hosts = ref<AuthorizedHostQueryResponse>({} as AuthorizedHostQueryResponse);
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await getCurrentAuthorizedHost();
|
||||
hosts.value = data;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.host-filter {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.hosts-container {
|
||||
justify-content: space-between;
|
||||
|
||||
.host-tree {
|
||||
margin-right: 16px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.host-list {
|
||||
width: 490px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { TerminalThemeSchema } from '@/store/modules/terminal/types';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { Terminal } from 'xterm';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -23,7 +23,7 @@
|
||||
onMounted(() => {
|
||||
term.value = new Terminal({
|
||||
theme: props.theme,
|
||||
cols: 47,
|
||||
cols: 42,
|
||||
rows: 6,
|
||||
fontSize: 15,
|
||||
cursorInactiveStyle: 'none',
|
||||
@@ -35,7 +35,7 @@
|
||||
'[92mdr-xr-xr-x.[0m 2 root root [96msbin[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 43 root root [96mlib[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 62 root root [96mlib64[0m\r\n' +
|
||||
'[92mlrwxrwxrwx.[0m 1 root root [90;42mtmp[0m'
|
||||
'[92mlrwxrwxrwx.[0m 1 root root [90;102mtmp[0m'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<!-- 内容区域 -->
|
||||
<div class="terminal-setting-body">
|
||||
<div class="terminal-setting-body terminal-theme-container">
|
||||
<div class="theme-row"
|
||||
v-for="index in ThemeSchema.length / 2"
|
||||
:key="index">
|
||||
@@ -101,9 +101,13 @@
|
||||
@terminal-width: 458px;
|
||||
@terminal-height: 138px;
|
||||
|
||||
.theme-row {
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
.terminal-theme-container {
|
||||
flex-direction: column;
|
||||
|
||||
.theme-row {
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-theme-card {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="terminal-setting-container view-setting-container">
|
||||
<div class="view-setting-wrapper">
|
||||
<div class="terminal-setting-container">
|
||||
<div class="terminal-setting-wrapper">
|
||||
<!-- 主标题 -->
|
||||
<h2 class="terminal-setting-title">外观设置</h2>
|
||||
<!-- 显示设置 -->
|
||||
@@ -24,8 +24,5 @@
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.view-setting-wrapper {
|
||||
width: 932px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -42,14 +42,14 @@
|
||||
import TerminalRightSidebar from './components/layout/terminal-right-sidebar.vue';
|
||||
import TerminalContent from './components/layout/terminal-content.vue';
|
||||
import './assets/styles/layout.less';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import 'xterm/css/xterm.css';
|
||||
|
||||
const terminalStore = useTerminalStore();
|
||||
const dictStore = useDictStore();
|
||||
|
||||
const render = ref(false);
|
||||
const activeKey = ref(InnerTabs.THEME_SETTING.key);
|
||||
const tabs = ref<Array<TabItem>>([InnerTabs.THEME_SETTING]);
|
||||
const activeKey = ref(InnerTabs.NEW_CONNECTION.key);
|
||||
const tabs = ref<Array<TabItem>>([InnerTabs.NEW_CONNECTION]);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
tabs.value.push({
|
||||
key: `host${i}`,
|
||||
@@ -125,7 +125,7 @@
|
||||
}
|
||||
|
||||
&-content {
|
||||
width: 100%;
|
||||
width: calc(100% - var(--sidebar-width) * 2);
|
||||
height: 100%;
|
||||
background: var(--color-bg-content);
|
||||
overflow: auto;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { CSSProperties } from 'vue';
|
||||
export interface SidebarAction {
|
||||
icon: string;
|
||||
content: string;
|
||||
style?: CSSProperties;
|
||||
iconStyle?: CSSProperties;
|
||||
visible?: boolean;
|
||||
click: () => void;
|
||||
}
|
||||
@@ -17,8 +17,8 @@ export const TabType = {
|
||||
|
||||
// 内置 tab
|
||||
export const InnerTabs = {
|
||||
HOST_LIST: {
|
||||
key: 'hostList',
|
||||
NEW_CONNECTION: {
|
||||
key: 'newConnection',
|
||||
title: '新建连接',
|
||||
type: TabType.SETTING
|
||||
},
|
||||
|
||||
@@ -8,9 +8,9 @@ export const DEFAULT_SCHEMA = {
|
||||
foreground: '#C6D0F5',
|
||||
cursor: '#F2D5CF',
|
||||
cursorAccent: '#232634',
|
||||
// selectionInactiveBackground: 'rgba(98, 104, 128, 0.30078125)',
|
||||
selectionBackground: '#C9DDF0',
|
||||
selectionForeground: '#303446',
|
||||
// selectionInactiveBackground: 'rgba(98, 104, 128, 0.30078125)',
|
||||
black: '#51576D',
|
||||
red: '#E78284',
|
||||
green: '#A6D189',
|
||||
@@ -38,9 +38,9 @@ export default [
|
||||
foreground: '#4C4F69',
|
||||
cursor: '#DC8A78',
|
||||
cursorAccent: '#EFF1F5',
|
||||
// selectionInactiveBackground: 'rgba(172, 176, 190, 0.30078125)',
|
||||
selectionForeground: '#EFF1F5',
|
||||
selectionBackground: '#6C6F85',
|
||||
selectionForeground: '#EFF1F5',
|
||||
// selectionInactiveBackground: 'rgba(172, 176, 190, 0.30078125)',
|
||||
black: '#5C5F77',
|
||||
red: '#D20F39',
|
||||
green: '#40A02B',
|
||||
@@ -65,9 +65,9 @@ export default [
|
||||
foreground: '#CAD3F5',
|
||||
cursor: '#F4DBD6',
|
||||
cursorAccent: '#181926',
|
||||
// selectionInactiveBackground: 'rgba(91, 96, 120, 0.30078125)',
|
||||
selectionForeground: '#24273A',
|
||||
selectionBackground: '#A5ADCB',
|
||||
selectionForeground: '#24273A',
|
||||
// selectionInactiveBackground: 'rgba(91, 96, 120, 0.30078125)',
|
||||
black: '#494D64',
|
||||
red: '#ED8796',
|
||||
green: '#A6DA95',
|
||||
@@ -92,9 +92,9 @@ export default [
|
||||
foreground: '#CDD6F4',
|
||||
cursor: '#F5E0DC',
|
||||
cursorAccent: '#11111B',
|
||||
// selectionInactiveBackground: 'rgba(88, 91, 112, 0.30078125)',
|
||||
selectionForeground: '#1E1E2E',
|
||||
selectionBackground: '#A6ADC8',
|
||||
selectionForeground: '#1E1E2E',
|
||||
// selectionInactiveBackground: 'rgba(88, 91, 112, 0.30078125)',
|
||||
black: '#45475A',
|
||||
red: '#F38BA8',
|
||||
green: '#A6E3A1',
|
||||
@@ -163,8 +163,8 @@ export default [
|
||||
foreground: '#F8F8F2',
|
||||
cursor: '#F8F8F2',
|
||||
cursorAccent: '#282A36',
|
||||
selectionForeground: '#44475A',
|
||||
selectionBackground: '#50FA7B',
|
||||
selectionForeground: '#44475A',
|
||||
black: '#21222C',
|
||||
red: '#FF5555',
|
||||
green: '#50FA7B',
|
||||
|
||||
Reference in New Issue
Block a user