mirror of
https://gitee.com/dapppp/ruoyi-plus-vben5.git
synced 2026-04-11 11:23:14 +08:00
fix: 侧边栏菜单拖拽功能在设置内增加开关
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ interface SidebarPreferences {
|
|||||||
collapsedShowTitle: boolean;
|
collapsedShowTitle: boolean;
|
||||||
/** 侧边栏折叠宽度 */
|
/** 侧边栏折叠宽度 */
|
||||||
collapseWidth: number;
|
collapseWidth: number;
|
||||||
|
/** 侧边栏菜单拖拽 */
|
||||||
|
draggable: boolean;
|
||||||
/** 侧边栏是否可见 */
|
/** 侧边栏是否可见 */
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
/** 菜单自动展开状态 */
|
/** 菜单自动展开状态 */
|
||||||
|
|||||||
@@ -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,
|
min: minLimit,
|
||||||
currentWidth,
|
max: maxLimit,
|
||||||
asideRef.value,
|
startWidth,
|
||||||
dragBarRef.value,
|
},
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
|||||||
@@ -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;
|
const state: {
|
||||||
let startWidth = 0;
|
cleanup: (() => void) | null;
|
||||||
let targetTransition = '';
|
isDragging: boolean;
|
||||||
let dragBarTransition = '';
|
originalStyles: {
|
||||||
let dragBarOffsetLeft = 0;
|
bodyCursor: string;
|
||||||
let dragBarLeft = '';
|
bodyUserSelect: string;
|
||||||
let dragBarRight = '';
|
dragBarLeft: string;
|
||||||
let userSelect = '';
|
dragBarRight: string;
|
||||||
let cursor = '';
|
dragBarTransition: string;
|
||||||
let cleanup: (() => void) | null = null;
|
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,
|
options: DragOptions,
|
||||||
max: number,
|
elements: DragElements,
|
||||||
currentWidth: number,
|
onDrag: DragCallback,
|
||||||
targetElement: HTMLElement | null,
|
|
||||||
dragBarElement: HTMLElement | null,
|
|
||||||
onDrag: (newWidth: number) => void,
|
|
||||||
) => {
|
) => {
|
||||||
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;
|
state.startX = e.clientX;
|
||||||
startWidth = currentWidth;
|
state.startWidth = startWidth;
|
||||||
|
state.startLeft = dragBar.offsetLeft;
|
||||||
|
|
||||||
targetTransition = targetElement.style.transition;
|
state.originalStyles = {
|
||||||
dragBarTransition = dragBarElement.style.transition;
|
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.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 onMouseMove = (moveEvent: MouseEvent) => {
|
||||||
const deltaX = moveEvent.clientX - startX;
|
if (!state.isDragging || !dragBar) return;
|
||||||
let newLeft = dragBarOffsetLeft + deltaX;
|
|
||||||
|
const deltaX = moveEvent.clientX - state.startX;
|
||||||
|
let newLeft = state.startLeft + 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;
|
if (!state.isDragging || !dragBar || !target) return;
|
||||||
let newWidth = startWidth + deltaX;
|
|
||||||
|
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) {
|
dragBar.classList.remove('bg-primary');
|
||||||
dragBarElement.style.left = dragBarLeft;
|
|
||||||
dragBarElement.style.right = dragBarRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
document.body.style.cursor = state.originalStyles.bodyCursor;
|
||||||
targetElement.style.transition = targetTransition;
|
document.body.style.userSelect = state.originalStyles.bodyUserSelect;
|
||||||
}
|
|
||||||
if (dragBarElement) {
|
if (dragBar) {
|
||||||
dragBarElement.style.transition = dragBarTransition;
|
dragBar.style.left = state.originalStyles.dragBarLeft;
|
||||||
dragBarElement.style.left = dragBarLeft;
|
dragBar.style.right = state.originalStyles.dragBarRight;
|
||||||
dragBarElement.style.right = dragBarRight;
|
dragBar.style.transition = state.originalStyles.dragBarTransition;
|
||||||
|
dragBar.classList.remove('bg-primary');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.style.userSelect = userSelect;
|
if (target) {
|
||||||
document.body.style.cursor = cursor;
|
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;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"title": "侧边栏",
|
"title": "侧边栏",
|
||||||
"width": "宽度",
|
"width": "宽度",
|
||||||
"visible": "显示侧边栏",
|
"visible": "显示侧边栏",
|
||||||
|
"draggable": "侧边栏菜单拖拽",
|
||||||
"collapsed": "折叠菜单",
|
"collapsed": "折叠菜单",
|
||||||
"collapsedShowTitle": "折叠显示菜单名",
|
"collapsedShowTitle": "折叠显示菜单名",
|
||||||
"autoActivateChild": "自动激活子菜单",
|
"autoActivateChild": "自动激活子菜单",
|
||||||
|
|||||||
Reference in New Issue
Block a user