Files
Yi.Framework/Yi.Ai.Vue3/src/pages/chat/index.vue
2026-01-11 14:21:06 +08:00

346 lines
7.1 KiB
Vue
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.
<script setup lang="ts">
import { Expand, Fold } from '@element-plus/icons-vue';
import { computed, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const route = useRoute();
const router = useRouter();
// 控制侧边栏折叠状态
const isCollapsed = ref(true);
// 菜单项配置
const navItems = [
{ name: 'conversation', label: 'AI对话', icon: 'ChatDotRound', path: '/chat/conversation' },
{ name: 'image', label: 'AI图片', icon: 'Picture', path: '/chat/image' },
{ name: 'video', label: 'AI视频', icon: 'VideoCamera', path: '/chat/video' },
{ name: 'monitor', label: 'AI智能体', icon: 'Monitor', path: '/chat/agent' },
];
// 当前激活的菜单
const activeNav = computed(() => {
const path = route.path;
const item = navItems.find(item => item.path === path);
return item?.name || 'user';
});
// 切换菜单
function handleNavSelect(menu: typeof navItems[0]) {
router.push(menu.path);
}
// 在移动端默认折叠侧边栏
const isMobile = ref(false);
function checkIsMobile() {
isMobile.value = window.innerWidth <= 768;
// 移动端默认折叠
if (isMobile.value) {
isCollapsed.value = true;
}
}
// 初始检查和监听窗口大小变化
checkIsMobile();
window.addEventListener('resize', checkIsMobile);
</script>
<template>
<div class="console-page" :class="{ 'is-collapsed': isCollapsed }">
<!-- 侧边栏导航 -->
<div class="nav-sidebar" :class="{ 'is-collapsed': isCollapsed }">
<div v-if="false" class="nav-header">
<h2 v-show="!isCollapsed" class="nav-title">
AI聊天
</h2>
<div class="collapse-btn" @click="isCollapsed = !isCollapsed">
<el-icon>
<Expand v-if="isCollapsed" />
<Fold v-else />
</el-icon>
</div>
</div>
<el-menu
:default-active="activeNav"
class="nav-menu custom-menu"
:collapse="isCollapsed"
:collapse-transition="false"
>
<el-menu-item
v-for="item in navItems"
:key="item.name"
:index="item.name"
class="nav-menu-item"
@click="handleNavSelect(item)"
>
<template #default>
<div class="menu-content">
<el-icon class="menu-icon">
<component :is="item.icon" />
</el-icon>
<span class="menu-label">{{ item.label }}</span>
</div>
</template>
</el-menu-item>
</el-menu>
</div>
<!-- 折叠遮罩层移动端 -->
<div
v-if="isMobile && !isCollapsed"
class="sidebar-overlay"
@click="isCollapsed = true"
/>
<!-- 主内容区 -->
<div class="content-main">
<!-- <div v-if="isMobile" class="content-header"> -->
<!-- <div class="mobile-toggle" @click="isCollapsed = false"> -->
<!-- <el-icon><Expand /></el-icon> -->
<!-- <span>AI应用</span> -->
<!-- </div> -->
<!-- </div> -->
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</div>
</div>
</template>
<style scoped lang="scss">
.console-page {
display: flex;
width: 100%;
overflow: auto;
&.is-collapsed {
.content-main {
margin-left: 0;
}
}
}
.nav-sidebar {
width: 240px;
height: 100%;
border-right: var(--header-border) !important;
flex-shrink: 0;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
position: relative;
z-index: 1001;
&.is-collapsed {
width: 64px;
.nav-title {
opacity: 0;
width: 0;
overflow: hidden;
}
}
}
.nav-header {
padding: 20px;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 70px;
box-sizing: border-box;
}
.nav-title {
margin: 0;
font-size: 20px;
font-weight: 600;
color: var(--el-text-color-primary);
transition: all 0.3s ease;
white-space: nowrap;
}
.collapse-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 4px;
cursor: pointer;
color: var(--el-text-color-secondary);
transition: all 0.2s;
flex-shrink: 0;
&:hover {
background-color: var(--el-fill-color-light);
color: var(--el-text-color-primary);
}
.el-icon {
font-size: 18px;
}
}
.nav-menu {
flex: 1;
//border-right: none;
overflow-y: auto;
overflow-x: hidden;
:deep(.el-menu-item) {
&.is-active {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
span {
transition: opacity 0.3s ease;
}
}
&:not(.el-menu--collapse) {
:deep(.el-menu-item) {
justify-content: flex-start;
}
}
}
.sidebar-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
transition: opacity 0.3s ease;
}
.content-main {
flex: 1;
//padding: 20px;
position: relative;
}
.content-header {
display: none;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid var(--el-border-color);
}
.mobile-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-radius: 4px;
background-color: var(--el-fill-color-light);
cursor: pointer;
color: var(--el-text-color-primary);
transition: background-color 0.2s;
&:hover {
background-color: var(--el-fill-color);
}
.el-icon {
font-size: 18px;
}
}
// 移动端适配
@media (max-width: 768px) {
.console-page {
position: relative;
}
.nav-sidebar {
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 1001;
transform: translateX(-100%);
transition: transform 0.3s ease;
&.is-collapsed {
transform: translateX(-100%);
width: 240px;
}
&:not(.is-collapsed) {
transform: translateX(0);
}
.collapse-btn {
display: none;
}
}
.content-header {
display: block;
}
.content-main {
//padding: 15px;
margin-left: 0 !important;
}
}
// 平板适配
@media (min-width: 769px) and (max-width: 1024px) {
.nav-sidebar {
width: 200px;
&.is-collapsed {
width: 64px;
}
}
.content-main {
padding: 15px;
}
}
/* 重要:需要深度选择器覆盖 Element Plus 默认样式 */
.custom-menu :deep(.el-menu-item) {
height: auto !important;
min-height: 56px !important; /* 设置最小高度 */
padding: 12px 0 !important;
line-height: normal !important;
}
.custom-menu :deep(.el-menu-item .menu-content) {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.custom-menu :deep(.el-menu-item .menu-icon) {
margin: 0 !important;
font-size: 20px;
}
.custom-menu :deep(.el-menu-item .menu-label) {
margin-top: 4px;
font-size: 12px;
white-space: nowrap;
}
/* 处理激活状态 */
.custom-menu :deep(.el-menu-item.is-active) {
background-color: var(--el-color-primary-light-9);
}
.custom-menu :deep(.el-menu-item:hover) {
background-color: var(--el-color-primary-light-8);
}
</style>