Quellcode durchsuchen

fix: 侧边栏菜单拖拽功能在设置内增加开关

zouawen vor 3 Monaten
Ursprung
Commit
8e71261d49

+ 1 - 0
packages/@core/preferences/src/config.ts

@@ -85,6 +85,7 @@ const defaultPreferences: Preferences = {
     collapsedButton: true,
     collapsedButton: true,
     collapsedShowTitle: false,
     collapsedShowTitle: false,
     collapseWidth: 60,
     collapseWidth: 60,
+    draggable: true,
     enable: true,
     enable: true,
     expandOnHover: true,
     expandOnHover: true,
     extraCollapse: false,
     extraCollapse: false,

+ 2 - 0
packages/@core/preferences/src/types.ts

@@ -170,6 +170,8 @@ interface SidebarPreferences {
   collapsedShowTitle: boolean;
   collapsedShowTitle: boolean;
   /** 侧边栏折叠宽度 */
   /** 侧边栏折叠宽度 */
   collapseWidth: number;
   collapseWidth: number;
+  /** 侧边栏菜单拖拽 */
+  draggable: boolean;
   /** 侧边栏是否可见 */
   /** 侧边栏是否可见 */
   enable: boolean;
   enable: boolean;
   /** 菜单自动展开状态 */
   /** 菜单自动展开状态 */

+ 13 - 6
packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue

@@ -109,6 +109,7 @@ const props = withDefaults(defineProps<Props>(), {
 });
 });
 
 
 const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>();
 const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>();
+const draggable = defineModel<boolean>('draggable');
 const collapse = defineModel<boolean>('collapse');
 const collapse = defineModel<boolean>('collapse');
 const extraCollapse = defineModel<boolean>('extraCollapse');
 const extraCollapse = defineModel<boolean>('extraCollapse');
 const expandOnHovering = defineModel<boolean>('expandOnHovering');
 const expandOnHovering = defineModel<boolean>('expandOnHovering');
