refactor: unify header tab menu definitions

This commit is contained in:
jxxghp
2026-06-03 07:21:34 +08:00
parent 8c3380e8f5
commit 841e9479af
9 changed files with 50 additions and 79 deletions

View File

@@ -121,11 +121,20 @@ export interface NavLink extends NavLinkProps, Partial<AclProperties> {
disable?: boolean disable?: boolean
} }
export interface NavMenuTabItem {
title: string
icon?: string
tab: string
description?: string
}
export interface NavMenu extends NavLink { export interface NavMenu extends NavLink {
header: string header: string
description?: string description?: string
admin?: boolean admin?: boolean
footer?: boolean footer?: boolean
// 水平三级菜单和页面动态标签页共用的静态标签定义。
tabs?: NavMenuTabItem[]
} }
// 👉 Vertical nav group // 👉 Vertical nav group

View File

@@ -10,15 +10,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue'
import QuickAccess from '@/layouts/components/QuickAccess.vue' import QuickAccess from '@/layouts/components/QuickAccess.vue'
import HeaderTab from '@/layouts/components/HeaderTab.vue' import HeaderTab from '@/layouts/components/HeaderTab.vue'
import { usePluginSidebarNavStore, useUserStore } from '@/stores' import { usePluginSidebarNavStore, useUserStore } from '@/stores'
import { import { getNavMenus } from '@/router/i18n-menu'
getDiscoverTabs,
getNavMenus,
getPluginTabs,
getSettingTabs,
getSubscribeMovieTabs,
getSubscribeTvTabs,
getWorkflowTabs,
} from '@/router/i18n-menu'
import { filterPluginSidebarNavEntries } from '@/utils/pluginSidebarNav' import { filterPluginSidebarNavEntries } from '@/utils/pluginSidebarNav'
import { NavMenu } from '@/@layouts/types' import { NavMenu } from '@/@layouts/types'
import { useDisplay } from 'vuetify' import { useDisplay } from 'vuetify'
@@ -210,20 +202,6 @@ const visibleHorizontalHeaderButtons = computed(() => {
return (dynamicHeaderTab.value?.appendButtons ?? []).filter(button => resolveMaybeRefValue(button.show, true) !== false) return (dynamicHeaderTab.value?.appendButtons ?? []).filter(button => resolveMaybeRefValue(button.show, true) !== false)
}) })
const staticHorizontalNavTabs = computed<Record<string, DynamicHeaderTabItem[]>>(() => ({
'/recommend': getRecommendTabs(),
'/discover': getDiscoverTabs(t).map(tab => ({
title: tab.name,
icon: tab.icon,
tab: tab.tab,
})),
'/subscribe/movie': getSubscribeMovieTabs(t),
'/subscribe/tv': getSubscribeTvTabs(t),
'/workflow': getWorkflowTabs(t),
'/plugins': getPluginTabs(t),
'/setting': getSettingTabs(t),
}))
// 在组件销毁时清理 // 在组件销毁时清理
onUnmounted(() => { onUnmounted(() => {
dynamicHeaderTab.value = null dynamicHeaderTab.value = null
@@ -370,17 +348,7 @@ function getHorizontalNavTabs(item: NavMenu): DynamicHeaderTabItem[] {
return dynamicHeaderTab.value?.items ?? [] return dynamicHeaderTab.value?.items ?? []
} }
return staticHorizontalNavTabs.value[targetPath] ?? [] return item.tabs ?? []
}
function getRecommendTabs(): DynamicHeaderTabItem[] {
return [
{ title: t('recommend.all'), icon: 'mdi-filmstrip-box-multiple', tab: t('recommend.all') },
{ title: t('recommend.categoryMovie'), icon: 'mdi-movie', tab: t('recommend.categoryMovie') },
{ title: t('recommend.categoryTV'), icon: 'mdi-television-classic', tab: t('recommend.categoryTV') },
{ title: t('recommend.categoryAnime'), icon: 'mdi-animation', tab: t('recommend.categoryAnime') },
{ title: t('recommend.categoryRankings'), icon: 'mdi-trophy', tab: t('recommend.categoryRankings') },
]
} }
function applyPendingHorizontalTab() { function applyPendingHorizontalTab() {

View File

@@ -87,7 +87,7 @@ function initDiscoverTabs() {
const tabs = getDiscoverTabs(t) const tabs = getDiscoverTabs(t)
for (const tab of tabs) { for (const tab of tabs) {
discoverTabs.value.push({ discoverTabs.value.push({
name: tab.name, name: tab.title,
mediaid_prefix: tab.tab, mediaid_prefix: tab.tab,
api_path: '', api_path: '',
filter_params: {}, filter_params: {},

View File

@@ -8,6 +8,7 @@ import { useDynamicButton } from '@/composables/useDynamicButton'
import { usePWA } from '@/composables/usePWA' import { usePWA } from '@/composables/usePWA'
import { getItemColor, initializeItemColors } from '@/utils/colorUtils' import { getItemColor, initializeItemColors } from '@/utils/colorUtils'
import { openSharedDialog } from '@/composables/useSharedDialog' import { openSharedDialog } from '@/composables/useSharedDialog'
import { getRecommendTabs } from '@/router/i18n-menu'
const ContentToggleSettingsDialog = defineAsyncComponent(() => import('@/components/dialog/ContentToggleSettingsDialog.vue')) const ContentToggleSettingsDialog = defineAsyncComponent(() => import('@/components/dialog/ContentToggleSettingsDialog.vue'))
@@ -222,34 +223,8 @@ async function saveConfig(payload?: { enabled?: Record<string, boolean> }) {
settingsDialogController = null settingsDialogController = null
} }
// 标签图标映射 // 推荐分类标签与导航三级菜单共用同一份定义。
const categoryItems = computed(() => [ const categoryItems = computed(() => getRecommendTabs(t))
{
title: t('recommend.all'),
icon: 'mdi-filmstrip-box-multiple',
tab: t('recommend.all'),
},
{
title: t('recommend.categoryMovie'),
icon: 'mdi-movie',
tab: t('recommend.categoryMovie'),
},
{
title: t('recommend.categoryTV'),
icon: 'mdi-television-classic',
tab: t('recommend.categoryTV'),
},
{
title: t('recommend.categoryAnime'),
icon: 'mdi-animation',
tab: t('recommend.categoryAnime'),
},
{
title: t('recommend.categoryRankings'),
icon: 'mdi-trophy',
tab: t('recommend.categoryRankings'),
},
])
// 注册动态标签页 // 注册动态标签页
registerHeaderTab({ registerHeaderTab({

View File

@@ -44,7 +44,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
// 注册动态标签页 // 注册动态标签页
registerHeaderTab({ registerHeaderTab({
items: settingTabs.value, items: settingTabs,
modelValue: activeTab, modelValue: activeTab,
}) })

View File

@@ -313,7 +313,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
// 注册动态标签页 // 注册动态标签页
registerHeaderTab({ registerHeaderTab({
items: subscribeTabs.value, items: subscribeTabs,
modelValue: activeTab, modelValue: activeTab,
appendButtons: [ appendButtons: [
{ {

View File

@@ -69,7 +69,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
// 注册动态标签页 // 注册动态标签页
registerHeaderTab({ registerHeaderTab({
items: workflowTabs.value, items: workflowTabs,
modelValue: activeTab, modelValue: activeTab,
appendButtons: [ appendButtons: [
{ {

View File

@@ -1,4 +1,5 @@
import { useGlobalSettingsStore } from '@/stores' import { useGlobalSettingsStore } from '@/stores'
import type { NavMenuTabItem } from '@/@layouts/types'
import type { Composer } from 'vue-i18n' import type { Composer } from 'vue-i18n'
// 构建路由菜单,每次调用时使用当前的语言环境 // 构建路由菜单,每次调用时使用当前的语言环境
@@ -34,6 +35,7 @@ export function getNavMenus(t: Composer['t']) {
admin: false, admin: false,
footer: true, footer: true,
permission: 'discovery', permission: 'discovery',
tabs: getRecommendTabs(t),
}, },
{ {
title: t('navItems.explore'), title: t('navItems.explore'),
@@ -43,6 +45,7 @@ export function getNavMenus(t: Composer['t']) {
admin: false, admin: false,
footer: true, footer: true,
permission: 'discovery', permission: 'discovery',
tabs: getDiscoverTabs(t),
}, },
{ {
title: t('navItems.movie'), title: t('navItems.movie'),
@@ -53,6 +56,7 @@ export function getNavMenus(t: Composer['t']) {
admin: false, admin: false,
footer: false, footer: false,
permission: 'subscribe', permission: 'subscribe',
tabs: getSubscribeMovieTabs(t),
}, },
{ {
title: t('navItems.tv'), title: t('navItems.tv'),
@@ -63,6 +67,7 @@ export function getNavMenus(t: Composer['t']) {
admin: false, admin: false,
footer: false, footer: false,
permission: 'subscribe', permission: 'subscribe',
tabs: getSubscribeTvTabs(t),
}, },
{ {
title: t('navItems.workflow'), title: t('navItems.workflow'),
@@ -73,6 +78,7 @@ export function getNavMenus(t: Composer['t']) {
admin: true, admin: true,
footer: false, footer: false,
permission: 'manage', permission: 'manage',
tabs: getWorkflowTabs(t),
}, },
{ {
title: t('navItems.calendar'), title: t('navItems.calendar'),
@@ -114,6 +120,7 @@ export function getNavMenus(t: Composer['t']) {
header: t('menu.system'), header: t('menu.system'),
admin: true, admin: true,
permission: 'manage', permission: 'manage',
tabs: getPluginTabs(t),
}, },
{ {
title: t('navItems.siteManager'), title: t('navItems.siteManager'),
@@ -140,14 +147,26 @@ export function getNavMenus(t: Composer['t']) {
header: t('menu.system'), header: t('menu.system'),
admin: true, admin: true,
permission: 'admin', permission: 'admin',
tabs: getSettingTabs(t),
}, },
] ]
: []), : []),
] ]
} }
// 获取推荐标签页
export function getRecommendTabs(t: Composer['t']): NavMenuTabItem[] {
return [
{ title: t('recommend.all'), icon: 'mdi-filmstrip-box-multiple', tab: t('recommend.all') },
{ title: t('recommend.categoryMovie'), icon: 'mdi-movie', tab: t('recommend.categoryMovie') },
{ title: t('recommend.categoryTV'), icon: 'mdi-television-classic', tab: t('recommend.categoryTV') },
{ title: t('recommend.categoryAnime'), icon: 'mdi-animation', tab: t('recommend.categoryAnime') },
{ title: t('recommend.categoryRankings'), icon: 'mdi-trophy', tab: t('recommend.categoryRankings') },
]
}
// 获取设置标签页 // 获取设置标签页
export function getSettingTabs(t: Composer['t']) { export function getSettingTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
title: t('settingTabs.system.title'), title: t('settingTabs.system.title'),
@@ -195,7 +214,7 @@ export function getSettingTabs(t: Composer['t']) {
} }
// 获取电影订阅标签页 // 获取电影订阅标签页
export function getSubscribeMovieTabs(t: Composer['t']) { export function getSubscribeMovieTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
title: t('subscribeTabs.movie.mysub'), title: t('subscribeTabs.movie.mysub'),
@@ -211,7 +230,7 @@ export function getSubscribeMovieTabs(t: Composer['t']) {
} }
// 获取电视剧订阅标签页 // 获取电视剧订阅标签页
export function getSubscribeTvTabs(t: Composer['t']) { export function getSubscribeTvTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
title: t('subscribeTabs.tv.mysub'), title: t('subscribeTabs.tv.mysub'),
@@ -232,7 +251,7 @@ export function getSubscribeTvTabs(t: Composer['t']) {
} }
// 获取插件标签页 // 获取插件标签页
export function getPluginTabs(t: Composer['t']) { export function getPluginTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
title: t('pluginTabs.installed'), title: t('pluginTabs.installed'),
@@ -248,28 +267,28 @@ export function getPluginTabs(t: Composer['t']) {
} }
// 获取发现标签页 // 获取发现标签页
export function getDiscoverTabs(t: Composer['t']) { export function getDiscoverTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
name: t('discoverTabs.themoviedb'), title: t('discoverTabs.themoviedb'),
tab: 'themoviedb', tab: 'themoviedb',
icon: 'themoviedb', icon: 'mdi-movie-search-outline',
}, },
{ {
name: t('discoverTabs.douban'), title: t('discoverTabs.douban'),
tab: 'douban', tab: 'douban',
icon: 'douban', icon: 'mdi-book-open-page-variant-outline',
}, },
{ {
name: t('discoverTabs.bangumi'), title: t('discoverTabs.bangumi'),
tab: 'bangumi', tab: 'bangumi',
icon: 'bangumi', icon: 'mdi-calendar-star-outline',
}, },
] ]
} }
// 获取工作流标签页 // 获取工作流标签页
export function getWorkflowTabs(t: Composer['t']) { export function getWorkflowTabs(t: Composer['t']): NavMenuTabItem[] {
return [ return [
{ {
title: t('workflowTabs.list'), title: t('workflowTabs.list'),

View File

@@ -50,7 +50,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
// 注册动态标签页在setup顶层立即执行 // 注册动态标签页在setup顶层立即执行
registerHeaderTab({ registerHeaderTab({
items: pluginTabs.value, items: pluginTabs,
modelValue: activeTab, modelValue: activeTab,
appendButtons: [ appendButtons: [
{ {