外链,携带用户上下文参数直接打开。
+function customMenuToNavItem(item: CustomMenuItem): NavItem {
+ const mode = item.open_mode ?? 'embed'
+ const url = item.url ?? ''
+ const isHttp = /^https?:\/\//i.test(url)
+ if ((mode === 'redirect' || mode === 'newtab') && isHttp) {
+ const withParams = item.with_user_params !== false
+ return {
+ path: `/custom/${item.id}`,
+ label: item.label,
+ icon: null,
+ iconSvg: item.icon_svg,
+ externalUrl: withParams
+ ? buildEmbeddedUrl(
+ url,
+ authStore.user?.id,
+ authStore.token,
+ isDark.value ? 'dark' : 'light',
+ locale.value,
+ )
+ : url,
+ externalNewTab: mode === 'newtab',
+ }
+ }
+ return {
+ path: `/custom/${item.id}`,
+ label: item.label,
+ icon: null,
+ iconSvg: item.icon_svg,
+ }
+}
+
// Custom menu items filtered by visibility
const customMenuItemsForUser = computed(() => {
const items = appStore.cachedPublicSettings?.custom_menu_items ?? []
@@ -775,14 +825,14 @@ const adminNavItems = computed((): NavItem[] => {
filtered.push({ path: '/keys', label: t('nav.apiKeys'), icon: KeyIcon })
filtered.push({ path: '/admin/settings', label: t('nav.settings'), icon: CogIcon })
for (const cm of customMenuItemsForAdmin.value) {
- filtered.push({ path: `/custom/${cm.id}`, label: cm.label, icon: null, iconSvg: cm.icon_svg })
+ filtered.push(customMenuToNavItem(cm))
}
return filtered
}
visible.push({ path: '/admin/settings', label: t('nav.settings'), icon: CogIcon })
for (const cm of customMenuItemsForAdmin.value) {
- visible.push({ path: `/custom/${cm.id}`, label: cm.label, icon: null, iconSvg: cm.icon_svg })
+ visible.push(customMenuToNavItem(cm))
}
return visible
})
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index b4e231e73f1a..3090a3d0d4a2 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -5758,6 +5758,13 @@ export default {
visibility: 'Visible To',
visibilityUser: 'Regular Users',
visibilityAdmin: 'Administrators',
+ openMode: 'Open Mode',
+ openModeEmbed: 'Embed URL (iframe)',
+ openModeRedirect: 'Redirect current tab',
+ openModeNewTab: 'Open in new tab',
+ openModeHint: 'Embed: open the URL in an in-app iframe; Redirect: navigate the current tab to the URL; New tab: open the URL in a new browser tab. Markdown pages (md: prefix) always open in-app.',
+ withUserParams: 'Carry User Params',
+ withUserParamsHint: 'When enabled, user context params (token, user_id, theme, lang, ...) are appended to the URL; when disabled, the raw URL is used.',
add: 'Add Menu Item',
remove: 'Remove',
moveUp: 'Move Up',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index 70a7f6dfcbb8..cc5a356d2137 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -5914,6 +5914,13 @@ export default {
visibility: '可见角色',
visibilityUser: '普通用户',
visibilityAdmin: '管理员',
+ openMode: '打开方式',
+ openModeEmbed: '网址内嵌(iframe)',
+ openModeRedirect: '当前标签页跳转',
+ openModeNewTab: '新开标签页',
+ openModeHint: '内嵌:在应用内以 iframe 打开;跳转:当前页面直接跳转到该网址;新开标签页:在浏览器新标签打开。Markdown 页面(md: 开头)始终在应用内打开。',
+ withUserParams: '携带用户参数',
+ withUserParamsHint: '开启后,打开网址时会附加用户上下文参数(token、user_id、theme、lang 等);关闭则使用原始网址。',
add: '添加菜单项',
remove: '删除',
moveUp: '上移',
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 718c35cb27ef..03a9e77bc12a 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -171,6 +171,19 @@ export interface CustomMenuItem {
url: string
page_slug?: string
visibility: 'user' | 'admin'
+ /**
+ * How the menu item opens its URL:
+ * - 'embed' (default): render the URL in an in-app iframe page (/custom/:id)
+ * - 'redirect': navigate the current tab directly to the URL
+ * - 'newtab': open the URL in a new browser tab
+ */
+ open_mode?: 'embed' | 'redirect' | 'newtab'
+ /**
+ * Whether to append user context params (token, user_id, theme, lang, ...)
+ * to the URL when opening it. Defaults to true (undefined is treated as true)
+ * to preserve legacy behavior.
+ */
+ with_user_params?: boolean
sort_order: number
}
diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue
index 239ce2d7b72d..296cab6884e4 100644
--- a/frontend/src/views/admin/SettingsView.vue
+++ b/frontend/src/views/admin/SettingsView.vue
@@ -4888,6 +4888,47 @@
+
+
+
+
+
+ {{ t("admin.settings.customMenu.openModeHint") }}
+
+
+
+
+
+
+
+ (item.with_user_params = v)"
+ />
+
+
+ {{ t("admin.settings.customMenu.withUserParamsHint") }}
+
+
+