From 46ea9170cbf4334decbcd28be9d76a76db2a75ab Mon Sep 17 00:00:00 2001 From: shaw Date: Wed, 4 Mar 2026 10:44:28 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E8=8F=9C=E5=8D=95=E9=A1=B5=E9=9D=A2=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E8=A7=86=E8=A7=92=E8=8F=9C=E5=8D=95=E4=B8=8D=E7=94=9F?= =?UTF-8?q?=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/layout/AppHeader.vue | 7 +++++-- frontend/src/components/layout/AppSidebar.vue | 3 +-- frontend/src/router/index.ts | 7 +++++-- frontend/src/stores/adminSettings.ts | 5 +++++ frontend/src/views/admin/SettingsView.vue | 5 ++++- frontend/src/views/user/CustomPageView.vue | 16 +++++++++++----- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/layout/AppHeader.vue b/frontend/src/components/layout/AppHeader.vue index ffc7c5e2..76bf684f 100644 --- a/frontend/src/components/layout/AppHeader.vue +++ b/frontend/src/components/layout/AppHeader.vue @@ -211,6 +211,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue' import { useRouter, useRoute } from 'vue-router' import { useI18n } from 'vue-i18n' import { useAppStore, useAuthStore, useOnboardingStore } from '@/stores' +import { useAdminSettingsStore } from '@/stores/adminSettings' import LocaleSwitcher from '@/components/common/LocaleSwitcher.vue' import SubscriptionProgressMini from '@/components/common/SubscriptionProgressMini.vue' import AnnouncementBell from '@/components/common/AnnouncementBell.vue' @@ -221,6 +222,7 @@ const route = useRoute() const { t } = useI18n() const appStore = useAppStore() const authStore = useAuthStore() +const adminSettingsStore = useAdminSettingsStore() const onboardingStore = useOnboardingStore() const user = computed(() => authStore.user) @@ -257,8 +259,9 @@ const pageTitle = computed(() => { // For custom pages, use the menu item's label instead of generic "自定义页面" if (route.name === 'CustomPage') { const id = route.params.id as string - const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] - const menuItem = items.find((item) => item.id === id) + const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? [] + const menuItem = publicItems.find((item) => item.id === id) + ?? (authStore.isAdmin ? adminSettingsStore.customMenuItems.find((item) => item.id === id) : undefined) if (menuItem?.label) return menuItem.label } const titleKey = route.meta.titleKey as string diff --git a/frontend/src/components/layout/AppSidebar.vue b/frontend/src/components/layout/AppSidebar.vue index dcfc60bb..3c384e64 100644 --- a/frontend/src/components/layout/AppSidebar.vue +++ b/frontend/src/components/layout/AppSidebar.vue @@ -579,8 +579,7 @@ const customMenuItemsForUser = computed(() => { }) const customMenuItemsForAdmin = computed(() => { - const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] - return items + return adminSettingsStore.customMenuItems .filter((item) => item.visibility === 'admin') .sort((a, b) => a.sort_order - b.sort_order) }) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 08f492d4..8aa9cfff 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -6,6 +6,7 @@ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' import { useAuthStore } from '@/stores/auth' import { useAppStore } from '@/stores/app' +import { useAdminSettingsStore } from '@/stores/adminSettings' import { useNavigationLoadingState } from '@/composables/useNavigationLoading' import { useRoutePrefetch } from '@/composables/useRoutePrefetch' import { resolveDocumentTitle } from './title' @@ -431,8 +432,10 @@ router.beforeEach((to, _from, next) => { // For custom pages, use menu item label as document title if (to.name === 'CustomPage') { const id = to.params.id as string - const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] - const menuItem = items.find((item) => item.id === id) + const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? [] + const adminSettingsStore = useAdminSettingsStore() + const menuItem = publicItems.find((item) => item.id === id) + ?? (authStore.isAdmin ? adminSettingsStore.customMenuItems.find((item) => item.id === id) : undefined) if (menuItem?.label) { const siteName = appStore.siteName || 'Sub2API' document.title = `${menuItem.label} - ${siteName}` diff --git a/frontend/src/stores/adminSettings.ts b/frontend/src/stores/adminSettings.ts index 460cc92b..76010c5e 100644 --- a/frontend/src/stores/adminSettings.ts +++ b/frontend/src/stores/adminSettings.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { adminAPI } from '@/api' +import type { CustomMenuItem } from '@/types' export const useAdminSettingsStore = defineStore('adminSettings', () => { const loaded = ref(false) @@ -47,6 +48,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => { const opsMonitoringEnabled = ref(readCachedBool('ops_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 customMenuItems = ref([]) async function fetch(force = false): Promise { if (loaded.value && !force) return @@ -64,6 +66,8 @@ export const useAdminSettingsStore = defineStore('adminSettings', () => { opsQueryModeDefault.value = settings.ops_query_mode_default || 'auto' writeCachedString('ops_query_mode_default_cached', opsQueryModeDefault.value) + customMenuItems.value = Array.isArray(settings.custom_menu_items) ? settings.custom_menu_items : [] + loaded.value = true } catch (err) { // 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, opsRealtimeMonitoringEnabled, opsQueryModeDefault, + customMenuItems, fetch, setOpsMonitoringEnabledLocal, setOpsRealtimeMonitoringEnabledLocal, diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue index e3f89ac9..32bbeb75 100644 --- a/frontend/src/views/admin/SettingsView.vue +++ b/frontend/src/views/admin/SettingsView.vue @@ -1363,9 +1363,11 @@ import Toggle from '@/components/common/Toggle.vue' import ImageUpload from '@/components/common/ImageUpload.vue' import { useClipboard } from '@/composables/useClipboard' import { useAppStore } from '@/stores' +import { useAdminSettingsStore } from '@/stores/adminSettings' const { t } = useI18n() const appStore = useAppStore() +const adminSettingsStore = useAdminSettingsStore() const { copyToClipboard } = useClipboard() const loading = ref(true) @@ -1661,8 +1663,9 @@ async function saveSettings() { form.smtp_password = '' form.turnstile_secret_key = '' 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 adminSettingsStore.fetch(true) appStore.showSuccess(t('admin.settings.settingsSaved')) } catch (error: any) { appStore.showError( diff --git a/frontend/src/views/user/CustomPageView.vue b/frontend/src/views/user/CustomPageView.vue index ed1c11d7..532830a5 100644 --- a/frontend/src/views/user/CustomPageView.vue +++ b/frontend/src/views/user/CustomPageView.vue @@ -70,6 +70,7 @@ import { useRoute } from 'vue-router' import { useI18n } from 'vue-i18n' import { useAppStore } from '@/stores' import { useAuthStore } from '@/stores/auth' +import { useAdminSettingsStore } from '@/stores/adminSettings' import AppLayout from '@/components/layout/AppLayout.vue' import Icon from '@/components/icons/Icon.vue' import { buildEmbeddedUrl, detectTheme } from '@/utils/embedded-url' @@ -78,6 +79,7 @@ const { t } = useI18n() const route = useRoute() const appStore = useAppStore() const authStore = useAuthStore() +const adminSettingsStore = useAdminSettingsStore() const loading = ref(false) const pageTheme = ref<'light' | 'dark'>('light') @@ -86,12 +88,16 @@ let themeObserver: MutationObserver | null = null const menuItemId = computed(() => route.params.id as string) const menuItem = computed(() => { - const items = appStore.cachedPublicSettings?.custom_menu_items ?? [] - const found = items.find((item) => item.id === menuItemId.value) ?? null - if (found && found.visibility === 'admin' && !authStore.isAdmin) { - return null + const id = menuItemId.value + // Try public settings first (contains user-visible items) + const publicItems = appStore.cachedPublicSettings?.custom_menu_items ?? [] + 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(() => {