mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-27 11:10:12 +08:00
refactor: unify header tab menu definitions
This commit is contained in:
9
src/@layouts/types.d.ts
vendored
9
src/@layouts/types.d.ts
vendored
@@ -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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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: {},
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
|
|||||||
|
|
||||||
// 注册动态标签页
|
// 注册动态标签页
|
||||||
registerHeaderTab({
|
registerHeaderTab({
|
||||||
items: settingTabs.value,
|
items: settingTabs,
|
||||||
modelValue: activeTab,
|
modelValue: activeTab,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
|
|||||||
|
|
||||||
// 注册动态标签页
|
// 注册动态标签页
|
||||||
registerHeaderTab({
|
registerHeaderTab({
|
||||||
items: subscribeTabs.value,
|
items: subscribeTabs,
|
||||||
modelValue: activeTab,
|
modelValue: activeTab,
|
||||||
appendButtons: [
|
appendButtons: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
|
|||||||
|
|
||||||
// 注册动态标签页
|
// 注册动态标签页
|
||||||
registerHeaderTab({
|
registerHeaderTab({
|
||||||
items: workflowTabs.value,
|
items: workflowTabs,
|
||||||
modelValue: activeTab,
|
modelValue: activeTab,
|
||||||
appendButtons: [
|
appendButtons: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const { registerHeaderTab } = useDynamicHeaderTab()
|
|||||||
|
|
||||||
// 注册动态标签页(在setup顶层立即执行)
|
// 注册动态标签页(在setup顶层立即执行)
|
||||||
registerHeaderTab({
|
registerHeaderTab({
|
||||||
items: pluginTabs.value,
|
items: pluginTabs,
|
||||||
modelValue: activeTab,
|
modelValue: activeTab,
|
||||||
appendButtons: [
|
appendButtons: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user