feat: 终端操作栏配置化.
This commit is contained in:
@@ -24,15 +24,18 @@ public class TerminalPreferenceModel implements PreferenceModel {
|
|||||||
@Schema(description = "新建连接类型")
|
@Schema(description = "新建连接类型")
|
||||||
private String newConnectionType;
|
private String newConnectionType;
|
||||||
|
|
||||||
|
@Schema(description = "终端主题")
|
||||||
|
private JSONObject theme;
|
||||||
|
|
||||||
@Schema(description = "显示设置")
|
@Schema(description = "显示设置")
|
||||||
private JSONObject displaySetting;
|
private JSONObject displaySetting;
|
||||||
|
|
||||||
|
@Schema(description = "操作栏设置")
|
||||||
|
private JSONObject actionBarSetting;
|
||||||
|
|
||||||
@Schema(description = "背景设置")
|
@Schema(description = "背景设置")
|
||||||
private JSONObject backgroundSetting;
|
private JSONObject backgroundSetting;
|
||||||
|
|
||||||
@Schema(description = "终端主题")
|
|
||||||
private JSONObject theme;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|||||||
@@ -16,21 +16,24 @@ public class TerminalPreferenceStrategy implements IPreferenceStrategy<TerminalP
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TerminalPreferenceModel getDefault() {
|
public TerminalPreferenceModel getDefault() {
|
||||||
return TerminalPreferenceModel.builder()
|
// 默认显示设置
|
||||||
.newConnectionType("group")
|
JSONObject defaultDisplaySetting = TerminalPreferenceModel.DisplaySettingModel
|
||||||
.displaySetting(TerminalPreferenceModel.DisplaySettingModel.builder()
|
.builder()
|
||||||
.fontFamily("_")
|
.fontFamily("_")
|
||||||
.fontSize(12)
|
.fontSize(14)
|
||||||
.lineHeight(1.00)
|
.lineHeight(1.00)
|
||||||
.fontWeight("normal")
|
.fontWeight("normal")
|
||||||
.fontWeightBold("bold")
|
.fontWeightBold("bold")
|
||||||
.cursorStyle("bar")
|
.cursorStyle("bar")
|
||||||
.cursorBlink(true)
|
.cursorBlink(true)
|
||||||
.build()
|
.build()
|
||||||
.toJson()
|
.toJson();
|
||||||
)
|
return TerminalPreferenceModel.builder()
|
||||||
.backgroundSetting(new JSONObject())
|
.newConnectionType("group")
|
||||||
.theme(new JSONObject())
|
.theme(new JSONObject())
|
||||||
|
.displaySetting(defaultDisplaySetting)
|
||||||
|
.actionBarSetting(new JSONObject())
|
||||||
|
.backgroundSetting(new JSONObject())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TerminalDisplaySetting, TerminalPreference, TerminalState } from './types';
|
import type { TerminalActionBarSetting, TerminalDisplaySetting, TerminalPreference, TerminalState } from './types';
|
||||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
@@ -15,14 +15,17 @@ export const PreferenceItem = {
|
|||||||
THEME: 'theme',
|
THEME: 'theme',
|
||||||
// 显示设置
|
// 显示设置
|
||||||
DISPLAY_SETTING: 'displaySetting',
|
DISPLAY_SETTING: 'displaySetting',
|
||||||
|
// 操作栏设置
|
||||||
|
ACTION_BAR_SETTING: 'actionBarSetting',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineStore('terminal', {
|
export default defineStore('terminal', {
|
||||||
state: (): TerminalState => ({
|
state: (): TerminalState => ({
|
||||||
preference: {
|
preference: {
|
||||||
newConnectionType: 'group',
|
newConnectionType: 'group',
|
||||||
|
theme: {} as TerminalTheme,
|
||||||
displaySetting: {} as TerminalDisplaySetting,
|
displaySetting: {} as TerminalDisplaySetting,
|
||||||
theme: {} as TerminalTheme
|
actionBarSetting: {} as TerminalActionBarSetting,
|
||||||
},
|
},
|
||||||
tabManager: new TerminalTabManager(),
|
tabManager: new TerminalTabManager(),
|
||||||
sessionManager: new TerminalSessionManager()
|
sessionManager: new TerminalSessionManager()
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ export interface TerminalState {
|
|||||||
// 终端配置
|
// 终端配置
|
||||||
export interface TerminalPreference {
|
export interface TerminalPreference {
|
||||||
newConnectionType: string;
|
newConnectionType: string;
|
||||||
displaySetting: TerminalDisplaySetting;
|
|
||||||
theme: TerminalTheme;
|
theme: TerminalTheme;
|
||||||
|
displaySetting: TerminalDisplaySetting;
|
||||||
|
actionBarSetting: TerminalActionBarSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示设置
|
// 显示设置
|
||||||
@@ -25,3 +26,11 @@ export interface TerminalDisplaySetting {
|
|||||||
cursorBlink?: boolean;
|
cursorBlink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 操作栏设置
|
||||||
|
export interface TerminalActionBarSetting {
|
||||||
|
commandInput?: boolean;
|
||||||
|
connectStatus?: boolean;
|
||||||
|
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -192,11 +192,14 @@ body[terminal-theme='dark'] .arco-modal-container {
|
|||||||
background: var(--color-sidebar-icon-bg);
|
background: var(--color-sidebar-icon-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.checked-item {
|
||||||
|
background: var(--color-sidebar-icon-bg);
|
||||||
|
}
|
||||||
|
|
||||||
&.disabled-item {
|
&.disabled-item {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端设置容器
|
// 终端设置容器
|
||||||
@@ -239,6 +242,14 @@ body[terminal-theme='dark'] .arco-modal-container {
|
|||||||
|
|
||||||
.terminal-setting-body {
|
.terminal-setting-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
&.block-body {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--color-fill-4);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
:content="action.content">
|
:content="action.content">
|
||||||
<div class="terminal-sidebar-icon-wrapper" v-if="action.visible !== false">
|
<div class="terminal-sidebar-icon-wrapper" v-if="action.visible !== false">
|
||||||
<div class="terminal-sidebar-icon"
|
<div class="terminal-sidebar-icon"
|
||||||
:class="[iconClass, action.disabled !== false ? '' : 'disabled-item']"
|
:class="[
|
||||||
|
iconClass,
|
||||||
|
action.disabled !== false ? '' : 'disabled-item',
|
||||||
|
action.checked === true ? 'checked-item' : '',
|
||||||
|
]"
|
||||||
@click="action.disabled !== false ? action.click() : false">
|
@click="action.disabled !== false ? action.click() : false">
|
||||||
<component :is="action.icon" :style="action?.iconStyle" />
|
<component :is="action.icon" :style="action?.iconStyle" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,12 +85,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-logo-text {
|
&-logo-text {
|
||||||
height: var(--header-height);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-tabs {
|
&-tabs {
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div class="terminal-setting-block">
|
||||||
|
<!-- 顶部 -->
|
||||||
|
<div class="terminal-setting-subtitle-wrapper">
|
||||||
|
<h3 class="terminal-setting-subtitle">
|
||||||
|
操作栏设置
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<!-- 提示 -->
|
||||||
|
<a-alert class="mb16">修改后会立刻保存, 立即生效 (无需刷新页面)</a-alert>
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<div class="terminal-setting-body block-body setting-body">
|
||||||
|
<a-form class="terminal-setting-form"
|
||||||
|
:model="formModel"
|
||||||
|
layout="vertical">
|
||||||
|
<a-space>
|
||||||
|
<!-- 顶部操作按钮 -->
|
||||||
|
<a-form-item field="actions" label="顶部操作按钮">
|
||||||
|
<icon-actions class="form-item-actions"
|
||||||
|
:actions="actions"
|
||||||
|
position="bottom" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 命令输入框 -->
|
||||||
|
<a-form-item field="showCommandInput" label="命令输入框">
|
||||||
|
<a-switch v-model="formModel.commandInput"
|
||||||
|
class="form-item-command-input"
|
||||||
|
:default-checked="true"
|
||||||
|
type="round" />
|
||||||
|
</a-form-item>
|
||||||
|
<!-- 连接状态 -->
|
||||||
|
<a-form-item field="showStatus" label="连接状态">
|
||||||
|
<a-switch v-model="formModel.connectStatus"
|
||||||
|
:default-checked="true"
|
||||||
|
type="round" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-space>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'TerminalActionBarBlock'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { TerminalActionBarSetting } from '@/store/modules/terminal/types';
|
||||||
|
import type { SidebarAction } from '../../types/terminal.type';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { useTerminalStore } from '@/store';
|
||||||
|
import { PreferenceItem } from '@/store/modules/terminal';
|
||||||
|
import { ActionBarItems } from '../../types/terminal.const';
|
||||||
|
import IconActions from '../layout/icon-actions.vue';
|
||||||
|
|
||||||
|
const { preference, updateTerminalPreference } = useTerminalStore();
|
||||||
|
|
||||||
|
const formModel = ref<TerminalActionBarSetting>({ ...preference.actionBarSetting });
|
||||||
|
|
||||||
|
// 监听同步
|
||||||
|
watch(formModel, (v) => {
|
||||||
|
if (!v) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 同步
|
||||||
|
updateTerminalPreference(PreferenceItem.ACTION_BAR_SETTING, formModel.value, true);
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
|
// 右侧操作
|
||||||
|
const actions = computed<Array<SidebarAction>>(() => {
|
||||||
|
return ActionBarItems.map(s => {
|
||||||
|
return {
|
||||||
|
icon: s.icon,
|
||||||
|
content: (formModel.value[s.item] === false ? '显示 ' : '隐藏 ') + s.content,
|
||||||
|
checked: formModel.value[s.item] !== false,
|
||||||
|
click: () => {
|
||||||
|
formModel.value[s.item] = formModel.value[s.item] === false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
|
||||||
|
.form-item-actions {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
:deep(.terminal-sidebar-icon-wrapper) {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.terminal-sidebar-icon) {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item-actions, .form-item-command-input {
|
||||||
|
margin-right: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-form) {
|
||||||
|
.arco-form-item-label {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -3,18 +3,21 @@
|
|||||||
<!-- 顶部 -->
|
<!-- 顶部 -->
|
||||||
<div class="terminal-setting-subtitle-wrapper">
|
<div class="terminal-setting-subtitle-wrapper">
|
||||||
<h3 class="terminal-setting-subtitle">
|
<h3 class="terminal-setting-subtitle">
|
||||||
显示设置
|
显示偏好
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 提示 -->
|
||||||
|
<a-alert class="mb16">修改后会立刻保存, 重新打开终端后生效 (无需刷新页面)</a-alert>
|
||||||
<!-- 内容区域 -->
|
<!-- 内容区域 -->
|
||||||
<div class="terminal-setting-body">
|
<div class="terminal-setting-body block-body setting-body">
|
||||||
<div class="terminal-setting-form">
|
<a-form class="terminal-setting-form"
|
||||||
<a-form :model="formModel" layout="vertical">
|
:model="formModel"
|
||||||
|
layout="vertical">
|
||||||
<a-space>
|
<a-space>
|
||||||
<!-- 字体样式 -->
|
<!-- 字体样式 -->
|
||||||
<a-form-item field="fontFamily" label="字体样式">
|
<a-form-item field="fontFamily" label="字体样式">
|
||||||
<a-select v-model="formModel.fontFamily"
|
<a-select v-model="formModel.fontFamily"
|
||||||
class="form-item form-item-font-family"
|
class="form-item-font-family"
|
||||||
placeholder="请选择字体样式"
|
placeholder="请选择字体样式"
|
||||||
:options="toOptions(fontFamilyKey)"
|
:options="toOptions(fontFamilyKey)"
|
||||||
:allow-create="true"
|
:allow-create="true"
|
||||||
@@ -27,14 +30,14 @@
|
|||||||
<!-- 字体大小 -->
|
<!-- 字体大小 -->
|
||||||
<a-form-item field="fontSize" label="字体大小">
|
<a-form-item field="fontSize" label="字体大小">
|
||||||
<a-select v-model="formModel.fontSize"
|
<a-select v-model="formModel.fontSize"
|
||||||
class="form-item form-item-font-size"
|
class="form-item-font-size"
|
||||||
placeholder="请选择字体大小"
|
placeholder="请选择字体大小"
|
||||||
:options="toOptions(fontSizeKey)" />
|
:options="toOptions(fontSizeKey)" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 行高 -->
|
<!-- 行高 -->
|
||||||
<a-form-item field="lineHeight" label="行高">
|
<a-form-item field="lineHeight" label="行高">
|
||||||
<a-input-number v-model="formModel.lineHeight"
|
<a-input-number v-model="formModel.lineHeight"
|
||||||
class="form-item form-item-line-height"
|
class="form-item-line-height"
|
||||||
placeholder="请输入行高"
|
placeholder="请输入行高"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
:min="1"
|
:min="1"
|
||||||
@@ -46,14 +49,14 @@
|
|||||||
<!-- 普通文本字重 -->
|
<!-- 普通文本字重 -->
|
||||||
<a-form-item field="fontWeight" label="普通文本字重">
|
<a-form-item field="fontWeight" label="普通文本字重">
|
||||||
<a-select v-model="formModel.fontWeight"
|
<a-select v-model="formModel.fontWeight"
|
||||||
class="form-item form-item-font-weight"
|
class="form-item-font-weight"
|
||||||
placeholder="请选择字重"
|
placeholder="请选择字重"
|
||||||
:options="toOptions(fontWeightKey)" />
|
:options="toOptions(fontWeightKey)" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 加粗文本字重 -->
|
<!-- 加粗文本字重 -->
|
||||||
<a-form-item field="fontWeightBold" label="加粗文本字重">
|
<a-form-item field="fontWeightBold" label="加粗文本字重">
|
||||||
<a-select v-model="formModel.fontWeightBold"
|
<a-select v-model="formModel.fontWeightBold"
|
||||||
class="form-item form-item-font-bold-weight"
|
class="form-item-font-bold-weight"
|
||||||
placeholder="请选择字重"
|
placeholder="请选择字重"
|
||||||
:options="toOptions(fontWeightKey)" />
|
:options="toOptions(fontWeightKey)" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -63,18 +66,17 @@
|
|||||||
<a-form-item field="cursorStyle" label="光标样式">
|
<a-form-item field="cursorStyle" label="光标样式">
|
||||||
<a-radio-group type="button"
|
<a-radio-group type="button"
|
||||||
v-model="formModel.cursorStyle"
|
v-model="formModel.cursorStyle"
|
||||||
class="form-item form-item-cursor-style usn"
|
class="form-item-cursor-style usn"
|
||||||
:options="toRadioOptions(cursorStyleKey)" />
|
:options="toRadioOptions(cursorStyleKey)" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<!-- 光标闪烁 -->
|
<!-- 光标闪烁 -->
|
||||||
<a-form-item field="cursorBlink" label="光标是否闪烁">
|
<a-form-item field="cursorBlink" label="光标是否闪烁">
|
||||||
<a-switch v-model="formModel.cursorBlink"
|
<a-switch v-model="formModel.cursorBlink"
|
||||||
type="round"
|
type="round"
|
||||||
class="form-item form-item-cursor-blink" />
|
class="form-item-cursor-blink" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
|
||||||
<!-- 预览区域 -->
|
<!-- 预览区域 -->
|
||||||
<div class="terminal-example">
|
<div class="terminal-example">
|
||||||
<span class="terminal-example-label">预览效果</span>
|
<span class="terminal-example-label">预览效果</span>
|
||||||
@@ -90,7 +92,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'TerminalFontBlock'
|
name: 'TerminalDisplayBlock'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -109,15 +111,6 @@
|
|||||||
const previewTerminal = ref();
|
const previewTerminal = ref();
|
||||||
const formModel = ref<TerminalDisplaySetting>({ ...preference.displaySetting });
|
const formModel = ref<TerminalDisplaySetting>({ ...preference.displaySetting });
|
||||||
|
|
||||||
// 监听主题变化 动态修改预览样式
|
|
||||||
watch(() => preference.theme, (v) => {
|
|
||||||
if (!v) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const options = previewTerminal.value?.term?.options;
|
|
||||||
options && (options.theme = v);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听内容变化
|
// 监听内容变化
|
||||||
watch(formModel, (v) => {
|
watch(formModel, (v) => {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
@@ -136,7 +129,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 同步
|
// 同步
|
||||||
updateTerminalPreference(PreferenceItem.DISPLAY_SETTING, formModel.value);
|
updateTerminalPreference(PreferenceItem.DISPLAY_SETTING, formModel.value, true);
|
||||||
// 聚焦
|
// 聚焦
|
||||||
previewTerminal.value.term.focus();
|
previewTerminal.value.term.focus();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
@@ -146,13 +139,8 @@
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@terminal-width: 458px;
|
@terminal-width: 458px;
|
||||||
|
|
||||||
.terminal-setting-body {
|
.setting-body {
|
||||||
height: 248px;
|
height: 248px;
|
||||||
width: 100%;
|
|
||||||
padding: 16px;
|
|
||||||
border: 1px solid var(--color-fill-4);
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,21 +2,24 @@
|
|||||||
<div class="terminal-setting-container">
|
<div class="terminal-setting-container">
|
||||||
<div class="terminal-setting-wrapper">
|
<div class="terminal-setting-wrapper">
|
||||||
<!-- 主标题 -->
|
<!-- 主标题 -->
|
||||||
<h2 class="terminal-setting-title">外观设置</h2>
|
<h2 class="terminal-setting-title">显示设置</h2>
|
||||||
<!-- 显示设置 -->
|
<!-- 显示偏好 -->
|
||||||
<terminal-display-block />
|
<terminal-display-block />
|
||||||
|
<!-- 顶部工具栏 -->
|
||||||
|
<terminal-action-bar-block />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'TerminalViewSetting'
|
name: 'TerminalDisplaySetting'
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import TerminalDisplayBlock from './terminal-display-block.vue';
|
import TerminalDisplayBlock from './terminal-display-block.vue';
|
||||||
|
import TerminalActionBarBlock from './terminal-action-bar-block.vue';
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -17,19 +17,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 右侧操作 -->
|
<!-- 右侧操作 -->
|
||||||
<div class="terminal-header-right">
|
<div class="terminal-header-right">
|
||||||
<!-- 代码输入框 -->
|
<!-- 命令输入框 -->
|
||||||
<a-textarea class="command-input mr8"
|
<a-textarea class="command-input mr8"
|
||||||
|
v-if="preference.actionBarSetting.commandInput !== false"
|
||||||
v-model="commandInput"
|
v-model="commandInput"
|
||||||
:auto-size="{ minRows: 1, maxRows: 1 }"
|
:auto-size="{ minRows: 1, maxRows: 1 }"
|
||||||
placeholder="F8 发送命令"
|
placeholder="F8 发送命令"
|
||||||
allow-clear
|
allow-clear
|
||||||
@keyup="writeCommandInput" />
|
@keyup="writeCommandInput" />
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<icon-actions class="terminal-header-right-icon-actions"
|
<icon-actions class="terminal-header-right-action-bar"
|
||||||
:actions="rightActions"
|
:actions="rightActions"
|
||||||
position="bottom" />
|
position="bottom" />
|
||||||
<!-- 状态 -->
|
<!-- 连接状态 -->
|
||||||
<a-badge class="status-bridge"
|
<a-badge v-if="preference.actionBarSetting.connectStatus !== false"
|
||||||
|
class="status-bridge"
|
||||||
:status="getDictValue(connectStatusKey, session ? session.status : 0, 'status')"
|
:status="getDictValue(connectStatusKey, session ? session.status : 0, 'status')"
|
||||||
:text="getDictValue(connectStatusKey, session ? session.status : 0)" />
|
:text="getDictValue(connectStatusKey, session ? session.status : 0)" />
|
||||||
</div>
|
</div>
|
||||||
@@ -59,12 +61,11 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SidebarAction } from '../../types/terminal.type';
|
import type { ITerminalSession, TerminalTabItem, SidebarAction } from '../../types/terminal.type';
|
||||||
import type { ITerminalSession, TerminalTabItem } from '../../types/terminal.type';
|
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||||
import { useDictStore, useTerminalStore } from '@/store';
|
import { useDictStore, useTerminalStore } from '@/store';
|
||||||
import useCopy from '@/hooks/copy';
|
import useCopy from '@/hooks/copy';
|
||||||
import { connectStatusKey } from '../../types/terminal.const';
|
import { ActionBarItems, connectStatusKey } from '../../types/terminal.const';
|
||||||
import IconActions from '../layout/icon-actions.vue';
|
import IconActions from '../layout/icon-actions.vue';
|
||||||
import ShellEditorModal from '@/components/view/shell-editor/shell-editor-modal.vue';
|
import ShellEditorModal from '@/components/view/shell-editor/shell-editor-modal.vue';
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@
|
|||||||
|
|
||||||
const { copy, readText } = useCopy();
|
const { copy, readText } = useCopy();
|
||||||
const { getDictValue } = useDictStore();
|
const { getDictValue } = useDictStore();
|
||||||
const { preference, sessionManager } = useTerminalStore();
|
const { preference, tabManager, sessionManager } = useTerminalStore();
|
||||||
|
|
||||||
const modal = ref();
|
const modal = ref();
|
||||||
const commandInput = ref();
|
const commandInput = ref();
|
||||||
@@ -127,69 +128,40 @@
|
|||||||
session.value?.focus();
|
session.value?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 右侧操作
|
// 操作禁用状态
|
||||||
const rightActions = computed<Array<SidebarAction>>(() => [
|
const actionsDisableStatus = computed<Record<string, boolean | undefined>>(() => {
|
||||||
{
|
return {
|
||||||
icon: 'icon-up',
|
paste: session.value?.canWrite,
|
||||||
content: '去顶部',
|
interrupt: session.value?.canWrite,
|
||||||
click: () => {
|
enter: session.value?.canWrite,
|
||||||
session.value?.toTop();
|
commandEditor: session.value?.canWrite,
|
||||||
}
|
disconnect: session.value?.connected,
|
||||||
}, {
|
};
|
||||||
icon: 'icon-down',
|
});
|
||||||
content: '去底部',
|
|
||||||
click: () => {
|
// 操作点击逻辑
|
||||||
session.value?.toBottom();
|
const actionsClickHandler: Record<string, () => void> = {
|
||||||
}
|
// 去顶部
|
||||||
}, {
|
toTop: () => session.value?.toTop(),
|
||||||
icon: 'icon-expand',
|
// 去底部
|
||||||
content: '全选',
|
toBottom: () => session.value?.toBottom(),
|
||||||
click: () => {
|
// 全选
|
||||||
session.value?.selectAll();
|
checkAll: () => session.value?.selectAll(),
|
||||||
}
|
// 复制选中部分
|
||||||
}, {
|
copy: () => copy(session.value?.getSelection(), '已复制'),
|
||||||
icon: 'icon-copy',
|
// 粘贴
|
||||||
content: '复制选中部分',
|
paste: async () => session.value?.paste(await readText()),
|
||||||
click: () => {
|
// ctrl + c
|
||||||
copy(session.value?.getSelection(), '已复制');
|
interrupt: () => session.value?.paste(String.fromCharCode(3)),
|
||||||
}
|
// 回车
|
||||||
}, {
|
enter: () => session.value?.paste(String.fromCharCode(13)),
|
||||||
icon: 'icon-paste',
|
// 命令编辑器
|
||||||
content: '粘贴',
|
commandEditor: () => modal.value.open('', ''),
|
||||||
disabled: session.value?.canWrite,
|
// 搜索
|
||||||
click: async () => {
|
search: () => {
|
||||||
session.value?.paste(await readText());
|
},
|
||||||
}
|
// 增大字号
|
||||||
}, {
|
fontSizePlus: () => {
|
||||||
icon: 'icon-formula',
|
|
||||||
content: 'ctrl + c',
|
|
||||||
disabled: session.value?.canWrite,
|
|
||||||
click: () => {
|
|
||||||
session.value?.paste(String.fromCharCode(3));
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-play-arrow-fill',
|
|
||||||
content: '回车',
|
|
||||||
disabled: session.value?.canWrite,
|
|
||||||
click: () => {
|
|
||||||
session.value?.paste(String.fromCharCode(13));
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-code-square',
|
|
||||||
content: '命令编辑器',
|
|
||||||
disabled: session.value?.canWrite,
|
|
||||||
click: () => {
|
|
||||||
modal.value.open('', '');
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-search',
|
|
||||||
content: '搜索',
|
|
||||||
click: () => {
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-zoom-in',
|
|
||||||
content: '增大字号',
|
|
||||||
click: () => {
|
|
||||||
if (session.value) {
|
if (session.value) {
|
||||||
session.value.setOption('fontSize', session.value.getOption('fontSize') + 1);
|
session.value.setOption('fontSize', session.value.getOption('fontSize') + 1);
|
||||||
if (session.value.connected) {
|
if (session.value.connected) {
|
||||||
@@ -197,11 +169,9 @@
|
|||||||
session.value.focus();
|
session.value.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}, {
|
// 减小字号
|
||||||
icon: 'icon-zoom-out',
|
fontSizeSubtract: () => {
|
||||||
content: '减小字号',
|
|
||||||
click: () => {
|
|
||||||
if (session.value) {
|
if (session.value) {
|
||||||
session.value.setOption('fontSize', session.value.getOption('fontSize') - 1);
|
session.value.setOption('fontSize', session.value.getOption('fontSize') - 1);
|
||||||
if (session.value.connected) {
|
if (session.value.connected) {
|
||||||
@@ -209,22 +179,29 @@
|
|||||||
session.value.focus();
|
session.value.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-delete',
|
|
||||||
content: '清空',
|
|
||||||
click: () => {
|
|
||||||
session.value?.clear();
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
icon: 'icon-poweroff',
|
|
||||||
content: '关闭',
|
|
||||||
disabled: session.value?.connected,
|
|
||||||
click: () => {
|
|
||||||
session.value?.logout();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]);
|
// 清空
|
||||||
|
clear: () => session.value?.clear(),
|
||||||
|
// 断开连接
|
||||||
|
disconnect: () => session.value?.disconnect(),
|
||||||
|
// 关闭
|
||||||
|
close: () => tabManager.deleteTab(props.tab.key),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 右侧操作
|
||||||
|
const rightActions = computed<Array<SidebarAction>>(() => {
|
||||||
|
return ActionBarItems.map(s => {
|
||||||
|
return {
|
||||||
|
icon: s.icon,
|
||||||
|
content: s.content,
|
||||||
|
visible: preference.actionBarSetting[s.item] !== false,
|
||||||
|
disabled: actionsDisableStatus.value[s.item] !== false,
|
||||||
|
click: () => {
|
||||||
|
actionsClickHandler[s.item] && actionsClickHandler[s.item]();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 初始化会话
|
// 初始化会话
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -297,7 +274,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-right-icon-actions {
|
&-right-action-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
:deep(.terminal-sidebar-icon-wrapper) {
|
:deep(.terminal-sidebar-icon-wrapper) {
|
||||||
@@ -315,6 +292,10 @@
|
|||||||
.status-bridge {
|
.status-bridge {
|
||||||
margin: 0 2px 0 8px;
|
margin: 0 2px 0 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
:deep(.arco-badge-status-text) {
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ export default class TerminalSessionManager implements ITerminalSessionManager {
|
|||||||
if (!session) {
|
if (!session) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 登出
|
// 关闭连接
|
||||||
session.logout();
|
session.disconnect();
|
||||||
// 关闭 session
|
// 关闭 session
|
||||||
session.close();
|
session.close();
|
||||||
// 移除 session
|
// 移除 session
|
||||||
|
|||||||
@@ -165,8 +165,8 @@ export default class TerminalSession implements ITerminalSession {
|
|||||||
this.inst.options[option as keyof ITerminalOptions] = value;
|
this.inst.options[option as keyof ITerminalOptions] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登出
|
// 断开连接
|
||||||
logout(): void {
|
disconnect(): void {
|
||||||
// 发送关闭消息
|
// 发送关闭消息
|
||||||
this.channel.send(InputProtocol.CLOSE, {
|
this.channel.send(InputProtocol.CLOSE, {
|
||||||
sessionId: this.sessionId
|
sessionId: this.sessionId
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
&-header {
|
&-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 44px;
|
height: var(--header-height);
|
||||||
background: var(--color-bg-header);
|
background: var(--color-bg-header);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|||||||
@@ -68,6 +68,67 @@ export const TerminalStatus = {
|
|||||||
CLOSED: 2
|
CLOSED: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 终端操作栏-操作项
|
||||||
|
export const ActionBarItems = [
|
||||||
|
{
|
||||||
|
item: 'toTop',
|
||||||
|
icon: 'icon-up',
|
||||||
|
content: '去顶部',
|
||||||
|
}, {
|
||||||
|
item: 'toBottom',
|
||||||
|
icon: 'icon-down',
|
||||||
|
content: '去底部',
|
||||||
|
}, {
|
||||||
|
item: 'checkAll',
|
||||||
|
icon: 'icon-expand',
|
||||||
|
content: '全选',
|
||||||
|
}, {
|
||||||
|
item: 'copy',
|
||||||
|
icon: 'icon-copy',
|
||||||
|
content: '复制选中部分',
|
||||||
|
}, {
|
||||||
|
item: 'paste',
|
||||||
|
icon: 'icon-paste',
|
||||||
|
content: '粘贴',
|
||||||
|
}, {
|
||||||
|
item: 'interrupt',
|
||||||
|
icon: 'icon-formula',
|
||||||
|
content: 'ctrl + c',
|
||||||
|
}, {
|
||||||
|
item: 'enter',
|
||||||
|
icon: 'icon-play-arrow-fill',
|
||||||
|
content: '回车',
|
||||||
|
}, {
|
||||||
|
item: 'commandEditor',
|
||||||
|
icon: 'icon-code-square',
|
||||||
|
content: '命令编辑器',
|
||||||
|
}, {
|
||||||
|
item: 'search',
|
||||||
|
icon: 'icon-search',
|
||||||
|
content: '搜索',
|
||||||
|
}, {
|
||||||
|
item: 'fontSizePlus',
|
||||||
|
icon: 'icon-zoom-in',
|
||||||
|
content: '增大字号',
|
||||||
|
}, {
|
||||||
|
item: 'fontSizeSubtract',
|
||||||
|
icon: 'icon-zoom-out',
|
||||||
|
content: '减小字号',
|
||||||
|
}, {
|
||||||
|
item: 'clear',
|
||||||
|
icon: 'icon-delete',
|
||||||
|
content: '清空',
|
||||||
|
}, {
|
||||||
|
item: 'disconnect',
|
||||||
|
icon: 'icon-poweroff',
|
||||||
|
content: '断开连接',
|
||||||
|
}, {
|
||||||
|
item: 'close',
|
||||||
|
icon: 'icon-close',
|
||||||
|
content: '关闭',
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
// 获取会话id
|
// 获取会话id
|
||||||
export const nextSessionId = (): string => {
|
export const nextSessionId = (): string => {
|
||||||
return getUUID().replaceAll('-', '').substring(0, 10);
|
return getUUID().replaceAll('-', '').substring(0, 10);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export interface SidebarAction {
|
|||||||
content: string;
|
content: string;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
checked?: boolean;
|
||||||
iconStyle?: CSSProperties;
|
iconStyle?: CSSProperties;
|
||||||
click: () => void;
|
click: () => void;
|
||||||
}
|
}
|
||||||
@@ -161,8 +162,8 @@ export interface ITerminalSession {
|
|||||||
getOption: (option: string) => any;
|
getOption: (option: string) => any;
|
||||||
// 设置配置
|
// 设置配置
|
||||||
setOption: (option: string, value: any) => void;
|
setOption: (option: string, value: any) => void;
|
||||||
// 登出
|
// 断开连接
|
||||||
logout: () => void;
|
disconnect: () => void;
|
||||||
// 关闭
|
// 关闭
|
||||||
close: () => void;
|
close: () => void;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user