fix: 修复自定义菜单页面管理员视角菜单不生效问题

This commit is contained in:
shaw
2026-03-04 10:44:28 +08:00
parent 7d318aeefa
commit 46ea9170cb
6 changed files with 31 additions and 12 deletions

View File

@@ -211,6 +211,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAppStore, useAuthStore, useOnboardingStore } from '@/stores' import { useAppStore, useAuthStore, useOnboardingStore } from '@/stores'
import { useAdminSettingsStore } from '@/stores/adminSettings'
import LocaleSwitcher from '@/components/common/LocaleSwitcher.vue' import LocaleSwitcher from '@/components/common/LocaleSwitcher.vue'
import SubscriptionProgressMini from '@/components/common/SubscriptionProgressMini.vue' import SubscriptionProgressMini from '@/components/common/SubscriptionProgressMini.vue'
import AnnouncementBell from '@/components/common/AnnouncementBell.vue' import AnnouncementBell from '@/components/common/AnnouncementBell.vue'
@@ -221,6 +222,7 @@ const route = useRoute()
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStore() const appStore = useAppStore()
const authStore = useAuthStore() const authStore = useAuthStore()
const adminSettingsStore = useAdminSettingsStore()
const onboardingStore = useOnboardingStore() const onboardingStore = useOnboardingStore()
const user = computed(() => authStore.user) const user = computed(() => authStore.user)
@@ -257,8 +259,9 @@ const pageTitle = computed(() => {
// For custom pages, use the menu item's label instead of generic "自定义页面" // For custom pages, use the menu item's label instead of generic "自定义页面"
if (route.name === 'CustomPage') { if (route.name === 'CustomPage') {
const id = route.params.id as string const id = route.params.id as string
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? []
const menuItem = items.find((item) => item.id === id) const menuItem = publicItems.find((item) => item.id === id)
?? (authStore.isAdmin ? adminSettingsStore.customMenuItems.find((item) => item.id === id) : undefined)
if (menuItem?.label) return menuItem.label if (menuItem?.label) return menuItem.label
} }
const titleKey = route.meta.titleKey as string const titleKey = route.meta.titleKey as string

View File

@@ -579,8 +579,7 @@ const customMenuItemsForUser = computed(() => {
}) })
const customMenuItemsForAdmin = computed(() => { const customMenuItemsForAdmin = computed(() => {
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] return adminSettingsStore.customMenuItems
return items
.filter((item) => item.visibility === 'admin') .filter((item) => item.visibility === 'admin')
.sort((a, b) => a.sort_order - b.sort_order) .sort((a, b) => a.sort_order - b.sort_order)
}) })

View File

