🔨 命令发送.

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("openCommandSnippet", true, true, true, "KeyC", true),
new TerminalPreferenceModel.ShortcutKeysModel("openPathBookmark", true, true, true, "KeyP", true), new TerminalPreferenceModel.ShortcutKeysModel("openPathBookmark", true, true, true, "KeyP", true),
new TerminalPreferenceModel.ShortcutKeysModel("openTransferList", true, true, true, "KeyT", 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("screenshot", true, true, true, "KeyS", true),
// 会话快捷键 // 会话快捷键
new TerminalPreferenceModel.ShortcutKeysModel("openNewConnectModal", true, false, true, "KeyN", true), new TerminalPreferenceModel.ShortcutKeysModel("openNewConnectModal", true, false, true, "KeyN", true),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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