feat: 添加锁定布局功能
可以屏蔽鼠标扫过分割线产生的样式闪烁问题
This commit is contained in:
@@ -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<LayoutNode | null> = 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) => {
|
||||
<div class="flex flex-col">
|
||||
<!-- Main Layout Preview -->
|
||||
<section class="flex flex-col min-w-[350px] flex-grow">
|
||||
<h3 class="mt-0 mb-4 text-base font-semibold text-text-secondary">{{ t('layoutConfigurator.layoutPreview', '主布局预览(拖拽到此处)') }}</h3>
|
||||
<div class="flex justify-between items-center mb-4"> <!-- +++ Flex container for title and switch +++ -->
|
||||
<h3 class="mt-0 mb-0 text-base font-semibold text-text-secondary">{{ t('layoutConfigurator.layoutPreview', '主布局预览(拖拽到此处)') }}</h3>
|
||||
<!-- +++ Layout Lock Switch +++ -->
|
||||
<div class="flex items-center gap-2">
|
||||
<label id="layout-lock-label" class="text-sm text-text-secondary cursor-pointer select-none" @click="handleLayoutLockChange">{{ t('layoutConfigurator.lockLayout', '锁定布局') }}</label>
|
||||
<button
|
||||
type="button"
|
||||
@click="handleLayoutLockChange"
|
||||
:class="[
|
||||
'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary',
|
||||
layoutLockedBoolean ? 'bg-primary' : 'bg-gray-300 dark:bg-gray-600'
|
||||
]"
|
||||
role="switch"
|
||||
:aria-checked="layoutLockedBoolean.toString()"
|
||||
aria-labelledby="layout-lock-label"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
:class="[
|
||||
'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200',
|
||||
layoutLockedBoolean ? 'translate-x-5' : 'translate-x-0'
|
||||
]"
|
||||
></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow border-2 border-dashed border-border-alt rounded p-4 bg-background-alt flex flex-col overflow-auto min-h-[250px]">
|
||||
<LayoutNodeEditor
|
||||
v-if="localLayoutTree"
|
||||
|
||||
@@ -55,6 +55,7 @@ interface SettingsState {
|
||||
dashboardSortOrder?: SortOrder;
|
||||
showConnectionTags?: string; // 'true' or 'false'
|
||||
showQuickCommandTags?: string; // 'true' or 'false'
|
||||
layoutLocked?: string; // 'true' or 'false' - NEW: 布局锁定状态
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
|
||||
@@ -261,9 +262,16 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
}
|
||||
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<SettingsState> = {};
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user