@@ -262,14 +263,19 @@ const handleDragSidebar = (e: MouseEvent) => {
   const { isSidebarMixed, collapseWidth, extraWidth, width } = props;
   const { isSidebarMixed, collapseWidth, extraWidth, width } = props;
   const minLimit = collapseWidth;
   const minLimit = collapseWidth;
   const maxLimit = 320;
   const maxLimit = 320;
-  const currentWidth = isSidebarMixed ? extraWidth : width;
+  const startWidth = isSidebarMixed ? extraWidth : width;
+
   startDrag(
   startDrag(
     e,
     e,
-    minLimit,
-    maxLimit,
-    currentWidth,
-    asideRef.value,
-    dragBarRef.value,
+    {
+      min: minLimit,
+      max: maxLimit,
+      startWidth,
+    },
+    {
+      target: asideRef.value,
+      dragBar: dragBarRef.value,
+    },
     (newWidth) => {
     (newWidth) => {
       emit('update:width', newWidth);
       emit('update:width', newWidth);
       if (isSidebarMixed) {
       if (isSidebarMixed) {
@@ -357,6 +363,7 @@ const handleDragSidebar = (e: MouseEvent) => {
       </VbenScrollbar>
       </VbenScrollbar>
     </div>
     </div>
     <div
     <div
+      v-if="draggable"
       ref="dragBarRef"
       ref="dragBarRef"
       class="absolute inset-y-0 -right-[1px] z-1000 w-[2px] cursor-col-resize hover:bg-primary"
       class="absolute inset-y-0 -right-[1px] z-1000 w-[2px] cursor-col-resize hover:bg-primary"
       @mousedown="handleDragSidebar"
       @mousedown="handleDragSidebar"

+ 109 - 58
packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts

@@ -1,106 +1,157 @@
 import { onUnmounted } from 'vue';
 import { onUnmounted } from 'vue';
 
 
+interface DragOptions {
+  max: number;
+  min: number;
+  startWidth: number;
+}
+
+interface DragElements {
+  dragBar: HTMLElement | null;
+  target: HTMLElement | null;
+}
+
+type DragCallback = (newWidth: number) => void;
+
 export function useSidebarDrag() {
 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 = (
   const startDrag = (
     e: MouseEvent,
     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.preventDefault();
     e.stopPropagation();
     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;
+    document.body.style.cursor = 'col-resize';
+    document.body.style.userSelect = 'none';
 
 
-    userSelect = document.body.style.userSelect;
-    cursor = document.body.style.cursor;
+    dragBar.style.left = `${state.startLeft}px`;
+    dragBar.style.right = 'auto';
+    dragBar.style.transition = 'none';
+    target.style.transition = 'none';
 
 
-    targetElement.style.transition = 'none';
-    dragBarElement.style.transition = 'none';
+    const onMouseMove = (moveEvent: MouseEvent) => {
+      if (!state.isDragging || !dragBar) return;
 
 
-    dragBarElement.style.left = `${dragBarOffsetLeft}px`;
-    dragBarElement.style.right = 'auto';
+      const deltaX = moveEvent.clientX - state.startX;
+      let newLeft = state.startLeft + deltaX;
 
 
-    document.body.style.userSelect = 'none';
-    document.body.style.cursor = 'col-resize';
-
-    const onMouseMove = (moveEvent: MouseEvent) => {
-      const deltaX = moveEvent.clientX - startX;
-      let newLeft = dragBarOffsetLeft + deltaX;
       if (newLeft < min) newLeft = min;
       if (newLeft < min) newLeft = min;
       if (newLeft > max) newLeft = max;
       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 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));
       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);
       onDrag?.(newWidth);
 
 
-      cleanup?.();
+      endDrag();
     };
     };
 
 
-    document.addEventListener('mousemove', onMouseMove, { passive: true });
+    document.addEventListener('mousemove', onMouseMove);
     document.addEventListener('mouseup', onMouseUp);
     document.addEventListener('mouseup', onMouseUp);
 
 
-    cleanup = () => {
+    const cleanup = () => {
+      if (!state.cleanup) return;
+
       document.removeEventListener('mousemove', onMouseMove);
       document.removeEventListener('mousemove', onMouseMove);
       document.removeEventListener('mouseup', onMouseUp);
       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(() => {
   onUnmounted(() => {
-    cleanup?.();
+    endDrag();
   });
   });
 
 
   return {
   return {
     startDrag,
     startDrag,
+    endDrag,
+    get isDragging() {
+      return state.isDragging;
+    },
   };
   };
 }
 }

+ 4 - 0
packages/@core/ui-kit/layout-ui/src/vben-layout.vue

@@ -69,6 +69,9 @@ const emit = defineEmits<{
   toggleSidebar: [];
   toggleSidebar: [];
   'update:sidebar-width': [value: number];
   'update:sidebar-width': [value: number];
 }>();
 }>();
+const sidebarDraggable = defineModel<boolean>('sidebarDraggable', {
+  default: true,
+});
 const sidebarCollapse = defineModel<boolean>('sidebarCollapse', {
 const sidebarCollapse = defineModel<boolean>('sidebarCollapse', {
   default: false,
   default: false,
 });
 });
@@ -493,6 +496,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
   <div class="relative flex min-h-full w-full">
   <div class="relative flex min-h-full w-full">
     <LayoutSidebar
     <LayoutSidebar
       v-if="sidebarEnableState"
       v-if="sidebarEnableState"
+      v-model:draggable="sidebarDraggable"
       v-model:collapse="sidebarCollapse"
       v-model:collapse="sidebarCollapse"
       v-model:expand-on-hover="sidebarExpandOnHover"
       v-model:expand-on-hover="sidebarExpandOnHover"
       v-model:expand-on-hovering="sidebarExpandOnHovering"
       v-model:expand-on-hovering="sidebarExpandOnHovering"

+ 1 - 0
packages/effects/layouts/src/basic/layout.vue

@@ -234,6 +234,7 @@ const headerSlots = computed(() => {
     :header-visible="preferences.header.enable"
     :header-visible="preferences.header.enable"
     :is-mobile="preferences.app.isMobile"
     :is-mobile="preferences.app.isMobile"
     :layout="layout"
     :layout="layout"
+    :sidebar-draggable="preferences.sidebar.draggable"
     :sidebar-collapse="preferences.sidebar.collapsed"
     :sidebar-collapse="preferences.sidebar.collapsed"
     :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
     :sidebar-collapse-show-title="preferences.sidebar.collapsedShowTitle"
     :sidebar-enable="sidebarVisible"
     :sidebar-enable="sidebarVisible"

+ 4 - 0
packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue

@@ -19,6 +19,7 @@ const sidebarCollapsedShowTitle = defineModel<boolean>(
 const sidebarAutoActivateChild = defineModel<boolean>(
 const sidebarAutoActivateChild = defineModel<boolean>(
   'sidebarAutoActivateChild',
   'sidebarAutoActivateChild',
 );
 );
+const sidebarDraggable = defineModel<boolean>('sidebarDraggable');
 const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
 const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
 
 
@@ -48,6 +49,9 @@ const handleCheckboxChange = () => {
   <SwitchItem v-model="sidebarEnable" :disabled="disabled">
   <SwitchItem v-model="sidebarEnable" :disabled="disabled">
     {{ $t('preferences.sidebar.visible') }}
     {{ $t('preferences.sidebar.visible') }}
   </SwitchItem>
   </SwitchItem>
+  <SwitchItem v-model="sidebarDraggable" :disabled="!sidebarEnable || disabled">
+    {{ $t('preferences.sidebar.draggable') }}
+  </SwitchItem>
   <SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
   <SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
     {{ $t('preferences.sidebar.collapsed') }}
     {{ $t('preferences.sidebar.collapsed') }}
   </SwitchItem>
   </SwitchItem>

+ 2 - 0
packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue

@@ -93,6 +93,7 @@ const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
 
 
 const sidebarEnable = defineModel<boolean>('sidebarEnable');
 const sidebarEnable = defineModel<boolean>('sidebarEnable');
 const sidebarWidth = defineModel<number>('sidebarWidth');
 const sidebarWidth = defineModel<number>('sidebarWidth');
+const sidebarDraggable = defineModel<boolean>('sidebarDraggable');
 const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
 const sidebarCollapsedShowTitle = defineModel<boolean>(
 const sidebarCollapsedShowTitle = defineModel<boolean>(
   'sidebarCollapsedShowTitle',
   'sidebarCollapsedShowTitle',
@@ -354,6 +355,7 @@ async function handleReset() {
             <Block :title="$t('preferences.sidebar.title')">
             <Block :title="$t('preferences.sidebar.title')">
               <Sidebar
               <Sidebar
                 v-model:sidebar-auto-activate-child="sidebarAutoActivateChild"
                 v-model:sidebar-auto-activate-child="sidebarAutoActivateChild"
+                v-model:sidebar-draggable="sidebarDraggable"
                 v-model:sidebar-collapsed="sidebarCollapsed"
                 v-model:sidebar-collapsed="sidebarCollapsed"
                 v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
                 v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
                 v-model:sidebar-enable="sidebarEnable"
                 v-model:sidebar-enable="sidebarEnable"

+ 1 - 0
packages/locales/src/langs/en-US/preferences.json

@@ -54,6 +54,7 @@
     "title": "Sidebar",
     "title": "Sidebar",
     "width": "Width",
     "width": "Width",
     "visible": "Show Sidebar",
     "visible": "Show Sidebar",
+    "draggable": "Drag Sidebar Menu",
     "collapsed": "Collpase Menu",
     "collapsed": "Collpase Menu",
     "collapsedShowTitle": "Show Menu Title",
     "collapsedShowTitle": "Show Menu Title",
     "autoActivateChild": "Auto Activate SubMenu",
     "autoActivateChild": "Auto Activate SubMenu",

+ 1 - 0
packages/locales/src/langs/zh-CN/preferences.json

@@ -54,6 +54,7 @@
     "title": "侧边栏",
     "title": "侧边栏",
     "width": "宽度",
     "width": "宽度",
     "visible": "显示侧边栏",
     "visible": "显示侧边栏",
+    "draggable": "侧边栏菜单拖拽",
     "collapsed": "折叠菜单",
     "collapsed": "折叠菜单",
     "collapsedShowTitle": "折叠显示菜单名",
     "collapsedShowTitle": "折叠显示菜单名",
     "autoActivateChild": "自动激活子菜单",
     "autoActivateChild": "自动激活子菜单",