diff --git a/core/app/service/setting.go b/core/app/service/setting.go
index 30841415b..db9d34933 100644
--- a/core/app/service/setting.go
+++ b/core/app/service/setting.go
@@ -147,7 +147,7 @@ func repairAndSortHideMenu(settingMap map[string]string) {
return
}
- menus, changed := menutree.EnsureXpackAppMenus(menus, nil)
+ menus, changed := menutree.ReconcileHideMenuIntegrity(menus, nil)
if changed {
repairedBytes, err := json.Marshal(menus)
if err != nil {
@@ -213,7 +213,7 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
if len(menus) == 0 {
return fmt.Errorf("hide menu cannot be empty")
}
- menus, _ = menutree.EnsureXpackAppMenus(menus, previousMenus)
+ menus, _ = menutree.ReconcileHideMenuIntegrity(menus, previousMenus)
for i := 0; i < len(menus); i++ {
if menus[i].Label == "Home-Menu" || menus[i].Label == "App-Menu" || menus[i].Label == "Setting-Menu" {
menus[i].IsShow = true
diff --git a/core/init/migration/helper/menu.go b/core/init/migration/helper/menu.go
index 28aeef8b4..b73e38c6d 100644
--- a/core/init/migration/helper/menu.go
+++ b/core/init/migration/helper/menu.go
@@ -131,7 +131,6 @@ func LoadMenus() string {
{ID: "111", Disabled: false, Title: "xpack.node.nodeManagement", IsShow: true, Label: "NodeDashboard", Path: "/xpack/node/dashboard", Sort: 300},
{ID: "113", Disabled: false, Title: "xpack.monitor.name", IsShow: true, Label: "MonitorDashboard", Path: "/xpack/monitor/dashboard", Sort: 600},
{ID: "115", Disabled: false, Title: "xpack.sync.menu", IsShow: true, Label: "Sync", Path: "/xpack/sync", Sort: 700},
- {ID: "119", Disabled: false, Title: "xpack.upage", IsShow: true, Label: "Upage", Path: "/xpack/upage", Sort: 800},
{ID: "114", Disabled: false, Title: "xpack.tamper.tamper", IsShow: true, Label: "Tamper", Path: "/xpack/tamper", Sort: 1000},
{ID: "120", Disabled: false, Title: "xpack.cluster.cluster", IsShow: true, Label: "Cluster", Path: "/xpack/cluster", Sort: 1100},
{ID: "117", Disabled: false, Title: "xpack.setting.setting", IsShow: true, Label: "XSetting", Path: "/xpack/setting", Sort: 1200},
@@ -200,7 +199,7 @@ func LoadMenus() string {
Label: "VirtualMachine",
Path: "/enterprise/vm",
Sort: 900,
- }, "Upage")
+ }, "Sync")
break
}
}
@@ -255,7 +254,6 @@ func XpackMenuSort() []dto.MenuLabelSort {
{Label: "OpsReport", Sort: 500},
{Label: "MonitorDashboard", Sort: 600},
{Label: "Sync", Sort: 700},
- {Label: "Upage", Sort: 800},
{Label: "VirtualMachine", Sort: 900},
{Label: "Tamper", Sort: 1000},
{Label: "Cluster", Sort: 1100},
diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go
index 5cfe8a713..5f75d285a 100644
--- a/core/init/migration/migrate.go
+++ b/core/init/migration/migrate.go
@@ -65,5 +65,6 @@ func coreMigrations() []*gormigrate.Migration {
migrations.AddWebsiteTemplateMenu,
migrations.RepairXpackAppMenus,
migrations.UpdateFirewallMenuPath,
+ migrations.RemoveUpageHideMenu,
}
}
diff --git a/core/init/migration/migrations/init.go b/core/init/migration/migrations/init.go
index 161ecd323..5f5bdf1ff 100644
--- a/core/init/migration/migrations/init.go
+++ b/core/init/migration/migrations/init.go
@@ -1333,40 +1333,47 @@ var AddWebsiteTemplateMenu = &gormigrate.Migration{
},
}
+func reconcileHideMenuSetting(tx *gorm.DB) error {
+ var setting model.Setting
+ if err := tx.Where("key = ?", "HideMenu").First(&setting).Error; err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ return tx.Create(&model.Setting{Key: "HideMenu", Value: helper.LoadMenus()}).Error
+ }
+ return err
+ }
+
+ updateValue := func(value string) error {
+ return tx.Model(&setting).Update("value", value).Error
+ }
+
+ if strings.TrimSpace(setting.Value) == "" {
+ return updateValue(helper.LoadMenus())
+ }
+
+ var menus []dto.ShowMenu
+ if err := json.Unmarshal([]byte(setting.Value), &menus); err != nil || len(menus) == 0 {
+ return updateValue(helper.LoadMenus())
+ }
+
+ updatedMenus, changed := menutree.ReconcileHideMenuIntegrity(menus, nil)
+ if !changed {
+ return nil
+ }
+ updatedJSON, err := json.Marshal(updatedMenus)
+ if err != nil {
+ return err
+ }
+ return updateValue(string(updatedJSON))
+}
+
var RepairXpackAppMenus = &gormigrate.Migration{
- ID: "20260818-repair-xapp-upage-hide-menu",
- Migrate: func(tx *gorm.DB) error {
- var setting model.Setting
- if err := tx.Where("key = ?", "HideMenu").First(&setting).Error; err != nil {
- if errors.Is(err, gorm.ErrRecordNotFound) {
- return tx.Create(&model.Setting{Key: "HideMenu", Value: helper.LoadMenus()}).Error
- }
- return err
- }
+ ID: "20260818-repair-xapp-upage-hide-menu",
+ Migrate: reconcileHideMenuSetting,
+}
- updateValue := func(value string) error {
- return tx.Model(&setting).Update("value", value).Error
- }
-
- if strings.TrimSpace(setting.Value) == "" {
- return updateValue(helper.LoadMenus())
- }
-
- var menus []dto.ShowMenu
- if err := json.Unmarshal([]byte(setting.Value), &menus); err != nil || len(menus) == 0 {
- return updateValue(helper.LoadMenus())
- }
-
- updatedMenus, changed := menutree.EnsureXpackAppMenus(menus, nil)
- if !changed {
- return nil
- }
- updatedJSON, err := json.Marshal(updatedMenus)
- if err != nil {
- return err
- }
- return updateValue(string(updatedJSON))
- },
+var RemoveUpageHideMenu = &gormigrate.Migration{
+ ID: "20260901-remove-upage-hide-menu",
+ Migrate: reconcileHideMenuSetting,
}
var UpdateFirewallMenuPath = &gormigrate.Migration{
diff --git a/core/utils/menutree/hide_menu_integrity.go b/core/utils/menutree/hide_menu_integrity.go
index 315c00078..96f4fb1b5 100644
--- a/core/utils/menutree/hide_menu_integrity.go
+++ b/core/utils/menutree/hide_menu_integrity.go
@@ -32,34 +32,46 @@ func preserveMissingMenus(root, current *[]dto.ShowMenu, fallback []dto.ShowMenu
return changed
}
-func EnsureXpackAppMenus(menus, fallback []dto.ShowMenu) ([]dto.ShowMenu, bool) {
+func ReconcileHideMenuIntegrity(menus, fallback []dto.ShowMenu) ([]dto.ShowMenu, bool) {
updated := cloneMenus(menus)
- parentIndex := findXpackMenu(updated)
- fallbackParentIndex := findXpackMenu(fallback)
-
changed := false
+ if preferredXApp := findSystemMenu(updated, defaultXAppMenu()); preferredXApp != nil {
+ changed = normalizeSystemFields(preferredXApp, defaultXAppMenu())
+ }
+
+ var removed bool
+ updated, removed = removeSystemMenus(updated, retiredUpageMenuIdentity())
+ sanitizedFallback := cloneMenus(fallback)
+ if preferredFallbackXApp := findSystemMenu(sanitizedFallback, defaultXAppMenu()); preferredFallbackXApp != nil {
+ normalizeSystemFields(preferredFallbackXApp, defaultXAppMenu())
+ }
+ sanitizedFallback, _ = removeSystemMenus(sanitizedFallback, retiredUpageMenuIdentity())
+ changed = changed || removed
+ if existingXApp := findSystemMenu(updated, defaultXAppMenu()); existingXApp != nil {
+ normalized := normalizeSystemFields(existingXApp, defaultXAppMenu())
+ deduplicated, deduplicatedChanged := deduplicateSystemMenu(updated, existingXApp, defaultXAppMenu())
+ return deduplicated, changed || normalized || deduplicatedChanged
+ }
+
+ parentIndex := findXpackMenu(updated)
if parentIndex < 0 {
parent := defaultXpackMenu()
+ fallbackParentIndex := findXpackMenu(sanitizedFallback)
if fallbackParentIndex >= 0 {
- parent = cloneMenu(fallback[fallbackParentIndex])
+ parent = cloneMenu(sanitizedFallback[fallbackParentIndex])
}
updated = append(updated, parent)
parentIndex = len(updated) - 1
changed = true
}
- required := []dto.ShowMenu{defaultXAppMenu(), defaultUpageMenu()}
- for _, canonical := range required {
- parentIndex = findXpackMenu(updated)
- var childChanged bool
- updated, childChanged = ensureSystemMenu(
- updated,
- parentIndex,
- fallback,
- canonical,
- )
- changed = changed || childChanged
- }
+ updated, xAppChanged := ensureSystemMenu(
+ updated,
+ parentIndex,
+ sanitizedFallback,
+ defaultXAppMenu(),
+ )
+ changed = changed || xAppChanged
return updated, changed
}
@@ -87,15 +99,11 @@ func defaultXAppMenu() dto.ShowMenu {
}
}
-func defaultUpageMenu() dto.ShowMenu {
+func retiredUpageMenuIdentity() dto.ShowMenu {
return dto.ShowMenu{
- ID: "119",
- Disabled: false,
- Title: "xpack.upage",
- IsShow: true,
- Label: "Upage",
- Path: "/xpack/upage",
- Sort: 800,
+ ID: "119",
+ Label: "Upage",
+ Path: "/xpack/upage",
}
}
@@ -231,6 +239,29 @@ func deduplicateSystemMenu(menus []dto.ShowMenu, selected *dto.ShowMenu, canonic
return deduplicated, changed
}
+func removeSystemMenus(menus []dto.ShowMenu, identity dto.ShowMenu) ([]dto.ShowMenu, bool) {
+ if menus == nil {
+ return nil, false
+ }
+ filtered := make([]dto.ShowMenu, 0, len(menus))
+ changed := false
+ for i := range menus {
+ menu := &menus[i]
+ children, childChanged := removeSystemMenus(menu.Children, identity)
+ if matchesSystemIdentity(*menu, identity) {
+ filtered = append(filtered, children...)
+ changed = true
+ continue
+ }
+ if childChanged {
+ menu.Children = children
+ changed = true
+ }
+ filtered = append(filtered, *menu)
+ }
+ return filtered, changed
+}
+
func matchesSystemIdentity(menu, canonical dto.ShowMenu) bool {
return menu.ID == canonical.ID || menu.Label == canonical.Label || menu.Path == canonical.Path
}
diff --git a/frontend/src/layout/components/Sidebar/components/SubItem.vue b/frontend/src/layout/components/Sidebar/components/SubItem.vue
index 6356505b2..95109ad59 100644
--- a/frontend/src/layout/components/Sidebar/components/SubItem.vue
+++ b/frontend/src/layout/components/Sidebar/components/SubItem.vue
@@ -19,16 +19,6 @@
-
-
-
-
-
- {{ $t(subItem.meta?.title as string, 2) }}
- {{ $t(subItem.meta?.title as string, 2) }}
-
-
-
@@ -46,10 +36,6 @@ import { RouteRecordRaw } from 'vue-router';
import SvgIcon from '@/components/svg-icon/svg-icon.vue';
defineProps<{ menuList: RouteRecordRaw[]; level?: number }>();
-
-const goUpage = () => {
- window.open('https://www.lxware.cn/upage', '_blank', 'noopener,noreferrer');
-};