Files
Yi.Framework/Yi.Ai.Vue3/src/hooks/useWindowWidthObserver.ts

149 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { MaybeRef } from 'vue';
import { onBeforeUnmount, ref, unref, watch } from 'vue';
import { COLLAPSE_THRESHOLD, SIDE_BAR_WIDTH } from '@/config/index';
import { useDesignStore } from '@/stores';
/**
* 这里逻辑是研究豆包的折叠逻辑后,设计的折叠方法
* 基于ResizeObserver的窗口宽度监听hooks高性能实时监控
* @param threshold 宽度阈值默认600px支持响应式
* @param onChange 自定义回调(传入则覆盖默认逻辑,参数:当前视口宽度是否超过阈值)
* @returns {object} 包含卸载监听的方法及当前状态
*/
export function useWindowWidthObserver(
threshold: MaybeRef<number> = COLLAPSE_THRESHOLD,
onChange?: (isAboveThreshold: boolean) => void,
) {
const designStore = useDesignStore();
const currentWidth = ref(window.innerWidth);
const isAboveThreshold = ref(false);
const thresholdRef = ref(threshold);
let prevIsAbove = false; // 记录上一次状态,避免重复触发
// 默认逻辑:修改全局折叠状态
const updateCollapseState = (isAbove: boolean) => {
// 判断当前的折叠状态
switch (designStore.collapseType) {
case 'alwaysCollapsed':
designStore.setCollapse(true);
break;
case 'followSystem':
designStore.setCollapse(!isAbove);
break;
case 'alwaysExpanded':
designStore.setCollapse(false);
if (isAbove) {
// 大于的时候执行关闭动画 (豆包是有的,第一版本暂未添加)
}
else {
// 小于的时候执行打开动画 (豆包是有的,第一版本暂未添加)
}
break;
case 'narrowExpandWideCollapse':
designStore.setCollapse(isAbove);
}
// console.log('最终的折叠状态:', designStore.isCollapse);
if (!designStore.isCollapse) {
document.documentElement.style.setProperty(
`--sidebar-left-container-default-width`,
`${SIDE_BAR_WIDTH}px`,
);
}
else {
document.documentElement.style.setProperty(`--sidebar-left-container-default-width`, ``);
}
};
// 初始化状态计算
const updateState = () => {
const thresholdVal = unref(threshold);
const current = unref(currentWidth);
const newIsAbove = current > thresholdVal; // 正确比较两个 number 类型
if (newIsAbove !== prevIsAbove) {
isAboveThreshold.value = newIsAbove;
prevIsAbove = newIsAbove;
// 触发用户回调或默认逻辑
if (onChange) {
onChange(newIsAbove);
}
else {
updateCollapseState(newIsAbove);
}
}
};
// 创建ResizeObserver监听根元素宽度直接反映视口宽度
const observer = new ResizeObserver(([entry]) => {
currentWidth.value = entry.contentRect.width; // 实时获取最新宽度
updateState(); // 立即更新状态并触发逻辑
});
// 监听根元素HTML元素的尺寸变化
observer.observe(document.documentElement);
// 监听阈值变化(支持响应式阈值)
watch(thresholdRef, () => {
updateState(); // 阈值变化时重新计算状态
});
// 卸载监听的方法
const unmount = () => {
observer.disconnect();
};
// 组件卸载时自动清理
onBeforeUnmount(unmount);
return {
unmount,
currentWidth, // 实时当前宽度Ref
isAboveThreshold, // 当前是否超过阈值Ref
};
}
// 使用示例与特性说明
// 1. 基础使用(保留默认折叠逻辑)
// <script setup lang="ts">
// import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
// // 使用默认阈值600px自动修改全局折叠状态
// useWindowWidthObserver();
// </script>
// 2. 自定义阈值(例如 768px
// <script setup lang="ts">
// import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
// // 自定义阈值768px仍使用默认折叠逻辑
// useWindowWidthObserver(768);
// </script>
// 3. 自定义回调(覆盖默认逻辑)
// <script setup lang="ts">
// import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
// // 传入自定义回调,例如控制其他组件显隐
// const { unmount, isAboveThreshold } = useWindowWidthObserver(992, (isAbove) => {
// console.log(`当前窗口是否大于992px${isAbove}`);
// // 可在此处执行任意自定义逻辑(如修改其他状态、控制组件等)
// });
// </script>
// 4. 响应式阈值(动态调整阈值)
// <script setup lang="ts">
// import { ref } from 'vue';
// import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
// // 动态阈值(例如根据路由参数变化)
// const dynamicThreshold = ref(600);
// // 监听阈值变化,自动触发逻辑更新
// useWindowWidthObserver(dynamicThreshold, (isAbove) => {
// console.log(`当前阈值${dynamicThreshold.value},是否超过:${isAbove}`);
// });
// // 模拟阈值变化(实际场景中可能由用户操作或路由变化触发)
// setTimeout(() => {
// dynamicThreshold.value = 768;
// }, 3000);
// </script>