Files
MrleeAdmin/frontend/web/app/layouts/components/aside.vue
T

248 lines
6.0 KiB
Vue

<template>
<el-aside class="member-sidebar">
<!-- 用户信息区域 -->
<div class="member-sidebar__user-info">
<div @click="routerPush('/member/profile')" class="member-sidebar__avatar-container">
<img class="member-sidebar__avatar" :src="getAvatarUrl(memberStore?.info?.avatar)" alt="" />
</div>
<p class="member-sidebar__username">{{ memberStore?.info?.nickname }}</p>
</div>
<el-menu
class="member-sidebar__menu"
:default-active="activeMenu"
unique-opened
>
<div v-for="(item, idx) in systemStore.site.member_menu" :key="idx">
<el-sub-menu
v-if="item.children && item.children.length && checkMenuShow(item)"
:index="item.id.toString()"
>
<template #title>
<Icon v-if="item.icon" :icon="item.icon" size="16" />
<span>{{ item.title }}</span>
</template>
<div v-for="(menu, index) in item.children.filter(m => m)" :key="index">
<el-menu-item
v-if="checkMenuShow(menu)"
:index="menu.path"
@click="routerPush(menu)"
>
<Icon v-if="menu.icon" :icon="menu.icon" size="16" />
<span>{{ menu.title }}</span>
</el-menu-item>
</div>
</el-sub-menu>
<el-menu-item
v-else-if="checkMenuShow(item)"
:index="item.path"
@click="routerPush(item)"
>
<Icon v-if="item.icon" :icon="item.icon" size="16" />
<span>{{ item.title }}</span>
</el-menu-item>
</div>
</el-menu>
</el-aside>
</template>
<script setup lang="ts">
import type { Menus } from '~/stores/interface'
// 在组件顶部导入图片
import defaultAvatar from '~/assets/images/default_avatar.png'
import { useSystemStore } from '~/stores/system'
import { useMemberStore } from '~/stores/member'
import { useRoute, navigateTo } from 'nuxt/app'
import { computed } from 'vue'
import { fullUrl } from '~/utils/common'
import { Icon } from '~/components/icon'
const route = useRoute()
const memberStore = useMemberStore()
const systemStore = useSystemStore()
const checkMenuShow = (menu: Menus): boolean => {
if (!menu) {
return false
}
const permissions = menu.meta?.permissions
if (!permissions || permissions.length === 0) {
return true
}
const result = systemStore.checkMenuPermission(menu)
return result
}
// 当前激活的菜单
const activeMenu = computed(() => {
return route.path
})
/**
* 获取用户头像URL
*/
const getAvatarUrl = (avatarUrl: string | null | undefined): string => {
if (!avatarUrl || avatarUrl.trim() === "") {
return defaultAvatar
}
return fullUrl(avatarUrl)
}
/**
* 处理菜单点击
*/
const onClickMenu = (menu: Menus) => {
if (systemStore.isDirectory(menu)) {
return
}
if (systemStore.isExternalLink(menu)) {
const target = systemStore.getTarget(menu)
if (menu.url) {
window.open(menu.url, target)
}
return
}
if (menu.path) {
navigateTo(menu.path)
}
}
/**
* 菜单跳转
*/
const routerPush = (route: string | Menus) => {
if (typeof route === 'string') {
navigateTo(route)
} else {
onClickMenu(route)
}
}
</script>
<style scoped lang="scss">
.member-sidebar {
width: 240px;
background-color: var(--ma-bg-color-overlay);
box-shadow: var(--el-box-shadow-light);
border-radius: 8px;
overflow: hidden;
}
.member-sidebar__user-info {
display: flex;
flex-direction: column;
align-items: center;
padding: 24px 20px;
border-bottom: 1px solid var(--el-border-color-light);
}
.member-sidebar__avatar-container {
position: relative;
cursor: pointer;
margin-bottom: 12px;
}
.member-sidebar__avatar {
display: block;
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
background-color: black;
}
.member-sidebar__avatar-gender {
position: absolute;
bottom: 0;
right: 0;
height: 20px;
width: 20px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--ma-bg-color-overlay);
border-radius: 50%;
box-shadow: var(--el-box-shadow);
}
.member-sidebar__username {
text-align: center;
width: 100%;
margin: 8px 0 16px;
font-size: var(--el-font-size-large);
font-weight: 600;
color: var(--el-text-color-primary);
}
.member-sidebar__button-group {
display: flex;
gap: 8px;
}
.member-sidebar__button {
font-size: var(--el-font-size-small);
height: 32px;
min-width: 80px;
}
.member-sidebar__menu {
border: none;
background: transparent;
:deep(.el-menu-item),
:deep(.el-sub-menu__title) {
height: 44px;
line-height: 44px;
.icon {
margin-right: 8px;
width: 16px;
height: 16px;
}
}
:deep(.el-menu-item.is-active) {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
}
/* 响应式设计 */
@media screen and (max-width: 991px) {
.member-sidebar {
width: 100%;
border-radius: 0;
box-shadow: none;
}
.member-sidebar__user-info {
padding: 20px 16px;
}
.member-sidebar__avatar {
width: 60px;
height: 60px;
}
.member-sidebar__button-group {
flex-direction: column;
width: 100%;
}
.member-sidebar__button {
width: 100%;
}
}
@media screen and (max-width: 768px) {
.member-sidebar {
display: none;
}
}
</style>