初始化
需要注意的是饥荒不够完善:动作、手持工具、细节交互、系统逻辑
This commit is contained in:
290
src/components/NavBar.vue
Normal file
290
src/components/NavBar.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<script setup>
|
||||
// 顶部导航栏:站点名、主导航(SVG 图标在上 + 文字在下的竖排布局)、消息入口(带未读角标)、积分、用户菜单
|
||||
// 小屏(≤900px)不再收进汉堡抽屉,而是隐藏文字仅留图标(title 悬浮提示文字)
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useUserStore } from '../stores/user'
|
||||
import { useThemeStore } from '../stores/theme'
|
||||
import { useChatStore } from '../stores/chat'
|
||||
import NavIcon from './NavIcon.vue'
|
||||
import PixelAvatar from './PixelAvatar.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const themeStore = useThemeStore()
|
||||
const chatStore = useChatStore()
|
||||
|
||||
// 主导航配置(icon 为 NavIcon 组件里的 SVG 名称)
|
||||
const navs = [
|
||||
{ name: 'lobby', path: '/', label: '大厅', icon: 'lobby' },
|
||||
{ name: 'center', path: '/center', label: '游戏中心', icon: 'center' },
|
||||
{ name: 'battle', path: '/battle', label: '联机对战', icon: 'battle' },
|
||||
{ name: 'signin', path: '/signin', label: '签到中心', icon: 'signin' },
|
||||
{ name: 'vip', path: '/vip', label: 'VIP', icon: 'vip' },
|
||||
{ name: 'rank', path: '/rank', label: '排行榜', icon: 'rank' },
|
||||
]
|
||||
|
||||
const points = computed(() => userStore.user?.points ?? 0)
|
||||
|
||||
// 未读私聊 + 待处理好友申请合计(角标)
|
||||
const msgBadge = computed(() => {
|
||||
const n = chatStore.unread + chatStore.reqBadge
|
||||
return n > 99 ? '99+' : n
|
||||
})
|
||||
|
||||
// 当前路由是否命中某导航
|
||||
function isActive(path) {
|
||||
return route.path === path || (path !== '/' && route.path.startsWith(path))
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
function logout() {
|
||||
userStore.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="nav">
|
||||
<div class="nav-inner">
|
||||
<router-link to="/" class="brand">
|
||||
<span class="brand-icon"><NavIcon name="lobby" :size="26" /></span>
|
||||
<span class="brand-name">{{ themeStore.siteName }}</span>
|
||||
</router-link>
|
||||
<!-- 主导航:图标在上、文字在下;小屏自动隐藏文字 -->
|
||||
<nav class="links">
|
||||
<router-link
|
||||
v-for="n in navs"
|
||||
:key="n.name"
|
||||
:to="n.path"
|
||||
class="link"
|
||||
:class="{ active: isActive(n.path) }"
|
||||
:title="n.label"
|
||||
>
|
||||
<NavIcon :name="n.icon" :size="22" class="link-icon" />
|
||||
<span class="link-text">{{ n.label }}</span>
|
||||
</router-link>
|
||||
<router-link
|
||||
to="/chat"
|
||||
class="link msg-link"
|
||||
:class="{ active: route.path.startsWith('/chat') }"
|
||||
title="消息"
|
||||
>
|
||||
<NavIcon name="chat" :size="22" class="link-icon" />
|
||||
<span class="link-text">消息</span>
|
||||
<em v-if="chatStore.unread + chatStore.reqBadge > 0" class="msg-badge">{{ msgBadge }}</em>
|
||||
</router-link>
|
||||
</nav>
|
||||
<div class="right">
|
||||
<span class="points" title="当前积分"><NavIcon name="coin" :size="17" class="coin-icon" /> {{ points }}</span>
|
||||
<router-link v-if="userStore.isAdmin" to="/admin" class="link tool-link" :class="{ active: route.path.startsWith('/admin') }" title="后台管理">
|
||||
<NavIcon name="admin" :size="22" class="link-icon" />
|
||||
<span class="link-text">后台</span>
|
||||
</router-link>
|
||||
<router-link to="/profile" class="user" title="个人中心">
|
||||
<span class="avatar"><PixelAvatar :code="userStore.user?.avatar || 'px:01'" :size="30" /></span>
|
||||
<span class="nickname">{{ userStore.user?.nickname }}</span>
|
||||
</router-link>
|
||||
<button class="link tool-link logout-link" title="退出登录" @click="logout">
|
||||
<NavIcon name="logout" :size="22" class="link-icon" />
|
||||
<span class="link-text">退出</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: color-mix(in srgb, var(--bg-panel) 88%, transparent);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 800;
|
||||
font-size: 17px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.brand-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--primary);
|
||||
filter: drop-shadow(var(--glow));
|
||||
}
|
||||
.brand-name {
|
||||
background: linear-gradient(90deg, var(--primary), var(--accent));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.links {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
/* 竖排导航项:SVG 图标在上、11px 文字在下 */
|
||||
.link {
|
||||
padding: 6px 12px 5px;
|
||||
border-radius: calc(var(--radius) / 1.6);
|
||||
color: var(--text-dim);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
transition: all 0.15s;
|
||||
white-space: nowrap;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
.link-icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.link-text {
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
}
|
||||
.link:hover {
|
||||
color: var(--text);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
.link.active {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-2));
|
||||
box-shadow: var(--glow);
|
||||
}
|
||||
.msg-link {
|
||||
position: relative;
|
||||
}
|
||||
.msg-badge {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 4px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
border-radius: 999px;
|
||||
background: #e5484d;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
font-style: normal;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.points {
|
||||
font-weight: 700;
|
||||
color: var(--primary-2);
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.coin-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tool-link {
|
||||
padding: 5px 9px 4px;
|
||||
}
|
||||
.tool-link.active {
|
||||
color: #fff;
|
||||
}
|
||||
.logout-link:hover {
|
||||
color: #ff8a8e;
|
||||
}
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.nickname {
|
||||
font-size: 13px;
|
||||
max-width: 90px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* 小屏:隐藏文字仅留图标(title 悬浮提示),不再使用汉堡抽屉 */
|
||||
@media (max-width: 900px) {
|
||||
.nav-inner {
|
||||
gap: 8px;
|
||||
height: 56px;
|
||||
}
|
||||
.links {
|
||||
gap: 0;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.links::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.link {
|
||||
padding: 8px 9px;
|
||||
}
|
||||
.link-text {
|
||||
display: none;
|
||||
}
|
||||
.msg-badge {
|
||||
top: 1px;
|
||||
right: 0;
|
||||
}
|
||||
.nickname {
|
||||
display: none;
|
||||
}
|
||||
.right {
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.brand-name {
|
||||
display: none;
|
||||
}
|
||||
.points {
|
||||
font-size: 13px;
|
||||
}
|
||||
.avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user