mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-03-15 18:52:02 +08:00
fix: 侧边栏菜单拖拽功能在设置内增加开关
This commit is contained in:
@@ -85,6 +85,7 @@ const defaultPreferences: Preferences = {
|
||||
collapsedButton: true,
|
||||
collapsedShowTitle: false,
|
||||
collapseWidth: 60,
|
||||
draggable: true,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: false,
|
||||
|
||||
@@ -170,6 +170,8 @@ interface SidebarPreferences {
|
||||
collapsedShowTitle: boolean;
|
||||
/** 侧边栏折叠宽度 */
|
||||
collapseWidth: number;
|
||||
/** 侧边栏菜单拖拽 */
|
||||
draggable: boolean;
|
||||
/** 侧边栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 菜单自动展开状态 */
|
||||
|
||||
@@ -109,6 +109,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{ leave: []; 'update:width': [value: number] }>();
|
||||
const draggable = defineModel<boolean>('draggable');
|
||||
const collapse = defineModel<boolean>('collapse');
|
||||
const extraCollapse = defineModel<boolean>('extraCollapse');
|
||||
const expandOnHovering = defineModel<boolean>('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) => {
|
||||
</VbenScrollbar>
|
||||
</div>
|
||||
<div
|
||||
v-if="draggable"
|
||||
ref="dragBarRef"
|
||||
class="absolute inset-y-0 -right-[1px] z-1000 w-[2px] cursor-col-resize hover:bg-primary"
|
||||
@mousedown="handleDragSidebar"
|
||||
|
||||
@@ -1,106 +1,157 @@
|
||||
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() {
|
||||
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;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ const emit = defineEmits<{
|
||||
toggleSidebar: [];
|
||||
'update:sidebar-width': [value: number];
|
||||
}>();
|
||||
const sidebarDraggable = defineModel<boolean>('sidebarDraggable', {
|
||||
default: true,
|
||||
});
|
||||
const sidebarCollapse = defineModel<boolean>('sidebarCollapse', {
|
||||
default: false,
|
||||
});
|
||||
@@ -493,6 +496,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
|
||||
<div class="relative flex min-h-full w-full">
|
||||
<LayoutSidebar
|
||||
v-if="sidebarEnableState"
|
||||
v-model:draggable="sidebarDraggable"
|
||||
v-model:collapse="sidebarCollapse"
|
||||
v-model:expand-on-hover="sidebarExpandOnHover"
|
||||
v-model:expand-on-hovering="sidebarExpandOnHovering"
|
||||
|
||||
@@ -234,6 +234,7 @@ const headerSlots = computed(() => {
|
||||
: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"
|
||||
|
||||
@@ -19,6 +19,7 @@ const sidebarCollapsedShowTitle = defineModel<boolean>(
|
||||
const sidebarAutoActivateChild = defineModel<boolean>(
|
||||
'sidebarAutoActivateChild',
|
||||
);
|
||||
const sidebarDraggable = defineModel<boolean>('sidebarDraggable');
|
||||
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
|
||||
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
||||
|
||||
@@ -48,6 +49,9 @@ const handleCheckboxChange = () => {
|
||||
<SwitchItem v-model="sidebarEnable" :disabled="disabled">
|
||||
{{ $t('preferences.sidebar.visible') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="sidebarDraggable" :disabled="!sidebarEnable || disabled">
|
||||
{{ $t('preferences.sidebar.draggable') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="sidebarCollapsed" :disabled="!sidebarEnable || disabled">
|
||||
{{ $t('preferences.sidebar.collapsed') }}
|
||||
</SwitchItem>
|
||||
|
||||
@@ -93,6 +93,7 @@ const themeSemiDarkHeader = defineModel<boolean>('themeSemiDarkHeader');
|
||||
|
||||
const sidebarEnable = defineModel<boolean>('sidebarEnable');
|
||||
const sidebarWidth = defineModel<number>('sidebarWidth');
|
||||
const sidebarDraggable = defineModel<boolean>('sidebarDraggable');
|
||||
const sidebarCollapsed = defineModel<boolean>('sidebarCollapsed');
|
||||
const sidebarCollapsedShowTitle = defineModel<boolean>(
|
||||
'sidebarCollapsedShowTitle',
|
||||
@@ -354,6 +355,7 @@ async function handleReset() {
|
||||
<Block :title="$t('preferences.sidebar.title')">
|
||||
<Sidebar
|
||||
v-model:sidebar-auto-activate-child="sidebarAutoActivateChild"
|
||||
v-model:sidebar-draggable="sidebarDraggable"
|
||||
v-model:sidebar-collapsed="sidebarCollapsed"
|
||||
v-model:sidebar-collapsed-show-title="sidebarCollapsedShowTitle"
|
||||
v-model:sidebar-enable="sidebarEnable"
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"title": "Sidebar",
|
||||
"width": "Width",
|
||||
"visible": "Show Sidebar",
|
||||
"draggable": "Drag Sidebar Menu",
|
||||
"collapsed": "Collpase Menu",
|
||||
"collapsedShowTitle": "Show Menu Title",
|
||||
"autoActivateChild": "Auto Activate SubMenu",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"title": "侧边栏",
|
||||
"width": "宽度",
|
||||
"visible": "显示侧边栏",
|
||||
"draggable": "侧边栏菜单拖拽",
|
||||
"collapsed": "折叠菜单",
|
||||
"collapsedShowTitle": "折叠显示菜单名",
|
||||
"autoActivateChild": "自动激活子菜单",
|
||||
|
||||
Reference in New Issue
Block a user