From d7edbd1e9587460eeea6e249820007775c0389d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD?= Date: Tue, 1 Sep 2026 17:52:12 +0800 Subject: [PATCH] feat: reconcile hide menu integrity (#13681) --- core/app/service/setting.go | 4 +- core/init/migration/helper/menu.go | 4 +- core/init/migration/migrate.go | 1 + core/init/migration/migrations/init.go | 71 ++++++++-------- core/utils/menutree/hide_menu_integrity.go | 81 +++++++++++++------ .../components/Sidebar/components/SubItem.vue | 14 ---- .../src/layout/components/Sidebar/index.vue | 39 +++++---- .../views/setting/panel/hidemenu/index.vue | 40 ++------- 8 files changed, 130 insertions(+), 124 deletions(-) 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 @@ - - - - - - - @@ -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'); -};