🔨 命令发送.

This commit is contained in:
lijiahangmax
2024-12-16 23:20:57 +08:00
parent 1227ed1770
commit 786d07907d
13 changed files with 116 additions and 73 deletions

View File

@@ -92,6 +92,7 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
new TerminalPreferenceModel.ShortcutKeysModel("openCommandSnippet", true, true, true, "KeyC", true),
new TerminalPreferenceModel.ShortcutKeysModel("openPathBookmark", true, true, true, "KeyP", true),
new TerminalPreferenceModel.ShortcutKeysModel("openTransferList", true, true, true, "KeyT", true),
new TerminalPreferenceModel.ShortcutKeysModel("openCommandBar", true, true, true, "KeyI", true),
new TerminalPreferenceModel.ShortcutKeysModel("screenshot", true, true, true, "KeyS", true),
// 会话快捷键
new TerminalPreferenceModel.ShortcutKeysModel("openNewConnectModal", true, false, true, "KeyN", true),

View File

@@ -65,7 +65,7 @@
logRefs.value.push({
id: ref.id,
el: ref.appenderRef,
openSearch: ref.openSearch
openSearch: ref.openSearch,
});
});
};

View File

@@ -65,7 +65,10 @@ export default defineStore('terminal', {
keys: []
} as TerminalShortcutSetting,
},
commandBarVisible: false,
layoutState: {
commandBar: false,
fullscreen: false,
},
hosts: {} as AuthorizedHostQueryResponse,
tabManager: new TerminalTabManager(),
panelManager: new TerminalPanelManager(),
@@ -125,11 +128,6 @@ export default defineStore('terminal', {
}
},
// 修改命令发送显示
setCommandBarVisible(visible: boolean) {
this.commandBarVisible = visible;
},
// 加载主机列表
async loadHosts() {
if (this.hosts.hostList?.length) {

View File

@@ -4,7 +4,7 @@ import type { TerminalTheme } from '@/api/asset/terminal';
export interface TerminalState {
preference: TerminalPreference;
commandBarVisible: boolean;
layoutState: TerminalLayoutState;
hosts: AuthorizedHostQueryResponse;
tabManager: ITerminalTabManager;
panelManager: ITerminalPanelManager;
@@ -99,3 +99,9 @@ export interface TerminalShortcutKeyEditable extends TerminalShortcutKey {
type: number;
shortcutKey?: string;
}
// 终端布局状态
export interface TerminalLayoutState {
commandBar: boolean;
fullscreen: boolean;
}

View File

@@ -1,9 +1,10 @@
import type { VNodeRef } from 'vue';
import { nextTick } from 'vue';
// 设置 ref 自动聚焦
export const setAutoFocus = (el: HTMLElement) => {
export const setAutoFocus: VNodeRef = ((el: HTMLElement) => {
// 自动聚焦
nextTick(() => {
el && el.focus();
});
};
}) as unknown as VNodeRef;

View File

@@ -28,7 +28,7 @@
top: 50%;
left: 50%;
margin-left: -96px;
margin-top: -124px;
margin-top: -128px;
text-align: center;
}
</style>

View File

@@ -28,7 +28,7 @@
top: 50%;
left: 50%;
margin-left: -96px;
margin-top: -124px;
margin-top: -128px;
text-align: center;
}
</style>

View File

@@ -22,8 +22,8 @@
</div>
<!-- 右侧按钮 -->
<div class="command-header-right">
<!-- 隐藏 -->
<a-button size="mini" @click="setCommandBarVisible(false)">
<!-- 关闭 -->
<a-button size="mini" @click="close">
<template #icon>
<icon-down />
</template>
@@ -34,8 +34,8 @@
<!-- 命令框 -->
<div class="command-input">
<a-textarea v-model="text"
placeholder="输入命令, F8 发送"
:auto-size="{ minRows: 3, maxRows: 3 }"
placeholder="输入命令, F8 发送"
@keyup="checkCommandKey" />
</div>
</div>
@@ -52,7 +52,7 @@
import { ref } from 'vue';
import { useTerminalStore } from '@/store';
const { setCommandBarVisible, appendCommandToCurrentSession } = useTerminalStore();
const { layoutState, appendCommandToCurrentSession } = useTerminalStore();
const text = ref('');
@@ -74,11 +74,18 @@
}
};
// 关闭
const close = () => {
// 隐藏
layoutState.commandBar = false;
};
</script>
<style lang="less" scoped>
.command-bar {
height: 122px;
height: 128px;
overflow: hidden;
}
.command-header {

View File

@@ -1,30 +1,33 @@
<template>
<div class="terminal-content">
<!-- 内容 tabs -->
<a-tabs v-if="tabManager.active"
v-model:active-key="tabManager.active"
class="main-tabs">
<a-tab-pane v-for="tab in tabManager.items"
:key="tab.key"
:title="tab.title">
<!-- 新建连接 -->
<new-connection-view v-if="tab.key === TerminalTabs.NEW_CONNECTION.key" />
<!-- 快捷键设置 -->
<terminal-shortcut-setting v-else-if="tab.key === TerminalTabs.SHORTCUT_SETTING.key" />
<!-- 显示设置 -->
<terminal-display-setting v-else-if="tab.key === TerminalTabs.DISPLAY_SETTING.key" />
<!-- 主题设置 -->
<terminal-theme-setting v-else-if="tab.key === TerminalTabs.THEME_SETTING.key" />
<!-- 终端设置 -->
<terminal-general-setting v-else-if="tab.key === TerminalTabs.TERMINAL_SETTING.key" />
<!-- 终端面板 -->
<terminal-panels-view v-else-if="tab.key === TerminalTabs.TERMINAL_PANEL.key" />
</a-tab-pane>
</a-tabs>
<!-- 承载页推荐 -->
<empty-recommend v-else />
<div class="main-content">
<!-- 内容部分 -->
<div class="main-content-wrapper" :style="{ height: `calc(100% - ${mainSubtractHeight})` }">
<!-- 内容 tabs -->
<a-tabs v-if="tabManager.active"
v-model:active-key="tabManager.active"
class="main-tabs">
<a-tab-pane v-for="tab in tabManager.items"
:key="tab.key"
:title="tab.title">
<!-- 新建连接 -->
<new-connection-view v-if="tab.key === TerminalTabs.NEW_CONNECTION.key" />
<!-- 快捷键设置 -->
<terminal-shortcut-setting v-else-if="tab.key === TerminalTabs.SHORTCUT_SETTING.key" />
<!-- 显示设置 -->
<terminal-display-setting v-else-if="tab.key === TerminalTabs.DISPLAY_SETTING.key" />
<!-- 主题设置 -->
<terminal-theme-setting v-else-if="tab.key === TerminalTabs.THEME_SETTING.key" />
<!-- 终端设置 -->
<terminal-general-setting v-else-if="tab.key === TerminalTabs.TERMINAL_SETTING.key" />
<!-- 终端面板 -->
<terminal-panels-view v-else-if="tab.key === TerminalTabs.TERMINAL_PANEL.key" />
</a-tab-pane>
</a-tabs>
<!-- 承载页推荐 -->
<empty-recommend v-else />
</div>
<!-- 底部发送命令 -->
<command-bar v-if="commandBarVisible" />
<command-bar v-if="layoutState.commandBar && tabManager.active === TerminalTabs.TERMINAL_PANEL.key" />
</div>
</template>
@@ -38,7 +41,7 @@
import type { ISshSession } from '../../types/define';
import { TerminalTabs, TerminalShortcutKeys, PanelSessionType } from '../../types/const';
import { useTerminalStore } from '@/store';
import { onMounted, onUnmounted, watch } from 'vue';
import { computed, onMounted, onUnmounted, watch } from 'vue';
import { addEventListen, removeEventListen } from '@/utils/event';
import EmptyRecommend from './empty-recommend.vue';
import TerminalPanelsView from './terminal-panels-view.vue';
@@ -51,7 +54,21 @@
const emits = defineEmits(['openCommandSnippet', 'openPathBookmark', 'openTransferList', 'openCommandBar', 'screenshot']);
const { commandBarVisible, preference, tabManager, getCurrentSession } = useTerminalStore();
const { layoutState, preference, tabManager, getCurrentSession, sessionManager } = useTerminalStore();
// 内容部分减去的高度
const mainSubtractHeight = computed(() => {
let height = 0;
// 底部发送命令高度
if (layoutState.commandBar && tabManager.active === TerminalTabs.TERMINAL_PANEL.key) {
height += 128;
}
// 自适应
setTimeout(() => {
sessionManager.dispatchResize();
}, 200);
return `${height}px`;
});
// 监听 tab 切换
watch(() => tabManager.active, (active, before) => {
@@ -144,11 +161,16 @@
</script>
<style lang="less" scoped>
.terminal-content {
.main-content {
width: 100%;
height: 100%;
position: relative;
&-wrapper {
width: 100%;
height: 100%;
}
:deep(.main-tabs) {
width: 100%;
height: 100%;
@@ -166,7 +188,13 @@
&::-webkit-scrollbar {
display: none;
}
.arco-tabs-content-list, .arco-tabs-pane {
width: 100%;
height: 100%;
}
}
}
}
</style>

View File

@@ -115,7 +115,7 @@
<style lang="less" scoped>
.terminal-panels-container {
width: 100%;
height: calc(100vh - var(--header-height));
height: 100%;
position: relative;
display: flex;
}

View File

@@ -17,7 +17,7 @@
<!-- 启用-修改中 -->
<a-input v-if="item.editable && item.enabled"
v-model="item.shortcutKey"
:ref="setAutoFocus as unknown as VNodeRef"
:ref="setAutoFocus"
class="trigger-input"
size="small"
placeholder="请按下快捷键"
@@ -73,7 +73,6 @@
<script lang="ts" setup>
import type { TerminalShortcutKeyEditable } from '@/store/modules/terminal/types';
import type { VNodeRef } from 'vue';
import { setAutoFocus } from '@/utils/dom';
import { TerminalShortcutKeys } from '../../../types/const';

View File

@@ -18,7 +18,7 @@
<a-space direction="vertical">
<!-- 过滤输入框 -->
<a-input size="small"
:ref="setAutoFocus as unknown as VNodeRef"
:ref="setAutoFocus"
:model-value="filterValue[0]"
@input="(value: string) => setFilterValue([value])"
@press-enter="handleFilterConfirm" />
@@ -142,7 +142,6 @@
</script>
<script lang="ts" setup>
import type { VNodeRef } from 'vue';
import type { TableData } from '@arco-design/web-vue/es/table/interface';
import type { SftpFile, ISftpSession } from '../../types/define';
import type { SftpSetting } from '@/api/system/setting';

View File

@@ -1,10 +1,10 @@
<template>
<div v-if="render"
class="host-terminal-layout"
:class="{ 'terminal-full-layout': fullscreen }">
:class="{ 'terminal-full-layout': layoutState.fullscreen }">
<!-- 头部区域 -->
<header class="host-terminal-layout-header">
<layout-header @fullscreen="enterFullscreen" />
<layout-header @fullscreen="toggleFullscreen" />
</header>
<!-- 主体区域 -->
<main class="host-terminal-layout-main">
@@ -21,7 +21,7 @@
@open-command-snippet="() => snippetRef.open()"
@open-path-bookmark="() => pathRef.open()"
@open-transfer-list="() => transferRef.open()"
@open-command-bar="setCommandBarVisible(true)"
@open-command-bar="openCommandBar"
@screenshot="screenshot" />
</main>
<!-- 右侧操作栏 -->
@@ -29,16 +29,16 @@
<right-sidebar @open-command-snippet="() => snippetRef.open()"
@open-path-bookmark="() => pathRef.open()"
@open-transfer-list="() => transferRef.open()"
@open-command-bar="setCommandBarVisible(true)"
@open-command-bar="openCommandBar"
@screenshot="screenshot" />
</div>
</main>
<!-- 退出全屏 -->
<a-button v-if="fullscreen"
<a-button v-if="layoutState.fullscreen"
class="exit-fullscreen"
shape="circle"
title="退出全屏"
@click="exitFullscreen">
@click="toggleFullscreen">
<icon-fullscreen-exit />
</a-button>
<!-- 命令片段列表抽屉 -->
@@ -79,8 +79,7 @@
const {
fetchPreference, getCurrentSession, openSession,
preference, loadHosts, hosts, tabManager,
setCommandBarVisible
layoutState, preference, loadHosts, hosts, tabManager, sessionManager
} = useTerminalStore();
const { loading, setLoading } = useLoading(true);
const { enter: enterFull, exit: exitFull } = useFullscreen();
@@ -91,7 +90,6 @@
const snippetRef = ref();
const pathRef = ref();
const transferRef = ref();
const fullscreen = ref();
// 终端截屏
const screenshot = () => {
@@ -101,18 +99,28 @@
}
};
// 进入全屏
const enterFullscreen = () => {
fullscreen.value = true;
// 进入全屏
enterFull();
// 打开命令发送
const openCommandBar = () => {
const session = getCurrentSession<ISshSession>(PanelSessionType.SSH.type, true);
if (session) {
layoutState.commandBar = true;
}
};
// 退出全屏
const exitFullscreen = () => {
fullscreen.value = false;
// 退出全屏
exitFull();
// 切换全屏
const toggleFullscreen = () => {
layoutState.fullscreen = !layoutState.fullscreen;
if (layoutState.fullscreen) {
// 进入全屏
enterFull();
} else {
// 退出全屏
exitFull();
}
// 自适应
setTimeout(() => {
sessionManager.dispatchResize();
}, 200);
};
// 自动聚焦
@@ -222,10 +230,6 @@
width: 100%;
}
}
:deep(.terminal-panels-container) {
height: 100vh !important;
}
}
&-header {