246 lines
5.3 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>
<div class="app-header">
<div class="header-left">
<n-button
quaternary
circle
@click="$emit('toggle-sidebar')"
class="sidebar-toggle"
>
<template #icon>
<Icon icon="tabler:menu-2" />
</template>
</n-button>
<n-breadcrumb class="breadcrumb">
<n-breadcrumb-item>
<router-link to="/">{{ appName }}</router-link>
</n-breadcrumb-item>
<template v-for="(item, index) in breadcrumbItems" :key="index">
<n-breadcrumb-item v-if="item.href">
<router-link :to="item.href">{{ item.label }}</router-link>
</n-breadcrumb-item>
<n-breadcrumb-item v-else>
{{ item.label }}
</n-breadcrumb-item>
</template>
</n-breadcrumb>
</div>
<div class="header-right">
<n-space>
<!-- 搜索框 - 使用Naive UI的响应式属性仅System User显示 -->
<n-input
v-if="isSystemUser"
v-model:value="searchQuery"
:placeholder="t('Search...')"
clearable
class="search-input"
@keyup.enter="handleSearch"
@clear="handleSearchClear"
>
<template #prefix>
<Icon icon="tabler:search" />
</template>
</n-input>
<!-- 通知 -->
<n-button quaternary circle>
<template #icon>
<Icon icon="tabler:bell" />
</template>
</n-button>
<!-- 用户菜单 -->
<UserMenu />
</n-space>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { NButton, NBreadcrumb, NBreadcrumbItem, NSpace, NInput } from 'naive-ui'
import { Icon } from '@iconify/vue'
import { useAuthStore } from '../../shared/stores/auth'
import { t } from '../../shared/i18n'
import UserMenu from '../../shared/components/UserMenu.vue'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
// 搜索相关状态
const searchQuery = ref('')
// 从 localStorage 读取应用名称
const appName = computed(() => localStorage.getItem('appName') || 'Jingrow')
const isSystemUser = computed(() => authStore.user?.user_type === 'System User')
const breadcrumbItems = computed(() => {
const items: Array<{ label: string; href?: string }> = []
// 根据路由名称和参数生成面包屑
if (route.name === 'PageTypeList') {
const entity = route.params.entity as string
if (entity) {
items.push({
label: entity,
href: `/app/${entity}`
})
}
} else if (route.name === 'PageTypeDetail') {
const entity = route.params.entity as string
const id = route.params.id as string
if (entity) {
items.push({
label: entity,
href: `/app/${entity}`
})
}
if (id && id !== 'new') {
items.push({
label: id === 'new' ? t('Create') : id
})
}
} else {
// 其他页面的标题映射
const map: Record<string, string> = {
Dashboard: t('Dashboard'),
AgentList: t('Agents'),
AgentDetail: t('Agent Detail'),
NodeList: t('Node Management'),
NodeDetail: t('Node Detail'),
LocalJobList: t('Local Jobs'),
LocalJobDetail: t('Local Job Detail'),
FlowBuilder: t('Flow Builder'),
ScheduledJobList: t('Scheduled Jobs'),
ScheduledJobDetail: t('Scheduled Job Detail'),
MenuManager: t('Menu Manager'),
Settings: t('Settings'),
SearchResults: t('Search Results')
}
const title = map[route.name as string]
if (title) {
items.push({ label: title })
}
}
return items
})
// 搜索处理函数
const handleSearch = () => {
if (searchQuery.value.trim()) {
router.push({
name: 'SearchResults',
query: { q: searchQuery.value.trim() }
})
}
}
const handleSearchClear = () => {
searchQuery.value = ''
}
</script>
<style scoped>
.app-header {
height: 64px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
background: white;
}
.header-left {
display: flex;
align-items: center;
gap: 16px;
min-width: 0; /* 允许flex子项收缩 */
}
.header-right {
display: flex;
align-items: center;
min-width: 0; /* 允许flex子项收缩 */
}
/* 搜索框响应式样式 */
.search-input {
width: 280px;
}
/* 面包屑响应式样式 */
.breadcrumb {
min-width: 0; /* 允许收缩 */
}
/* 平板端样式 (768px - 1024px) */
@media (max-width: 1024px) {
.app-header {
padding: 0 16px;
}
.search-input {
width: 200px;
}
.header-left {
gap: 12px;
}
}
/* 移动端样式 (最大768px) */
@media (max-width: 768px) {
.app-header {
padding: 0 12px;
}
.header-left {
gap: 8px;
}
/* 移动端隐藏面包屑 */
.breadcrumb {
display: none;
}
/* 移动端隐藏搜索框 */
.search-input {
display: none;
}
/* 移动端隐藏用户名文字 */
.username {
display: none;
}
/* 移动端调整按钮间距 */
.header-right :deep(.n-space) {
gap: 4px;
}
}
/* 小屏移动端样式 (最大480px) */
@media (max-width: 480px) {
.app-header {
padding: 0 8px;
}
.header-left {
gap: 4px;
}
/* 小屏移动端进一步压缩间距 */
.header-right :deep(.n-space) {
gap: 2px;
}
}
</style>