From 7535bbb59190cda2e54a4a4a28471137c9fb0705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E5=98=89=E6=82=A6?= <2834007710@qq.com> Date: Tue, 20 May 2025 23:49:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20:sparkles:=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=8F=B3=E4=BE=A7=E8=8F=9C=E5=8D=95=E5=8A=A8?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/design.ts | 4 + src/hooks/useCollapseToggle.ts | 2 +- src/hooks/useSafeArea.ts | 86 +++++ src/hooks/useWindowWidthObserver.ts | 4 +- src/layouts/LayoutVertical/index.vue | 32 +- src/layouts/components/Aside/index.vue | 324 ++++++++++++++---- .../components/Header/components/Collapse.vue | 3 +- .../Header/components/TitleEditing.vue | 22 +- src/layouts/components/Header/index.vue | 46 ++- src/layouts/components/Main/index.vue | 2 +- src/layouts/index.vue | 2 +- src/pages/chat/home/index.vue | 5 +- src/store/index.ts | 1 + src/store/modules/design.ts | 10 + types/components.d.ts | 7 +- 15 files changed, 438 insertions(+), 112 deletions(-) create mode 100644 src/hooks/useSafeArea.ts diff --git a/src/config/design.ts b/src/config/design.ts index ea87a65..5141e7c 100644 --- a/src/config/design.ts +++ b/src/config/design.ts @@ -24,6 +24,8 @@ export interface DesignConfigState { collapseType: CollapseType; // 是否折叠菜单 isCollapse: boolean; + // 头部折叠按钮是否被悬停 + isCollapseHover: boolean; } export const themeColorList: string[] = [ @@ -65,6 +67,8 @@ const design: DesignConfigState = { collapseType: 'followSystem', // 是否折叠菜单 isCollapse: false, + // 头部折叠按钮是否被悬停 + isCollapseHover: false, }; export default design; diff --git a/src/hooks/useCollapseToggle.ts b/src/hooks/useCollapseToggle.ts index ff7636f..07db4be 100644 --- a/src/hooks/useCollapseToggle.ts +++ b/src/hooks/useCollapseToggle.ts @@ -1,6 +1,6 @@ import { COLLAPSE_THRESHOLD } from '@/config/index'; import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver'; -import { useDesignStore } from '@/store/modules/design'; +import { useDesignStore } from '@/store'; /** * 侧边栏折叠切换逻辑组合式函数 方便多个页面调用 diff --git a/src/hooks/useSafeArea.ts b/src/hooks/useSafeArea.ts new file mode 100644 index 0000000..675d1fe --- /dev/null +++ b/src/hooks/useSafeArea.ts @@ -0,0 +1,86 @@ +import type { Ref } from 'vue'; + +type SafeAreaDirection = 'left' | 'right' | 'top' | 'bottom'; + +interface SafeAreaOptions { + direction: SafeAreaDirection; // 必选方向 + size: number; // 安全区域尺寸(像素) + onChange?: (isInArea: boolean, e: MouseEvent) => void; // 状态变化回调 + enabled?: Ref; // 改为接收响应式变量(Ref 类型) +} + +export function useSafeArea(options: SafeAreaOptions): { isInSafeArea: Ref } { + const { direction, size, onChange, enabled = ref(true) } = options; // 默认值改为 ref(true) + const isInSafeArea = ref(false); + let isThrottling = false; + + // 方向检测逻辑(未修改) + const checkInArea = (e: MouseEvent) => { + const { clientX, clientY } = e; + const w = window.innerWidth; + const h = window.innerHeight; + return direction === 'left' + ? clientX <= size + : direction === 'right' + ? clientX >= w - size + : direction === 'top' + ? clientY <= size + : direction === 'bottom' + ? clientY >= h - size + : false; + }; + + // 鼠标移动处理函数(未修改) + const handleMouseMove = (e: MouseEvent) => { + if (!enabled.value || isThrottling) + return; // 直接使用 enabled.value 判断 + + requestAnimationFrame(() => { + const current = checkInArea(e); + if (current !== isInSafeArea.value) { + isInSafeArea.value = current; + onChange?.(current, e); + } + isThrottling = false; + }); + isThrottling = true; + }; + + // 动态绑定/解绑事件(简化逻辑) + const toggleListener = (enable: boolean) => { + if (enable) { + window.addEventListener('mousemove', handleMouseMove); + } + else { + window.removeEventListener('mousemove', handleMouseMove); + isInSafeArea.value = false; // 关闭时重置状态 + } + }; + + // 监听 enabled 的变化(直接监听传入的 ref) + watch( + enabled, + (newVal) => { + toggleListener(newVal); + }, + { immediate: true }, + ); // 立即执行一次,处理初始状态 + + // 组件卸载时强制解绑 + onUnmounted(() => { + toggleListener(false); + }); + + return { isInSafeArea }; +} + +// 使用示例 +// 外部组件中 +// const isListening = ref(false); // 响应式开关 + +// const { isInSafeArea } = useSafeArea({ +// direction: 'left', +// size: 50, +// onChange: (isIn, e) => console.log('状态变化:', isIn), +// enabled: isListening // 直接传入响应式变量(无需 .value) +// }); diff --git a/src/hooks/useWindowWidthObserver.ts b/src/hooks/useWindowWidthObserver.ts index 77429ba..049873e 100644 --- a/src/hooks/useWindowWidthObserver.ts +++ b/src/hooks/useWindowWidthObserver.ts @@ -1,7 +1,7 @@ import type { MaybeRef } from 'vue'; import { onBeforeUnmount, ref, unref, watch } from 'vue'; import { COLLAPSE_THRESHOLD, SIDE_BAR_WIDTH } from '@/config/index'; -import { useDesignStore } from '@/store/modules/design'; +import { useDesignStore } from '@/store'; /** * 这里逻辑是研究豆包的折叠逻辑后,设计的折叠方法 @@ -28,7 +28,6 @@ export function useWindowWidthObserver( designStore.setCollapseFinal(true); break; case 'followSystem': - designStore.setCollapseFinal(!isAbove); designStore.setCollapseFinal(!isAbove); break; case 'alwaysExpanded': @@ -44,7 +43,6 @@ export function useWindowWidthObserver( break; case 'narrowExpandWideCollapse': designStore.setCollapseFinal(isAbove); - designStore.setCollapseFinal(isAbove); } console.log('最终的折叠状态:', designStore.isCollapse); diff --git a/src/layouts/LayoutVertical/index.vue b/src/layouts/LayoutVertical/index.vue index f2c6287..ab03fdf 100644 --- a/src/layouts/LayoutVertical/index.vue +++ b/src/layouts/LayoutVertical/index.vue @@ -1,14 +1,27 @@ diff --git a/src/layouts/components/Header/components/Collapse.vue b/src/layouts/components/Header/components/Collapse.vue index a17e373..de621a1 100644 --- a/src/layouts/components/Header/components/Collapse.vue +++ b/src/layouts/components/Header/components/Collapse.vue @@ -3,13 +3,14 @@ import SvgIcon from '@/components/SvgIcon/index.vue'; import { SIDE_BAR_WIDTH } from '@/config/index'; import { useCollapseToggle } from '@/hooks/useCollapseToggle'; -import { useDesignStore } from '@/store/modules/design'; +import { useDesignStore } from '@/store'; const { changeCollapse } = useCollapseToggle(); const designStore = useDesignStore(); function handleChangeCollapse() { changeCollapse(); + designStore.isCollapseHover = false; if (!designStore.isCollapse) { document.documentElement.style.setProperty( `--sidebar-left-container-default-width`, diff --git a/src/layouts/components/Header/components/TitleEditing.vue b/src/layouts/components/Header/components/TitleEditing.vue index 46bde23..37a0ddd 100644 --- a/src/layouts/components/Header/components/TitleEditing.vue +++ b/src/layouts/components/Header/components/TitleEditing.vue @@ -30,24 +30,34 @@ function handleClickTitle() {