fix: 前端页面架构重构初版

This commit is contained in:
Gsh
2025-12-28 22:42:17 +08:00
parent c649ad31c2
commit e4621d9049
53 changed files with 6098 additions and 845 deletions

View File

@@ -1,94 +1,50 @@
<!-- Header 头部 -->
<!--
&lt;!&ndash; Header 头部 &ndash;&gt;
<script setup lang="ts">
import { onKeyStroke } from '@vueuse/core';
import { SIDE_BAR_WIDTH } from '@/config/index';
import { useDesignStore, useUserStore } from '@/stores';
import { useSessionStore } from '@/stores/modules/session';
import { useRouter } from 'vue-router';
import logo from '@/assets/images/logo.png';
import { useUserStore } from '@/stores';
import AiTutorialBtn from './components/AiTutorialBtn.vue';
import AnnouncementBtn from './components/AnnouncementBtn.vue';
import Avatar from './components/Avatar.vue';
import BuyBtn from './components/BuyBtn.vue';
import Collapse from './components/Collapse.vue';
import ConsoleBtn from './components/ConsoleBtn.vue';
import CreateChat from './components/CreateChat.vue';
import LoginBtn from './components/LoginBtn.vue';
import ModelLibraryBtn from './components/ModelLibraryBtn.vue';
import TitleEditing from './components/TitleEditing.vue';
import StartChatBtn from './components/StartChatBtn.vue';
import ThemeBtn from './components/ThemeBtn.vue';
const router = useRouter();
const userStore = useUserStore();
const designStore = useDesignStore();
const sessionStore = useSessionStore();
const avatarRef = ref();
const currentSession = computed(() => sessionStore.currentSession);
onMounted(() => {
// 全局设置侧边栏默认宽度 (这个是不变的,一开始就设置)
document.documentElement.style.setProperty(`--sidebar-default-width`, `${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`,
);
}
});
// 定义 Ctrl+K 的处理函数
function handleCtrlK(event: KeyboardEvent) {
event.preventDefault(); // 防止默认行为
sessionStore.createSessionBtn();
}
// 设置全局的键盘按键监听
onKeyStroke(event => event.ctrlKey && event.key.toLowerCase() === 'k', handleCtrlK, {
passive: false,
});
// 打开控制台
function handleOpenConsole() {
avatarRef.value?.openDialog?.();
router.push('/console');
}
</script>
<template>
<div class="header-container">
<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="overflow-hidden flex h-full items-center flex-row flex-1 w-fit flex-shrink-0 min-w-0"
>
<div class="w-full flex items-center flex-row">
<!-- 左边 -->
<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 v-if="currentSession" class="w-0.5px h-30px bg-[rgba(217,217,217)]" />
</div>
<!-- 中间 -->
<div class="middle-box flex-1 min-w-0 ml-12px">
<TitleEditing />
</div>
</div>
<div class="header-box">
&lt;!&ndash; 左侧logo和品牌区域 &ndash;&gt;
<div class="left-section">
<div class="brand-container">
<el-image :src="logo" alt="logo" fit="contain" class="logo-img" />
<span class="brand-text">意心AI</span>
</div>
</div>
<!-- 右边 -->
<div class="right-box flex h-full items-center pr-20px flex-shrink-0 mr-auto flex-row">
<AnnouncementBtn />
<ModelLibraryBtn />
<AiTutorialBtn />
<ConsoleBtn @open-console="handleOpenConsole" />
<BuyBtn v-show="userStore.userInfo" />
<Avatar v-show="userStore.userInfo" ref="avatarRef" />
<LoginBtn v-show="!userStore.userInfo" />
</div>
&lt;!&ndash; 右侧功能按钮区域 &ndash;&gt;
<div class="right-section">
<StartChatBtn />
<AnnouncementBtn />
<ModelLibraryBtn />
<AiTutorialBtn />
<ConsoleBtn @open-console="handleOpenConsole" />
<BuyBtn v-show="userStore.userInfo" />
<ThemeBtn />
<LoginBtn v-show="!userStore.userInfo" />
<Avatar v-show="userStore.userInfo" />
</div>
</div>
</div>
@@ -98,20 +54,483 @@ function handleOpenConsole() {
.header-container {
display: flex;
flex-shrink: 0;
flex-direction: column;
width: 100%;
height: fit-content;
height: var(&#45;&#45;header-container-default-height, 60px);
.header-box {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
width: calc(
100% - var(--sidebar-left-container-default-width, 0px) - var(
--sidebar-right-container-default-width,
0px
)
);
height: var(--header-container-default-heigth);
margin: 0 var(--sidebar-right-container-default-width, 0) 0
var(--sidebar-left-container-default-width, 0);
height: 100%;
padding: 0 16px;
background: var(&#45;&#45;header-bg-color, #ffffff);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
// 左侧品牌区域
.left-section {
display: flex;
align-items: center;
min-width: fit-content;
flex-shrink: 0;
.brand-container {
display: flex;
align-items: center;
gap: 8px;
.logo-img {
width: 36px; // 优化为更合适的大小
height: 36px;
flex-shrink: 0;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
}
.brand-text {
font-size: 22px; // 减小字体大小
font-weight: bold;
color: var(&#45;&#45;brand-color, #000000);
white-space: nowrap;
letter-spacing: -0.5px;
transition: color 0.2s ease;
&:hover {
//color: var(&#45;&#45;brand-hover-color, #40a9ff);
}
}
}
}
// 右侧功能区域
.right-section {
display: flex;
align-items: center;
gap: 12px; // 优化按钮间距
height: 100%;
flex-shrink: 0;
// 统一按钮样式
:deep(.menu-button) {
height: 36px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>
-->
<!-- Header 头部 -->
<script setup lang="ts">
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import logo from '@/assets/images/logo.png';
import ConsoleBtn from '@/layouts/components0/Header/components/ConsoleBtn.vue';
import { useUserStore } from '@/stores';
import AiTutorialBtn from './components/AiTutorialBtn.vue';
import AnnouncementBtn from './components/AnnouncementBtn.vue';
import Avatar from './components/Avatar.vue';
import BuyBtn from './components/BuyBtn.vue';
import LoginBtn from './components/LoginBtn.vue';
import ModelLibraryBtn from './components/ModelLibraryBtn.vue';
import ThemeBtn from './components/ThemeBtn.vue';
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
// 当前激活的菜单项
const activeIndex = computed(() => {
if (route.path.startsWith('/console'))
return 'console';
if (route.path.startsWith('/model-library'))
return 'model-library';
if (route.path.includes('/chat/'))
return 'chat';
return '';
});
// 导航处理
function handleSelect(key: string) {
if (key && key !== 'no-route') {
router.push(key);
}
}
</script>
<template>
<div class="header-container">
<el-menu
:default-active="activeIndex"
class="header-menu"
mode="horizontal"
:ellipsis="false"
:router="false"
@select="handleSelect"
>
<!-- 左侧品牌区域 -->
<div class="menu-left">
<div class="brand-container" @click="router.push('/')">
<el-image :src="logo" alt="logo" fit="contain" class="logo-img" />
<span class="brand-text">意心AI</span>
</div>
</div>
<!-- 右侧功能区域 -->
<div class="menu-right">
<!-- AI聊天菜单 -->
<el-sub-menu index="chat" class="chat-submenu" popper-class="custom-popover">
<template #title>
<span class="menu-title">AI聊天</span>
</template>
<el-menu-item index="/chat/conversation">
AI对话
</el-menu-item>
<el-menu-item index="/chat/image">
图片生成
</el-menu-item>
<el-menu-item index="/chat/video">
视频生成
</el-menu-item>
</el-sub-menu>
<!-- 公告按钮 -->
<el-menu-item class="custom-menu-item" index="no-route">
<AnnouncementBtn :is-menu-item="true" />
</el-menu-item>
<!-- 模型库 -->
<el-menu-item index="/model-library" class="custom-menu-item">
<ModelLibraryBtn :is-menu-item="true" />
</el-menu-item>
<!-- AI教程 -->
<el-menu-item class="custom-menu-item" index="no-route">
<AiTutorialBtn />
</el-menu-item>
<!-- 控制台菜单 -->
<el-sub-menu index="console" class="console-submenu" popper-class="custom-popover">
<template #title>
<ConsoleBtn />
</template>
<el-menu-item index="/console/user">
用户信息
</el-menu-item>
<el-menu-item index="/console/apikey">
API密钥
</el-menu-item>
<el-menu-item index="/console/recharge-log">
充值记录
</el-menu-item>
<el-menu-item index="/console/usage">
用量统计
</el-menu-item>
<el-menu-item index="/console/premium">
尊享服务
</el-menu-item>
<el-menu-item index="/console/daily-task">
每日任务
</el-menu-item>
<el-menu-item index="/console/invite">
每周邀请
</el-menu-item>
<el-menu-item index="/console/activation">
激活码兑换
</el-menu-item>
</el-sub-menu>
<!-- 购买按钮 -->
<el-menu-item v-if="userStore.userInfo" class="custom-menu-item" index="no-route">
<BuyBtn :is-menu-item="true" />
</el-menu-item>
<!-- 主题切换暂不显示 -->
<el-menu-item v-if="false" class="custom-menu-item" index="no-route">
<ThemeBtn :is-menu-item="true" />
</el-menu-item>
<!-- 用户头像 -->
<div v-if="userStore.userInfo" class="avatar-container">
<Avatar />
</div>
<!-- 登录按钮 -->
<el-menu-item v-if="!userStore.userInfo" class="login-menu-item" index="no-route">
<LoginBtn :is-menu-item="true" />
</el-menu-item>
</div>
</el-menu>
</div>
</template>
<style scoped lang="scss">
.header-container {
--menu-hover-bg: #f5f5f5;
--menu-active-color: var(--el-color-primary);
--menu-transition: all 0.2s ease;
width: 100%;
height: var(--header-container-default-height, 64px);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
user-select: none;
}
.header-menu {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
border-bottom: none !important;
}
// 左侧品牌区域
.menu-left {
flex-shrink: 0;
margin-left: 20px;
}
.brand-container {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
padding: 8px 12px;
border-radius: 6px;
transition: background-color var(--menu-transition);
&:hover {
background-color: var(--menu-hover-bg);
}
}
.logo-img {
width: 34px;
height: 34px;
flex-shrink: 0;
transition: transform var(--menu-transition);
&:hover {
transform: scale(1.05);
}
}
.brand-text {
font-size: 20px;
font-weight: 600;
color: var(--brand-color, #000000);
white-space: nowrap;
letter-spacing: -0.5px;
}
// 右侧功能区域
.menu-right {
display: flex;
align-items: center;
height: 100%;
margin-right: 16px;
gap: 4px;
}
// 公共菜单项样式
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
height: 100% !important;
border-bottom: none !important;
padding: 0 12px !important;
color: inherit !important;
&:hover {
background-color: transparent !important;
color: var(--menu-active-color) !important;
}
&.is-active {
background-color: transparent !important;
color: var(--menu-active-color) !important;
.menu-title {
color: var(--menu-active-color) !important;
}
}
}
// 聊天和控制台子菜单
.chat-submenu,
.console-submenu {
:deep(.el-sub-menu__title) {
display: flex;
align-items: center;
justify-content: center;
min-width: 80px;
}
}
.menu-title {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 1.2rem;
font-weight: bold;
color: #606266;
transition: all 0.2s;
}
// 自定义按钮菜单项
.custom-menu-item,
.login-menu-item {
:deep(.el-menu-item-content) {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 0 !important;
}
}
// Avatar 容器
.avatar-container {
height: 100%;
display: flex;
align-items: center;
padding: 0 12px;
margin-left: 4px;
}
// 响应式设计
@media (max-width: 1280px) {
.brand-text {
font-size: 18px;
}
.menu-left {
margin-left: 16px;
}
.menu-right {
margin-right: 12px;
gap: 2px;
}
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
padding: 0 10px !important;
}
}
@media (max-width: 1024px) {
.brand-container {
gap: 8px;
padding: 6px 10px;
}
.logo-img {
width: 30px;
height: 30px;
}
.brand-text {
font-size: 16px;
}
.menu-title {
font-size: 13px;
}
.avatar-container {
padding: 0 8px;
}
}
@media (max-width: 768px) {
.brand-text {
display: none;
}
.logo-img {
width: 32px;
height: 32px;
}
.menu-left {
margin-left: 12px;
}
.menu-right {
margin-right: 8px;
// 隐藏按钮文字
:deep(.button-text) {
display: none;
}
.menu-title {
display: none;
}
// 显示图标
:deep(.el-icon) {
font-size: 18px;
}
}
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
padding: 0 8px !important;
min-width: auto !important;
}
}
@media (max-width: 480px) {
.menu-right {
gap: 0;
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
padding: 0 6px !important;
}
}
}
</style>
<style lang="scss">
// 自定义弹出框样式
.custom-popover {
.el-menu {
border: none;
border-radius: 8px;
padding: 6px 0;
min-width: 160px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
.el-menu-item {
height: 40px;
line-height: 40px;
padding: 0 20px;
margin: 2px 8px;
border-radius: 6px;
font-size: 14px;
transition: all 0.2s ease;
&:hover {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
&.is-active {
color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
font-weight: 500;
}
}
}
}
</style>