119 lines
4.3 KiB
Vue
119 lines
4.3 KiB
Vue
<template>
|
||
<aside class="sidebar" :class="{ collapsed }">
|
||
<div class="sidebar-header">
|
||
<div class="brand">
|
||
<span class="brand-icon">✨</span>
|
||
<span class="brand-text">Luxx</span>
|
||
</div>
|
||
<button @click="$emit('toggle')" class="toggle-btn">{{ collapsed ? '→' : '←' }}</button>
|
||
</div>
|
||
|
||
<nav class="sidebar-nav">
|
||
<router-link v-for="item in navItems" :key="item.path" :to="item.path" class="nav-item" active-class="active">
|
||
<span class="nav-icon">{{ item.icon }}</span>
|
||
<span class="nav-text">{{ item.label }}</span>
|
||
</router-link>
|
||
</nav>
|
||
|
||
<div class="sidebar-footer">
|
||
<button @click="handleLogout" class="user-button">
|
||
<div class="user-avatar">{{ user?.username?.charAt(0)?.toUpperCase() || 'U' }}</div>
|
||
<div class="user-info">
|
||
<span class="user-name">{{ user?.username }}</span>
|
||
<span class="user-role">{{ user?.role || '用户' }}</span>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { useAuth } from '../composables/useAuth.js'
|
||
import { authAPI } from '../services/api.js'
|
||
import { useRouter } from 'vue-router'
|
||
|
||
defineProps({ collapsed: Boolean })
|
||
defineEmits(['toggle'])
|
||
|
||
const router = useRouter()
|
||
const { user, logout } = useAuth()
|
||
|
||
const navItems = [
|
||
{ path: '/', icon: '🏠', label: '首页' },
|
||
{ path: '/conversations', icon: '💬', label: '会话' },
|
||
{ path: '/tools', icon: '🛠️', label: '工具' },
|
||
{ path: '/about', icon: 'ℹ️', label: '关于' }
|
||
]
|
||
|
||
const handleLogout = async () => {
|
||
await logout(authAPI)
|
||
router.push('/auth')
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.sidebar {
|
||
width: 260px; height: 100vh;
|
||
background: var(--bg); border-right: 1px solid var(--border);
|
||
display: flex; flex-direction: column;
|
||
position: fixed; left: 0; top: 0; z-index: 100;
|
||
transition: width 0.3s;
|
||
}
|
||
.sidebar.collapsed { width: 70px; }
|
||
.sidebar-header {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 1.25rem; border-bottom: 1px solid var(--border);
|
||
}
|
||
.brand { display: flex; align-items: center; gap: 0.75rem; }
|
||
.brand-icon { font-size: 1.75rem; }
|
||
.brand-text {
|
||
font-size: 1.5rem; font-weight: 700;
|
||
background: linear-gradient(135deg, var(--accent) 0%, #8b5cf6 100%);
|
||
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
||
}
|
||
.collapsed .brand-text, .collapsed .toggle-btn { display: none; }
|
||
.toggle-btn {
|
||
background: var(--code-bg); border: none; border-radius: 6px;
|
||
padding: 0.5rem 0.75rem; cursor: pointer;
|
||
}
|
||
.sidebar-nav { flex: 1; padding: 1rem 0.75rem; overflow-y: auto; }
|
||
.nav-item {
|
||
display: flex; align-items: center; gap: 0.875rem;
|
||
padding: 0.875rem 1rem; border-radius: 10px;
|
||
text-decoration: none; color: var(--text); margin-bottom: 0.25rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.nav-item:hover { background: var(--code-bg); color: var(--text-h); }
|
||
.nav-item.active { background: var(--accent-bg); color: var(--accent); }
|
||
.nav-icon { font-size: 1.25rem; width: 24px; text-align: center; }
|
||
.nav-text { font-size: 0.95rem; font-weight: 500; white-space: nowrap; }
|
||
.collapsed .nav-text { display: none; }
|
||
.collapsed .nav-item { justify-content: center; padding: 0.875rem; }
|
||
.sidebar-footer { padding: 1rem; border-top: 1px solid var(--border); }
|
||
.user-button {
|
||
display: flex; align-items: center; gap: 0.75rem;
|
||
width: 100%; padding: 0.75rem; background: var(--code-bg);
|
||
border: 1px solid var(--border); border-radius: 10px;
|
||
cursor: pointer; transition: all 0.2s;
|
||
}
|
||
.user-button:hover { border-color: var(--accent); }
|
||
.user-avatar {
|
||
width: 40px; height: 40px; border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--accent) 0%, #8b5cf6 100%);
|
||
color: white; display: flex; align-items: center; justify-content: center;
|
||
font-weight: 600; flex-shrink: 0;
|
||
}
|
||
.user-info { display: flex; flex-direction: column; overflow: hidden; }
|
||
.user-name { font-weight: 600; color: var(--text-h); font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.user-role { font-size: 0.8rem; color: var(--text); }
|
||
.collapsed .user-info { display: none; }
|
||
.collapsed .user-button { justify-content: center; }
|
||
|
||
@media (max-width: 1024px) {
|
||
.sidebar { width: 70px; }
|
||
.brand-text, .toggle-btn, .nav-text, .user-info { display: none !important; }
|
||
.nav-item { justify-content: center; padding: 0.875rem; }
|
||
.user-button { justify-content: center; }
|
||
}
|
||
</style>
|