feat(terminal): add terminal button visibility setting (#13772)

This commit is contained in:
ssongliu
2026-09-09 18:39:08 +08:00
committed by GitHub
parent 605c8cc6db
commit a02c25ebcc
24 changed files with 200 additions and 52 deletions

View File

@@ -149,14 +149,14 @@ func checkSettingValueRange(key, value string) bool {
// @Tags System Setting
// @Summary Update system terminal setting
// @Accept json
// @Param request body dto.TerminalInfo true "request"
// @Param request body dto.TerminalUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /core/settings/terminal/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统终端配置","formatEN":"update system terminal setting"}
func (b *BaseApi) UpdateTerminalSetting(c *gin.Context) {
var req dto.TerminalInfo
var req dto.TerminalUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

View File

@@ -236,16 +236,31 @@ type MenuLabelSort struct {
}
type TerminalInfo struct {
LineHeight string `json:"lineHeight"`
LetterSpacing string `json:"letterSpacing"`
FontSize string `json:"fontSize"`
FontFamily string `json:"fontFamily"`
BackgroundColor string `json:"backgroundColor"`
ForegroundColor string `json:"foregroundColor"`
CursorBlink string `json:"cursorBlink"`
CursorStyle string `json:"cursorStyle"`
Scrollback string `json:"scrollback"`
ScrollSensitivity string `json:"scrollSensitivity"`
ShowTerminalButton string `json:"showTerminalButton"`
LineHeight string `json:"lineHeight"`
LetterSpacing string `json:"letterSpacing"`
FontSize string `json:"fontSize"`
FontFamily string `json:"fontFamily"`
BackgroundColor string `json:"backgroundColor"`
ForegroundColor string `json:"foregroundColor"`
CursorBlink string `json:"cursorBlink"`
CursorStyle string `json:"cursorStyle"`
Scrollback string `json:"scrollback"`
ScrollSensitivity string `json:"scrollSensitivity"`
}
type TerminalUpdate struct {
ShowTerminalButton *string `json:"showTerminalButton,omitempty" validate:"omitempty,oneof=Enable Disable"`
LineHeight *string `json:"lineHeight,omitempty"`
LetterSpacing *string `json:"letterSpacing,omitempty"`
FontSize *string `json:"fontSize,omitempty"`
FontFamily *string `json:"fontFamily,omitempty"`
BackgroundColor *string `json:"backgroundColor,omitempty"`
ForegroundColor *string `json:"foregroundColor,omitempty"`
CursorBlink *string `json:"cursorBlink,omitempty"`
CursorStyle *string `json:"cursorStyle,omitempty"`
Scrollback *string `json:"scrollback,omitempty"`
ScrollSensitivity *string `json:"scrollSensitivity,omitempty"`
}
type AppstoreUpdate struct {

View File

@@ -57,7 +57,7 @@ type ISettingService interface {
UpdateProxy(req dto.ProxyUpdate) error
GetTerminalInfo() (*dto.TerminalInfo, error)
UpdateTerminal(req dto.TerminalInfo) error
UpdateTerminal(req dto.TerminalUpdate) error
UpdateSystemSSL() error
GenerateRSAKey() error
@@ -559,7 +559,7 @@ func (u *SettingService) GetTerminalInfo() (*dto.TerminalInfo, error) {
for _, set := range setting {
settingMap[set.Key] = set.Value
}
var info dto.TerminalInfo
info := dto.TerminalInfo{ShowTerminalButton: "Enable"}
arr, err := json.Marshal(settingMap)
if err != nil {
return nil, err
@@ -569,39 +569,30 @@ func (u *SettingService) GetTerminalInfo() (*dto.TerminalInfo, error) {
}
return &info, err
}
func (u *SettingService) UpdateTerminal(req dto.TerminalInfo) error {
if err := settingRepo.UpdateOrCreate("LineHeight", req.LineHeight); err != nil {
return err
func (u *SettingService) UpdateTerminal(req dto.TerminalUpdate) error {
settings := []struct {
key string
value *string
}{
{"ShowTerminalButton", req.ShowTerminalButton},
{"LineHeight", req.LineHeight},
{"LetterSpacing", req.LetterSpacing},
{"FontSize", req.FontSize},
{"FontFamily", req.FontFamily},
{"CursorBlink", req.CursorBlink},
{"BackgroundColor", req.BackgroundColor},
{"ForegroundColor", req.ForegroundColor},
{"CursorStyle", req.CursorStyle},
{"Scrollback", req.Scrollback},
{"ScrollSensitivity", req.ScrollSensitivity},
}
if err := settingRepo.UpdateOrCreate("LetterSpacing", req.LetterSpacing); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("FontSize", req.FontSize); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("FontFamily", req.FontFamily); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("CursorBlink", req.CursorBlink); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("BackgroundColor", req.BackgroundColor); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("ForegroundColor", req.ForegroundColor); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("CursorBlink", req.CursorBlink); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("CursorStyle", req.CursorStyle); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("Scrollback", req.Scrollback); err != nil {
return err
}
if err := settingRepo.UpdateOrCreate("ScrollSensitivity", req.ScrollSensitivity); err != nil {
return err
for _, setting := range settings {
if setting.value == nil {
continue
}
if err := settingRepo.UpdateOrCreate(setting.key, *setting.value); err != nil {
return err
}
}
return nil
}

View File

@@ -11683,7 +11683,7 @@ const docTemplate = `{
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/dto.TerminalInfo"
"$ref": "#/definitions/dto.TerminalUpdate"
}
}
],
@@ -40869,6 +40869,47 @@ const docTemplate = `{
},
"scrollback": {
"type": "string"
},
"showTerminalButton": {
"type": "string"
}
},
"type": "object"
},
"dto.TerminalUpdate": {
"properties": {
"backgroundColor": {
"type": "string"
},
"cursorBlink": {
"type": "string"
},
"cursorStyle": {
"type": "string"
},
"fontFamily": {
"type": "string"
},
"fontSize": {
"type": "string"
},
"foregroundColor": {
"type": "string"
},
"letterSpacing": {
"type": "string"
},
"lineHeight": {
"type": "string"
},
"scrollSensitivity": {
"type": "string"
},
"scrollback": {
"type": "string"
},
"showTerminalButton": {
"type": "string"
}
},
"type": "object"

View File

@@ -11679,7 +11679,7 @@
"name": "request",
"required": true,
"schema": {
"$ref": "#/definitions/dto.TerminalInfo"
"$ref": "#/definitions/dto.TerminalUpdate"
}
}
],
@@ -40865,6 +40865,47 @@
},
"scrollback": {
"type": "string"
},
"showTerminalButton": {
"type": "string"
}
},
"type": "object"
},
"dto.TerminalUpdate": {
"properties": {
"backgroundColor": {
"type": "string"
},
"cursorBlink": {
"type": "string"
},
"cursorStyle": {
"type": "string"
},
"fontFamily": {
"type": "string"
},
"fontSize": {
"type": "string"
},
"foregroundColor": {
"type": "string"
},
"letterSpacing": {
"type": "string"
},
"lineHeight": {
"type": "string"
},
"scrollSensitivity": {
"type": "string"
},
"scrollback": {
"type": "string"
},
"showTerminalButton": {
"type": "string"
}
},
"type": "object"

View File

@@ -101,6 +101,7 @@ export namespace Setting {
dashboardSimpleNodeVisible: string;
}
export interface TerminalInfo {
showTerminalButton?: string;
lineHeight: string;
letterSpacing: string;
fontSize: string;

View File

@@ -126,7 +126,7 @@ export const getSettingBaseInfo = () => {
export const getTerminalInfo = () => {
return http.post<Setting.TerminalInfo>(`/core/settings/terminal/search`);
};
export const UpdateTerminalInfo = (param: Setting.TerminalInfo) => {
export const UpdateTerminalInfo = (param: Partial<Setting.TerminalInfo>) => {
return http.post(`/core/settings/terminal/update`, param);
};
export const getSystemAvailable = () => {

View File

@@ -1,6 +1,6 @@
<template>
<!-- Right edge handle: open a terminal from any page without leaving it. Hidden on the terminal page itself. -->
<div v-if="!onTerminalPage" class="terminal-dock-handle" @click="show">
<div v-if="terminalStore.showTerminalButton && !onTerminalPage" class="terminal-dock-handle" @click="show">
<el-badge
:value="store.entries.length"
:hidden="store.entries.length === 0"
@@ -143,11 +143,12 @@
</template>
<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import i18n from '@/lang';
import { ElTree } from 'element-plus';
import { TerminalSessionStore } from '@/store';
import { TerminalSessionStore, TerminalStore } from '@/store';
import { getTerminalInfo } from '@/api/modules/setting';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { getHostTree, testByID, testLocalConn } from '@/api/modules/terminal';
import { MsgError } from '@/utils/message';
@@ -155,10 +156,16 @@ import { ElMessageBox } from 'element-plus';
import { Host } from '@/api/interface/host';
const store = TerminalSessionStore();
const terminalStore = TerminalStore();
const { isNodeAdmin } = useGlobalStore();
const route = useRoute();
const onTerminalPage = computed(() => route.path.startsWith('/terminal'));
onMounted(async () => {
const res = await getTerminalInfo();
terminalStore.showTerminalButton = res.data.showTerminalButton !== 'Disable';
});
const open = ref(false);
const active = ref('');
const showConnections = ref(false);

View File

@@ -2068,6 +2068,8 @@ const message = {
profileBlockDesc: 'Measures blocking on channels, select statements, and synchronization primitives.',
},
terminal: {
showTerminalButton: 'Show Terminal Button',
showTerminalButtonHelper: 'Show the terminal shortcut button in the bottom-right corner of the page.',
local: 'Local',
defaultConn: 'Default Connection',
defaultConnHelper:

View File

@@ -2107,6 +2107,8 @@ const message = {
profileBlockDesc: 'Mide los bloqueos en canales, select y primitivas de sincronización.',
},
terminal: {
showTerminalButton: 'Mostrar botón de terminal',
showTerminalButtonHelper: 'Mostrar el botón de acceso al terminal en la esquina inferior derecha de la página.',
local: 'Local',
defaultConn: 'Conexión predeterminada',
defaultConnHelper:

View File

@@ -2049,6 +2049,8 @@ const message = {
profileBlockDesc: 'زمان انسداد روی Channel، Select و ابزارهای همگام‌سازی را اندازه‌گیری می‌کند.',
},
terminal: {
showTerminalButton: 'نمایش دکمه ترمینال',
showTerminalButtonHelper: 'نمایش دکمه میانبر ترمینال در گوشه پایین سمت راست صفحه.',
local: 'محلی',
defaultConn: 'اتصال پیش‌فرض',
defaultConnHelper:

View File

@@ -2057,6 +2057,8 @@ const message = {
profileBlockDesc: 'ChannelSelect同期プリミティブでのブロック待機時間を測定します',
},
terminal: {
showTerminalButton: 'ターミナルボタンを表示',
showTerminalButtonHelper: 'ページの右下にターミナルのショートカットボタンを表示します',
local: 'ローカル',
defaultConn: 'デフォルト接続',
defaultConnHelper:

View File

@@ -2031,6 +2031,8 @@ const message = {
profileBlockDesc: 'Channel, Select 및 동기화 프리미티브의 차단 대기 시간을 측정합니다.',
},
terminal: {
showTerminalButton: '터미널 버튼 표시',
showTerminalButtonHelper: '페이지 오른쪽 아래에 터미널 바로가기 버튼을 표시합니다.',
local: '로컬',
defaultConn: '기본 연결',
defaultConnHelper: '이 작업은 【{0}】의 터미널을 연 후 자동으로 노드 터미널에 연결됩니다. 계속하시겠습니까?',

View File

@@ -2015,6 +2015,8 @@ const message = {
profileBlockDesc: 'ວັດແທກການບລັອກໃນ Channel, Select Statement ແລະ ກົນໄກ Synchronization.',
},
terminal: {
showTerminalButton: 'ສະແດງປຸ່ມ Terminal',
showTerminalButtonHelper: 'ສະແດງປຸ່ມທາງລັດ Terminal ຢູ່ມຸມຂວາລຸ່ມຂອງໜ້າ.',
local: 'ພາຍໃນເຄື່ອງ (Local)',
defaultConn: 'ການເຊື່ອມຕໍ່ເລີ່ມຕົ້ນ',
defaultConnHelper:

View File

@@ -2095,6 +2095,8 @@ const message = {
profileBlockDesc: 'Mengukur sekatan pada Channel, Select dan primitif penyegerakan.',
},
terminal: {
showTerminalButton: 'Papar Butang Terminal',
showTerminalButtonHelper: 'Paparkan butang pintasan terminal di sudut kanan bawah halaman.',
local: 'Tempatan',
defaultConn: 'Sambungan Lalai',
defaultConnHelper:

View File

@@ -2100,6 +2100,8 @@ const message = {
profileBlockDesc: 'Mede bloqueios em canais, select e primitivas de sincronização.',
},
terminal: {
showTerminalButton: 'Mostrar botão do terminal',
showTerminalButtonHelper: 'Mostrar o botão de acesso ao terminal no canto inferior direito da página.',
local: 'Local',
defaultConn: 'Conexão Padrão',
defaultConnHelper:

View File

@@ -2081,6 +2081,8 @@ const message = {
profileBlockDesc: 'Измеряет блокировки на каналах, select и примитивах синхронизации.',
},
terminal: {
showTerminalButton: 'Показывать кнопку терминала',
showTerminalButtonHelper: 'Показывать кнопку быстрого доступа к терминалу в правом нижнем углу страницы.',
local: 'Локальный',
defaultConn: 'Соединение по умолчанию',
defaultConnHelper:

View File

@@ -2087,6 +2087,8 @@ const message = {
profileBlockDesc: 'Channel, select ve eşzamanlama araçlarındaki engelleme süresini ölçer.',
},
terminal: {
showTerminalButton: 'Terminal düğmesini göster',
showTerminalButtonHelper: 'Sayfanın sağ alt köşesinde terminal kısayol düğmesini gösterir.',
local: 'Yerel',
defaultConn: 'Varsayılan Bağlantı',
defaultConnHelper:

View File

@@ -1954,6 +1954,8 @@ const message = {
profileBlockDesc: '統計 Channel、Select 和同步原語的阻塞等待,用於定位長時間阻塞。',
},
terminal: {
showTerminalButton: '顯示終端按鈕',
showTerminalButtonHelper: '在頁面右下角顯示終端快捷按鈕。',
local: '本機',
defaultConn: '預設連接',
defaultConnHelper: '該操作將【{0}】開啟終端後自動連線到所在節點終端,是否繼續?',

View File

@@ -1984,6 +1984,8 @@ const message = {
profileBlockDesc: '统计 Channel、Select 和同步原语的阻塞等待,用于定位长时间阻塞。',
},
terminal: {
showTerminalButton: '显示终端按钮',
showTerminalButtonHelper: '在页面右下角显示终端快捷按钮。',
local: '本机',
defaultConn: '默认连接',
defaultConnHelper: '该操作将【{0}】打开终端后自动连接所在节点终端,是否继续?',

View File

@@ -84,6 +84,7 @@ export interface MenuState {
}
export interface TerminalState {
showTerminalButton: boolean;
lineHeight: number;
letterSpacing: number;
fontSize: number;

View File

@@ -5,6 +5,7 @@ import { TerminalState } from '../interface';
export const TerminalStore = defineStore('TerminalState', {
state: (): TerminalState => ({
showTerminalButton: true,
lineHeight: 1.2,
letterSpacing: 1.2,
fontSize: 12,

View File

@@ -68,6 +68,7 @@ const handleChange = (tab: any) => {
const loadTerminalSetting = async () => {
await getTerminalInfo().then((res) => {
terminalStore.$patch({
showTerminalButton: res.data.showTerminalButton !== 'Disable',
lineHeight: Number(res.data.lineHeight),
letterSpacing: Number(res.data.letterSpacing),
fontSize: Number(res.data.fontSize),

View File

@@ -126,6 +126,13 @@
</template>
</el-input>
</el-form-item>
<el-divider border-style="dashed" />
<el-form-item :label="$t('terminal.showTerminalButton')">
<el-switch v-model="form.showTerminalButton" @change="changeTerminalButton" />
<span class="input-help">{{ $t('terminal.showTerminalButtonHelper') }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
@@ -182,6 +189,7 @@ const fontFamilyOptions = [
];
const form = reactive({
showTerminalButton: true,
lineHeight: 1.2,
letterSpacing: 1.2,
fontSize: 12,
@@ -254,6 +262,7 @@ const search = async (withReset?: boolean) => {
await getTerminalInfo()
.then((res) => {
loading.value = false;
form.showTerminalButton = res.data.showTerminalButton !== 'Disable';
form.lineHeight = Number(res.data.lineHeight);
form.letterSpacing = Number(res.data.letterSpacing);
form.fontSize = Number(res.data.fontSize);
@@ -276,6 +285,22 @@ const search = async (withReset?: boolean) => {
});
};
const changeTerminalButton = async () => {
const showTerminalButton = form.showTerminalButton;
loading.value = true;
try {
await UpdateTerminalInfo({
showTerminalButton: showTerminalButton ? 'Enable' : 'Disable',
});
terminalStore.showTerminalButton = showTerminalButton;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
} catch {
form.showTerminalButton = !showTerminalButton;
} finally {
loading.value = false;
}
};
const loadConnShow = async () => {
await loadLocalConn().then((res) => {
form.showDefaultConn = res.data.localSSHConnShow === 'Enable';