fix: 修复自定义菜单页面管理员视角菜单不生效问题
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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}`
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user