🔨 命令发送.
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
logRefs.value.push({
|
||||
id: ref.id,
|
||||
el: ref.appenderRef,
|
||||
openSearch: ref.openSearch
|
||||
openSearch: ref.openSearch,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -96px;
|
||||
margin-top: -124px;
|
||||
margin-top: -128px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-left: -96px;
|
||||
margin-top: -124px;
|
||||
margin-top: -128px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user