Files
nebula/web/src/layout/default.vue
T
2026-03-10 16:26:48 +08:00

153 lines
4.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<n-layout class="h-screen w-full" has-sider>
<!-- 侧边栏 -->
<n-layout-sider
bordered
collapse-mode="width"
:collapsed-width="64"
:width="240"
:collapsed="collapsed"
show-trigger
@collapse="collapsed = true"
@expand="collapsed = false"
class="h-full"
>
<div class="flex h-[64px] items-center justify-center overflow-hidden whitespace-nowrap px-4 py-2">
<div class="flex items-center gap-2">
<div class="h-8 w-8 flex-shrink-0 rounded-lg bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center text-white font-bold">N</div>
<span v-show="!collapsed" class="text-lg font-bold text-slate-700 transition-opacity duration-300">Nebula</span>
</div>
</div>
<n-menu
:collapsed="collapsed"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
:value="activeKey"
@update:value="handleMenuUpdate"
/>
</n-layout-sider>
<n-layout class="h-full flex flex-col">
<!-- 顶部 Header -->
<n-layout-header bordered class="h-16 flex items-center justify-between px-6 bg-white z-10">
<!-- 左侧面包屑等 -->
<div class="flex items-center">
<!-- 可以在这里放面包屑 -->
</div>
<!-- 右侧用户菜单 -->
<div class="flex items-center gap-4">
<n-dropdown :options="userOptions" @select="handleUserSelect">
<div class="flex items-center gap-2 cursor-pointer hover:bg-gray-100 px-2 py-1 rounded transition-colors">
<n-avatar round size="small" class="bg-emerald-500 text-white">
A
</n-avatar>
<span class="text-sm font-medium text-slate-700">Admin</span>
</div>
</n-dropdown>
</div>
</n-layout-header>
<!-- 主要内容区域 -->
<n-layout-content content-style="padding: 24px; min-height: calc(100vh - 64px);" class="bg-gray-50/50">
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</n-layout-content>
</n-layout>
</n-layout>
</template>
<script setup lang="ts">
import { ref, h, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import {
NLayout,
NLayoutSider,
NLayoutHeader,
NLayoutContent,
NMenu,
NIcon,
NButton,
NAvatar,
NDropdown,
type MenuOption
} from 'naive-ui'
import { LayoutDashboard, AppWindow, Settings, LogOut } from 'lucide-vue-next'
import { useAuthStore } from '@/stores/auth'
function renderIcon(icon: any) {
return () => h(NIcon, null, { default: () => h(icon) })
}
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const collapsed = ref(false)
// 菜单配置
const menuOptions: MenuOption[] = [
{
label: '概览',
key: 'home',
icon: renderIcon(LayoutDashboard)
},
{
label: '应用管理', // Uncommented and updated
key: 'apps',
icon: renderIcon(AppWindow)
},
{
label: '系统设置', // Uncommented and updated
key: 'settings',
icon: renderIcon(Settings)
}
]
// 当前选中的菜单项,根据路由自动匹配
const activeKey = computed(() => {
return (route.name as string) || 'home'
})
const handleMenuUpdate = (key: string) => {
if (key === 'home') {
router.push('/')
} else {
router.push({ name: key })
}
}
// 用户下拉菜单
const userOptions = [
{
label: '退出登录',
key: 'logout',
icon: renderIcon(LogOut)
}
]
const handleUserSelect = (key: string) => {
if (key === 'logout') {
authStore.logout()
router.push('/login')
}
}
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>