122 lines
3.6 KiB
Vue
122 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import AppIcon from '@/components/ui/AppIcon.vue'
|
|
|
|
const auth = useAuthStore()
|
|
const route = useRoute()
|
|
|
|
interface NavItem { label: string; icon: string; to: string; roles: string[] }
|
|
|
|
const navItems: NavItem[] = [
|
|
{ label: 'Главная', icon: 'home', to: '/', roles: ['student'] },
|
|
{ label: 'Каталог', icon: 'books', to: '/catalog', roles: ['student'] },
|
|
{ label: 'Мои записи', icon: 'clipboard-list', to: '/my-lectures', roles: ['student'] },
|
|
{ label: 'Достижения', icon: 'trophy', to: '/achievements', roles: ['student'] },
|
|
{ label: 'Уведомления', icon: 'bell', to: '/notifications', roles: ['student'] },
|
|
{ label: 'Профиль', icon: 'user', to: '/profile', roles: ['student'] },
|
|
// Teacher
|
|
{ label: 'Дашборд', icon: 'chart-bar', to: '/teacher', roles: ['teacher'] },
|
|
{ label: 'Лекции', icon: 'book-2', to: '/teacher/lectures', roles: ['teacher'] },
|
|
{ label: 'Аналитика', icon: 'chart-line', to: '/teacher/analytics', roles: ['teacher'] },
|
|
{ label: 'Профиль', icon: 'user', to: '/profile', roles: ['teacher'] },
|
|
// Admin
|
|
{ label: 'Дашборд', icon: 'shield', to: '/admin', roles: ['admin'] },
|
|
{ label: 'Пользователи', icon: 'users', to: '/admin/users', roles: ['admin'] },
|
|
{ label: 'Лекции', icon: 'books', to: '/admin/lectures', roles: ['admin'] },
|
|
{ label: 'ИИ очередь', icon: 'robot', to: '/admin/llm-queue', roles: ['admin'] },
|
|
]
|
|
|
|
const visible = computed(() =>
|
|
navItems.filter(n => auth.user && n.roles.includes(auth.user.activeRole))
|
|
)
|
|
|
|
function isActive(to: string) {
|
|
if (to === '/') return route.path === '/'
|
|
return route.path.startsWith(to) && to !== '/'
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<aside class="sidebar">
|
|
<nav class="sidebar-nav">
|
|
<RouterLink
|
|
v-for="item in visible"
|
|
:key="item.to + item.label"
|
|
:to="item.to"
|
|
class="nav-item"
|
|
:class="{ active: isActive(item.to) }"
|
|
>
|
|
<AppIcon class="nav-icon" :icon="item.icon" :size="17" />
|
|
<span class="nav-label">{{ item.label }}</span>
|
|
</RouterLink>
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<span class="copyright">© 2026 UniVerse</span>
|
|
</div>
|
|
</aside>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.sidebar {
|
|
position: fixed;
|
|
top: var(--topbar-height);
|
|
left: 0;
|
|
bottom: 0;
|
|
width: var(--sidebar-width);
|
|
background: var(--color-white-glass);
|
|
backdrop-filter: blur(16px);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
border-right: 1px solid var(--color-border-glass);
|
|
display: flex;
|
|
flex-direction: column;
|
|
z-index: 90;
|
|
padding: 16px 0;
|
|
}
|
|
.sidebar-nav {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
padding: 0 10px;
|
|
overflow-y: auto;
|
|
}
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--color-text-secondary);
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
.nav-item:hover {
|
|
background: var(--color-primary-a10);
|
|
color: var(--color-primary-dark);
|
|
}
|
|
.nav-item.active {
|
|
background: var(--gradient-nav-active);
|
|
color: var(--color-primary-dark);
|
|
font-weight: 700;
|
|
box-shadow: 0 2px 8px var(--color-primary-a12);
|
|
}
|
|
.nav-icon { flex-shrink: 0; color: currentColor; }
|
|
.sidebar-footer {
|
|
padding: 10px 18px 8px;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
.copyright {
|
|
color: var(--color-text-secondary);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
@media (max-width: 768px) { .sidebar { display: none; } }
|
|
</style>
|