diff --git a/packages/@core/preferences/src/config.ts b/packages/@core/preferences/src/config.ts index 6bba8099..d2caf154 100644 --- a/packages/@core/preferences/src/config.ts +++ b/packages/@core/preferences/src/config.ts @@ -85,6 +85,7 @@ const defaultPreferences: Preferences = { collapsedButton: true, collapsedShowTitle: false, collapseWidth: 60, + draggable: true, enable: true, expandOnHover: true, extraCollapse: false, diff --git a/packages/@core/preferences/src/types.ts b/packages/@core/preferences/src/types.ts index 24c41c85..7348e42a 100644 --- a/packages/@core/preferences/src/types.ts +++ b/packages/@core/preferences/src/types.ts @@ -170,6 +170,8 @@ interface SidebarPreferences { collapsedShowTitle: boolean; /** 侧边栏折叠宽度 */ collapseWidth: number; + /** 侧边栏菜单拖拽 */ + draggable: boolean; /** 侧边栏是否可见 */ enable: boolean; /** 菜单自动展开状态 */ diff --git a/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue b/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue index 74f12311..dfeca386 100644 --- a/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue +++ b/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue @@ -109,6 +109,7 @@ const props = withDefaults(defineProps(), { }); const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>(); +const draggable = defineModel('draggable'); const collapse = defineModel('collapse'); const extraCollapse = defineModel('extraCollapse'); const expandOnHovering = defineModel('expandOnHovering'); @@ -262,14 +263,19 @@ const handleDragSidebar = (e: MouseEvent) => { const { isSidebarMixed, collapseWidth, extraWidth, width } = props; const minLimit = collapseWidth; const maxLimit = 320; - const currentWidth = isSidebarMixed ? extraWidth : width; + const startWidth = isSidebarMixed ? extraWidth : width; + startDrag( e, - minLimit, - maxLimit, - currentWidth, - asideRef.value, - dragBarRef.value, + { + min: minLimit, + max: maxLimit, + startWidth, + }, + { + target: asideRef.value, + dragBar: dragBarRef.value, + }, (newWidth) => { emit('update:width', newWidth); if (isSidebarMixed) { @@ -357,6 +363,7 @@ const handleDragSidebar = (e: MouseEvent) => {
void; + export function useSidebarDrag() { - let startX = 0; - let startWidth = 0; - let targetTransition = ''; - let dragBarTransition = ''; - let dragBarOffsetLeft = 0; - let dragBarLeft = ''; - let dragBarRight = ''; - let userSelect = ''; - let cursor = ''; - let cleanup: (() => void) | null = null; + const state: { + cleanup: (() => void) | null; + isDragging: boolean; + originalStyles: { + bodyCursor: string; + bodyUserSelect: string; + dragBarLeft: string; + dragBarRight: string; + dragBarTransition: string; + targetTransition: string; + }; + startLeft: number; + startWidth: number; + startX: number; + } = { + cleanup: null, + isDragging: false, + startLeft: 0, + startWidth: 0, + startX: 0, + originalStyles: { + bodyCursor: '', + bodyUserSelect: '', + dragBarLeft: '', + dragBarRight: '', + dragBarTransition: '', + targetTransition: '', + }, + }; const startDrag = ( e: MouseEvent, - min: number, - max: number, - currentWidth: number, - targetElement: HTMLElement | null, - dragBarElement: HTMLElement | null, - onDrag: (newWidth: number) => void, + options: DragOptions, + elements: DragElements, + onDrag: DragCallback, ) => { - cleanup?.(); + const { min, max, startWidth } = options; + const { dragBar, target } = elements; + + if (state.isDragging || !dragBar || !target) return; e.preventDefault(); e.stopPropagation(); - if (!dragBarElement || !targetElement) return; + state.isDragging = true; - startX = e.clientX; - startWidth = currentWidth; + state.startX = e.clientX; + state.startWidth = startWidth; + state.startLeft = dragBar.offsetLeft; - targetTransition = targetElement.style.transition; - dragBarTransition = dragBarElement.style.transition; + state.originalStyles = { + bodyCursor: document.body.style.cursor, + bodyUserSelect: document.body.style.userSelect, + dragBarLeft: dragBar.style.left, + dragBarRight: dragBar.style.right, + dragBarTransition: dragBar.style.transition, + targetTransition: target.style.transition, + }; - dragBarOffsetLeft = dragBarElement.offsetLeft; - dragBarLeft = dragBarElement.style.left; - dragBarRight = dragBarElement.style.right; - - userSelect = document.body.style.userSelect; - cursor = document.body.style.cursor; - - targetElement.style.transition = 'none'; - dragBarElement.style.transition = 'none'; - - dragBarElement.style.left = `${dragBarOffsetLeft}px`; - dragBarElement.style.right = 'auto'; - - document.body.style.userSelect = 'none'; document.body.style.cursor = 'col-resize'; + document.body.style.userSelect = 'none'; + + dragBar.style.left = `${state.startLeft}px`; + dragBar.style.right = 'auto'; + dragBar.style.transition = 'none'; + target.style.transition = 'none'; const onMouseMove = (moveEvent: MouseEvent) => { - const deltaX = moveEvent.clientX - startX; - let newLeft = dragBarOffsetLeft + deltaX; + if (!state.isDragging || !dragBar) return; + + const deltaX = moveEvent.clientX - state.startX; + let newLeft = state.startLeft + deltaX; + if (newLeft < min) newLeft = min; if (newLeft > max) newLeft = max; - dragBarElement.style.left = `${newLeft}px`; + + dragBar.style.left = `${newLeft}px`; + dragBar.classList.add('bg-primary'); }; const onMouseUp = (upEvent: MouseEvent) => { - const deltaX = upEvent.clientX - startX; - let newWidth = startWidth + deltaX; + if (!state.isDragging || !dragBar || !target) return; + + const deltaX = upEvent.clientX - state.startX; + let newWidth = state.startWidth + deltaX; + newWidth = Math.min(max, Math.max(min, newWidth)); - if (dragBarElement) { - dragBarElement.style.left = dragBarLeft; - dragBarElement.style.right = dragBarRight; - } + dragBar.classList.remove('bg-primary'); onDrag?.(newWidth); - cleanup?.(); + endDrag(); }; - document.addEventListener('mousemove', onMouseMove, { passive: true }); + document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); - cleanup = () => { + const cleanup = () => { + if (!state.cleanup) return; + document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); - if (targetElement) { - targetElement.style.transition = targetTransition; - } - if (dragBarElement) { - dragBarElement.style.transition = dragBarTransition; - dragBarElement.style.left = dragBarLeft; - dragBarElement.style.right = dragBarRight; + document.body.style.cursor = state.originalStyles.bodyCursor; + document.body.style.userSelect = state.originalStyles.bodyUserSelect; + + if (dragBar) { + dragBar.style.left = state.originalStyles.dragBarLeft; + dragBar.style.right = state.originalStyles.dragBarRight; + dragBar.style.transition = state.originalStyles.dragBarTransition; + dragBar.classList.remove('bg-primary'); } - document.body.style.userSelect = userSelect; - document.body.style.cursor = cursor; + if (target) { + target.style.transition = state.originalStyles.targetTransition; + } - cleanup = null; + state.isDragging = false; + state.cleanup = null; }; + + state.cleanup = cleanup; + }; + + const endDrag = () => { + state.cleanup?.(); }; onUnmounted(() => { - cleanup?.(); + endDrag(); }); return { startDrag, + endDrag, + get isDragging() { + return state.isDragging; + }, }; } diff --git a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue index a7b9ab58..2ab4aaa2 100644 --- a/packages/@core/ui-kit/layout-ui/src/vben-layout.vue +++ b/packages/@core/ui-kit/layout-ui/src/vben-layout.vue @@ -69,6 +69,9 @@ const emit = defineEmits<{ toggleSidebar: []; 'update:sidebar-width': [value: number]; }>(); +const sidebarDraggable = defineModel('sidebarDraggable', { + default: true, +}); const sidebarCollapse = defineModel('sidebarCollapse', { default: false, }); @@ -493,6 +496,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
{ :header-visible="preferences.header.enable" :is-mobile="preferences.app.isMobile" :layout="layout" + :sidebar-draggable="preferences.sidebar.draggable" :sidebar-collapse="preferences.sidebar.collapsed" :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle" :sidebar-enable="sidebarVisible" diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue b/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue index e8d84afa..913775a9 100644 --- a/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue +++ b/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue @@ -19,6 +19,7 @@ const sidebarCollapsedShowTitle = defineModel( const sidebarAutoActivateChild = defineModel( 'sidebarAutoActivateChild', ); +const sidebarDraggable = defineModel('sidebarDraggable'); const sidebarCollapsed = defineModel('sidebarCollapsed'); const sidebarExpandOnHover = defineModel('sidebarExpandOnHover'); @@ -48,6 +49,9 @@ const handleCheckboxChange = () => { {{ $t('preferences.sidebar.visible') }} + + {{ $t('preferences.sidebar.draggable') }} + {{ $t('preferences.sidebar.collapsed') }} diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue index ca68d355..1abb3342 100644 --- a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -93,6 +93,7 @@ const themeSemiDarkHeader = defineModel('themeSemiDarkHeader'); const sidebarEnable = defineModel('sidebarEnable'); const sidebarWidth = defineModel('sidebarWidth'); +const sidebarDraggable = defineModel('sidebarDraggable'); const sidebarCollapsed = defineModel('sidebarCollapsed'); const sidebarCollapsedShowTitle = defineModel( 'sidebarCollapsedShowTitle', @@ -354,6 +355,7 @@ async function handleReset() {