Spaces:
Sleeping
Sleeping
<template> | |
<div class="pc-aside"> | |
<!-- Logo 区域 --> | |
<div class="pc-aside__logo"> | |
<img :src="logo" alt="Cloud Saver Logo" class="logo__image" /> | |
<h1 class="logo__title">Cloud Saver</h1> | |
</div> | |
<!-- 菜单区域 --> | |
<el-menu | |
:default-active="currentMenu?.index || '1'" | |
:default-openeds="currentMenuOpen" | |
class="pc-aside__menu" | |
> | |
<template v-for="menu in menuList" :key="menu.index"> | |
<!-- 子菜单 --> | |
<el-sub-menu v-if="menu.children" :index="menu.index"> | |
<template #title> | |
<el-icon><component :is="menu.icon" /></el-icon> | |
<span>{{ menu.title }}</span> | |
</template> | |
<el-menu-item | |
v-for="child in menu.children" | |
:key="child.index" | |
:index="child.index" | |
@click="handleMenuClick(child)" | |
> | |
<span>{{ child.title }}</span> | |
</el-menu-item> | |
</el-sub-menu> | |
<!-- 普通菜单项 --> | |
<el-menu-item | |
v-else | |
:index="menu.index" | |
:disabled="menu.disabled" | |
@click="handleMenuClick(menu)" | |
> | |
<el-icon><component :is="menu.icon" /></el-icon> | |
<span>{{ menu.title }}</span> | |
</el-menu-item> | |
</template> | |
</el-menu> | |
<!-- GitHub 链接 --> | |
<div class="pc-aside__footer"> | |
<a :href="PROJECT_GITHUB" target="_blank" rel="noopener noreferrer" class="github-link"> | |
<svg | |
height="20" | |
aria-hidden="true" | |
viewBox="0 0 24 24" | |
version="1.1" | |
width="20" | |
class="github-icon" | |
> | |
<path | |
fill="currentColor" | |
d="M12.5.75C6.146.75 1 5.896 1 12.25c0 5.089 3.292 9.387 7.863 10.91.575.101.79-.244.79-.546 0-.273-.014-1.178-.014-2.142-2.889.532-3.636-.704-3.866-1.35-.13-.331-.69-1.352-1.18-1.625-.402-.216-.977-.748-.014-.762.906-.014 1.553.834 1.769 1.179 1.035 1.74 2.688 1.25 3.349.948.1-.747.402-1.25.733-1.538-2.559-.287-5.232-1.279-5.232-5.678 0-1.25.445-2.285 1.178-3.09-.115-.288-.517-1.467.115-3.048 0 0 .963-.302 3.163 1.179.92-.259 1.897-.388 2.875-.388.977 0 1.955.13 2.875.388 2.2-1.495 3.162-1.179 3.162-1.179.633 1.581.23 2.76.115 3.048.733.805 1.179 1.825 1.179 3.09 0 4.413-2.688 5.39-5.247 5.678.417.36.776 1.05.776 2.128 0 1.538-.014 2.774-.014 3.162 0 .302.216.662.79.547C20.709 21.637 24 17.324 24 12.25 24 5.896 18.854.75 12.5.75Z" | |
/> | |
</svg> | |
<span>GitHub</span> | |
<span class="version">v{{ pkg.version }}</span> | |
</a> | |
</div> | |
</div> | |
</template> | |
<script setup lang="ts"> | |
import { computed } from "vue"; | |
import { useRouter, useRoute } from "vue-router"; | |
import { Search, Film, Setting, Link } from "@element-plus/icons-vue"; | |
import logo from "@/assets/images/logo.png"; | |
import { PROJECT_GITHUB } from "@/constants/project"; | |
import pkg from "../../package.json"; | |
// 类型定义 | |
interface MenuItem { | |
index: string; | |
title: string; | |
icon?: typeof Search | typeof Film | typeof Setting | typeof Link; | |
router?: string; | |
children?: MenuItem[]; | |
disabled?: boolean; | |
} | |
// 路由相关 | |
const router = useRouter(); | |
const route = useRoute(); | |
// 菜单配置 | |
const menuList: MenuItem[] = [ | |
{ | |
index: "1", | |
title: "资源搜索", | |
icon: Search, | |
router: "/resource", | |
}, | |
{ | |
index: "2", | |
title: "豆瓣榜单", | |
icon: Film, | |
children: [ | |
{ | |
index: "2-1", | |
title: "热门电影", | |
router: "/douban?type=movie", | |
}, | |
{ | |
index: "2-2", | |
title: "热门电视剧", | |
router: "/douban?type=tv", | |
}, | |
{ | |
index: "2-3", | |
title: "最新电影", | |
router: "/douban?type=movie&tag=最新", | |
}, | |
{ | |
index: "2-4", | |
title: "热门综艺", | |
router: "/douban?type=tv&tag=综艺", | |
}, | |
], | |
}, | |
{ | |
index: "3", | |
title: "设置", | |
icon: Setting, | |
router: "/setting", | |
disabled: false, | |
}, | |
{ | |
index: "4", | |
title: "鸣谢", | |
icon: Link, | |
router: "/thanks", | |
}, | |
]; | |
// 计算当前激活的菜单 | |
const currentMenu = computed(() => { | |
const flatMenus = menuList.reduce<MenuItem[]>((acc, menu) => { | |
if (!menu.children) { | |
acc.push(menu); | |
} else { | |
acc.push(...menu.children); | |
} | |
return acc; | |
}, []); | |
return flatMenus.find((menu) => menu.router === decodeURIComponent(route.fullPath)); | |
}); | |
// 计算当前展开的子菜单 | |
const currentMenuOpen = computed(() => { | |
if (currentMenu.value?.index.includes("-")) { | |
return [currentMenu.value.index.split("-")[0]]; | |
} | |
return []; | |
}); | |
// 菜单点击处理 | |
const handleMenuClick = (menu: MenuItem) => { | |
if (menu.router) { | |
router.push(menu.router); | |
} | |
}; | |
</script> | |
<style lang="scss" scoped> | |
@import "@/styles/common.scss"; | |
.pc-aside { | |
height: 100%; | |
background: var(--theme-card-bg); | |
border-right: 1px solid rgba(0, 0, 0, 0.1); | |
// Logo 区域 | |
&__logo { | |
@include flex-center; | |
padding: 24px 16px; | |
gap: 12px; | |
.logo__image { | |
width: 32px; | |
height: 32px; | |
object-fit: contain; | |
} | |
.logo__title { | |
margin: 0; | |
font-size: 18px; | |
font-weight: 600; | |
color: var(--theme-text-primary); | |
@include text-overflow; | |
} | |
} | |
// 菜单区域 | |
&__menu { | |
border-right: none; | |
background: transparent; | |
:deep(.el-menu-item) { | |
height: 48px; | |
line-height: 48px; | |
color: var(--theme-text-regular); | |
&.is-active { | |
color: var(--theme-primary); | |
background: rgba(0, 102, 204, 0.1); | |
} | |
&:hover { | |
color: var(--theme-primary); | |
background: rgba(0, 102, 204, 0.05); | |
} | |
} | |
:deep(.el-sub-menu) { | |
.el-sub-menu__title { | |
color: var(--theme-text-regular); | |
&:hover { | |
color: var(--theme-primary); | |
background: rgba(0, 102, 204, 0.05); | |
} | |
} | |
} | |
:deep(.el-icon) { | |
font-size: 18px; | |
margin-right: 12px; | |
color: inherit; | |
} | |
} | |
// GitHub 链接区域 | |
&__footer { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
padding: 16px; | |
border-top: 1px solid rgba(0, 0, 0, 0.1); | |
background: var(--theme-card-bg); | |
.github-link { | |
@include flex-center; | |
gap: 8px; | |
padding: 12px; | |
color: var(--theme-text-regular); | |
text-decoration: none; | |
border-radius: var(--theme-radius); | |
transition: var(--theme-transition); | |
.github-icon { | |
font-size: 20px; | |
transition: var(--theme-transition); | |
} | |
.version { | |
font-size: 12px; | |
opacity: 0.7; | |
margin-left: 4px; | |
} | |
&:hover { | |
color: var(--theme-primary); | |
background: rgba(0, 102, 204, 0.05); | |
transform: translateY(-1px); | |
.github-icon { | |
transform: scale(1.1); | |
} | |
.version { | |
opacity: 1; | |
} | |
} | |
} | |
} | |
} | |
// 自定义滚动条 | |
.pc-aside__menu { | |
height: calc(100vh - 80px - 69px); // 减去 logo 高度和 footer 高度 | |
overflow-y: auto; | |
&::-webkit-scrollbar { | |
width: 6px; | |
} | |
&::-webkit-scrollbar-thumb { | |
background: rgba(0, 0, 0, 0.2); | |
border-radius: 3px; | |
&:hover { | |
background: rgba(0, 0, 0, 0.3); | |
} | |
} | |
&::-webkit-scrollbar-track { | |
background: transparent; | |
} | |
} | |
</style> | |