fischer-agentkit/src/agentkit/server/frontend/src/components/layout/TopNav.vue

190 lines
4.7 KiB
Vue

<template>
<header class="top-nav">
<div class="top-nav__left">
<button
v-if="props.iconNavCollapsed !== undefined"
class="top-nav__icon-btn"
@click="emit('toggleIconNav')"
title="切换导航"
>
<MenuFoldOutlined v-if="!iconNavCollapsed" />
<MenuUnfoldOutlined v-else />
</button>
<div class="top-nav__logo" @click="router.push('/agent')">
<span class="top-nav__logo-text">Fischer</span>
<span class="top-nav__logo-badge">AgentKit</span>
</div>
</div>
<div class="top-nav__center">
</div>
<div class="top-nav__right">
<div class="top-nav__status">
<a-badge
:status="wsConnected ? 'success' : 'error'"
:text="wsConnected ? '已连接' : '未连接'"
/>
</div>
<a-tooltip title="多维表格">
<button class="top-nav__icon-btn" aria-label="多维表格" @click="router.push('/bitable')">
<TableOutlined />
</button>
</a-tooltip>
<a-tooltip :title="isDark ? '切换亮色模式' : '切换暗色模式'">
<button
class="top-nav__icon-btn"
:aria-label="isDark ? '切换亮色模式' : '切换暗色模式'"
@click="themeStore.toggle()"
>
<BulbOutlined v-if="isDark" />
<BulbOutlined v-else style="opacity: 0.5" />
</button>
</a-tooltip>
<a-tooltip title="设置">
<button class="top-nav__icon-btn" @click="router.push('/agent/monitor?tab=settings')">
<SettingOutlined />
</button>
</a-tooltip>
<a-tooltip v-if="authStore.isAdmin()" title="管理控制台">
<button class="top-nav__icon-btn" @click="router.push('/admin/dashboard')">
<TeamOutlined />
</button>
</a-tooltip>
</div>
</header>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { Badge as ABadge, Tooltip as ATooltip } from 'ant-design-vue'
import {
SettingOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
BulbOutlined,
TeamOutlined,
TableOutlined,
} from '@ant-design/icons-vue'
import { useChatStore } from '@/stores/chat'
import { useThemeStore } from '@/stores/theme'
import { useAuthStore } from '@/stores/auth'
const props = defineProps<{
iconNavCollapsed?: boolean
}>()
const emit = defineEmits<{
toggleIconNav: []
}>()
const router = useRouter()
const chatStore = useChatStore()
const themeStore = useThemeStore()
const authStore = useAuthStore()
const wsConnected = computed(() => chatStore.isWsConnected)
const iconNavCollapsed = computed(() => props.iconNavCollapsed ?? false)
const isDark = computed(() => themeStore.resolvedMode === 'dark')
</script>
<style scoped>
.top-nav {
display: flex;
align-items: center;
justify-content: space-between;
height: var(--topnav-height);
padding: 0 var(--space-5);
background: var(--bg-primary);
border-bottom: 1px solid var(--border-color-split);
flex-shrink: 0;
z-index: 100;
}
.top-nav__left {
display: flex;
align-items: center;
gap: var(--space-3);
}
.top-nav__logo {
display: flex;
align-items: center;
gap: var(--space-2);
cursor: pointer;
user-select: none;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-md);
transition: background var(--transition-fast);
}
.top-nav__logo:hover {
background: var(--bg-tertiary);
}
.top-nav__logo-text {
font-size: var(--font-lg);
font-weight: var(--font-weight-bold);
background: var(--gradient-brand);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.top-nav__logo-badge {
font-size: var(--font-xs);
font-weight: var(--font-weight-medium);
color: var(--text-inverse);
background: var(--gradient-brand);
padding: 2px var(--space-2);
border-radius: var(--radius-full);
letter-spacing: 0.3px;
}
.top-nav__center {
display: flex;
align-items: center;
}
.top-nav__task-select {
min-width: 200px;
}
.top-nav__right {
display: flex;
align-items: center;
gap: var(--space-2);
}
.top-nav__status {
font-size: var(--font-xs);
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-full);
background: var(--bg-tertiary);
}
.top-nav__status :deep(.ant-badge-status-text) {
color: var(--text-tertiary);
font-size: var(--font-xs);
}
.top-nav__icon-btn {
display: flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
border: none;
background: transparent;
color: var(--text-tertiary);
cursor: pointer;
border-radius: var(--radius-md);
transition: all var(--transition-fast);
}
.top-nav__icon-btn:hover {
color: var(--text-primary);
background: var(--bg-tertiary);
}
</style>