diff --git a/src/@layouts/types.d.ts b/src/@layouts/types.d.ts index 67eaa66c..3aba2622 100644 --- a/src/@layouts/types.d.ts +++ b/src/@layouts/types.d.ts @@ -121,11 +121,20 @@ export interface NavLink extends NavLinkProps, Partial { disable?: boolean } +export interface NavMenuTabItem { + title: string + icon?: string + tab: string + description?: string +} + export interface NavMenu extends NavLink { header: string description?: string admin?: boolean footer?: boolean + // 水平三级菜单和页面动态标签页共用的静态标签定义。 + tabs?: NavMenuTabItem[] } // 👉 Vertical nav group diff --git a/src/layouts/components/DefaultLayout.vue b/src/layouts/components/DefaultLayout.vue index 663b036a..92c92000 100644 --- a/src/layouts/components/DefaultLayout.vue +++ b/src/layouts/components/DefaultLayout.vue @@ -10,15 +10,7 @@ import UserProfile from '@/layouts/components/UserProfile.vue' import QuickAccess from '@/layouts/components/QuickAccess.vue' import HeaderTab from '@/layouts/components/HeaderTab.vue' import { usePluginSidebarNavStore, useUserStore } from '@/stores' -import { - getDiscoverTabs, - getNavMenus, - getPluginTabs, - getSettingTabs, - getSubscribeMovieTabs, - getSubscribeTvTabs, - getWorkflowTabs, -} from '@/router/i18n-menu' +import { getNavMenus } from '@/router/i18n-menu' import { filterPluginSidebarNavEntries } from '@/utils/pluginSidebarNav' import { NavMenu } from '@/@layouts/types' import { useDisplay } from 'vuetify' @@ -210,20 +202,6 @@ const visibleHorizontalHeaderButtons = computed(() => { return (dynamicHeaderTab.value?.appendButtons ?? []).filter(button => resolveMaybeRefValue(button.show, true) !== false) }) -const staticHorizontalNavTabs = computed>(() => ({ - '/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(() => { dynamicHeaderTab.value = null @@ -370,17 +348,7 @@ function getHorizontalNavTabs(item: NavMenu): DynamicHeaderTabItem[] { return dynamicHeaderTab.value?.items ?? [] } - return staticHorizontalNavTabs.value[targetPath] ?? [] -} - -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') }, - ] + return item.tabs ?? [] } function applyPendingHorizontalTab() { diff --git a/src/pages/discover.vue b/src/pages/discover.vue index 31524640..61c98100 100644 --- a/src/pages/discover.vue +++ b/src/pages/discover.vue @@ -87,7 +87,7 @@ function initDiscoverTabs() { const tabs = getDiscoverTabs(t) for (const tab of tabs) { discoverTabs.value.push({ - name: tab.name, + name: tab.title, mediaid_prefix: tab.tab, api_path: '', filter_params: {}, diff --git a/src/pages/recommend.vue b/src/pages/recommend.vue index 4382e9e9..5d459be7 100644 --- a/src/pages/recommend.vue +++ b/src/pages/recommend.vue @@ -8,6 +8,7 @@ import { useDynamicButton } from '@/composables/useDynamicButton' import { usePWA } from '@/composables/usePWA' import { getItemColor, initializeItemColors } from '@/utils/colorUtils' import { openSharedDialog } from '@/composables/useSharedDialog' +import { getRecommendTabs } from '@/router/i18n-menu' const ContentToggleSettingsDialog = defineAsyncComponent(() => import('@/components/dialog/ContentToggleSettingsDialog.vue')) @@ -222,34 +223,8 @@ async function saveConfig(payload?: { enabled?: Record }) { settingsDialogController = null } -// 标签图标映射 -const categoryItems = computed(() => [ - { - 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'), - }, -]) +// 推荐分类标签与导航三级菜单共用同一份定义。 +const categoryItems = computed(() => getRecommendTabs(t)) // 注册动态标签页 registerHeaderTab({ diff --git a/src/pages/setting.vue b/src/pages/setting.vue index 7991542f..a9bc9a26 100644 --- a/src/pages/setting.vue +++ b/src/pages/setting.vue @@ -44,7 +44,7 @@ const { registerHeaderTab } = useDynamicHeaderTab() // 注册动态标签页 registerHeaderTab({ - items: settingTabs.value, + items: settingTabs, modelValue: activeTab, }) diff --git a/src/pages/subscribe.vue b/src/pages/subscribe.vue index 2ca67cde..6ce748ca 100644 --- a/src/pages/subscribe.vue +++ b/src/pages/subscribe.vue @@ -313,7 +313,7 @@ const { registerHeaderTab } = useDynamicHeaderTab() // 注册动态标签页 registerHeaderTab({ - items: subscribeTabs.value, + items: subscribeTabs, modelValue: activeTab, appendButtons: [ { diff --git a/src/pages/workflow.vue b/src/pages/workflow.vue index 371d4261..66390165 100644 --- a/src/pages/workflow.vue +++ b/src/pages/workflow.vue @@ -69,7 +69,7 @@ const { registerHeaderTab } = useDynamicHeaderTab() // 注册动态标签页 registerHeaderTab({ - items: workflowTabs.value, + items: workflowTabs, modelValue: activeTab, appendButtons: [ { diff --git a/src/router/i18n-menu.ts b/src/router/i18n-menu.ts index d1d9d989..d143f299 100644 --- a/src/router/i18n-menu.ts +++ b/src/router/i18n-menu.ts @@ -1,4 +1,5 @@ import { useGlobalSettingsStore } from '@/stores' +import type { NavMenuTabItem } from '@/@layouts/types' import type { Composer } from 'vue-i18n' // 构建路由菜单,每次调用时使用当前的语言环境 @@ -34,6 +35,7 @@ export function getNavMenus(t: Composer['t']) { admin: false, footer: true, permission: 'discovery', + tabs: getRecommendTabs(t), }, { title: t('navItems.explore'), @@ -43,6 +45,7 @@ export function getNavMenus(t: Composer['t']) { admin: false, footer: true, permission: 'discovery', + tabs: getDiscoverTabs(t), }, { title: t('navItems.movie'), @@ -53,6 +56,7 @@ export function getNavMenus(t: Composer['t']) { admin: false, footer: false, permission: 'subscribe', + tabs: getSubscribeMovieTabs(t), }, { title: t('navItems.tv'), @@ -63,6 +67,7 @@ export function getNavMenus(t: Composer['t']) { admin: false, footer: false, permission: 'subscribe', + tabs: getSubscribeTvTabs(t), }, { title: t('navItems.workflow'), @@ -73,6 +78,7 @@ export function getNavMenus(t: Composer['t']) { admin: true, footer: false, permission: 'manage', + tabs: getWorkflowTabs(t), }, { title: t('navItems.calendar'), @@ -114,6 +120,7 @@ export function getNavMenus(t: Composer['t']) { header: t('menu.system'), admin: true, permission: 'manage', + tabs: getPluginTabs(t), }, { title: t('navItems.siteManager'), @@ -140,14 +147,26 @@ export function getNavMenus(t: Composer['t']) { header: t('menu.system'), admin: true, 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 [ { 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 [ { 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 [ { 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 [ { 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 [ { - name: t('discoverTabs.themoviedb'), + title: t('discoverTabs.themoviedb'), tab: 'themoviedb', - icon: 'themoviedb', + icon: 'mdi-movie-search-outline', }, { - name: t('discoverTabs.douban'), + title: t('discoverTabs.douban'), tab: 'douban', - icon: 'douban', + icon: 'mdi-book-open-page-variant-outline', }, { - name: t('discoverTabs.bangumi'), + title: t('discoverTabs.bangumi'), tab: 'bangumi', - icon: 'bangumi', + icon: 'mdi-calendar-star-outline', }, ] } // 获取工作流标签页 -export function getWorkflowTabs(t: Composer['t']) { +export function getWorkflowTabs(t: Composer['t']): NavMenuTabItem[] { return [ { title: t('workflowTabs.list'), diff --git a/src/views/plugin/PluginCardListView.vue b/src/views/plugin/PluginCardListView.vue index 114d365c..0db5cefc 100644 --- a/src/views/plugin/PluginCardListView.vue +++ b/src/views/plugin/PluginCardListView.vue @@ -50,7 +50,7 @@ const { registerHeaderTab } = useDynamicHeaderTab() // 注册动态标签页(在setup顶层立即执行) registerHeaderTab({ - items: pluginTabs.value, + items: pluginTabs, modelValue: activeTab, appendButtons: [ {