@@ -6,6 +6,7 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { useAppStore } from '@/stores/app' import { useAppStore } from '@/stores/app'
import { useAdminSettingsStore } from '@/stores/adminSettings'
import { useNavigationLoadingState } from '@/composables/useNavigationLoading' import { useNavigationLoadingState } from '@/composables/useNavigationLoading'
import { useRoutePrefetch } from '@/composables/useRoutePrefetch' import { useRoutePrefetch } from '@/composables/useRoutePrefetch'
import { resolveDocumentTitle } from './title' import { resolveDocumentTitle } from './title'
@@ -431,8 +432,10 @@ router.beforeEach((to, _from, next) => {
// For custom pages, use menu item label as document title // For custom pages, use menu item label as document title
if (to.name === 'CustomPage') { if (to.name === 'CustomPage') {
const id = to.params.id as string const id = to.params.id as string
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? []
const menuItem = items.find((item) => item.id === id) const adminSettingsStore = useAdminSettingsStore()
const menuItem = publicItems.find((item) => item.id === id)
?? (authStore.isAdmin ? adminSettingsStore.customMenuItems.find((item) => item.id === id) : undefined)
if (menuItem?.label) { if (menuItem?.label) {
const siteName = appStore.siteName || 'Sub2API' const siteName = appStore.siteName || 'Sub2API'
document.title = `${menuItem.label} - ${siteName}` document.title = `${menuItem.label} - ${siteName}`

View File

@@ -1,6 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
import { adminAPI } from '@/api' import { adminAPI } from '@/api'
import type { CustomMenuItem } from '@/types'
export const useAdminSettingsStore = defineStore('adminSettings', () => { export const useAdminSettingsStore = defineStore('adminSettings', () => {
const loaded = ref(false) const loaded = ref(false)
@@ -47,6 +48,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
const opsMonitoringEnabled = ref(readCachedBool('ops_monitoring_enabled_cached', true)) const opsMonitoringEnabled = ref(readCachedBool('ops_monitoring_enabled_cached', true))
const opsRealtimeMonitoringEnabled = ref(readCachedBool('ops_realtime_monitoring_enabled_cached', true)) const opsRealtimeMonitoringEnabled = ref(readCachedBool('ops_realtime_monitoring_enabled_cached', true))
const opsQueryModeDefault = ref(readCachedString('ops_query_mode_default_cached', 'auto')) const opsQueryModeDefault = ref(readCachedString('ops_query_mode_default_cached', 'auto'))
const customMenuItems = ref<CustomMenuItem[]>([])
async function fetch(force = false): Promise<void> { async function fetch(force = false): Promise<void> {
if (loaded.value && !force) return if (loaded.value && !force) return
@@ -64,6 +66,8 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
opsQueryModeDefault.value = settings.ops_query_mode_default || 'auto' opsQueryModeDefault.value = settings.ops_query_mode_default || 'auto'
writeCachedString('ops_query_mode_default_cached', opsQueryModeDefault.value) writeCachedString('ops_query_mode_default_cached', opsQueryModeDefault.value)
customMenuItems.value = Array.isArray(settings.custom_menu_items) ? settings.custom_menu_items : []
loaded.value = true loaded.value = true
} catch (err) { } catch (err) {
// Keep cached/default value: do not "flip" the UI based on a transient fetch failure. // Keep cached/default value: do not "flip" the UI based on a transient fetch failure.
@@ -122,6 +126,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => {
opsMonitoringEnabled, opsMonitoringEnabled,
opsRealtimeMonitoringEnabled, opsRealtimeMonitoringEnabled,
opsQueryModeDefault, opsQueryModeDefault,
customMenuItems,
fetch, fetch,
setOpsMonitoringEnabledLocal, setOpsMonitoringEnabledLocal,
setOpsRealtimeMonitoringEnabledLocal, setOpsRealtimeMonitoringEnabledLocal,

View File

@@ -1363,9 +1363,11 @@ import Toggle from '@/components/common/Toggle.vue'
import ImageUpload from '@/components/common/ImageUpload.vue' import ImageUpload from '@/components/common/ImageUpload.vue'
import { useClipboard } from '@/composables/useClipboard' import { useClipboard } from '@/composables/useClipboard'
import { useAppStore } from '@/stores' import { useAppStore } from '@/stores'
import { useAdminSettingsStore } from '@/stores/adminSettings'
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStore() const appStore = useAppStore()
const adminSettingsStore = useAdminSettingsStore()
const { copyToClipboard } = useClipboard() const { copyToClipboard } = useClipboard()
const loading = ref(true) const loading = ref(true)
@@ -1661,8 +1663,9 @@ async function saveSettings() {
form.smtp_password = '' form.smtp_password = ''
form.turnstile_secret_key = '' form.turnstile_secret_key = ''
form.linuxdo_connect_client_secret = '' form.linuxdo_connect_client_secret = ''
// Refresh cached public settings so sidebar/header update immediately // Refresh cached settings so sidebar/header update immediately
await appStore.fetchPublicSettings(true) await appStore.fetchPublicSettings(true)
await adminSettingsStore.fetch(true)
appStore.showSuccess(t('admin.settings.settingsSaved')) appStore.showSuccess(t('admin.settings.settingsSaved'))
} catch (error: any) { } catch (error: any) {
appStore.showError( appStore.showError(

View File

@@ -70,6 +70,7 @@ import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores' import { useAppStore } from '@/stores'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { useAdminSettingsStore } from '@/stores/adminSettings'
import AppLayout from '@/components/layout/AppLayout.vue' import AppLayout from '@/components/layout/AppLayout.vue'
import Icon from '@/components/icons/Icon.vue' import Icon from '@/components/icons/Icon.vue'
import { buildEmbeddedUrl, detectTheme } from '@/utils/embedded-url' import { buildEmbeddedUrl, detectTheme } from '@/utils/embedded-url'
@@ -78,6 +79,7 @@ const { t } = useI18n()
const route = useRoute() const route = useRoute()
const appStore = useAppStore() const appStore = useAppStore()
const authStore = useAuthStore() const authStore = useAuthStore()
const adminSettingsStore = useAdminSettingsStore()
const loading = ref(false) const loading = ref(false)
const pageTheme = ref<'light' | 'dark'>('light') const pageTheme = ref<'light' | 'dark'>('light')
@@ -86,12 +88,16 @@ let themeObserver: MutationObserver | null = null
const menuItemId = computed(() => route.params.id as string) const menuItemId = computed(() => route.params.id as string)
const menuItem = computed(() => { const menuItem = computed(() => {
const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] const id = menuItemId.value
const found = items.find((item) => item.id === menuItemId.value) ?? null // Try public settings first (contains user-visible items)
if (found && found.visibility === 'admin' && !authStore.isAdmin) { const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? []
return null const found = publicItems.find((item) => item.id === id) ?? null
if (found) return found
// For admin users, also check admin settings (contains admin-only items)
if (authStore.isAdmin) {
return adminSettingsStore.customMenuItems.find((item) => item.id === id) ?? null
} }
return found return null
}) })
const embeddedUrl = computed(() => { const embeddedUrl = computed(() => {