🔨 命令发送.
This commit is contained in:
@@ -1,18 +1,18 @@
|
|||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
druid:
|
druid:
|
||||||
url: jdbc:mysql://116.62.194.246:3306/orion_visor?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true
|
url: jdbc:mysql://127.0.0.1:3306/orion_visor?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true
|
||||||
username: root
|
username: root
|
||||||
password: Orionsec@0379
|
password: Data@123456
|
||||||
initial-size: 0
|
initial-size: 0
|
||||||
min-idle: 1
|
min-idle: 1
|
||||||
max-active: 5
|
max-active: 5
|
||||||
stat-view-servlet:
|
stat-view-servlet:
|
||||||
enabled: false
|
enabled: false
|
||||||
redis:
|
redis:
|
||||||
host: 116.62.194.246
|
host: 127.0.0.1
|
||||||
port: 6379
|
port: 6379
|
||||||
password: Orionsec@0379
|
password: Data@123456
|
||||||
redisson:
|
redisson:
|
||||||
threads: 2
|
threads: 2
|
||||||
netty-threads: 2
|
netty-threads: 2
|
||||||
|
|||||||
@@ -259,11 +259,6 @@ public class TerminalPreferenceModel implements GenericsDataModel {
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class ActionBarSettingModel implements IJsonObject {
|
public static class ActionBarSettingModel implements IJsonObject {
|
||||||
|
|
||||||
/**
|
|
||||||
* 命令输入框
|
|
||||||
*/
|
|
||||||
private Boolean commandInput;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接状态
|
* 连接状态
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ public class TerminalPreferenceStrategy extends AbstractGenericsDataStrategy<Ter
|
|||||||
.toJsonString();
|
.toJsonString();
|
||||||
// 操作栏设置
|
// 操作栏设置
|
||||||
String actionBarSetting = TerminalPreferenceModel.ActionBarSettingModel.builder()
|
String actionBarSetting = TerminalPreferenceModel.ActionBarSettingModel.builder()
|
||||||
.commandInput(false)
|
|
||||||
.connectStatus(true)
|
.connectStatus(true)
|
||||||
.toTop(false)
|
.toTop(false)
|
||||||
.toBottom(false)
|
.toBottom(false)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export default defineStore('terminal', {
|
|||||||
keys: []
|
keys: []
|
||||||
} as TerminalShortcutSetting,
|
} as TerminalShortcutSetting,
|
||||||
},
|
},
|
||||||
|
commandBarVisible: false,
|
||||||
hosts: {} as AuthorizedHostQueryResponse,
|
hosts: {} as AuthorizedHostQueryResponse,
|
||||||
tabManager: new TerminalTabManager(),
|
tabManager: new TerminalTabManager(),
|
||||||
panelManager: new TerminalPanelManager(),
|
panelManager: new TerminalPanelManager(),
|
||||||
@@ -124,6 +125,11 @@ export default defineStore('terminal', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 修改命令发送显示
|
||||||
|
setCommandBarVisible(visible: boolean) {
|
||||||
|
this.commandBarVisible = visible;
|
||||||
|
},
|
||||||
|
|
||||||
// 加载主机列表
|
// 加载主机列表
|
||||||
async loadHosts() {
|
async loadHosts() {
|
||||||
if (this.hosts.hostList?.length) {
|
if (this.hosts.hostList?.length) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { TerminalTheme } from '@/api/asset/terminal';
|
|||||||
|
|
||||||
export interface TerminalState {
|
export interface TerminalState {
|
||||||
preference: TerminalPreference;
|
preference: TerminalPreference;
|
||||||
|
commandBarVisible: boolean;
|
||||||
hosts: AuthorizedHostQueryResponse;
|
hosts: AuthorizedHostQueryResponse;
|
||||||
tabManager: ITerminalTabManager;
|
tabManager: ITerminalTabManager;
|
||||||
panelManager: ITerminalPanelManager;
|
panelManager: ITerminalPanelManager;
|
||||||
@@ -38,7 +39,6 @@ export interface TerminalDisplaySetting {
|
|||||||
|
|
||||||
// 操作栏设置
|
// 操作栏设置
|
||||||
export interface TerminalActionBarSetting {
|
export interface TerminalActionBarSetting {
|
||||||
commandInput?: boolean;
|
|
||||||
connectStatus?: boolean;
|
connectStatus?: boolean;
|
||||||
|
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<div class="command-bar">
|
||||||
|
<div class="command-header">
|
||||||
|
<!-- 左侧按钮 -->
|
||||||
|
<div class="command-header-left">
|
||||||
|
<!-- 粘贴 -->
|
||||||
|
<a-button size="mini"
|
||||||
|
class="mr8"
|
||||||
|
@click="paste">
|
||||||
|
粘贴
|
||||||
|
<template #icon>
|
||||||
|
<icon-send />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<!-- 清空 -->
|
||||||
|
<a-button size="mini" @click="clear">
|
||||||
|
清空
|
||||||
|
<template #icon>
|
||||||
|
<icon-delete />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧按钮 -->
|
||||||
|
<div class="command-header-right">
|
||||||
|
<!-- 隐藏 -->
|
||||||
|
<a-button size="mini" @click="setCommandBarVisible(false)">
|
||||||
|
<template #icon>
|
||||||
|
<icon-down />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="command-body">
|
||||||
|
<!-- 命令框 -->
|
||||||
|
<div class="command-input">
|
||||||
|
<a-textarea v-model="text"
|
||||||
|
placeholder="输入命令, F8 发送"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 3 }"
|
||||||
|
@keyup="checkCommandKey" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'commandBar'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
|
||||||
|
const { setCommandBarVisible, appendCommandToCurrentSession } = useTerminalStore();
|
||||||
|
|
||||||
|
const text = ref('');
|
||||||
|
|
||||||
|
// 粘贴
|
||||||
|
const paste = () => {
|
||||||
|
appendCommandToCurrentSession(text.value);
|
||||||
|
text.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空
|
||||||
|
const clear = () => {
|
||||||
|
text.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查命令快捷键
|
||||||
|
const checkCommandKey = async (e: KeyboardEvent) => {
|
||||||
|
if (text.value && e.code === 'F8') {
|
||||||
|
paste();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.command-bar {
|
||||||
|
height: 122px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-header {
|
||||||
|
border-top: 1px var(--color-bg-sidebar) solid;
|
||||||
|
width: 100%;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&-left, &-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-body {
|
||||||
|
width: 100%;
|
||||||
|
height: 92px;
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.command-input {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(textarea) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -23,6 +23,8 @@
|
|||||||
</a-tabs>
|
</a-tabs>
|
||||||
<!-- 承载页推荐 -->
|
<!-- 承载页推荐 -->
|
||||||
<empty-recommend v-else />
|
<empty-recommend v-else />
|
||||||
|
<!-- 底部发送命令 -->
|
||||||
|
<command-bar v-if="commandBarVisible" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -45,10 +47,11 @@
|
|||||||
import TerminalThemeSetting from '../setting/theme/terminal-theme-setting.vue';
|
import TerminalThemeSetting from '../setting/theme/terminal-theme-setting.vue';
|
||||||
import TerminalGeneralSetting from '../setting/general/terminal-general-setting.vue';
|
import TerminalGeneralSetting from '../setting/general/terminal-general-setting.vue';
|
||||||
import TerminalShortcutSetting from '../setting/shortcut/terminal-shortcut-setting.vue';
|
import TerminalShortcutSetting from '../setting/shortcut/terminal-shortcut-setting.vue';
|
||||||
|
import CommandBar from '../command-bar/index.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['openCommandSnippet', 'openPathBookmark', 'openTransferList', 'screenshot']);
|
const emits = defineEmits(['openCommandSnippet', 'openPathBookmark', 'openTransferList', 'openCommandBar', 'screenshot']);
|
||||||
|
|
||||||
const { preference, tabManager, getCurrentSession } = useTerminalStore();
|
const { commandBarVisible, preference, tabManager, getCurrentSession } = useTerminalStore();
|
||||||
|
|
||||||
// 监听 tab 切换
|
// 监听 tab 切换
|
||||||
watch(() => tabManager.active, (active, before) => {
|
watch(() => tabManager.active, (active, before) => {
|
||||||
@@ -111,6 +114,10 @@
|
|||||||
// 打开文件传输列表
|
// 打开文件传输列表
|
||||||
emits('openTransferList');
|
emits('openTransferList');
|
||||||
break;
|
break;
|
||||||
|
case TerminalShortcutKeys.OPEN_COMMAND_BAR:
|
||||||
|
// 打开发送命令
|
||||||
|
emits('openCommandBar');
|
||||||
|
break;
|
||||||
case TerminalShortcutKeys.SCREENSHOT:
|
case TerminalShortcutKeys.SCREENSHOT:
|
||||||
// 截图
|
// 截图
|
||||||
emits('screenshot');
|
emits('screenshot');
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
import type { SidebarAction } from '../../types/define';
|
import type { SidebarAction } from '../../types/define';
|
||||||
import IconActions from './icon-actions.vue';
|
import IconActions from './icon-actions.vue';
|
||||||
|
|
||||||
const emits = defineEmits(['openCommandSnippet', 'openPathBookmark', 'openTransferList', 'screenshot']);
|
const emits = defineEmits(['openCommandSnippet', 'openPathBookmark', 'openTransferList', 'openCommandBar', 'screenshot']);
|
||||||
|
|
||||||
// 顶部操作
|
// 顶部操作
|
||||||
const topActions = [
|
const topActions = [
|
||||||
@@ -46,6 +46,10 @@
|
|||||||
// 底部操作
|
// 底部操作
|
||||||
const bottomActions: Array<SidebarAction> = [
|
const bottomActions: Array<SidebarAction> = [
|
||||||
{
|
{
|
||||||
|
icon: 'icon-send',
|
||||||
|
content: '发送命令',
|
||||||
|
click: () => emits('openCommandBar')
|
||||||
|
}, {
|
||||||
icon: 'icon-camera',
|
icon: 'icon-camera',
|
||||||
content: '截图',
|
content: '截图',
|
||||||
click: () => emits('screenshot')
|
click: () => emits('screenshot')
|
||||||
|
|||||||
@@ -24,12 +24,6 @@
|
|||||||
:actions="actions"
|
:actions="actions"
|
||||||
position="bottom" />
|
position="bottom" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 命令输入框 -->
|
|
||||||
<a-form-item field="commandInput" label="命令输入框">
|
|
||||||
<a-switch v-model="formModel.commandInput"
|
|
||||||
:default-checked="true"
|
|
||||||
type="round" />
|
|
||||||
</a-form-item>
|
|
||||||
<!-- 终端连接状态 -->
|
<!-- 终端连接状态 -->
|
||||||
<a-form-item field="showStatus" label="终端连接状态">
|
<a-form-item field="showStatus" label="终端连接状态">
|
||||||
<a-switch v-model="formModel.connectStatus"
|
<a-switch v-model="formModel.connectStatus"
|
||||||
|
|||||||
@@ -152,6 +152,7 @@
|
|||||||
// 非初始化则修改终端样式
|
// 非初始化则修改终端样式
|
||||||
if (before) {
|
if (before) {
|
||||||
Object.values(sessionManager.sessions)
|
Object.values(sessionManager.sessions)
|
||||||
|
.filter(Boolean)
|
||||||
.filter(s => s.type === PanelSessionType.SSH.type)
|
.filter(s => s.type === PanelSessionType.SSH.type)
|
||||||
.map(s => s as ISshSession)
|
.map(s => s as ISshSession)
|
||||||
.forEach(s => {
|
.forEach(s => {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
document.body.setAttribute('terminal-theme', theme.dark ? 'dark' : 'light');
|
document.body.setAttribute('terminal-theme', theme.dark ? 'dark' : 'light');
|
||||||
// 修改终端主题
|
// 修改终端主题
|
||||||
Object.values(sessionManager.sessions)
|
Object.values(sessionManager.sessions)
|
||||||
|
.filter(Boolean)
|
||||||
.filter(s => s.type === PanelSessionType.SSH.type)
|
.filter(s => s.type === PanelSessionType.SSH.type)
|
||||||
.map(s => s as ISshSession)
|
.map(s => s as ISshSession)
|
||||||
.forEach(s => {
|
.forEach(s => {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<a-doption v-for="(action, index) in actions"
|
<a-doption v-for="(action, index) in actions"
|
||||||
:key="index"
|
:key="index"
|
||||||
:disabled="!session.handler.enabledStatus(action.item)"
|
:disabled="!session.handler.enabledStatus(action.item)"
|
||||||
@click="emits('click', action.item)">
|
@click="emits('handle', action.item)">
|
||||||
<!-- 图标 -->
|
<!-- 图标 -->
|
||||||
<div class="terminal-context-menu-icon">
|
<div class="terminal-context-menu-icon">
|
||||||
<component :is="action.icon" />
|
<component :is="action.icon" />
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
session?: ISshSession;
|
session?: ISshSession;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emits = defineEmits(['click']);
|
const emits = defineEmits(['handle']);
|
||||||
|
|
||||||
const { preference } = useTerminalStore();
|
const { preference } = useTerminalStore();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 头部 -->
|
||||||
|
<div class="ssh-header">
|
||||||
|
<!-- 左侧操作 -->
|
||||||
|
<div class="ssh-header-left">
|
||||||
|
<!-- 主机地址 -->
|
||||||
|
<span class="address-wrapper">
|
||||||
|
<span class="text-copy"
|
||||||
|
:title="address"
|
||||||
|
@click="copy(address as string, true)">
|
||||||
|
{{ address }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧操作 -->
|
||||||
|
<div class="ssh-header-right">
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<icon-actions class="ssh-header-right-action-bar"
|
||||||
|
wrapper-class="ssh-header-icon-wrapper"
|
||||||
|
icon-class="ssh-header-icon"
|
||||||
|
:actions="rightActions"
|
||||||
|
position="bottom" />
|
||||||
|
<!-- 连接状态 -->
|
||||||
|
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
||||||
|
class="status-bridge"
|
||||||
|
:status="getDictValue(sessionStatusKey, session ? session.status : 0, 'status')"
|
||||||
|
:text="getDictValue(sessionStatusKey, session ? session.status : 0)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'sshHeader'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ISshSession, SidebarAction } from '../../types/define';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
|
import { ActionBarItems, sessionStatusKey } from '../../types/const';
|
||||||
|
import { copy } from '@/hooks/copy';
|
||||||
|
import IconActions from '../layout/icon-actions.vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['handle']);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
address: string;
|
||||||
|
session?: ISshSession;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { getDictValue } = useDictStore();
|
||||||
|
const { preference } = useTerminalStore();
|
||||||
|
|
||||||
|
// 右侧操作
|
||||||
|
const rightActions = computed<Array<SidebarAction>>(() => {
|
||||||
|
return ActionBarItems.map(s => {
|
||||||
|
return {
|
||||||
|
icon: s.icon,
|
||||||
|
content: s.content,
|
||||||
|
visible: preference.actionBarSetting[s.item] !== false,
|
||||||
|
disabled: props.session?.handler.enabledStatus(s.item) === false,
|
||||||
|
click: () => emits('handle', s.item)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@ssh-header-height: 36px;
|
||||||
|
|
||||||
|
.ssh-header {
|
||||||
|
width: 100%;
|
||||||
|
height: @ssh-header-height;
|
||||||
|
padding: 0 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: var(--color-bg-panel-bar);
|
||||||
|
|
||||||
|
&-left, &-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
width: 25%;
|
||||||
|
|
||||||
|
.address-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: 'IP:';
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
width: 75%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.command-input {
|
||||||
|
width: 36%;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right-action-bar {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
:deep(.ssh-header-icon-wrapper) {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ssh-header-icon) {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-bridge {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 2px 0 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
:deep(.arco-badge-status-text) {
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
height: 56%;
|
||||||
|
margin: 0 12px 0 6px;
|
||||||
|
border-left: 2px solid var(--color-fill-4);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,44 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ssh-container">
|
<div class="ssh-container">
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<div class="ssh-header">
|
<ssh-header :address="tab.address"
|
||||||
<!-- 左侧操作 -->
|
:session="session"
|
||||||
<div class="ssh-header-left">
|
@handle="doTerminalHandle" />
|
||||||
<!-- 主机地址 -->
|
|
||||||
<span class="address-wrapper">
|
|
||||||
<span class="text-copy"
|
|
||||||
:title="tab.address"
|
|
||||||
@click="copy(tab.address as string, true)">
|
|
||||||
{{ tab.address }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- 右侧操作 -->
|
|
||||||
<div class="ssh-header-right">
|
|
||||||
<!-- 命令输入框 -->
|
|
||||||
<a-textarea class="command-input mr8"
|
|
||||||
v-if="preference.actionBarSetting.commandInput !== false"
|
|
||||||
v-model="commandInput"
|
|
||||||
:auto-size="{ minRows: 1, maxRows: 1 }"
|
|
||||||
placeholder="F8 发送命令"
|
|
||||||
allow-clear
|
|
||||||
@keyup="writeCommandInput" />
|
|
||||||
<!-- 操作按钮 -->
|
|
||||||
<icon-actions class="ssh-header-right-action-bar"
|
|
||||||
wrapper-class="ssh-header-icon-wrapper"
|
|
||||||
icon-class="ssh-header-icon"
|
|
||||||
:actions="rightActions"
|
|
||||||
position="bottom" />
|
|
||||||
<!-- 连接状态 -->
|
|
||||||
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
|
||||||
class="status-bridge"
|
|
||||||
:status="getDictValue(sessionStatusKey, session ? session.status : 0, 'status')"
|
|
||||||
:text="getDictValue(sessionStatusKey, session ? session.status : 0)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 终端右键菜单 -->
|
<!-- 终端右键菜单 -->
|
||||||
<ssh-context-menu :session="session"
|
<ssh-context-menu :session="session"
|
||||||
@click="doTerminalHandle">
|
@handle="doTerminalHandle">
|
||||||
<!-- 终端容器 -->
|
<!-- 终端容器 -->
|
||||||
<div class="ssh-wrapper"
|
<div class="ssh-wrapper"
|
||||||
:style="{ background: preference.theme.schema.background }">
|
:style="{ background: preference.theme.schema.background }">
|
||||||
@@ -49,10 +17,11 @@
|
|||||||
class="search-modal"
|
class="search-modal"
|
||||||
@find="findWords"
|
@find="findWords"
|
||||||
@close="focus" />
|
@close="focus" />
|
||||||
<!-- 上传文件模态框 -->
|
|
||||||
<sftp-upload-modal ref="uploadModal" @closed="focus" />
|
|
||||||
</div>
|
</div>
|
||||||
</ssh-context-menu>
|
</ssh-context-menu>
|
||||||
|
<!-- 上传文件模态框 -->
|
||||||
|
<sftp-upload-modal ref="uploadModal" @closed="focus" />
|
||||||
<!-- 命令编辑器 -->
|
<!-- 命令编辑器 -->
|
||||||
<shell-editor-modal ref="editorModal"
|
<shell-editor-modal ref="editorModal"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
@@ -71,13 +40,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ISshSession, TerminalPanelTabItem, SidebarAction } from '../../types/define';
|
import type { ISshSession, TerminalPanelTabItem } from '../../types/define';
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import { copy } from '@/hooks/copy';
|
import SshHeader from './ssh-header.vue';
|
||||||
import { ActionBarItems, sessionStatusKey } from '../../types/const';
|
|
||||||
import ShellEditorModal from '@/components/view/shell-editor/modal/index.vue';
|
import ShellEditorModal from '@/components/view/shell-editor/modal/index.vue';
|
||||||
import IconActions from '../layout/icon-actions.vue';
|
|
||||||
import SshContextMenu from './ssh-context-menu.vue';
|
import SshContextMenu from './ssh-context-menu.vue';
|
||||||
import SftpUploadModal from '../sftp/sftp-upload-modal.vue';
|
import SftpUploadModal from '../sftp/sftp-upload-modal.vue';
|
||||||
import XtermSearchModal from '@/components/xterm/search-modal/index.vue';
|
import XtermSearchModal from '@/components/xterm/search-modal/index.vue';
|
||||||
@@ -92,19 +59,9 @@
|
|||||||
const editorModal = ref();
|
const editorModal = ref();
|
||||||
const searchModal = ref();
|
const searchModal = ref();
|
||||||
const uploadModal = ref();
|
const uploadModal = ref();
|
||||||
const commandInput = ref();
|
|
||||||
const terminalRef = ref();
|
const terminalRef = ref();
|
||||||
const session = ref<ISshSession>();
|
const session = ref<ISshSession>();
|
||||||
|
|
||||||
// 发送命令
|
|
||||||
const writeCommandInput = async (e: KeyboardEvent) => {
|
|
||||||
const value = commandInput.value;
|
|
||||||
if (value && e.code === 'F8') {
|
|
||||||
writeCommand(value);
|
|
||||||
commandInput.value = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送命令
|
// 发送命令
|
||||||
const writeCommand = (value: string) => {
|
const writeCommand = (value: string) => {
|
||||||
if (session.value?.canWrite) {
|
if (session.value?.canWrite) {
|
||||||
@@ -127,19 +84,6 @@
|
|||||||
session.value?.handler.invokeHandle.call(session.value?.handler, handle);
|
session.value?.handler.invokeHandle.call(session.value?.handler, handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 右侧操作
|
|
||||||
const rightActions = computed<Array<SidebarAction>>(() => {
|
|
||||||
return ActionBarItems.map(s => {
|
|
||||||
return {
|
|
||||||
icon: s.icon,
|
|
||||||
content: s.content,
|
|
||||||
visible: preference.actionBarSetting[s.item] !== false,
|
|
||||||
disabled: session.value?.handler.enabledStatus(s.item) === false,
|
|
||||||
click: () => doTerminalHandle(s.item)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化会话
|
// 初始化会话
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 创建终端处理器
|
// 创建终端处理器
|
||||||
@@ -167,84 +111,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ssh-header {
|
|
||||||
width: 100%;
|
|
||||||
height: @ssh-header-height;
|
|
||||||
padding: 0 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background: var(--color-bg-panel-bar);
|
|
||||||
|
|
||||||
&-left, &-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-left {
|
|
||||||
width: 25%;
|
|
||||||
|
|
||||||
.address-wrapper {
|
|
||||||
height: 100%;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: 'IP:';
|
|
||||||
padding-right: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-right {
|
|
||||||
width: 75%;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.command-input {
|
|
||||||
width: 36%;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-right-action-bar {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
:deep(.ssh-header-icon-wrapper) {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
margin: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ssh-header-icon) {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-bridge {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 2px 0 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
:deep(.arco-badge-status-text) {
|
|
||||||
width: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
height: 56%;
|
|
||||||
margin: 0 12px 0 6px;
|
|
||||||
border-left: 2px solid var(--color-fill-4);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ssh-wrapper {
|
.ssh-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - @ssh-header-height);
|
height: calc(100% - @ssh-header-height);
|
||||||
|
|||||||
@@ -21,6 +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)"
|
||||||
@screenshot="screenshot" />
|
@screenshot="screenshot" />
|
||||||
</main>
|
</main>
|
||||||
<!-- 右侧操作栏 -->
|
<!-- 右侧操作栏 -->
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
<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)"
|
||||||
@screenshot="screenshot" />
|
@screenshot="screenshot" />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -75,7 +77,11 @@
|
|||||||
import '@/assets/style/host-terminal-layout.less';
|
import '@/assets/style/host-terminal-layout.less';
|
||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
|
||||||
const { fetchPreference, getCurrentSession, openSession, preference, loadHosts, hosts, tabManager } = useTerminalStore();
|
const {
|
||||||
|
fetchPreference, getCurrentSession, openSession,
|
||||||
|
preference, loadHosts, hosts, tabManager,
|
||||||
|
setCommandBarVisible
|
||||||
|
} = useTerminalStore();
|
||||||
const { loading, setLoading } = useLoading(true);
|
const { loading, setLoading } = useLoading(true);
|
||||||
const { enter: enterFull, exit: exitFull } = useFullscreen();
|
const { enter: enterFull, exit: exitFull } = useFullscreen();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|||||||
@@ -211,6 +211,8 @@ export const TerminalShortcutKeys = {
|
|||||||
OPEN_PATH_BOOKMARK: 'openPathBookmark',
|
OPEN_PATH_BOOKMARK: 'openPathBookmark',
|
||||||
// 打开文件传输列表
|
// 打开文件传输列表
|
||||||
OPEN_TRANSFER_LIST: 'openTransferList',
|
OPEN_TRANSFER_LIST: 'openTransferList',
|
||||||
|
// 打开发送命令
|
||||||
|
OPEN_COMMAND_BAR: 'openCommandBar',
|
||||||
// 截图
|
// 截图
|
||||||
SCREENSHOT: 'screenshot',
|
SCREENSHOT: 'screenshot',
|
||||||
// 打开新建连接弹框
|
// 打开新建连接弹框
|
||||||
@@ -257,6 +259,10 @@ export const TerminalShortcutItems: Array<ShortcutKeyItem> = [
|
|||||||
item: TerminalShortcutKeys.OPEN_TRANSFER_LIST,
|
item: TerminalShortcutKeys.OPEN_TRANSFER_LIST,
|
||||||
content: '打开文件传输列表',
|
content: '打开文件传输列表',
|
||||||
type: TerminalShortcutType.GLOBAL
|
type: TerminalShortcutType.GLOBAL
|
||||||
|
}, {
|
||||||
|
item: TerminalShortcutKeys.OPEN_COMMAND_BAR,
|
||||||
|
content: '打开发送命令',
|
||||||
|
type: TerminalShortcutType.GLOBAL
|
||||||
}, {
|
}, {
|
||||||
item: TerminalShortcutKeys.SCREENSHOT,
|
item: TerminalShortcutKeys.SCREENSHOT,
|
||||||
content: '截图',
|
content: '截图',
|
||||||
|
|||||||
Reference in New Issue
Block a user