From 3ea025d65a00fc01f30bb56cf9382018ae912b07 Mon Sep 17 00:00:00 2001 From: Baobhan Sith <80159437+Heavrnl@users.noreply.github.com> Date: Mon, 5 May 2025 09:32:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=94=81=E5=AE=9A?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 可以屏蔽鼠标扫过分割线产生的样式闪烁问题 --- .../src/settings/settings.controller.ts | 3 +- .../src/components/LayoutConfigurator.vue | 50 ++++++++++++++++++- .../frontend/src/stores/settings.store.ts | 25 ++++++++-- packages/frontend/src/views/WorkspaceView.vue | 7 +-- 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/settings/settings.controller.ts b/packages/backend/src/settings/settings.controller.ts index 2c417fd..ea36bcb 100644 --- a/packages/backend/src/settings/settings.controller.ts +++ b/packages/backend/src/settings/settings.controller.ts @@ -47,7 +47,8 @@ export const settingsController = { 'timezone', // NEW: 添加时区键 'rdpModalWidth', // NEW: 添加 RDP 模态框宽度键 'rdpModalHeight', // NEW: 添加 RDP 模态框高度键 - 'ipBlacklistEnabled' // <-- 添加 IP 黑名单启用键 + 'ipBlacklistEnabled', // <-- 添加 IP 黑名单启用键 + 'layoutLocked' // +++ 添加布局锁定键 +++ ]; const filteredSettings: Record = {}; for (const key in settingsToUpdate) { diff --git a/packages/frontend/src/components/LayoutConfigurator.vue b/packages/frontend/src/components/LayoutConfigurator.vue index 531ab90..7c18557 100644 --- a/packages/frontend/src/components/LayoutConfigurator.vue +++ b/packages/frontend/src/components/LayoutConfigurator.vue @@ -2,8 +2,12 @@ import { ref, computed, watch, type Ref, nextTick } from 'vue'; // Import nextTick import { useI18n } from 'vue-i18n'; import { useLayoutStore, type LayoutNode, type PaneName } from '../stores/layout.store'; +import { useSettingsStore } from '../stores/settings.store'; // +++ Import settings store +++ +import { storeToRefs } from 'pinia'; // +++ Import storeToRefs +++ import draggable from 'vuedraggable'; import LayoutNodeEditor from './LayoutNodeEditor.vue'; +// +++ Import a switch component if available, otherwise use checkbox +++ +// Assuming a simple checkbox for now // --- Props --- const props = defineProps({ @@ -19,6 +23,8 @@ const emit = defineEmits(['close']); // --- Setup --- const { t } = useI18n(); const layoutStore = useLayoutStore(); +const settingsStore = useSettingsStore(); // +++ Initialize settings store +++ +const { layoutLockedBoolean } = storeToRefs(settingsStore); // +++ Get reactive state +++ // --- State --- const localLayoutTree: Ref = ref(null); @@ -160,6 +166,23 @@ const paneLabels = computed(() => ({ // Assuming labels might depend on i18n })); // --- Methods --- +// +++ Method to update layout lock setting +++ +const handleLayoutLockChange = async () => { // Removed event parameter + const isLocked = !layoutLockedBoolean.value; // Toggle the current state + console.log(`[LayoutConfigurator] Layout lock toggled: ${isLocked}`); + try { + // +++ Convert boolean to string before sending +++ + await settingsStore.updateSetting('layoutLocked', String(isLocked)); + // No need to update local state directly, store watcher should handle it if needed, + // but the button's appearance relies on layoutLockedBoolean which comes from the store. + } catch (error) { + console.error('[LayoutConfigurator] Failed to update layout lock setting:', error); + // Optionally show an error message + // No UI element state to revert directly here, the button state depends on layoutLockedBoolean + alert(t('layoutConfigurator.lockUpdateError', '更新布局锁定状态失败。')); + } +}; + const closeDialog = () => { // Use the computed property for the check if (isModified.value) { @@ -388,7 +411,32 @@ const handleAvailablePaneDragEnd = (event: any) => {
-

{{ t('layoutConfigurator.layoutPreview', '主布局预览(拖拽到此处)') }}

+
+

{{ t('layoutConfigurator.layoutPreview', '主布局预览(拖拽到此处)') }}

+ +
+ + +
+
{ } if (settings.value.showQuickCommandTags === undefined) { settings.value.showQuickCommandTags = 'true'; // 默认显示 + } // +++ Add missing closing brace +++ + // NEW: Layout locked default - Only set if not provided by backend + if (settings.value.layoutLocked === undefined) { + settings.value.layoutLocked = 'false'; // 默认不锁定 + console.log('[SettingsStore] layoutLocked not found in fetched settings, set to default: false'); + } else { + console.log(`[SettingsStore] layoutLocked found in fetched settings: ${settings.value.layoutLocked}`); } - - + + // --- 语言设置 --- const langFromSettings = settings.value.language; console.log(`[SettingsStore] Language from fetched settings: ${langFromSettings}`); // <-- 添加日志 @@ -348,7 +356,8 @@ export const useSettingsStore = defineStore('settings', () => { 'dashboardSortBy', 'dashboardSortOrder', 'showConnectionTags', // NEW - 'showQuickCommandTags' // NEW + 'showQuickCommandTags', // NEW + 'layoutLocked' // NEW ]; if (!allowedKeys.includes(key)) { console.error(`[SettingsStore] 尝试更新不允许的设置键: ${key}`); @@ -432,7 +441,8 @@ export const useSettingsStore = defineStore('settings', () => { 'dashboardSortBy', 'dashboardSortOrder', 'showConnectionTags', // NEW - 'showQuickCommandTags' // NEW + 'showQuickCommandTags', // NEW + 'layoutLocked' // NEW ]; const filteredUpdates: Partial = {}; let languageUpdate: string | undefined = undefined; @@ -702,6 +712,11 @@ export const useSettingsStore = defineStore('settings', () => { return settings.value.showQuickCommandTags !== 'false'; // Default to true }); + // NEW: Getter for layout locked status + const layoutLockedBoolean = computed(() => { + return settings.value.layoutLocked === 'true'; + }); + return { settings, // 只包含通用设置 @@ -741,5 +756,7 @@ export const useSettingsStore = defineStore('settings', () => { // NEW: Expose tag visibility getters showConnectionTagsBoolean, showQuickCommandTagsBoolean, + // NEW: Expose layout locked getter + layoutLockedBoolean, }; }); diff --git a/packages/frontend/src/views/WorkspaceView.vue b/packages/frontend/src/views/WorkspaceView.vue index 4f9d3ce..8559770 100644 --- a/packages/frontend/src/views/WorkspaceView.vue +++ b/packages/frontend/src/views/WorkspaceView.vue @@ -22,8 +22,8 @@ import type { ISearchOptions } from '@xterm/addon-search'; // --- Setup --- const { t } = useI18n(); -const sessionStore = useSessionStore(); -const settingsStore = useSettingsStore(); +const sessionStore = useSessionStore(); +const settingsStore = useSettingsStore(); // Keep settingsStore instance const fileEditorStore = useFileEditorStore(); const layoutStore = useLayoutStore(); const commandHistoryStore = useCommandHistoryStore(); @@ -34,7 +34,7 @@ const isMobile = breakpoints.smaller('md'); // +++ 定义 isMobile (小于 md // --- 从 Store 获取响应式状态和 Getters --- const { sessionTabsWithStatus, activeSessionId, activeSession, isRdpModalOpen, rdpConnectionInfo } = storeToRefs(sessionStore); // 使用 storeToRefs 获取 RDP 状态 -const { shareFileEditorTabsBoolean } = storeToRefs(settingsStore); +const { shareFileEditorTabsBoolean, layoutLockedBoolean } = storeToRefs(settingsStore); // +++ Add layoutLockedBoolean +++ const { orderedTabs: globalEditorTabs, activeTabId: globalActiveEditorTabId } = storeToRefs(fileEditorStore); const { layoutTree } = storeToRefs(layoutStore); // 只获取布局树 @@ -563,6 +563,7 @@ const toggleVirtualKeyboard = () => { :is-root-renderer="true" :layout-node="layoutTree" :active-session-id="activeSessionId" + :layout-locked="layoutLockedBoolean" class="layout-renderer-wrapper" :editor-tabs="editorTabs" :active-editor-tab-id="activeEditorTabId"