701 lines
15 KiB
Vue
701 lines
15 KiB
Vue
<template>
|
|
<aside class="app-sidebar" :class="sidebarClasses">
|
|
|
|
<!-- 导航菜单 -->
|
|
<nav class="sidebar-nav">
|
|
<ul class="nav-list">
|
|
<li v-for="item in coreMenuItems" :key="item.id">
|
|
<router-link
|
|
:to="item.path"
|
|
class="nav-item"
|
|
:class="{ 'active': isActiveRoute(item.path) }"
|
|
@click="handleNavClick(item)"
|
|
>
|
|
<div class="nav-icon">
|
|
<BrainIcon v-if="item.icon === 'BrainIcon'" />
|
|
<DashboardIcon v-else-if="item.icon === 'DashboardIcon'" />
|
|
<CreationIcon v-else-if="item.icon === 'CreationIcon'" />
|
|
<GalleryIcon v-else-if="item.icon === 'GalleryIcon'" />
|
|
<OrdersIcon v-else-if="item.icon === 'OrdersIcon'" />
|
|
</div>
|
|
<transition name="fade">
|
|
<span v-if="!collapsed" class="nav-text">{{ item.label }}</span>
|
|
</transition>
|
|
<transition name="fade">
|
|
<span v-if="!collapsed && item.badge" class="nav-badge">{{ item.badge }}</span>
|
|
</transition>
|
|
</router-link>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<!-- 侧边栏底部 -->
|
|
<div class="sidebar-footer">
|
|
<div class="user-profile" v-if="currentUser && !collapsed">
|
|
<div class="user-avatar-container">
|
|
<el-avatar :size="32" :src="currentUser.avatar">
|
|
<UserIcon />
|
|
</el-avatar>
|
|
<div class="online-status"></div>
|
|
</div>
|
|
<div class="user-info">
|
|
<p class="user-role">{{ getRoleDisplayName(currentUser.role) }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 折叠状态下的用户头像 -->
|
|
<div v-if="currentUser && collapsed" class="user-profile-collapsed">
|
|
<div class="user-avatar-container">
|
|
<el-avatar :size="32" :src="currentUser.avatar">
|
|
<UserIcon />
|
|
</el-avatar>
|
|
<div class="online-status"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, computed, watch } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
// 图标组件
|
|
import {
|
|
Cpu as BrainIcon,
|
|
House as DashboardIcon,
|
|
Folder as CreationIcon,
|
|
Picture as GalleryIcon,
|
|
ShoppingCart as OrdersIcon,
|
|
User as UserIcon,
|
|
Document as DocumentIcon,
|
|
VideoPlay as VideoIcon,
|
|
ChatDotRound as ChatIcon,
|
|
DataAnalysis as AnalyticsIcon,
|
|
Folder as ProjectIcon,
|
|
Bell as NotificationIcon,
|
|
Key as ApiIcon
|
|
} from '@element-plus/icons-vue'
|
|
|
|
export default {
|
|
name: 'AppSidebar',
|
|
components: {
|
|
BrainIcon,
|
|
DashboardIcon,
|
|
CreationIcon,
|
|
GalleryIcon,
|
|
OrdersIcon,
|
|
UserIcon,
|
|
DocumentIcon,
|
|
VideoIcon,
|
|
ChatIcon,
|
|
AnalyticsIcon,
|
|
ProjectIcon,
|
|
NotificationIcon,
|
|
ApiIcon
|
|
},
|
|
props: {
|
|
collapsed: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
emits: ['navigate'],
|
|
setup(props, { emit }) {
|
|
const { t } = useI18n()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const authStore = useAuthStore()
|
|
|
|
// 响应式状态
|
|
const isMobile = ref(window.innerWidth < 768)
|
|
|
|
// 计算属性
|
|
const currentUser = computed(() => authStore.user)
|
|
|
|
const sidebarClasses = computed(() => ({
|
|
'sidebar-mobile': isMobile.value
|
|
}))
|
|
|
|
// 核心菜单项 (6个主要功能)
|
|
const coreMenuItems = computed(() => [
|
|
{
|
|
id: 'dashboard',
|
|
path: '/',
|
|
label: t('sidebar.dashboard'),
|
|
icon: 'DashboardIcon',
|
|
badge: null
|
|
},
|
|
{
|
|
id: 'creation-workspace',
|
|
path: '/creation-workspace',
|
|
label: t('sidebar.creationWorkspace'),
|
|
icon: 'CreationIcon',
|
|
badge: null
|
|
},
|
|
{
|
|
id: 'agent-management',
|
|
path: '/agent-management',
|
|
label: t('sidebar.agentManagement.title'),
|
|
icon: 'BrainIcon',
|
|
badge: null
|
|
},
|
|
{
|
|
id: 'order-management',
|
|
path: '/order-management',
|
|
label: t('sidebar.orderManagement'),
|
|
icon: 'OrdersIcon',
|
|
badge: null
|
|
},
|
|
])
|
|
|
|
// 判断当前路由是否激活
|
|
const isActiveRoute = (path) => {
|
|
if (path === '/') {
|
|
return route.path === '/'
|
|
}
|
|
return route.path.startsWith(path)
|
|
}
|
|
|
|
// 处理导航点击
|
|
const handleNavClick = (item) => {
|
|
emit('navigate', item)
|
|
}
|
|
|
|
// 获取角色显示名称
|
|
const getRoleDisplayName = (role) => {
|
|
const roleMap = {
|
|
'creator': t('roles.creator'),
|
|
'admin': t('roles.admin'),
|
|
'viewer': t('roles.viewer')
|
|
}
|
|
return roleMap[role] || role
|
|
}
|
|
|
|
// 监听窗口大小变化
|
|
const handleResize = () => {
|
|
isMobile.value = window.innerWidth < 768
|
|
}
|
|
|
|
// 初始化和清理
|
|
window.addEventListener('resize', handleResize)
|
|
|
|
return {
|
|
t,
|
|
isMobile,
|
|
currentUser,
|
|
sidebarClasses,
|
|
coreMenuItems,
|
|
isActiveRoute,
|
|
handleNavClick,
|
|
getRoleDisplayName
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.app-sidebar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 120px;
|
|
height: 100%;
|
|
background: var(--sidebar-bg, #ffffff);
|
|
border-right: 1px solid var(--border-color, #e5e7eb);
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
z-index: 100;
|
|
}
|
|
|
|
|
|
|
|
/* 侧边栏头部 */
|
|
.sidebar-header {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 16px;
|
|
border-bottom: 1px solid var(--border-color, #e5e7eb);
|
|
height: 64px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* 导航菜单 */
|
|
.sidebar-nav {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 20px 12px;
|
|
}
|
|
|
|
.nav-section {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.nav-section:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.nav-section-title {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--text-secondary, #6b7280);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
margin: 0 0 8px 16px;
|
|
padding: 0;
|
|
}
|
|
|
|
.nav-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 16px 12px;
|
|
margin: 6px 0;
|
|
border-radius: 14px;
|
|
color: var(--text-secondary, #6b7280);
|
|
text-decoration: none;
|
|
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
min-height: 80px;
|
|
width: 100%;
|
|
background: rgba(255, 255, 255, 0.02);
|
|
border: 1px solid rgba(107, 70, 193, 0.05);
|
|
box-shadow:
|
|
0 2px 8px rgba(107, 70, 193, 0.06),
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
backdrop-filter: blur(8px);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.nav-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(135deg, rgba(107, 70, 193, 0.03) 0%, rgba(147, 51, 234, 0.02) 100%);
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
border-radius: 14px;
|
|
}
|
|
|
|
.nav-item:hover {
|
|
background: rgba(107, 70, 193, 0.08);
|
|
color: #6B46C1;
|
|
transform: translateY(-3px) scale(1.02);
|
|
box-shadow:
|
|
0 12px 32px rgba(107, 70, 193, 0.2),
|
|
0 4px 12px rgba(107, 70, 193, 0.15),
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
|
border-color: rgba(107, 70, 193, 0.3);
|
|
}
|
|
|
|
.nav-item:hover::before {
|
|
opacity: 1;
|
|
}
|
|
|
|
.nav-item:active {
|
|
transform: translateY(-1px) scale(0.99);
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.nav-item.active {
|
|
background: linear-gradient(135deg,
|
|
rgba(107, 70, 193, 0.18) 0%,
|
|
rgba(147, 51, 234, 0.15) 50%,
|
|
rgba(168, 85, 247, 0.12) 100%);
|
|
color: #6B46C1;
|
|
font-weight: 600;
|
|
border-color: rgba(107, 70, 193, 0.4);
|
|
box-shadow:
|
|
0 16px 40px rgba(107, 70, 193, 0.3),
|
|
0 6px 20px rgba(107, 70, 193, 0.2),
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.3),
|
|
0 0 0 1px rgba(107, 70, 193, 0.1);
|
|
backdrop-filter: blur(12px) saturate(1.2);
|
|
}
|
|
|
|
.nav-item.active::before {
|
|
opacity: 1;
|
|
background: linear-gradient(135deg,
|
|
rgba(255, 255, 255, 0.15) 0%,
|
|
rgba(255, 255, 255, 0.05) 100%);
|
|
}
|
|
|
|
|
|
|
|
.nav-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-bottom: 10px;
|
|
flex-shrink: 0;
|
|
position: relative;
|
|
z-index: 1;
|
|
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
}
|
|
|
|
.nav-icon svg {
|
|
width: 24px;
|
|
height: 24px;
|
|
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
filter: drop-shadow(0 2px 4px rgba(107, 70, 193, 0.15));
|
|
}
|
|
|
|
.nav-item:hover .nav-icon svg {
|
|
transform: scale(1.1);
|
|
filter: drop-shadow(0 4px 8px rgba(107, 70, 193, 0.3));
|
|
}
|
|
|
|
.nav-item.active .nav-icon svg {
|
|
filter: drop-shadow(0 0 8px rgba(139, 92, 246, 0.5));
|
|
}
|
|
|
|
.nav-text {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
text-align: center;
|
|
white-space: normal;
|
|
line-height: 1.4;
|
|
word-wrap: break-word;
|
|
hyphens: auto;
|
|
max-width: 100%;
|
|
position: relative;
|
|
z-index: 1;
|
|
letter-spacing: 0.01em;
|
|
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
}
|
|
|
|
.nav-badge {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 20px;
|
|
height: 20px;
|
|
background: #ef4444;
|
|
color: white;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
border-radius: 10px;
|
|
padding: 0 6px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* 侧边栏底部 */
|
|
.sidebar-footer {
|
|
padding: 24px 16px 16px;
|
|
border-top: 1px solid rgba(107, 70, 193, 0.08);
|
|
flex-shrink: 0;
|
|
backdrop-filter: blur(12px);
|
|
position: relative;
|
|
}
|
|
|
|
.sidebar-footer::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 16px;
|
|
right: 16px;
|
|
height: 1px;
|
|
background: linear-gradient(90deg,
|
|
transparent 0%,
|
|
rgba(107, 70, 193, 0.2) 20%,
|
|
rgba(107, 70, 193, 0.4) 50%,
|
|
rgba(107, 70, 193, 0.2) 80%,
|
|
transparent 100%);
|
|
}
|
|
|
|
.user-profile {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
padding: 16px;
|
|
border-radius: 16px;
|
|
backdrop-filter: blur(16px);
|
|
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
|
|
.user-profile:active {
|
|
transform: translateY(-1px) scale(1.01);
|
|
}
|
|
|
|
.user-avatar-container {
|
|
position: relative;
|
|
padding: 2px;
|
|
border-radius: 50%;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.user-profile:hover .user-avatar-container {
|
|
box-shadow: 0 0 12px rgba(107, 70, 193, 0.2);
|
|
}
|
|
|
|
.online-status {
|
|
position: absolute;
|
|
bottom: 4px;
|
|
right: 2px;
|
|
width: 12px;
|
|
height: 12px;
|
|
background: #10B981;
|
|
border: 2px solid white;
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 4px rgba(16, 185, 129, 0.4);
|
|
}
|
|
|
|
.user-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
text-align: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.user-name {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--text-primary, #1f2937);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
letter-spacing: 0.02em;
|
|
line-height: 1.3;
|
|
max-width: 100%;
|
|
background: linear-gradient(135deg, var(--text-primary, #1f2937), var(--text-secondary, #6b7280));
|
|
background-clip: text;
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
.user-role {
|
|
margin: 0;
|
|
font-size: 11px;
|
|
color: var(--text-secondary, #6b7280);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
font-weight: 500;
|
|
line-height: 1.2;
|
|
padding: 2px 8px;
|
|
background: rgba(107, 70, 193, 0.08);
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(107, 70, 193, 0.1);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.user-profile:hover .user-role {
|
|
background: rgba(107, 70, 193, 0.12);
|
|
border-color: rgba(107, 70, 193, 0.18);
|
|
color: #8B5CF6;
|
|
}
|
|
|
|
.user-profile-collapsed {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 12px;
|
|
background: rgba(255, 255, 255, 0.7);
|
|
border: 1px solid rgba(107, 70, 193, 0.12);
|
|
border-radius: 16px;
|
|
backdrop-filter: blur(16px);
|
|
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.user-profile-collapsed::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: linear-gradient(90deg,
|
|
transparent 0%,
|
|
rgba(255, 255, 255, 0.8) 50%,
|
|
transparent 100%);
|
|
}
|
|
|
|
.user-profile-collapsed:hover {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-color: rgba(107, 70, 193, 0.2);
|
|
transform: translateY(-2px) scale(1.05);
|
|
box-shadow:
|
|
0 8px 32px rgba(107, 70, 193, 0.12),
|
|
0 2px 8px rgba(107, 70, 193, 0.08);
|
|
}
|
|
|
|
.user-profile-collapsed .user-avatar-container {
|
|
position: relative;
|
|
padding: 2px;
|
|
border-radius: 50%;
|
|
background: linear-gradient(135deg,
|
|
rgba(107, 70, 193, 0.1) 0%,
|
|
rgba(139, 92, 246, 0.2) 100%);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.user-profile-collapsed:hover .user-avatar-container {
|
|
background: linear-gradient(135deg,
|
|
rgba(107, 70, 193, 0.2) 0%,
|
|
rgba(139, 92, 246, 0.3) 100%);
|
|
box-shadow: 0 0 12px rgba(107, 70, 193, 0.2);
|
|
}
|
|
|
|
|
|
|
|
/* 动画 */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
/* 移动端样式 */
|
|
.sidebar-mobile {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
z-index: 1000;
|
|
transform: translateX(-100%);
|
|
box-shadow: 4px 0 24px rgba(107, 70, 193, 0.2);
|
|
transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
backdrop-filter: blur(12px);
|
|
border-right: 1px solid rgba(107, 70, 193, 0.1);
|
|
}
|
|
|
|
.sidebar-mobile.show {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
/* 平板端优化 */
|
|
@media (min-width: 768px) and (max-width: 1024px) {
|
|
.app-sidebar {
|
|
width: 140px;
|
|
}
|
|
|
|
.nav-item {
|
|
padding: 18px 14px;
|
|
min-height: 84px;
|
|
}
|
|
|
|
.nav-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.nav-icon svg {
|
|
width: 26px;
|
|
height: 26px;
|
|
}
|
|
|
|
.nav-text {
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
/* 小屏设备优化 */
|
|
@media (max-width: 767px) {
|
|
.app-sidebar {
|
|
width: 100%;
|
|
max-width: 320px;
|
|
}
|
|
|
|
.nav-item {
|
|
padding: 16px 20px;
|
|
min-height: 72px;
|
|
margin: 8px 16px;
|
|
}
|
|
|
|
.nav-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.nav-icon svg {
|
|
width: 22px;
|
|
height: 22px;
|
|
}
|
|
|
|
.nav-text {
|
|
font-size: 15px;
|
|
}
|
|
}
|
|
|
|
/* 深色主题 */
|
|
.dark .app-sidebar {
|
|
--sidebar-bg: #1f2937;
|
|
--text-primary: #f9fafb;
|
|
--text-secondary: #d1d5db;
|
|
--border-color: #374151;
|
|
--hover-bg: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.dark .nav-item.active {
|
|
background: rgba(139, 92, 246, 0.2);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.dark .nav-item.active .nav-text {
|
|
color: #ffffff;
|
|
}
|
|
|
|
.dark .nav-item.active .nav-icon svg {
|
|
filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.5));
|
|
}
|
|
|
|
.dark .nav-item.active .nav-icon {
|
|
color: #ffffff;
|
|
}
|
|
|
|
.dark .nav-item.active::before {
|
|
background: #8B5CF6;
|
|
}
|
|
|
|
.dark .logo-icon {
|
|
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
|
|
}
|
|
|
|
/* 滚动条样式 */
|
|
.sidebar-nav::-webkit-scrollbar {
|
|
width: 4px;
|
|
}
|
|
|
|
.sidebar-nav::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.sidebar-nav::-webkit-scrollbar-thumb {
|
|
background: var(--border-color, #e5e7eb);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.sidebar-nav::-webkit-scrollbar-thumb:hover {
|
|
background: var(--text-secondary, #6b7280);
|
|
}
|
|
</style> |