Files
sionrui/frontend/app/web-gold/src/components/SidebarNav.vue

129 lines
2.9 KiB
Vue
Raw Normal View History

2025-11-16 23:19:44 +08:00
<script setup>
import { computed } from 'vue'
2026-02-12 00:51:09 +08:00
import { useRoute } from 'vue-router'
2025-11-16 23:19:44 +08:00
import { useUserStore } from '@/stores/user'
2026-02-12 00:51:09 +08:00
import { navConfig, navIcons } from '@/router'
2025-11-16 23:19:44 +08:00
const route = useRoute()
const userStore = useUserStore()
2026-02-12 00:51:09 +08:00
function filterVisibleGroups(config, isLoggedIn) {
return config
.filter(group => !group.requiresAuth || isLoggedIn)
.map(group => ({
...group,
items: group.items.filter(item => !item.requiresAuth || isLoggedIn)
}))
.filter(group => group.items.length > 0)
2025-11-16 23:19:44 +08:00
}
2026-02-12 00:51:09 +08:00
const visibleNavConfig = computed(() => {
return filterVisibleGroups(navConfig, userStore.isLoggedIn)
2025-11-16 23:19:44 +08:00
})
</script>
<template>
<aside class="sidebar">
<nav class="sidebar__nav">
2026-02-12 00:51:09 +08:00
<div v-for="group in visibleNavConfig" :key="group.group" class="nav-group">
<div class="nav-group__title">{{ group.group }}</div>
<router-link
v-for="item in group.items"
:key="item.name"
:to="{ name: item.name, ...(item.params && { params: item.params }) }"
class="nav-item"
:class="{ 'is-active': route.name === item.name }"
custom
v-slot="{ navigate }"
>
<button class="nav-item" @click="navigate">
<span class="nav-item__icon" v-html="navIcons[item.icon]"></span>
<span class="nav-item__label">{{ item.name }}</span>
</button>
</router-link>
2025-11-16 23:19:44 +08:00
</div>
</nav>
</aside>
</template>
<style scoped>
.sidebar {
position: sticky;
2026-02-12 00:51:09 +08:00
top: 70px;
2025-11-16 23:19:44 +08:00
height: calc(100vh - 70px);
width: 220px;
border-right: 1px solid var(--color-border);
background: var(--color-surface);
2026-02-12 00:51:09 +08:00
overflow-y: auto;
2025-11-16 23:19:44 +08:00
}
.sidebar__nav {
display: flex;
flex-direction: column;
padding: 12px;
gap: 6px;
}
2026-02-12 00:51:09 +08:00
.nav-group {
display: flex;
flex-direction: column;
gap: 6px;
}
2025-11-16 23:19:44 +08:00
.nav-group__title {
height: 30px;
display: flex;
align-items: center;
padding: 0 8px;
font-size: var(--font-small-size);
color: var(--color-text-secondary);
letter-spacing: .06em;
}
.nav-item {
height: 40px;
2025-12-28 13:49:45 +08:00
border-radius: 12px;
2025-11-16 23:19:44 +08:00
display: flex;
align-items: center;
gap: 10px;
2025-12-28 13:49:45 +08:00
padding: 8px 12px;
color: var(--color-slate-600);
background: transparent;
border: 1px solid transparent;
2025-11-16 23:19:44 +08:00
cursor: pointer;
transition: background .2s ease, color .2s ease, box-shadow .2s ease, transform .12s ease, border-color .2s ease;
2025-12-28 13:49:45 +08:00
width: 100%;
text-align: left;
font-size: 14px;
font-weight: 400;
2025-11-16 23:19:44 +08:00
}
.nav-item:hover {
2025-12-28 13:49:45 +08:00
background: var(--color-slate-50);
color: var(--color-slate-700);
2025-11-16 23:19:44 +08:00
}
.nav-item.is-active {
2025-12-28 13:49:45 +08:00
background: var(--color-blue-50);
color: var(--color-blue-700);
border-color: transparent;
}
.nav-item.is-active:hover {
background: var(--color-blue-100);
color: var(--color-blue-800);
2025-11-16 23:19:44 +08:00
}
2026-02-12 00:51:09 +08:00
.nav-item__icon {
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
}
2025-11-16 23:19:44 +08:00
2026-02-12 00:51:09 +08:00
.nav-item__label {
font-size: 14px;
}
</style>