feat: ✨ 完美复刻豆包收起展开动画
This commit is contained in:
parent
7535bbb591
commit
e19b4ff765
@ -24,8 +24,10 @@ export interface DesignConfigState {
|
||||
collapseType: CollapseType;
|
||||
// 是否折叠菜单
|
||||
isCollapse: boolean;
|
||||
// 头部折叠按钮是否被悬停
|
||||
isCollapseHover: boolean;
|
||||
// 安全区是否被悬停
|
||||
isSafeAreaHover: boolean;
|
||||
// 跟踪是否首次激活悬停
|
||||
hasActivatedHover: boolean;
|
||||
}
|
||||
|
||||
export const themeColorList: string[] = [
|
||||
@ -67,8 +69,10 @@ const design: DesignConfigState = {
|
||||
collapseType: 'followSystem',
|
||||
// 是否折叠菜单
|
||||
isCollapse: false,
|
||||
// 头部折叠按钮是否被悬停
|
||||
isCollapseHover: false,
|
||||
// 安全区是否被悬停
|
||||
isSafeAreaHover: false,
|
||||
// 跟踪是否首次激活悬停
|
||||
hasActivatedHover: false,
|
||||
};
|
||||
|
||||
export default design;
|
||||
|
||||
@ -14,7 +14,7 @@ export function useCollapseToggle(threshold: number = COLLAPSE_THRESHOLD) {
|
||||
/** 核心折叠切换方法 */
|
||||
const changeCollapse = () => {
|
||||
// 切换最终折叠状态
|
||||
designStore.setCollapseFinal(!designStore.isCollapse);
|
||||
designStore.setCollapse(!designStore.isCollapse);
|
||||
|
||||
if (isAboveThreshold.value) {
|
||||
// 宽屏逻辑
|
||||
|
||||
@ -25,13 +25,13 @@ export function useWindowWidthObserver(
|
||||
// 判断当前的折叠状态
|
||||
switch (designStore.collapseType) {
|
||||
case 'alwaysCollapsed':
|
||||
designStore.setCollapseFinal(true);
|
||||
designStore.setCollapse(true);
|
||||
break;
|
||||
case 'followSystem':
|
||||
designStore.setCollapseFinal(!isAbove);
|
||||
designStore.setCollapse(!isAbove);
|
||||
break;
|
||||
case 'alwaysExpanded':
|
||||
designStore.setCollapseFinal(false);
|
||||
designStore.setCollapse(false);
|
||||
if (isAbove) {
|
||||
// 大于的时候执行关闭动画
|
||||
console.log('执行关闭动画');
|
||||
@ -42,7 +42,7 @@ export function useWindowWidthObserver(
|
||||
}
|
||||
break;
|
||||
case 'narrowExpandWideCollapse':
|
||||
designStore.setCollapseFinal(isAbove);
|
||||
designStore.setCollapse(isAbove);
|
||||
}
|
||||
console.log('最终的折叠状态:', designStore.isCollapse);
|
||||
|
||||
|
||||
@ -16,9 +16,8 @@ useSafeArea({
|
||||
direction: 'left',
|
||||
size: 50,
|
||||
onChange(isInSafeArea) {
|
||||
// console.log('是否移入了安全区', isInSafeArea, isCollapse.value);
|
||||
// 设置悬停为 true
|
||||
designStore.isCollapseHover = isInSafeArea;
|
||||
designStore.isSafeAreaHover = isInSafeArea;
|
||||
},
|
||||
enabled: isCollapse, // 折叠才开启监听
|
||||
});
|
||||
@ -58,6 +57,7 @@ useWindowWidthObserver();
|
||||
|
||||
.layout-container-main {
|
||||
margin-left: var(--sidebar-left-container-default-width, 0px);
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -101,35 +101,17 @@ function handleChange() {
|
||||
console.log('点击了会话');
|
||||
}
|
||||
/* 会话组件 结束 */
|
||||
|
||||
/* 鼠标事件 开始 */
|
||||
// 添加悬停和移除的类名自己控制
|
||||
const isHoverSelf = ref(false);
|
||||
function handleChangeMouse(type: string) {
|
||||
if (designStore.isCollapse) {
|
||||
if (type === 'enter') {
|
||||
isHoverSelf.value = !!designStore.isCollapse;
|
||||
}
|
||||
else {
|
||||
isHoverSelf.value = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
isHoverSelf.value = false;
|
||||
}
|
||||
}
|
||||
/* 鼠标事件 结束 */
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="aside-container"
|
||||
:class="{
|
||||
'aside-container-suspended': designStore.isCollapseHover,
|
||||
'aside-container-suspended': designStore.isSafeAreaHover,
|
||||
'aside-container-collapse': designStore.isCollapse,
|
||||
// 折叠且未激活悬停时添加 no-delay 类
|
||||
'no-delay': designStore.isCollapse && !designStore.hasActivatedHover,
|
||||
}"
|
||||
@mouseenter.stop="handleChangeMouse('enter')"
|
||||
@mouseleave.stop="handleChangeMouse('leave')"
|
||||
>
|
||||
<div class="aside-wrapper">
|
||||
<div v-if="!designStore.isCollapse" class="aside-header">
|
||||
@ -301,33 +283,51 @@ function handleChangeMouse(type: string) {
|
||||
|
||||
// 指定样式过渡
|
||||
transition-property: opacity, transform;
|
||||
transition-duration: 0.2s, 0.2s;
|
||||
transition-duration: 0.3s, 0.3s;
|
||||
transition-timing-function: ease, ease;
|
||||
transition-delay: 0.2s, 0s;
|
||||
}
|
||||
transition-delay: 0.3s, 0.3s;
|
||||
|
||||
// 悬停延迟样式
|
||||
.hover-delay {
|
||||
transition-delay: 0.2s;
|
||||
/* 新增:未激活悬停时覆盖延迟 */
|
||||
&.no-delay {
|
||||
transition-delay: 0s, 0s;
|
||||
}
|
||||
|
||||
/* 禁用悬停事件 */
|
||||
pointer-events: none;
|
||||
|
||||
&:hover,
|
||||
&.aside-container-suspended {
|
||||
pointer-events: auto;
|
||||
/* 悬停激活后恢复事件响应 */
|
||||
}
|
||||
}
|
||||
|
||||
// 悬停样式
|
||||
.aside-container-suspended {
|
||||
position: absolute;
|
||||
.aside-container-collapse:hover,
|
||||
.aside-container-collapse.aside-container-suspended {
|
||||
// 直接在这里写悬停时的样式(与 aside-container-suspended 一致)
|
||||
opacity: 1;
|
||||
transform: translateX(15px);
|
||||
top: 54px;
|
||||
border-radius: 15px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
box-shadow:
|
||||
0px 10px 20px 0px rgba(0, 0, 0, 0.1),
|
||||
0px 0px 1px 0px rgba(0, 0, 0, 0.15);
|
||||
height: auto;
|
||||
z-index: 22;
|
||||
overflow: hidden;
|
||||
max-height: calc(100% - 110px);
|
||||
padding-bottom: 12px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
// 过渡动画沿用原有设置
|
||||
transition-property: opacity, transform;
|
||||
transition-duration: 0.3s, 0.3s;
|
||||
transition-timing-function: ease, ease;
|
||||
transition-delay: 0s, 0s;
|
||||
|
||||
// 会话列表高度-悬停样式
|
||||
.conversations-wrap {
|
||||
height: calc(100vh - 155px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 样式穿透
|
||||
|
||||
@ -10,7 +10,10 @@ const designStore = useDesignStore();
|
||||
|
||||
function handleChangeCollapse() {
|
||||
changeCollapse();
|
||||
designStore.isCollapseHover = false;
|
||||
// 每次切换折叠状态,重置安全区状态
|
||||
designStore.isSafeAreaHover = false;
|
||||
// 重置首次激活悬停状态
|
||||
designStore.hasActivatedHover = false;
|
||||
if (!designStore.isCollapse) {
|
||||
document.documentElement.style.setProperty(
|
||||
`--sidebar-left-container-default-width`,
|
||||
|
||||
@ -11,7 +11,8 @@ const {
|
||||
layout: reLayout,
|
||||
collapseType: reCollapseType,
|
||||
isCollapse: reisCollapse,
|
||||
isCollapseHover: reisCollapseHover,
|
||||
isSafeAreaHover: reisSafeAreaHover,
|
||||
hasActivatedHover: rehasActivatedHover,
|
||||
} = designSetting;
|
||||
|
||||
export const useDesignStore = defineStore(
|
||||
@ -44,17 +45,39 @@ export const useDesignStore = defineStore(
|
||||
// 最终是否展开左侧菜单
|
||||
const isCollapse = ref<boolean>(reisCollapse);
|
||||
|
||||
const setCollapseFinal = (collapseFinal: boolean) => {
|
||||
const setCollapse = (collapseFinal: boolean) => {
|
||||
isCollapse.value = collapseFinal;
|
||||
};
|
||||
|
||||
// 折叠按钮是否被悬停
|
||||
const isCollapseHover = ref<boolean>(reisCollapseHover);
|
||||
const isSafeAreaHover = ref<boolean>(reisSafeAreaHover);
|
||||
|
||||
const setCollapseHover = (hover: boolean) => {
|
||||
isCollapseHover.value = hover;
|
||||
const setSafeAreaHover = (hover: boolean) => {
|
||||
isSafeAreaHover.value = hover;
|
||||
};
|
||||
|
||||
// 跟踪是否首次激活悬停
|
||||
const hasActivatedHover = ref<boolean>(rehasActivatedHover);
|
||||
|
||||
// 两个监听不要合并
|
||||
watch(
|
||||
() => isCollapse.value,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
hasActivatedHover.value = false;
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => isSafeAreaHover.value,
|
||||
() => {
|
||||
hasActivatedHover.value = true;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
return {
|
||||
darkMode,
|
||||
setDarkMode,
|
||||
@ -67,9 +90,10 @@ export const useDesignStore = defineStore(
|
||||
collapseType,
|
||||
setCollapseType,
|
||||
isCollapse,
|
||||
setCollapseFinal,
|
||||
isCollapseHover,
|
||||
setCollapseHover,
|
||||
setCollapse,
|
||||
isSafeAreaHover,
|
||||
setSafeAreaHover,
|
||||
hasActivatedHover,
|
||||
};
|
||||
},
|
||||
{
|
||||
|
||||
7
types/components.d.ts
vendored
7
types/components.d.ts
vendored
@ -3,23 +3,18 @@
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
export {};
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElAside: typeof import('element-plus/es')['ElAside']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImage: typeof import('element-plus/es')['ElImage']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElMain: typeof import('element-plus/es')['ElMain']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
|
||||
Popover: typeof import('./../src/components/Popover/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user