From 4c23c8b55eb12f6cd4b990e877526e3b6c5dc1e5 Mon Sep 17 00:00:00 2001 From: Hooray <304327508@qq.com> Date: Wed, 11 Jun 2025 18:26:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E8=88=AA=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E5=8A=A8=E6=95=88=E6=89=A7=E8=A1=8C=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/components/Menu/sub.vue | 255 +++++++++++++++------------- 1 file changed, 135 insertions(+), 120 deletions(-) diff --git a/src/layouts/components/Menu/sub.vue b/src/layouts/components/Menu/sub.vue index 1cb86d18..f33aa874 100644 --- a/src/layouts/components/Menu/sub.vue +++ b/src/layouts/components/Menu/sub.vue @@ -22,106 +22,107 @@ const rootMenu = inject(rootMenuInjectionKey)! const index = props.menu.path ?? rootMenu.getUseId(props.menu) -const opened = computed(() => { - return rootMenu.openedMenus.includes(props.uniqueKey.at(-1)!) -}) +const opened = computed(() => rootMenu.openedMenus.includes(props.uniqueKey.at(-1)!)) const transitionEvent = computed(() => { - return rootMenu.isMenuPopup - ? { - enter(el: HTMLElement) { - if (el.offsetHeight > window.innerHeight) { - el.style.height = `${window.innerHeight}px` - } - }, - afterEnter: () => {}, - beforeLeave: (el: HTMLElement) => { - el.style.maxHeight = `${el.offsetHeight}px` - el.style.overflow = 'hidden' - }, - leave: (el: HTMLElement) => { - el.style.maxHeight = '0' - }, - afterLeave(el: HTMLElement) { - el.style.maxHeight = '' - el.style.overflow = '' - }, - } - : CSS.supports('height', 'calc-size(auto, size)') - ? {} - : { - enter(el: HTMLElement) { - requestAnimationFrame(() => { - el.dataset.height = el.offsetHeight.toString() - el.style.maxHeight = '0' - void el.offsetHeight - el.style.maxHeight = `${el.dataset.height}px` - el.style.overflow = 'hidden' - }) - }, - afterEnter(el: HTMLElement) { - el.style.maxHeight = '' - el.style.overflow = '' - }, - enterCancelled(el: HTMLElement) { - el.style.maxHeight = '' - el.style.overflow = '' - }, - beforeLeave(el: HTMLElement) { - el.style.maxHeight = `${el.offsetHeight}px` - el.style.overflow = 'hidden' - }, - leave(el: HTMLElement) { - el.style.maxHeight = '0' - }, - afterLeave(el: HTMLElement) { - el.style.maxHeight = '' - el.style.overflow = '' - }, - leaveCancelled(el: HTMLElement) { - el.style.maxHeight = '' - el.style.overflow = '' - }, + if (rootMenu.isMenuPopup) { + return { + enter(el: HTMLElement) { + if (el.offsetHeight > window.innerHeight) { + el.style.height = `${window.innerHeight}px` } + }, + afterEnter: () => {}, + beforeLeave: (el: HTMLElement) => { + el.style.maxHeight = `${el.offsetHeight}px` + el.style.overflow = 'hidden' + }, + leave: (el: HTMLElement) => { + el.style.maxHeight = '0' + }, + afterLeave(el: HTMLElement) { + el.style.maxHeight = '' + el.style.overflow = '' + }, + } + } + if (CSS.supports('height', 'calc-size(auto, size)')) { + return {} + } + return { + enter(el: HTMLElement) { + requestAnimationFrame(() => { + el.dataset.height = el.offsetHeight.toString() + el.style.maxHeight = '0' + void el.offsetHeight + el.style.maxHeight = `${el.dataset.height}px` + el.style.overflow = 'hidden' + }) + }, + afterEnter(el: HTMLElement) { + el.style.maxHeight = '' + el.style.overflow = '' + }, + enterCancelled(el: HTMLElement) { + el.style.maxHeight = '' + el.style.overflow = '' + }, + beforeLeave(el: HTMLElement) { + el.style.maxHeight = `${el.offsetHeight}px` + el.style.overflow = 'hidden' + }, + leave(el: HTMLElement) { + el.style.maxHeight = '0' + }, + afterLeave(el: HTMLElement) { + el.style.maxHeight = '' + el.style.overflow = '' + }, + leaveCancelled(el: HTMLElement) { + el.style.maxHeight = '' + el.style.overflow = '' + }, + } }) const transitionClass = computed(() => { - return rootMenu.isMenuPopup - ? { - enterActiveClass: 'ease-in-out duration-300', - enterFromClass: 'opacity-0 translate-x-4', - enterToClass: 'opacity-100', - leaveActiveClass: 'ease-in-out duration-300', - leaveFromClass: 'opacity-100', - leaveToClass: 'opacity-0', - } - : { - enterActiveClass: 'ease-in-out duration-300', - enterFromClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'), - enterToClass: 'opacity-100 translate-y-0 scale-100 blur-0', - leaveActiveClass: 'ease-in-out duration-300', - leaveFromClass: 'opacity-100 translate-y-0 scale-100 blur-0', - leaveToClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'), - } + if (rootMenu.isMenuPopup) { + return { + enterActiveClass: 'ease-in-out duration-300', + enterFromClass: 'opacity-0 translate-x-4', + enterToClass: 'opacity-100', + leaveActiveClass: 'ease-in-out duration-300', + leaveFromClass: 'opacity-100', + leaveToClass: 'opacity-0', + } + } + return { + enterActiveClass: 'ease-in-out duration-300', + enterFromClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'), + enterToClass: 'opacity-100 translate-y-0 scale-100 blur-0', + leaveActiveClass: 'ease-in-out duration-300', + leaveFromClass: 'opacity-100 translate-y-0 scale-100 blur-0', + leaveToClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'), + } }) -const hasChildren = computed(() => { - return props.menu.children?.some((item: any) => item.meta?.menu !== false) ?? false -}) +const hasChildren = computed(() => props.menu.children?.some((item: any) => item.meta?.menu !== false) ?? false) function handleClick() { - if (rootMenu.isMenuPopup && hasChildren.value) { + if ( + (rootMenu.isMenuPopup && hasChildren.value) + || props.menu.meta?.link + ) { return } - if (props.menu.meta?.link) { - return - } - if (hasChildren.value) { - rootMenu.handleSubMenuClick(index, props.uniqueKey) - } - else { - rootMenu.handleMenuItemClick(index) - } + requestAnimationFrame(() => { + if (hasChildren.value) { + rootMenu.handleSubMenuClick(index, props.uniqueKey) + } + else { + rootMenu.handleMenuItemClick(index) + } + }) } let timeout: (() => void) | undefined @@ -136,32 +137,44 @@ function handleMouseenter() { if (hasChildren.value) { rootMenu.openMenu(index, props.uniqueKey) nextTick(() => { - const el = itemRef.value?.ref - const subMenuEl = subMenuRef.value?.$el - if (!el || !subMenuEl) { - return - } - let top = 0 - let left = 0 - if (rootMenu.props.mode === 'vertical' || props.level !== 0) { - top = el.getBoundingClientRect().top + el.scrollTop - left = el.getBoundingClientRect().left + el.getBoundingClientRect().width - if (top + subMenuEl.offsetHeight > window.innerHeight) { - top = Math.max(0, window.innerHeight - subMenuEl.offsetHeight) + requestAnimationFrame(() => { + const el = itemRef.value?.ref + const subMenuEl = subMenuRef.value?.$el + if (!el || !subMenuEl) { + return } - } - else { - top = el.getBoundingClientRect().top + el.getBoundingClientRect().height - left = el.getBoundingClientRect().left - if (top + subMenuEl.offsetHeight > window.innerHeight) { - subMenuEl.style.height = `${window.innerHeight - top}px` + const rect = el.getBoundingClientRect() + const { top, left, width, height } = rect + let menuTop = 0 + let menuLeft = 0 + if (rootMenu.props.mode === 'vertical' || props.level !== 0) { + menuTop = top + el.scrollTop + menuLeft = left + width + // 处理边界情况 + if (menuTop + subMenuEl.offsetHeight > window.innerHeight) { + menuTop = Math.max(0, window.innerHeight - subMenuEl.offsetHeight) + } } - } - if (left + subMenuEl.offsetWidth > document.documentElement.clientWidth) { - left = el.getBoundingClientRect().left - el.getBoundingClientRect().width - } - subMenuEl.style.top = `${top}px` - subMenuEl.style.insetInlineStart = `${left}px` + else { + menuTop = top + height + menuLeft = left + // 处理边界情况 + if (menuTop + subMenuEl.offsetHeight > window.innerHeight) { + subMenuEl.style.height = `${window.innerHeight - menuTop}px` + } + } + // 处理边界情况 + if (menuLeft + subMenuEl.offsetWidth > document.documentElement.clientWidth) { + menuLeft = left - width + } + // 设置样式 + Object.assign(subMenuEl.style, { + top: `${menuTop}px`, + insetInlineStart: `${menuLeft}px`, + willChange: 'transform', + transform: 'translateZ(0)', + }) + }) }) } else { @@ -178,14 +191,16 @@ function handleMouseleave() { rootMenu.mouseInMenu = [] timeout?.() ;({ stop: timeout } = useTimeoutFn(() => { - if (rootMenu.mouseInMenu.length === 0) { - rootMenu.closeMenu(props.uniqueKey) - } - else { - if (hasChildren.value) { - !rootMenu.mouseInMenu.includes(props.uniqueKey.at(-1)!) && rootMenu.closeMenu(props.uniqueKey.at(-1)!) + requestAnimationFrame(() => { + if (rootMenu.mouseInMenu.length === 0) { + rootMenu.closeMenu(props.uniqueKey) } - } + else { + if (hasChildren.value) { + !rootMenu.mouseInMenu.includes(props.uniqueKey.at(-1)!) && rootMenu.closeMenu(props.uniqueKey.at(-1)!) + } + } + }) }, 300)) } @@ -195,7 +210,7 @@ function handleMouseleave() {