feat: 新增关闭右侧菜单动画

This commit is contained in:
何嘉悦 2025-05-20 23:49:15 +08:00
parent 1f5eeb2146
commit 7535bbb591
15 changed files with 438 additions and 112 deletions

View File

@ -24,6 +24,8 @@ export interface DesignConfigState {
collapseType: CollapseType; collapseType: CollapseType;
// 是否折叠菜单 // 是否折叠菜单
isCollapse: boolean; isCollapse: boolean;
// 头部折叠按钮是否被悬停
isCollapseHover: boolean;
} }
export const themeColorList: string[] = [ export const themeColorList: string[] = [
@ -65,6 +67,8 @@ const design: DesignConfigState = {
collapseType: 'followSystem', collapseType: 'followSystem',
// 是否折叠菜单 // 是否折叠菜单
isCollapse: false, isCollapse: false,
// 头部折叠按钮是否被悬停
isCollapseHover: false,
}; };
export default design; export default design;

View File

@ -1,6 +1,6 @@
import { COLLAPSE_THRESHOLD } from '@/config/index'; import { COLLAPSE_THRESHOLD } from '@/config/index';
import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver'; import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
import { useDesignStore } from '@/store/modules/design'; import { useDesignStore } from '@/store';
/** /**
* 便 * 便

86
src/hooks/useSafeArea.ts Normal file
View File

@ -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<boolean>; // 改为接收响应式变量Ref 类型)
}
export function useSafeArea(options: SafeAreaOptions): { isInSafeArea: Ref<boolean> } {
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
// });

View File

@ -1,7 +1,7 @@
import type { MaybeRef } from 'vue'; import type { MaybeRef } from 'vue';
import { onBeforeUnmount, ref, unref, watch } from 'vue'; import { onBeforeUnmount, ref, unref, watch } from 'vue';
import { COLLAPSE_THRESHOLD, SIDE_BAR_WIDTH } from '@/config/index'; 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); designStore.setCollapseFinal(true);
break; break;
case 'followSystem': case 'followSystem':
designStore.setCollapseFinal(!isAbove);
designStore.setCollapseFinal(!isAbove); designStore.setCollapseFinal(!isAbove);
break; break;
case 'alwaysExpanded': case 'alwaysExpanded':
@ -44,7 +43,6 @@ export function useWindowWidthObserver(
break; break;
case 'narrowExpandWideCollapse': case 'narrowExpandWideCollapse':
designStore.setCollapseFinal(isAbove); designStore.setCollapseFinal(isAbove);
designStore.setCollapseFinal(isAbove);
} }
console.log('最终的折叠状态:', designStore.isCollapse); console.log('最终的折叠状态:', designStore.isCollapse);

View File

@ -1,14 +1,27 @@
<!-- 纵向布局作为基础布局 --> <!-- 纵向布局作为基础布局 -->
<script setup lang="ts"> <script setup lang="ts">
import { useSafeArea } from '@/hooks/useSafeArea';
import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver'; import { useWindowWidthObserver } from '@/hooks/useWindowWidthObserver';
import Aside from '@/layouts/components/Aside/index.vue'; import Aside from '@/layouts/components/Aside/index.vue';
import Header from '@/layouts/components/Header/index.vue'; import Header from '@/layouts/components/Header/index.vue';
import Main from '@/layouts/components/Main/index.vue'; import Main from '@/layouts/components/Main/index.vue';
import { useDesignStore } from '@/store/modules/design'; import { useDesignStore } from '@/store';
const designStore = useDesignStore(); const designStore = useDesignStore();
console.log('每次加载全局的折叠状态', designStore.collapseType); const isCollapse = computed(() => designStore.isCollapse);
/* 是否移入了安全区 */
useSafeArea({
direction: 'left',
size: 50,
onChange(isInSafeArea) {
// console.log('', isInSafeArea, isCollapse.value);
// true
designStore.isCollapseHover = isInSafeArea;
},
enabled: isCollapse, //
});
/** 监听窗口大小变化,折叠侧边栏 */ /** 监听窗口大小变化,折叠侧边栏 */
useWindowWidthObserver(); useWindowWidthObserver();
@ -20,9 +33,7 @@ useWindowWidthObserver();
<Header /> <Header />
</el-header> </el-header>
<el-container class="layout-container-main"> <el-container class="layout-container-main">
<Transition :class="designStore.pageAnimateType"> <Aside />
<Aside v-if="!designStore.isCollapse" class="layout-aside transition-all" />
</Transition>
<el-main class="layout-main"> <el-main class="layout-main">
<!-- 路由页面 --> <!-- 路由页面 -->
<Main /> <Main />
@ -35,21 +46,12 @@ useWindowWidthObserver();
.layout-container { .layout-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative;
.layout-header { .layout-header {
padding: 0; padding: 0;
} }
.layout-aside {
overflow: hidden;
width: var(--sidebar-left-container-default-width, 0px);
height: 100%;
position: absolute;
z-index: 10;
top: 0;
left: 0;
}
.layout-main { .layout-main {
padding: 0; padding: 0;
} }

View File

@ -1,7 +1,11 @@
<!-- Aside 侧边栏 --> <!-- Aside 侧边栏 -->
<script setup lang="ts"> <script setup lang="ts">
import type { GroupableOptions } from 'vue-element-plus-x/types/Conversations';
import logo from '@/assets/images/logo.png'; import logo from '@/assets/images/logo.png';
import Collapse from '@/layouts/components/Header/components/Collapse.vue'; import Collapse from '@/layouts/components/Header/components/Collapse.vue';
import { useDesignStore } from '@/store';
const designStore = useDesignStore();
/* 创建会话 开始 */ /* 创建会话 开始 */
function handleCreatChat() { function handleCreatChat() {
@ -10,19 +14,125 @@ function handleCreatChat() {
/* 创建会话 结束 */ /* 创建会话 结束 */
/* 会话组件 开始 */ /* 会话组件 开始 */
const active = ref(); const active = ref('m1');
const conversationsList = ref([]); const conversationsList = ref([
{
key: 'm1',
label: '菜单测试项目 1',
group: '工作',
},
{
key: 'm2',
label: '菜单测试项目 2',
disabled: true,
group: '工作',
},
{
key: 'm3',
label: '菜单测试项目 3',
group: '工作',
},
{
key: 'm4',
label: '菜单测试项目 4',
group: '学习',
},
{
key: 'm5',
label: '菜单测试项目 5',
group: '学习',
},
{
key: 'm6',
label: '菜单测试项目 6',
group: '学习',
},
{
key: 'm7',
label: '菜单测试项目 7',
group: '学习',
},
{
key: 'm8',
label: '菜单测试项目 8',
group: '个人',
},
{
key: 'm9',
label: '菜单测试项目 9',
group: '个人',
},
{
key: 'm10',
label: '菜单测试项目 10',
group: '个人',
},
{
key: 'm11',
label: '菜单测试项目 11',
group: '个人',
},
{
key: 'm12',
label: '菜单测试项目 12',
},
{
key: 'm13',
label: '菜单测试项目 13',
},
{
key: 'm14',
label: '菜单测试项目 14',
},
]);
//
const customGroupOptions: GroupableOptions = {
// > > >
sort: (a: any, b: any) => {
const order: Record<string, number> = { 学习: 0, 工作: 1, 个人: 2, 未分组: 3 };
const orderA = order[a] !== undefined ? order[a] : 999;
const orderB = order[b] !== undefined ? order[b] : 999;
return orderA - orderB;
},
};
function handleChange() { function handleChange() {
console.log('点击了会话'); 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> </script>
<template> <template>
<div class="aside-container"> <div
class="aside-container"
:class="{
'aside-container-suspended': designStore.isCollapseHover,
'aside-container-collapse': designStore.isCollapse,
}"
@mouseenter.stop="handleChangeMouse('enter')"
@mouseleave.stop="handleChangeMouse('leave')"
>
<div class="aside-wrapper"> <div class="aside-wrapper">
<div class="aside-header"> <div v-if="!designStore.isCollapse" class="aside-header">
<div class="flex items-center gap-8px hover:cursor-pointer" @click="handleCreatChat"> <div class="flex items-center gap-8px hover:cursor-pointer" @click="handleCreatChat">
<el-image :src="logo" alt="logo" fit="cover" class="logo-img" /> <el-image :src="logo" alt="logo" fit="cover" class="logo-img" />
<span class="logo-text max-w-150px text-overflow">Elemennt-Plus-X</span> <span class="logo-text max-w-150px text-overflow">Elemennt-Plus-X</span>
@ -30,47 +140,58 @@ function handleChange() {
<Collapse class="ml-auto" /> <Collapse class="ml-auto" />
</div> </div>
<div class="creat-chat-btn-wrapper">
<div class="creat-chat-btn" @click="handleCreatChat">
<el-icon class="add-icon">
<Plus />
</el-icon>
<span class="creat-chat-text">新对话</span>
<SvgIcon name="ctrl+k" size="37" />
</div>
</div>
<div class="aside-body"> <div class="aside-body">
<div v-if="conversationsList.length > 0" class="flex h-full"> <div class="creat-chat-btn-wrapper">
<Conversations <div class="creat-chat-btn" @click="handleCreatChat">
:active="active" <el-icon class="add-icon">
:items="conversationsList" <Plus />
row-key="id" </el-icon>
label-key="sessionTitle" <span class="creat-chat-text">新对话</span>
@change="handleChange" <SvgIcon name="ctrl+k" size="37" />
/> </div>
</div> </div>
<el-empty v-else class="h-full flex-center" description="暂无对话记录" /> <div class="aside-content">
<div v-if="conversationsList.length > 0" class="conversations-wrap overflow-hidden">
<Conversations
v-model:active="active"
:items="conversationsList"
:label-max-width="200"
:show-tooltip="true"
:tooltip-offset="35"
show-built-in-menu
:groupable="customGroupOptions"
row-key="key"
@change="handleChange"
/>
</div>
<el-empty v-else class="h-full flex-center" description="暂无对话记录" />
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
//
.aside-container { .aside-container {
height: 100%; height: 100%;
position: relative; left: 0;
user-select: none; top: 0;
box-sizing: border-box; position: absolute;
width: var(--sidebar-default-width);
pointer-events: auto;
background-color: var(--sidebar-background-color); background-color: var(--sidebar-background-color);
border-right: 1px solid rgba(0, 0, 0, 0.08); border-right: 0.5px solid var(--s-color-border-tertiary, rgba(0, 0, 0, 0.08));
z-index: 11;
.aside-wrapper { .aside-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
//
.aside-header { .aside-header {
height: 36px; height: 36px;
margin: 10px 12px 0px; margin: 10px 12px 0px;
@ -103,53 +224,122 @@ function handleChange() {
} }
} }
.creat-chat-btn-wrapper { //
padding: 0 12px; .aside-body {
.creat-chat-btn-wrapper {
padding: 0 12px;
.creat-chat-btn { .creat-chat-btn {
display: flex;
align-items: center;
padding: 8px 6px;
margin-top: 16px;
margin-bottom: 6px;
background-color: rgba(0, 87, 255, 0.06);
border-radius: 12px;
border: 1px solid rgba(0, 102, 255, 0.15);
cursor: pointer;
gap: 6px;
&:hover {
background-color: rgba(0, 87, 255, 0.12);
}
.creat-chat-text {
font-size: 14px;
font-weight: 700;
line-height: 22px;
}
.add-icon {
font-size: 16px;
width: 24px;
height: 24px;
}
.svg-icon {
margin-left: auto;
height: 24px;
color: rgba(0, 87, 255, 0.3);
}
}
}
.aside-content {
display: flex; display: flex;
align-items: center; flex-direction: column;
padding: 8px 6px; height: 100%;
margin-top: 16px; flex: 1;
margin-bottom: 6px; min-height: 0;
color: #0057ff;
background-color: rgba(0, 87, 255, 0.06);
border-radius: 12px;
border: 1px solid rgba(0, 102, 255, 0.15);
cursor: pointer;
gap: 6px;
&:hover { // -
background-color: rgba(0, 87, 255, 0.12); .conversations-wrap {
} height: calc(100vh - 110px);
.creat-chat-text {
font-size: 14px;
font-weight: 700;
line-height: 22px;
}
.add-icon {
font-size: 16px;
width: 24px;
height: 24px;
}
.svg-icon {
margin-left: auto;
height: 24px;
color: rgba(0, 87, 255, 0.3);
} }
} }
} }
}
}
.aside-body { //
display: flex; .aside-container-collapse {
flex-direction: column; position: absolute;
height: 100%; opacity: 0;
flex: 1; //
min-height: 0; transform: translateX(-100%);
} 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-property: opacity, transform;
transition-duration: 0.2s, 0.2s;
transition-timing-function: ease, ease;
transition-delay: 0.2s, 0s;
}
//
.hover-delay {
transition-delay: 0.2s;
}
//
.aside-container-suspended {
position: absolute;
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;
}
// 穿
:deep() {
//
.conversations-list {
background-color: transparent !important;
}
//
.conversation-group-title {
background-color: var(--sidebar-background-color) !important;
} }
} }
</style> </style>

View File

@ -3,13 +3,14 @@
import SvgIcon from '@/components/SvgIcon/index.vue'; import SvgIcon from '@/components/SvgIcon/index.vue';
import { SIDE_BAR_WIDTH } from '@/config/index'; import { SIDE_BAR_WIDTH } from '@/config/index';
import { useCollapseToggle } from '@/hooks/useCollapseToggle'; import { useCollapseToggle } from '@/hooks/useCollapseToggle';
import { useDesignStore } from '@/store/modules/design'; import { useDesignStore } from '@/store';
const { changeCollapse } = useCollapseToggle(); const { changeCollapse } = useCollapseToggle();
const designStore = useDesignStore(); const designStore = useDesignStore();
function handleChangeCollapse() { function handleChangeCollapse() {
changeCollapse(); changeCollapse();
designStore.isCollapseHover = false;
if (!designStore.isCollapse) { if (!designStore.isCollapse) {
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
`--sidebar-left-container-default-width`, `--sidebar-left-container-default-width`,

View File

@ -30,24 +30,34 @@ function handleClickTitle() {
</script> </script>
<template> <template>
<div <div class="w-full h-full flex flex-col justify-center">
class="title-editing-container hover:bg-[rgba(0,0,0,.04)] cursor-pointer rounded-md p-4px flex items-center" <div class="box-border mr-20px">
@click="handleClickTitle" <div
> class="title-editing-container p-4px w-fit max-w-full flex items-center justify-start cursor-pointer select-none hover:bg-[rgba(0,0,0,.04)] cursor-pointer rounded-md font-size-14px"
<span class="font-size-14px text-overflow max-w-320px">标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑</span> @click="handleClickTitle"
<SvgIcon name="draft-line" size="14" /> >
<div class="text-overflow select-none pr-8px">
标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑标题编辑
</div>
<SvgIcon name="draft-line" size="14" class="flex-none c-gray-500" />
</div>
</div>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.title-editing-container { .title-editing-container {
transition: all 0.3s ease;
&:hover { &:hover {
.svg-icon { .svg-icon {
opacity: 1;
display: block; display: block;
} }
} }
.svg-icon { .svg-icon {
transition: all 0.3s ease;
opacity: 0.5;
display: none; display: none;
} }
} }

View File

@ -1,7 +1,7 @@
<!-- Header 头部 --> <!-- Header 头部 -->
<script setup lang="ts"> <script setup lang="ts">
import { SIDE_BAR_WIDTH } from '@/config/index'; import { SIDE_BAR_WIDTH } from '@/config/index';
import { useUserStore } from '@/store'; import { useDesignStore, useUserStore } from '@/store';
import Avatar from './components/Avatar.vue'; import Avatar from './components/Avatar.vue';
import Collapse from './components/Collapse.vue'; import Collapse from './components/Collapse.vue';
import CreateChat from './components/CreateChat.vue'; import CreateChat from './components/CreateChat.vue';
@ -9,13 +9,21 @@ import LoginBtn from './components/LoginBtn.vue';
import TitleEditing from './components/TitleEditing.vue'; import TitleEditing from './components/TitleEditing.vue';
const userStore = useUserStore(); const userStore = useUserStore();
const designStore = useDesignStore();
console.log('userStore', userStore.token); console.log('userStore', userStore.token);
onMounted(() => { onMounted(() => {
document.documentElement.style.setProperty( // ()
`--sidebar-left-container-default-width`, document.documentElement.style.setProperty(`--sidebar-default-width`, `${SIDE_BAR_WIDTH}px`);
`${SIDE_BAR_WIDTH}px`, if (designStore.isCollapse) {
); document.documentElement.style.setProperty(`--sidebar-left-container-default-width`, ``);
}
else {
document.documentElement.style.setProperty(
`--sidebar-left-container-default-width`,
`${SIDE_BAR_WIDTH}px`,
);
}
}); });
</script> </script>
@ -23,16 +31,25 @@ onMounted(() => {
<div class="header-container"> <div class="header-container">
<div class="header-box relative z-10 top-0 left-0 right-0"> <div class="header-box relative z-10 top-0 left-0 right-0">
<div class="absolute left-0 right-0 top-0 bottom-0 flex items-center flex-row"> <div class="absolute left-0 right-0 top-0 bottom-0 flex items-center flex-row">
<!-- 左边 --> <div
<div class="left-box flex h-full items-center pl-20px gap-12px flex-shrink-0 flex-row"> class="overflow-hidden flex h-full items-center flex-row flex-1 w-fit flex-shrink-0 min-w-0"
<Collapse /> >
<CreateChat /> <div class="w-full flex items-center flex-row">
</div> <!-- 左边 -->
<div
v-if="designStore.isCollapse"
class="left-box flex h-full items-center pl-20px gap-12px flex-shrink-0 flex-row"
>
<Collapse />
<CreateChat />
<div class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
</div>
<!-- 中间 --> <!-- 中间 -->
<div class="middle-box flex h-full items-center gap-12px flex-1 pl-12px"> <div class="middle-box flex-1 min-w-0 ml-12px">
<div class="w-0.5px h-30px bg-[rgba(217,217,217)]" /> <TitleEditing />
<TitleEditing /> </div>
</div>
</div> </div>
<!-- 右边 --> <!-- 右边 -->
@ -54,6 +71,7 @@ onMounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 0;
.header-box { .header-box {
height: var(--header-container-default-heigth); height: var(--header-container-default-heigth);
width: 100%; width: 100%;

View File

@ -1,6 +1,6 @@
<!-- Main --> <!-- Main -->
<script setup lang="ts"> <script setup lang="ts">
import { useDesignStore } from '@/store/modules/design'; import { useDesignStore } from '@/store';
import { useKeepAliveStore } from '@/store/modules/keepAlive'; import { useKeepAliveStore } from '@/store/modules/keepAlive';
const designStore = useDesignStore(); const designStore = useDesignStore();

View File

@ -3,7 +3,7 @@
import type { LayoutType } from '@/config/design'; import type { LayoutType } from '@/config/design';
// import { useScreenStore } from '@/hooks/useScreen'; // import { useScreenStore } from '@/hooks/useScreen';
import LayoutVertical from '@/layouts/LayoutVertical/index.vue'; import LayoutVertical from '@/layouts/LayoutVertical/index.vue';
import { useDesignStore } from '@/store/modules/design'; import { useDesignStore } from '@/store';
// //
const LayoutComponent: Record<LayoutType, Component> = { const LayoutComponent: Record<LayoutType, Component> = {

View File

@ -30,7 +30,7 @@ function handleSend() {
@submit="handleSend" @submit="handleSend"
> >
<template #prefix> <template #prefix>
<div class="flex items-center gap-8px"> <div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
<div <div
class="flex items-center gap-4px px-12px py-8px rounded-15px cursor-pointer font-size-12px border-1px border-gray border-solid hover:bg-[rgba(0,0,0,.04)]" class="flex items-center gap-4px px-12px py-8px rounded-15px cursor-pointer font-size-12px border-1px border-gray border-solid hover:bg-[rgba(0,0,0,.04)]"
> >
@ -58,7 +58,8 @@ function handleSend() {
<style scoped lang="scss"> <style scoped lang="scss">
.chat-home-container { .chat-home-container {
width: 100%; padding: 0 16px;
width: calc(100% - 32px);
display: flex; display: flex;
position: relative; position: relative;
flex-direction: column; flex-direction: column;

View File

@ -7,4 +7,5 @@ store.use(piniaPluginPersistedstate);
export default store; export default store;
export * from './modules/design';
export * from './modules/user'; export * from './modules/user';

View File

@ -11,6 +11,7 @@ const {
layout: reLayout, layout: reLayout,
collapseType: reCollapseType, collapseType: reCollapseType,
isCollapse: reisCollapse, isCollapse: reisCollapse,
isCollapseHover: reisCollapseHover,
} = designSetting; } = designSetting;
export const useDesignStore = defineStore( export const useDesignStore = defineStore(
@ -47,6 +48,13 @@ export const useDesignStore = defineStore(
isCollapse.value = collapseFinal; isCollapse.value = collapseFinal;
}; };
// 折叠按钮是否被悬停
const isCollapseHover = ref<boolean>(reisCollapseHover);
const setCollapseHover = (hover: boolean) => {
isCollapseHover.value = hover;
};
return { return {
darkMode, darkMode,
setDarkMode, setDarkMode,
@ -60,6 +68,8 @@ export const useDesignStore = defineStore(
setCollapseType, setCollapseType,
isCollapse, isCollapse,
setCollapseFinal, setCollapseFinal,
isCollapseHover,
setCollapseHover,
}; };
}, },
{ {

View File

@ -3,18 +3,23 @@
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable // biome-ignore lint: disable
export {}; export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElHeader: typeof import('element-plus/es')['ElHeader'] ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage'] ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain'] 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'] IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default']
Popover: typeof import('./../src/components/Popover/index.vue')['default'] Popover: typeof import('./../src/components/Popover/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']