From cd95f8f1b8a4c3e00eae763714c9542319885cca Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期三, 01 四月 2026 08:10:59 +0800
Subject: [PATCH] #前端

---
 rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue               |   37 ++++++++++++++++--
 rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss              |    9 +++-
 rsf-design/pnpm-lock.yaml                                                                 |   38 +++++++++++++++++++
 rsf-design/src/components/core/layouts/art-settings-panel/composables/useSettingsState.js |    7 +++
 rsf-design/package.json                                                                   |    1 
 rsf-design/src/components/core/layouts/art-header-bar/index.vue                           |   10 +++-
 rsf-design/src/main.js                                                                    |   16 ++++++++
 7 files changed, 109 insertions(+), 9 deletions(-)

diff --git a/rsf-design/package.json b/rsf-design/package.json
index 82a0385..50c1f4f 100644
--- a/rsf-design/package.json
+++ b/rsf-design/package.json
@@ -57,6 +57,7 @@
   },
   "devDependencies": {
     "@eslint/js": "^9.9.1",
+    "@playwright/test": "^1.58.2",
     "@types/node": "^24.0.5",
     "@vitejs/plugin-vue": "^6.0.1",
     "@vue/compiler-sfc": "^3.0.5",
diff --git a/rsf-design/pnpm-lock.yaml b/rsf-design/pnpm-lock.yaml
index b62433e..f72aaef 100644
--- a/rsf-design/pnpm-lock.yaml
+++ b/rsf-design/pnpm-lock.yaml
@@ -117,6 +117,9 @@
       '@eslint/js':
         specifier: ^9.9.1
         version: 9.36.0
+      '@playwright/test':
+        specifier: ^1.58.2
+        version: 1.58.2
       '@types/node':
         specifier: ^24.0.5
         version: 24.8.1
@@ -788,6 +791,11 @@
   '@pkgr/core@0.2.9':
     resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
     engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+  '@playwright/test@1.58.2':
+    resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
+    engines: {node: '>=18'}
+    hasBin: true
 
   '@polka/url@1.0.0-next.29':
     resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
@@ -1829,6 +1837,11 @@
     resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
     engines: {node: '>=14.14'}
 
+  fsevents@2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+
   fsevents@2.3.3:
     resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -2437,6 +2450,16 @@
 
   pkg-types@2.3.0:
     resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+
+  playwright-core@1.58.2:
+    resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
+    engines: {node: '>=18'}
+    hasBin: true
+
+  playwright@1.58.2:
+    resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
+    engines: {node: '>=18'}
+    hasBin: true
 
   postcss-html@1.8.0:
     resolution: {integrity: sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==}
@@ -3654,6 +3677,10 @@
 
   '@pkgr/core@0.2.9': {}
 
+  '@playwright/test@1.58.2':
+    dependencies:
+      playwright: 1.58.2
+
   '@polka/url@1.0.0-next.29': {}
 
   '@rolldown/pluginutils@1.0.0-beta.29': {}
@@ -4752,6 +4779,9 @@
       jsonfile: 6.2.0
       universalify: 2.0.1
 
+  fsevents@2.3.2:
+    optional: true
+
   fsevents@2.3.3:
     optional: true
 
@@ -5246,6 +5276,14 @@
       exsolve: 1.0.7
       pathe: 2.0.3
 
+  playwright-core@1.58.2: {}
+
+  playwright@1.58.2:
+    dependencies:
+      playwright-core: 1.58.2
+    optionalDependencies:
+      fsevents: 2.3.2
+
   postcss-html@1.8.0:
     dependencies:
       htmlparser2: 8.0.2
diff --git a/rsf-design/src/components/core/layouts/art-header-bar/index.vue b/rsf-design/src/components/core/layouts/art-header-bar/index.vue
index ccebde0..d43b54a 100644
--- a/rsf-design/src/components/core/layouts/art-header-bar/index.vue
+++ b/rsf-design/src/components/core/layouts/art-header-bar/index.vue
@@ -54,10 +54,10 @@
         />
 
         <!-- 椤堕儴鑿滃崟 -->
-        <ArtHorizontalMenu v-if="isTopMenu" :list="menuList" />
+        <ArtHorizontalMenu v-if="isTopMenu" :key="topMenuRenderKey" :list="menuList" />
 
         <!-- 娣峰悎鑿滃崟-椤堕儴 -->
-        <ArtMixedMenu v-if="isTopLeftMenu" :list="menuList" />
+        <ArtMixedMenu v-if="isTopLeftMenu" :key="topMenuRenderKey" :list="menuList" />
       </div>
 
       <div class="flex-c gap-2.5">
@@ -175,7 +175,7 @@
   import { themeAnimation } from '@/utils/ui/animation'
 
   import { useI18n } from 'vue-i18n'
-  import { useRouter } from 'vue-router'
+  import { useRoute, useRouter } from 'vue-router'
   import { useFullscreen, useWindowSize } from '@vueuse/core'
   import { MenuTypeEnum } from '@/enums/appEnum'
   import { useSettingStore } from '@/store/modules/setting'
@@ -186,6 +186,7 @@
   import { useHeaderBar } from '@/hooks/core/useHeaderBar'
   defineOptions({ name: 'ArtHeaderBar' })
   const isWindows = navigator.userAgent.includes('Windows')
+  const route = useRoute()
   const router = useRouter()
   const { locale } = useI18n()
   const { width } = useWindowSize()
@@ -216,6 +217,9 @@
   const isDualMenu = computed(() => menuType.value === MenuTypeEnum.DUAL_MENU)
   const isTopMenu = computed(() => menuType.value === MenuTypeEnum.TOP)
   const isTopLeftMenu = computed(() => menuType.value === MenuTypeEnum.TOP_LEFT)
+  const topMenuRenderKey = computed(() => {
+    return `${menuType.value}:${String(route.meta.activePath || route.path)}`
+  })
   const { isFullscreen, toggle: toggleFullscreen } = useFullscreen()
   onMounted(() => {
     initLanguage()
diff --git a/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue b/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
index 9832685..3cc1fd4 100644
--- a/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
+++ b/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
@@ -88,6 +88,7 @@
       </div>
       <ElScrollbar :style="scrollbarStyle">
         <ElMenu
+          :key="menuRenderKey"
           :class="'el-menu-' + getMenuTheme.theme"
           :collapse="!menuOpen"
           :default-active="routerPath"
@@ -161,8 +162,35 @@
   )
   const isDualMenu = computed(() => menuType.value === MenuTypeEnum.DUAL_MENU)
   const isMobileScreen = computed(() => width.value < MOBILE_BREAKPOINT)
-  const firstLevelMenuPath = computed(() => route.matched[0]?.path)
   const routerPath = computed(() => String(route.meta.activePath || route.path))
+  const normalizeRoutePath = (path) => {
+    if (!path) {
+      return ''
+    }
+    return `/${String(path).replace(/^\/+/, '').replace(/\/+$/, '')}`
+  }
+  const findTopLevelMenuByPath = (menus, targetPath) => {
+    const normalizedTargetPath = normalizeRoutePath(targetPath)
+    const containsPath = (menu) => {
+      if (!menu) {
+        return false
+      }
+      if (normalizeRoutePath(menu.path) === normalizedTargetPath) {
+        return true
+      }
+      if (!Array.isArray(menu.children) || menu.children.length === 0) {
+        return false
+      }
+      return menu.children.some((child) => containsPath(child))
+    }
+    return menus.find((menu) => containsPath(menu)) || null
+  }
+  const currentTopLevelMenu = computed(() => {
+    return findTopLevelMenuByPath(useMenuStore().menuList, routerPath.value)
+  })
+  const firstLevelMenuPath = computed(
+    () => currentTopLevelMenu.value?.path || route.matched[0]?.path
+  )
   const firstLevelMenus = computed(() => {
     return useMenuStore().menuList.filter((menu) => !menu.meta.isHide)
   })
@@ -178,9 +206,10 @@
     if (route.meta.isFirstLevel) {
       return []
     }
-    const currentTopPath = `/${route.path.split('/')[1]}`
-    const currentMenu = allMenus.find((menu) => menu.path === currentTopPath)
-    return currentMenu?.children ?? []
+    return currentTopLevelMenu.value?.children ?? []
+  })
+  const menuRenderKey = computed(() => {
+    return `${menuType.value}:${firstLevelMenuPath.value || 'root'}`
   })
   const scrollbarStyle = computed(() => {
     const isCollapsed = isDualMenu.value && !menuOpen.value
diff --git a/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss b/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss
index 8cccdd9..1641584 100644
--- a/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss
+++ b/rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss
@@ -49,8 +49,13 @@
           border-radius: 5px;
 
           .art-svg-icon {
-            display: block;
-            margin: 0 auto;
+            display: flex !important;
+            align-items: center;
+            justify-content: center;
+            width: 100%;
+            margin: 0 auto !important;
+            margin-right: auto !important;
+            margin-left: auto !important;
             font-size: 20px;
           }
 
diff --git a/rsf-design/src/components/core/layouts/art-settings-panel/composables/useSettingsState.js b/rsf-design/src/components/core/layouts/art-settings-panel/composables/useSettingsState.js
index ee95813..ed9a46b 100644
--- a/rsf-design/src/components/core/layouts/art-settings-panel/composables/useSettingsState.js
+++ b/rsf-design/src/components/core/layouts/art-settings-panel/composables/useSettingsState.js
@@ -1,3 +1,4 @@
+import { nextTick } from 'vue'
 import { useSettingStore } from '@/store/modules/setting'
 import { MenuThemeEnum, MenuTypeEnum } from '@/enums/appEnum'
 function useSettingsState() {
@@ -11,6 +12,9 @@
     }
   }
   const switchMenuLayouts = (type) => {
+    if (settingStore.menuType === type) {
+      return
+    }
     if (type === MenuTypeEnum.LEFT || type === MenuTypeEnum.TOP_LEFT) {
       settingStore.setMenuOpen(true)
     }
@@ -19,6 +23,9 @@
       settingStore.switchMenuStyles(MenuThemeEnum.DESIGN)
       settingStore.setMenuOpen(true)
     }
+    nextTick(() => {
+      settingStore.reload()
+    })
   }
   return {
     // 鏂规硶
diff --git a/rsf-design/src/main.js b/rsf-design/src/main.js
index 65788a6..8f704c3 100644
--- a/rsf-design/src/main.js
+++ b/rsf-design/src/main.js
@@ -12,6 +12,22 @@
 document.addEventListener('touchstart', function () {}, { passive: false })
 registerLocalIconCollections()
 const app = createApp(App)
+
+// 娉ㄥ叆閿欒鏃ュ織闈㈡澘鐢ㄤ簬璋冭瘯
+app.config.errorHandler = (err, vm, info) => {
+  console.error("Vue Error:", err, info);
+  const div = document.createElement("div");
+  div.style = "position:fixed;top:0;left:0;z-index:99999;background:red;color:white;padding:20px;font-size:16px;white-space:pre-wrap;width:100vw;height:100vh;overflow:auto;";
+  div.innerText = "Error: " + (err.message || err) + "\n\nStack:\n" + err.stack + "\n\nInfo: " + info;
+  document.body.appendChild(div);
+};
+window.addEventListener("error", (event) => {
+  const div = document.createElement("div");
+  div.style = "position:fixed;top:0;left:0;z-index:99999;background:red;color:white;padding:20px;font-size:16px;white-space:pre-wrap;width:100vw;height:100vh;overflow:auto;";
+  div.innerText = "Global Error: " + event.message + "\n\n" + event.error?.stack;
+  document.body.appendChild(div);
+});
+
 initStore(app)
 initRouter(app)
 setupGlobDirectives(app)

--
Gitblit v1.9.1