update
This commit is contained in:
@@ -36,7 +36,9 @@ export const settingsController = {
|
||||
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
|
||||
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
|
||||
'workspaceSidebarPersistent' // +++ 添加侧边栏固定键 +++
|
||||
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
|
||||
'leftSidebarWidth', // +++ 添加左侧栏宽度键 +++
|
||||
'rightSidebarWidth' // +++ 添加右侧栏宽度键 +++
|
||||
];
|
||||
const filteredSettings: Record<string, string> = {};
|
||||
for (const key in settingsToUpdate) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, defineAsyncComponent, type PropType, type Component, ref, watch } from 'vue';
|
||||
import { computed, defineAsyncComponent, type PropType, type Component, ref, watch, onMounted } from 'vue'; // +++ Add onMounted +++
|
||||
import { useI18n } from 'vue-i18n'; // <-- Import useI18n
|
||||
// 添加依赖 font-awesome
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css';
|
||||
@@ -8,6 +8,7 @@ import { useLayoutStore, type LayoutNode, type PaneName } from '../stores/layout
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { useFileEditorStore } from '../stores/fileEditor.store'; // <-- Import FileEditorStore
|
||||
import { useSettingsStore } from '../stores/settings.store'; // +++ Import SettingsStore +++
|
||||
import { useSidebarResize } from '../composables/useSidebarResize'; // +++ Import useSidebarResize +++
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { defineEmits } from 'vue';
|
||||
|
||||
@@ -71,13 +72,17 @@ const fileEditorStore = useFileEditorStore(); // <-- Initialize FileEditorStore
|
||||
const settingsStore = useSettingsStore(); // +++ Initialize SettingsStore +++
|
||||
const { t } = useI18n(); // <-- Get translation function
|
||||
const { activeSession } = storeToRefs(sessionStore);
|
||||
const { workspaceSidebarPersistentBoolean } = storeToRefs(settingsStore); // +++ Get sidebar setting +++
|
||||
const { workspaceSidebarPersistentBoolean, leftSidebarWidthPx, rightSidebarWidthPx } = storeToRefs(settingsStore); // +++ Get sidebar setting and width getters +++
|
||||
const { sidebarPanes } = storeToRefs(layoutStore);
|
||||
const { orderedTabs: editorTabsFromStore, activeTabId: activeEditorTabIdFromStore } = storeToRefs(fileEditorStore); // <-- Get editor state
|
||||
|
||||
// --- Sidebar State ---
|
||||
const activeLeftSidebarPane = ref<PaneName | null>(null);
|
||||
const activeRightSidebarPane = ref<PaneName | null>(null);
|
||||
const leftSidebarPanelRef = ref<HTMLElement | null>(null); // +++ Ref for left panel +++
|
||||
const rightSidebarPanelRef = ref<HTMLElement | null>(null); // +++ Ref for right panel +++
|
||||
const leftResizeHandleRef = ref<HTMLElement | null>(null); // +++ Ref for left handle +++
|
||||
const rightResizeHandleRef = ref<HTMLElement | null>(null); // +++ Ref for right handle +++
|
||||
|
||||
// --- Component Mapping ---
|
||||
// 使用 defineAsyncComponent 优化加载,并映射 PaneName 到实际组件
|
||||
@@ -385,6 +390,32 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// --- Sidebar Resize Logic ---
|
||||
onMounted(() => {
|
||||
// Left Sidebar Resize
|
||||
useSidebarResize({
|
||||
sidebarRef: leftSidebarPanelRef,
|
||||
handleRef: leftResizeHandleRef,
|
||||
side: 'left',
|
||||
onResizeEnd: (newWidth) => {
|
||||
console.log(`Left sidebar resize ended. New width: ${newWidth}px`);
|
||||
settingsStore.updateSetting('leftSidebarWidth', `${newWidth}px`);
|
||||
},
|
||||
});
|
||||
|
||||
// Right Sidebar Resize
|
||||
useSidebarResize({
|
||||
sidebarRef: rightSidebarPanelRef,
|
||||
handleRef: rightResizeHandleRef,
|
||||
side: 'right',
|
||||
onResizeEnd: (newWidth) => {
|
||||
console.log(`Right sidebar resize ended. New width: ${newWidth}px`);
|
||||
settingsStore.updateSetting('rightSidebarWidth', `${newWidth}px`);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -550,11 +581,11 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
<div
|
||||
v-if="activeLeftSidebarPane || activeRightSidebarPane"
|
||||
class="sidebar-overlay"
|
||||
|
||||
></div>
|
||||
|
||||
<!-- Left Sidebar Panel -->
|
||||
<div :class="['sidebar-panel', 'left-sidebar-panel', { active: !!activeLeftSidebarPane }]">
|
||||
<div ref="leftSidebarPanelRef" :class="['sidebar-panel', 'left-sidebar-panel', { active: !!activeLeftSidebarPane }]" :style="{ width: leftSidebarWidthPx }"> <!-- +++ Add ref and bind width +++ -->
|
||||
<div ref="leftResizeHandleRef" class="resize-handle left-handle"></div> <!-- +++ Left Handle +++ -->
|
||||
<button class="close-sidebar-btn" @click="closeSidebars" title="Close Sidebar">×</button>
|
||||
<component
|
||||
v-if="currentLeftSidebarComponent && activeLeftSidebarPane && (!['fileManager', 'statusMonitor'].includes(activeLeftSidebarPane) || activeSession)"
|
||||
@@ -581,7 +612,8 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
</div>
|
||||
|
||||
<!-- Right Sidebar Panel -->
|
||||
<div :class="['sidebar-panel', 'right-sidebar-panel', { active: !!activeRightSidebarPane }]">
|
||||
<div ref="rightSidebarPanelRef" :class="['sidebar-panel', 'right-sidebar-panel', { active: !!activeRightSidebarPane }]" :style="{ width: rightSidebarWidthPx }"> <!-- +++ Add ref and bind width +++ -->
|
||||
<div ref="rightResizeHandleRef" class="resize-handle right-handle"></div> <!-- +++ Right Handle +++ -->
|
||||
<button class="close-sidebar-btn" @click="closeSidebars" title="Close Sidebar">×</button>
|
||||
<component
|
||||
v-if="currentRightSidebarComponent && activeRightSidebarPane && (!['fileManager', 'statusMonitor'].includes(activeRightSidebarPane) || activeSession)"
|
||||
@@ -778,9 +810,9 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* background-color: rgba(0, 0, 0, 0.4); */ /* <-- 移除背景色 */
|
||||
background-color: transparent; /* <-- 确保完全透明 */
|
||||
pointer-events: none; /* <-- 不拦截鼠标事件 */
|
||||
/* background-color: rgba(0, 0, 0, 0.4); */ /* <-- 移除背景色 --> */
|
||||
background-color: transparent; /* <-- 确保完全透明 --> */
|
||||
pointer-events: none; /* <-- 不拦截鼠标事件 --> */
|
||||
z-index: 100; /* Below panel, above main content */
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
@@ -798,13 +830,13 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
position: fixed; /* Use fixed for viewport positioning */
|
||||
top: 0; /* Adjust if you have a fixed header */
|
||||
bottom: 0; /* Adjust if you have a fixed footer */
|
||||
width: 350px; /* Adjust width as needed */
|
||||
/* width: 350px; */ /* Width is now controlled by style binding */
|
||||
max-width: 80vw;
|
||||
background-color: var(--app-bg-color, white);
|
||||
/* box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); */ /* <-- 移除阴影以隐藏边缘 */
|
||||
/* box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); */ /* <-- 移除阴影以隐藏边缘 --> */
|
||||
z-index: 110; /* Above overlay */
|
||||
transform: translateX(-100%); /* Start hidden */
|
||||
transition: transform 0.3s ease-in-out;
|
||||
transition: transform 0.3s ease-in-out; /* Keep transition for sliding */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden; /* Prevent content overflow */
|
||||
@@ -853,6 +885,27 @@ const getIconClasses = (paneName: PaneName): string[] => {
|
||||
padding-top: 2.5rem; /* Add padding to avoid close button overlap */
|
||||
}
|
||||
|
||||
/* Resize Handle Styles */
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 8px; /* Increased width for easier interaction */
|
||||
cursor: col-resize;
|
||||
z-index: 120; /* Above panel content */
|
||||
background-color: transparent; /* Make it invisible by default */
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.resize-handle:hover {
|
||||
background-color: var(--primary-color-light, #a0cfff); /* Highlight on hover */
|
||||
}
|
||||
.left-handle {
|
||||
right: -4px; /* Adjusted position for increased width */
|
||||
}
|
||||
.right-handle {
|
||||
left: -4px; /* Adjusted position for increased width */
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style> /* Global styles for splitpanes if needed, or scoped with :deep() */
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { ref, onMounted, onUnmounted, type Ref, watch } from 'vue'; // +++ Import watch +++
|
||||
|
||||
interface UseSidebarResizeOptions {
|
||||
sidebarRef: Ref<HTMLElement | null>;
|
||||
handleRef: Ref<HTMLElement | null>;
|
||||
side: 'left' | 'right';
|
||||
minWidth?: number;
|
||||
maxWidth?: number;
|
||||
onResizeEnd?: (newWidth: number) => void;
|
||||
}
|
||||
|
||||
export function useSidebarResize({
|
||||
sidebarRef,
|
||||
handleRef,
|
||||
side,
|
||||
minWidth = 200, // 默认最小宽度
|
||||
maxWidth = 800, // 默认最大宽度
|
||||
onResizeEnd,
|
||||
}: UseSidebarResizeOptions) {
|
||||
const isDragging = ref(false);
|
||||
const startX = ref(0);
|
||||
const startWidth = ref(0);
|
||||
|
||||
const handleMouseDown = (event: MouseEvent) => {
|
||||
console.log(`[useSidebarResize] handleMouseDown triggered for side: ${side}`, { sidebar: sidebarRef.value, handle: handleRef.value }); // +++ Add Log +++
|
||||
if (!sidebarRef.value || !handleRef.value) {
|
||||
console.log('[useSidebarResize] MouseDown ignored: sidebarRef or handleRef is null.'); // +++ Add Log +++
|
||||
return;
|
||||
}
|
||||
|
||||
isDragging.value = true;
|
||||
startX.value = event.clientX;
|
||||
startWidth.value = sidebarRef.value.offsetWidth;
|
||||
document.body.style.cursor = 'col-resize'; // 改变鼠标样式
|
||||
document.body.style.userSelect = 'none'; // 禁止选择文本
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
if (!isDragging.value || !sidebarRef.value) return;
|
||||
|
||||
const currentX = event.clientX;
|
||||
const deltaX = currentX - startX.value;
|
||||
let newWidth: number;
|
||||
|
||||
if (side === 'left') {
|
||||
newWidth = startWidth.value + deltaX;
|
||||
} else { // side === 'right'
|
||||
newWidth = startWidth.value - deltaX;
|
||||
}
|
||||
|
||||
// 应用宽度限制
|
||||
newWidth = Math.max(minWidth, Math.min(newWidth, maxWidth));
|
||||
|
||||
sidebarRef.value.style.width = `${newWidth}px`;
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (!isDragging.value) return;
|
||||
|
||||
isDragging.value = false;
|
||||
document.body.style.cursor = ''; // 恢复鼠标样式
|
||||
document.body.style.userSelect = ''; // 恢复文本选择
|
||||
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
|
||||
if (sidebarRef.value && onResizeEnd) {
|
||||
onResizeEnd(sidebarRef.value.offsetWidth); // 传递最终宽度
|
||||
}
|
||||
};
|
||||
|
||||
// 使用 watch 监视 handleRef 的变化
|
||||
watch(handleRef, (newHandle, oldHandle) => {
|
||||
// 移除旧句柄上的监听器(如果存在)
|
||||
if (oldHandle) {
|
||||
oldHandle.removeEventListener('mousedown', handleMouseDown);
|
||||
}
|
||||
// 在新句柄上添加监听器(如果存在)
|
||||
if (newHandle) {
|
||||
newHandle.addEventListener('mousedown', handleMouseDown);
|
||||
}
|
||||
}, { immediate: true }); // immediate: true 确保初始时也能尝试附加
|
||||
|
||||
onUnmounted(() => {
|
||||
// 组件卸载时确保移除监听器
|
||||
if (handleRef.value) {
|
||||
handleRef.value.removeEventListener('mousedown', handleMouseDown);
|
||||
}
|
||||
// 清理可能残留的全局监听器
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
});
|
||||
|
||||
// 返回 isDragging 状态,可能用于 UI 反馈
|
||||
return { isDragging };
|
||||
}
|
||||
@@ -18,8 +18,10 @@ interface SettingsState {
|
||||
dockerDefaultExpand?: string; // NEW: Docker 默认展开详情 'true' or 'false'
|
||||
statusMonitorIntervalSeconds?: string; // NEW: 状态监控轮询间隔 (秒)
|
||||
workspaceSidebarPersistent?: string; // NEW: 工作区侧边栏是否固定 'true' or 'false'
|
||||
// Add other general settings keys here as needed
|
||||
[key: string]: string | undefined; // Allow other string settings
|
||||
leftSidebarWidth?: string; // NEW: 左侧边栏宽度 (e.g., '350px')
|
||||
rightSidebarWidth?: string; // NEW: 右侧边栏宽度 (e.g., '350px')
|
||||
// Add other general settings keys here as needed
|
||||
[key: string]: string | undefined; // Allow other string settings
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +86,13 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
if (settings.value.workspaceSidebarPersistent === undefined) {
|
||||
settings.value.workspaceSidebarPersistent = 'false'; // 默认不固定
|
||||
}
|
||||
// NEW: Sidebar width defaults
|
||||
if (settings.value.leftSidebarWidth === undefined) {
|
||||
settings.value.leftSidebarWidth = '350px'; // 默认宽度
|
||||
}
|
||||
if (settings.value.rightSidebarWidth === undefined) {
|
||||
settings.value.rightSidebarWidth = '350px'; // 默认宽度
|
||||
}
|
||||
|
||||
// --- 语言设置 ---
|
||||
const langFromSettings = settings.value.language;
|
||||
@@ -135,7 +144,9 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
|
||||
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
|
||||
'workspaceSidebarPersistent' // +++ 添加侧边栏固定键 +++
|
||||
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
|
||||
'leftSidebarWidth', // +++ 添加左侧栏宽度键 +++
|
||||
'rightSidebarWidth' // +++ 添加右侧栏宽度键 +++
|
||||
];
|
||||
if (!allowedKeys.includes(key)) {
|
||||
console.error(`[SettingsStore] 尝试更新不允许的设置键: ${key}`);
|
||||
@@ -170,7 +181,9 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
'showPopupFileEditor', 'shareFileEditorTabs', 'ipWhitelistEnabled',
|
||||
'autoCopyOnSelect', 'dockerStatusIntervalSeconds', 'dockerDefaultExpand',
|
||||
'statusMonitorIntervalSeconds', // +++ 添加状态监控间隔键 +++
|
||||
'workspaceSidebarPersistent' // +++ 添加侧边栏固定键 +++
|
||||
'workspaceSidebarPersistent', // +++ 添加侧边栏固定键 +++
|
||||
'leftSidebarWidth', // +++ 添加左侧栏宽度键 +++
|
||||
'rightSidebarWidth' // +++ 添加右侧栏宽度键 +++
|
||||
];
|
||||
const filteredUpdates: Partial<SettingsState> = {};
|
||||
let languageUpdate: 'en' | 'zh' | undefined = undefined;
|
||||
@@ -237,6 +250,12 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
return settings.value.workspaceSidebarPersistent === 'true';
|
||||
});
|
||||
|
||||
// NEW: Getter for left sidebar width
|
||||
const leftSidebarWidthPx = computed(() => settings.value.leftSidebarWidth || '350px');
|
||||
|
||||
// NEW: Getter for right sidebar width
|
||||
const rightSidebarWidthPx = computed(() => settings.value.rightSidebarWidth || '350px');
|
||||
|
||||
// NEW: Getter for Docker default expand setting, returning boolean
|
||||
const dockerDefaultExpandBoolean = computed(() => {
|
||||
return settings.value.dockerDefaultExpand === 'true';
|
||||
@@ -260,6 +279,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
dockerDefaultExpandBoolean, // +++ 暴露 Docker 默认展开 getter +++
|
||||
statusMonitorIntervalSecondsNumber, // +++ 暴露状态监控间隔 getter +++
|
||||
workspaceSidebarPersistentBoolean, // +++ 暴露侧边栏固定 getter +++
|
||||
leftSidebarWidthPx, // +++ 暴露左侧栏宽度 getter +++
|
||||
rightSidebarWidthPx, // +++ 暴露右侧栏宽度 getter +++
|
||||
// 移除外观相关的 getters 和 actions
|
||||
loadInitialSettings,
|
||||
updateSetting,
|
||||
|
||||
Reference in New Issue
Block a user