feature: 简易版菜单宽度拖拽功能

This commit is contained in:
zouawen
2026-02-25 17:50:19 +08:00
parent 03ebbea46a
commit 57cf6cbc9e
3 changed files with 53 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import { computed, shallowRef, useSlots, watchEffect } from 'vue'; import { computed, ref, shallowRef, useSlots, watchEffect } from 'vue';
import { VbenScrollbar } from '@vben-core/shadcn-ui'; import { VbenScrollbar } from '@vben-core/shadcn-ui';
@@ -107,7 +107,7 @@ const props = withDefaults(defineProps<Props>(), {
zIndex: 0, zIndex: 0,
}); });
const emit = defineEmits<{ leave: [] }>(); const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>();
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');
@@ -254,6 +254,32 @@ function handleMouseleave() {
collapse.value = true; collapse.value = true;
extraVisible.value = false; extraVisible.value = false;
} }
const isDragging = ref(false);
function handleDragSidebar(e: MouseEvent) {
e.preventDefault();
isDragging.value = true;
const startX = e.clientX;
const startWidth = props.width;
function onMouseMove(moveEvent: MouseEvent) {
const deltaX = moveEvent.clientX - startX;
const newWidth = Math.min(320, Math.max(160, startWidth + deltaX));
emit('update:width', newWidth);
}
function onMouseUp() {
isDragging.value = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
}
</script> </script>
<template> <template>
@@ -266,6 +292,7 @@ function handleMouseleave() {
<aside <aside
:style="style" :style="style"
class="fixed left-0 top-0 h-full transition-all duration-150" class="fixed left-0 top-0 h-full transition-all duration-150"
:class="{ 'transition-none': isDragging }"
@mouseenter="handleMouseenter" @mouseenter="handleMouseenter"
@mouseleave="handleMouseleave" @mouseleave="handleMouseleave"
> >
@@ -296,6 +323,10 @@ function handleMouseleave() {
v-if="showCollapseButton && !isSidebarMixed" v-if="showCollapseButton && !isSidebarMixed"
v-model:collapsed="collapse" v-model:collapsed="collapse"
/> />
<div
class="absolute inset-y-0 -right-0.5 z-1000 w-1 cursor-col-resize"
@mousedown="handleDragSidebar"
></div>
</div> </div>
<div <div
v-if="isSidebarMixed" v-if="isSidebarMixed"
@@ -304,6 +335,7 @@ function handleMouseleave() {
themeSub, themeSub,
{ {
'border-l': extraVisible, 'border-l': extraVisible,
'transition-none': isDragging,
}, },
]" ]"
:style="extraStyle" :style="extraStyle"

View File

@@ -64,7 +64,11 @@ const props = withDefaults(defineProps<Props>(), {
zIndex: 200, zIndex: 200,
}); });
const emit = defineEmits<{ sideMouseLeave: []; toggleSidebar: [] }>(); const emit = defineEmits<{
sideMouseLeave: [];
toggleSidebar: [];
'update:sidebar-width': [value: number];
}>();
const sidebarCollapse = defineModel<boolean>('sidebarCollapse', { const sidebarCollapse = defineModel<boolean>('sidebarCollapse', {
default: false, default: false,
}); });
@@ -480,6 +484,10 @@ function handleHeaderToggle() {
} }
const idMainContent = ELEMENT_ID_MAIN_CONTENT; const idMainContent = ELEMENT_ID_MAIN_CONTENT;
function handleUpdateSidebarWidth(val: number) {
emit('update:sidebar-width', val);
}
</script> </script>
<template> <template>
@@ -507,6 +515,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:width="getSidebarWidth" :width="getSidebarWidth"
:z-index="sidebarZIndex" :z-index="sidebarZIndex"
@leave="() => emit('sideMouseLeave')" @leave="() => emit('sideMouseLeave')"
@update:width="handleUpdateSidebarWidth"
> >
<template v-if="isSideMode && !isMixedNav" #logo> <template v-if="isSideMode && !isMixedNav" #logo>
<slot name="logo"></slot> <slot name="logo"></slot>

View File

@@ -211,6 +211,14 @@ const slots: SetupContext['slots'] = useSlots();
const headerSlots = computed(() => { const headerSlots = computed(() => {
return Object.keys(slots).filter((key) => key.startsWith('header-')); return Object.keys(slots).filter((key) => key.startsWith('header-'));
}); });
function handleUpdateSidebarWidth(newWidth: number) {
updatePreferences({
sidebar: {
width: newWidth,
},
});
}
</script> </script>
<template> <template>
@@ -267,6 +275,7 @@ const headerSlots = computed(() => {
(value: boolean) => (value: boolean) =>
updatePreferences({ sidebar: { extraCollapse: value } }) updatePreferences({ sidebar: { extraCollapse: value } })
" "
@update:sidebar-width="handleUpdateSidebarWidth"
> >
<!-- logo --> <!-- logo -->
<template #logo> <template #logo>