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">
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';
@@ -107,7 +107,7 @@ const props = withDefaults(defineProps<Props>(), {
zIndex: 0,
});
const emit = defineEmits<{ leave: [] }>();
const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>();
const collapse = defineModel<boolean>('collapse');
const extraCollapse = defineModel<boolean>('extraCollapse');
const expandOnHovering = defineModel<boolean>('expandOnHovering');
@@ -254,6 +254,32 @@ function handleMouseleave() {
collapse.value = true;
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>
<template>
@@ -266,6 +292,7 @@ function handleMouseleave() {
<aside
:style="style"
class="fixed left-0 top-0 h-full transition-all duration-150"
:class="{ 'transition-none': isDragging }"
@mouseenter="handleMouseenter"
@mouseleave="handleMouseleave"
>
@@ -296,6 +323,10 @@ function handleMouseleave() {
v-if="showCollapseButton && !isSidebarMixed"
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
v-if="isSidebarMixed"
@@ -304,6 +335,7 @@ function handleMouseleave() {
themeSub,
{
'border-l': extraVisible,
'transition-none': isDragging,
},
]"
:style="extraStyle"

View File

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

View File

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