| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- <template>
- <el-container class="layout-container">
- <!-- 侧边栏 -->
- <el-aside :width="isCollapse ? '64px' : '240px'" class="layout-aside">
- <div class="logo-container">
- <img src="@/assets/qingluan.png" alt="Logo" class="logo" />
- <transition name="fade">
- <span v-if="!isCollapse" class="logo-text">青鸾之恋</span>
- </transition>
- </div>
-
- <el-menu
- :default-active="activeMenu"
- :collapse="isCollapse"
- :unique-opened="true"
- router
- class="aside-menu"
- background-color="transparent"
- text-color="#cbd5e1"
- active-text-color="#ffffff"
- >
- <!-- 数据面板 - 仅超级管理员 -->
- <el-menu-item v-if="isSuperAdmin" index="/dashboard">
- <el-icon><DataBoard /></el-icon>
- <template #title>数据面板</template>
- </el-menu-item>
-
- <el-menu-item index="/banner">
- <el-icon><Picture /></el-icon>
- <template #title>轮播图管理</template>
- </el-menu-item>
-
- <el-menu-item index="/announcement">
- <el-icon><Microphone /></el-icon>
- <template #title>小喇叭公告</template>
- </el-menu-item>
-
- <!-- 管理员管理 - 仅超级管理员 -->
- <el-menu-item v-if="isSuperAdmin" index="/admin-user">
- <el-icon><User /></el-icon>
- <template #title>管理员管理</template>
- </el-menu-item>
-
- <el-sub-menu index="/activity">
- <template #title>
- <el-icon><Calendar /></el-icon>
- <span>活动管理</span>
- </template>
- <el-menu-item index="/activity/list">活动列表</el-menu-item>
- <el-menu-item index="/activity/create">创建活动</el-menu-item>
- </el-sub-menu>
-
- <el-sub-menu index="/matchmaker">
- <template #title>
- <el-icon><User /></el-icon>
- <span>红娘管理</span>
- </template>
- <el-menu-item index="/matchmaker/list">红娘列表</el-menu-item>
- <el-menu-item index="/matchmaker/audit">红娘审核</el-menu-item>
- <el-menu-item index="/matchmaker/create">添加红娘</el-menu-item>
- <el-menu-item index="/matchmaker/points-product">积分商品</el-menu-item>
- <el-menu-item index="/matchmaker/points-order">积分商城订单</el-menu-item>
- <el-menu-item index="/matchmaker/resource">线索管理</el-menu-item>
- <el-menu-item index="/matchmaker/case-audit">案例审核</el-menu-item>
- </el-sub-menu>
-
- <el-sub-menu index="/course">
- <template #title>
- <el-icon><Reading /></el-icon>
- <span>课程管理</span>
- </template>
- <el-menu-item index="/course/list">课程列表</el-menu-item>
- <el-menu-item index="/course/create">创建课程</el-menu-item>
- </el-sub-menu>
-
- <el-sub-menu index="/success-case">
- <template #title>
- <el-icon><TrophyBase /></el-icon>
- <span>成功案例</span>
- </template>
- <el-menu-item index="/success-case/list">案例列表</el-menu-item>
- <el-menu-item index="/success-case/create">创建案例</el-menu-item>
- </el-sub-menu>
-
- <el-sub-menu index="/user">
- <template #title>
- <el-icon><UserFilled /></el-icon>
- <span>用户管理</span>
- </template>
- <el-menu-item index="/user/list">用户列表</el-menu-item>
- <el-menu-item index="/user/vip">VIP用户</el-menu-item>
- </el-sub-menu>
-
- <!-- VIP套餐 - 仅超级管理员 -->
- <el-menu-item v-if="isSuperAdmin" index="/vip-package">
- <el-icon><Medal /></el-icon>
- <template #title>VIP套餐</template>
- </el-menu-item>
-
- <el-menu-item index="/dynamic">
- <el-icon><ChatDotSquare /></el-icon>
- <template #title>动态管理</template>
- </el-menu-item>
- <el-menu-item index="/report">
- <el-icon><Warning /></el-icon>
- <template #title>举报管理</template>
- </el-menu-item>
- </el-menu>
- </el-aside>
-
- <!-- 主内容区 -->
- <el-container>
- <!-- 顶部导航 -->
- <el-header class="layout-header">
- <div class="header-left">
- <el-icon class="collapse-icon" @click="toggleCollapse">
- <Fold v-if="!isCollapse" />
- <Expand v-else />
- </el-icon>
- <el-breadcrumb separator="/">
- <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
- <el-breadcrumb-item v-if="currentRoute.meta.title">
- {{ currentRoute.meta.title }}
- </el-breadcrumb-item>
- </el-breadcrumb>
- </div>
-
- <div class="header-right">
- <span class="username">{{ userInfo?.username || '管理员' }}</span>
- <el-dropdown @command="handleCommand">
- <el-avatar :size="35" :src="userInfo?.avatar || 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'" />
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="profile">个人资料</el-dropdown-item>
- <el-dropdown-item divided command="logout">退出登录</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- </el-header>
-
- <!-- 主内容 -->
- <el-main class="layout-main">
- <router-view v-slot="{ Component }">
- <transition name="fade" mode="out-in">
- <component :is="Component" />
- </transition>
- </router-view>
- </el-main>
- </el-container>
- </el-container>
- </template>
- <script setup>
- import { ref, computed } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import { useUserStore } from '@/stores/user'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import {
- DataBoard,
- Picture,
- Microphone,
- Calendar,
- User,
- Reading,
- TrophyBase,
- UserFilled,
- ChatDotSquare,
- Warning,
- Fold,
- Expand,
- Medal
- } from '@element-plus/icons-vue'
- const route = useRoute()
- const router = useRouter()
- const userStore = useUserStore()
- const isCollapse = ref(false)
- const userInfo = computed(() => userStore.userInfo)
- const currentRoute = computed(() => route)
- const activeMenu = computed(() => route.path)
- const isSuperAdmin = computed(() => userStore.isSuperAdmin)
- // 切换侧边栏折叠状态
- const toggleCollapse = () => {
- isCollapse.value = !isCollapse.value
- }
- // 处理用户菜单命令
- const handleCommand = async (command) => {
- switch (command) {
- case 'profile':
- router.push('/profile')
- break
- case 'logout':
- try {
- await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- await userStore.logout()
- ElMessage.success('退出成功')
- router.push('/login')
- } catch (error) {
- // 取消退出
- }
- break
- }
- }
- </script>
- <style scoped>
- /* ==================== 布局容器 ==================== */
- .layout-container {
- height: 100vh;
- overflow: hidden;
- }
- /* ==================== 侧边栏样式 ==================== */
- .layout-aside {
- background: linear-gradient(180deg, #1e293b 0%, #0f172a 100%);
- box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
- transition: width var(--transition-slow);
- position: relative;
- z-index: 100;
- }
- /* Logo 容器 */
- .logo-container {
- height: var(--header-height);
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0 var(--spacing-lg);
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
- background: rgba(255, 255, 255, 0.02);
- gap: var(--spacing-base);
- }
- .logo {
- width: 36px;
- height: 36px;
- flex-shrink: 0;
- filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
- transition: transform var(--transition-base);
- }
- .logo:hover {
- transform: scale(1.05);
- }
- .logo-text {
- font-size: var(--font-lg);
- font-weight: var(--font-bold);
- color: #ffffff;
- white-space: nowrap;
- letter-spacing: 0.5px;
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
- }
- /* 侧边栏菜单 */
- .aside-menu {
- border: none;
- height: calc(100vh - var(--header-height));
- overflow-y: auto;
- overflow-x: hidden;
- background-color: transparent;
- }
- /* 菜单项样式覆盖 */
- .aside-menu :deep(.el-menu-item) {
- margin: 4px 12px;
- border-radius: var(--radius-base);
- transition: all var(--transition-base);
- height: 48px;
- line-height: 48px;
- position: relative;
- overflow: hidden;
- }
- .aside-menu :deep(.el-menu-item:hover) {
- background-color: var(--sidebar-hover-bg) !important;
- color: #ffffff !important;
- }
- .aside-menu :deep(.el-menu-item.is-active) {
- background: linear-gradient(90deg, var(--primary-color) 0%, var(--primary-light) 100%) !important;
- color: #ffffff !important;
- font-weight: var(--font-semibold);
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
- }
- .aside-menu :deep(.el-menu-item.is-active::before) {
- content: '';
- position: absolute;
- left: 0;
- top: 50%;
- transform: translateY(-50%);
- width: 4px;
- height: 24px;
- background-color: #ffffff;
- border-radius: 0 4px 4px 0;
- opacity: 0.8;
- }
- /* 子菜单样式 */
- .aside-menu :deep(.el-sub-menu__title) {
- margin: 4px 12px;
- border-radius: var(--radius-base);
- transition: all var(--transition-base);
- height: 48px;
- line-height: 48px;
- }
- .aside-menu :deep(.el-sub-menu__title:hover) {
- background-color: var(--sidebar-hover-bg) !important;
- color: #ffffff !important;
- }
- .aside-menu :deep(.el-sub-menu.is-active > .el-sub-menu__title) {
- color: var(--primary-light) !important;
- font-weight: var(--font-medium);
- }
- .aside-menu :deep(.el-menu--inline) {
- background-color: rgba(0, 0, 0, 0.15);
- }
- .aside-menu :deep(.el-menu--inline .el-menu-item) {
- padding-left: 52px !important;
- margin: 2px 8px;
- height: 42px;
- line-height: 42px;
- }
- /* 菜单图标 */
- .aside-menu :deep(.el-icon) {
- font-size: 18px;
- margin-right: var(--spacing-base);
- transition: transform var(--transition-base);
- }
- .aside-menu :deep(.el-menu-item:hover .el-icon),
- .aside-menu :deep(.el-sub-menu__title:hover .el-icon) {
- transform: scale(1.1);
- }
- /* 折叠状态 */
- .aside-menu.el-menu--collapse :deep(.el-menu-item),
- .aside-menu.el-menu--collapse :deep(.el-sub-menu__title) {
- justify-content: center;
- }
- /* 滚动条美化 */
- .aside-menu::-webkit-scrollbar {
- width: 6px;
- }
- .aside-menu::-webkit-scrollbar-thumb {
- background-color: rgba(255, 255, 255, 0.2);
- border-radius: 3px;
- transition: background-color var(--transition-base);
- }
- .aside-menu::-webkit-scrollbar-thumb:hover {
- background-color: rgba(255, 255, 255, 0.3);
- }
- .aside-menu::-webkit-scrollbar-track {
- background-color: transparent;
- }
- /* ==================== 顶部导航样式 ==================== */
- .layout-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- background-color: var(--bg-primary);
- border-bottom: 1px solid var(--border-color);
- padding: 0 var(--spacing-xl);
- height: var(--header-height);
- box-shadow: var(--shadow-sm);
- position: relative;
- z-index: 90;
- }
- .header-left {
- display: flex;
- align-items: center;
- gap: var(--spacing-xl);
- }
- .collapse-icon {
- font-size: 20px;
- cursor: pointer;
- color: var(--text-secondary);
- transition: all var(--transition-base);
- padding: var(--spacing-sm);
- border-radius: var(--radius-base);
- }
- .collapse-icon:hover {
- color: var(--primary-color);
- background-color: var(--bg-hover);
- transform: scale(1.05);
- }
- .header-right {
- display: flex;
- align-items: center;
- gap: var(--spacing-lg);
- }
- .username {
- font-size: var(--font-sm);
- color: var(--text-secondary);
- font-weight: var(--font-medium);
- margin-right: var(--spacing-sm);
- }
- /* 头像样式 */
- .header-right :deep(.el-avatar) {
- cursor: pointer;
- border: 2px solid var(--border-color);
- transition: all var(--transition-base);
- }
- .header-right :deep(.el-avatar:hover) {
- border-color: var(--primary-color);
- transform: scale(1.05);
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
- }
- /* 面包屑增强 */
- .header-left :deep(.el-breadcrumb) {
- font-size: var(--font-sm);
- }
- .header-left :deep(.el-breadcrumb__inner) {
- color: var(--text-secondary);
- transition: color var(--transition-base);
- }
- .header-left :deep(.el-breadcrumb__inner:hover) {
- color: var(--primary-color);
- }
- /* ==================== 主内容区样式 ==================== */
- .layout-main {
- background-color: var(--bg-secondary);
- padding: var(--spacing-xl);
- overflow-y: auto;
- height: calc(100vh - var(--header-height));
- position: relative;
- }
- /* ==================== 动画效果 ==================== */
- /* 路由过渡动画 */
- .fade-enter-active {
- animation: fadeInUp var(--transition-slow);
- }
- .fade-leave-active {
- animation: fadeOut var(--transition-fast);
- }
- @keyframes fadeOut {
- from {
- opacity: 1;
- }
- to {
- opacity: 0;
- }
- }
- /* Logo 文字淡入淡出 */
- .fade-enter-active,
- .fade-leave-active {
- transition: opacity var(--transition-base);
- }
- .fade-enter-from,
- .fade-leave-to {
- opacity: 0;
- }
- /* ==================== 响应式设计 ==================== */
- /* 平板设备 */
- @media (max-width: 1024px) {
- .layout-header {
- padding: 0 var(--spacing-lg);
- }
- .layout-main {
- padding: var(--spacing-lg);
- }
- .username {
- display: none;
- }
- }
- /* 移动设备 */
- @media (max-width: 768px) {
- .layout-aside {
- position: fixed;
- left: 0;
- top: 0;
- height: 100vh;
- z-index: 1000;
- }
- .layout-header {
- padding: 0 var(--spacing-md);
- }
- .header-left {
- gap: var(--spacing-md);
- }
- .layout-main {
- padding: var(--spacing-md);
- width: 100%;
- }
- .collapse-icon {
- font-size: 18px;
- }
- .header-right :deep(.el-avatar) {
- width: 32px;
- height: 32px;
- }
- }
- /* 超大屏幕优化 */
- @media (min-width: 1920px) {
- .layout-main {
- padding: var(--spacing-2xl) var(--spacing-3xl);
- }
- }
- </style>
|