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

---
 rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss |   10 ++
 rsf-design/src/router/core/ComponentLoader.js                                |   39 +++++++++
 rsf-design/src/router/core/MenuProcessor.js                                  |   80 ++++++++++++++++++++
 rsf-design/src/router/guards/beforeEach.js                                   |    1 
 rsf-design/src/router/core/RouteRegistry.js                                  |   53 +++++++++++++
 5 files changed, 179 insertions(+), 4 deletions(-)

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 1641584..2385966 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
@@ -42,6 +42,8 @@
           flex-direction: column;
           align-items: center;
           justify-content: center;
+          box-sizing: border-box;
+          width: calc(100% - 16px);
           margin: 8px;
           overflow: hidden;
           text-align: center;
@@ -52,11 +54,17 @@
             display: flex !important;
             align-items: center;
             justify-content: center;
-            width: 100%;
+            width: 24px;
+            height: 24px;
             margin: 0 auto !important;
             margin-right: auto !important;
             margin-left: auto !important;
             font-size: 20px;
+
+            :deep(svg) {
+              display: block;
+              margin: auto;
+            }
           }
 
           span {
diff --git a/rsf-design/src/router/core/ComponentLoader.js b/rsf-design/src/router/core/ComponentLoader.js
index eaf91b0..55f1490 100644
--- a/rsf-design/src/router/core/ComponentLoader.js
+++ b/rsf-design/src/router/core/ComponentLoader.js
@@ -2,6 +2,15 @@
 class ComponentLoader {
   constructor() {
     this.modules = import.meta.glob('../../views/**/*.vue')
+    this.warmPromises = new Map()
+  }
+  resolveModule(componentPath) {
+    if (!componentPath) {
+      return null
+    }
+    const fullPath = `../../views${componentPath}.vue`
+    const fullPathWithIndex = `../../views${componentPath}/index.vue`
+    return this.modules[fullPath] || this.modules[fullPathWithIndex] || null
   }
   /**
    * 鍔犺浇缁勪欢
@@ -10,10 +19,10 @@
     if (!componentPath) {
       return this.createEmptyComponent()
     }
-    const fullPath = `../../views${componentPath}.vue`
-    const fullPathWithIndex = `../../views${componentPath}/index.vue`
-    const module = this.modules[fullPath] || this.modules[fullPathWithIndex]
+    const module = this.resolveModule(componentPath)
     if (!module) {
+      const fullPath = `../../views${componentPath}.vue`
+      const fullPathWithIndex = `../../views${componentPath}/index.vue`
       console.error(
         `[ComponentLoader] 鏈壘鍒扮粍浠�: ${componentPath}锛屽皾璇曡繃鐨勮矾寰�: ${fullPath} 鍜� ${fullPathWithIndex}`
       )
@@ -22,6 +31,30 @@
     return module
   }
   /**
+   * 棰勭儹缁勪欢妯″潡锛岄伩鍏嶉娆$偣鍑婚〉闈㈡椂鍐嶈Е鍙戝喎鍔犺浇銆�
+   */
+  warm(componentPath) {
+    const normalizedPath = typeof componentPath === 'string' ? componentPath.trim() : ''
+    if (!normalizedPath) {
+      return Promise.resolve(false)
+    }
+    if (this.warmPromises.has(normalizedPath)) {
+      return this.warmPromises.get(normalizedPath)
+    }
+    const module = this.resolveModule(normalizedPath)
+    if (!module) {
+      return Promise.resolve(false)
+    }
+    const warmTask = module()
+      .then(() => true)
+      .catch((error) => {
+        console.warn(`[ComponentLoader] 缁勪欢棰勭儹澶辫触: ${normalizedPath}`, error)
+        return false
+      })
+    this.warmPromises.set(normalizedPath, warmTask)
+    return warmTask
+  }
+  /**
    * 鍔犺浇甯冨眬缁勪欢
    */
   loadLayout() {
diff --git a/rsf-design/src/router/core/MenuProcessor.js b/rsf-design/src/router/core/MenuProcessor.js
index f031609..c750811 100644
--- a/rsf-design/src/router/core/MenuProcessor.js
+++ b/rsf-design/src/router/core/MenuProcessor.js
@@ -5,6 +5,34 @@
 import { RoutesAlias } from '../routesAlias'
 import { adaptBackendMenuTree } from '../adapters/backendMenuAdapter'
 import { formatMenuTitle } from '@/utils'
+
+const WORKBENCH_PATH = '/dashboard/console'
+const WORKBENCH_ROOT_PATH = '/dashboard'
+
+const WORKBENCH_MENU = Object.freeze({
+  name: 'Dashboard',
+  path: WORKBENCH_ROOT_PATH,
+  component: RoutesAlias.Layout,
+  redirect: WORKBENCH_PATH,
+  meta: {
+    title: 'menus.dashboard.title',
+    icon: 'ri:home-smile-2-line'
+  },
+  children: [
+    {
+      name: 'Console',
+      path: 'console',
+      component: WORKBENCH_PATH,
+      meta: {
+        title: 'menus.dashboard.console',
+        icon: 'ri:home-smile-2-line',
+        keepAlive: false,
+        fixedTab: true
+      }
+    }
+  ]
+})
+
 class MenuProcessor {
   /**
    * 鑾峰彇鑿滃崟鏁版嵁
@@ -17,6 +45,7 @@
     } else {
       menuList = await this.processBackendMenu()
     }
+    menuList = this.prependWorkbenchMenu(menuList)
     this.validateMenuPaths(menuList)
     return this.normalizeMenuPaths(menuList)
   }
@@ -206,5 +235,56 @@
     }
     return `/${path}`
   }
+
+  prependWorkbenchMenu(menuList) {
+    if (!Array.isArray(menuList)) {
+      return [this.createWorkbenchMenu()]
+    }
+
+    const hasWorkbench = menuList.some((item) => this.containsWorkbenchMenu(item))
+    if (hasWorkbench) {
+      return menuList
+    }
+
+    return [this.createWorkbenchMenu(), ...menuList]
+  }
+
+  createWorkbenchMenu() {
+    return {
+      ...WORKBENCH_MENU,
+      meta: {
+        ...WORKBENCH_MENU.meta
+      },
+      children: WORKBENCH_MENU.children.map((child) => ({
+        ...child,
+        meta: {
+          ...child.meta
+        }
+      }))
+    }
+  }
+
+  isWorkbenchMenu(item) {
+    if (!item || typeof item !== 'object') {
+      return false
+    }
+    const path = String(item.path || '')
+    const component = String(item.component || '')
+    return (
+      path === WORKBENCH_ROOT_PATH ||
+      path === WORKBENCH_PATH ||
+      component === WORKBENCH_PATH
+    )
+  }
+
+  containsWorkbenchMenu(item) {
+    if (this.isWorkbenchMenu(item)) {
+      return true
+    }
+    if (!Array.isArray(item?.children) || item.children.length === 0) {
+      return false
+    }
+    return item.children.some((child) => this.containsWorkbenchMenu(child))
+  }
 }
 export { MenuProcessor }
diff --git a/rsf-design/src/router/core/RouteRegistry.js b/rsf-design/src/router/core/RouteRegistry.js
index 3132e78..cd59c62 100644
--- a/rsf-design/src/router/core/RouteRegistry.js
+++ b/rsf-design/src/router/core/RouteRegistry.js
@@ -1,6 +1,8 @@
 import { ComponentLoader } from './ComponentLoader.js'
 import { RouteValidator } from './RouteValidator.js'
 import { RouteTransformer } from './RouteTransformer.js'
+const DEFAULT_WARMUP_LIMIT = 12
+const HOME_COMPONENT_PATH = '/dashboard/console'
 class RouteRegistry {
   constructor(router, options = {}) {
     this.router = router
@@ -75,5 +77,56 @@
   markAsRegistered() {
     this.registered = true
   }
+  /**
+   * 绌洪棽鏃堕鐑珮棰戦〉闈紝闄嶄綆鐧诲綍鍚庨娆″垏椤电殑鍐峰姞杞芥垚鏈��
+   */
+  warm(menuList, options = {}) {
+    const limit = Number.isFinite(options.limit) ? options.limit : DEFAULT_WARMUP_LIMIT
+    const paths = collectWarmupPaths(menuList, limit)
+    if (paths.length === 0) {
+      return
+    }
+    const schedule = globalThis.requestIdleCallback
+      ? (task) => globalThis.requestIdleCallback(task, { timeout: 1200 })
+      : (task) => setTimeout(task, 80)
+    schedule(() => {
+      void warmSequentially(paths, this.componentLoader)
+    })
+  }
+}
+function collectWarmupPaths(menuList, limit) {
+  const paths = []
+  const visited = new Set()
+  const walk = (items) => {
+    if (!Array.isArray(items)) {
+      return
+    }
+    for (const item of items) {
+      if (paths.length >= limit) {
+        return
+      }
+      const componentPath = typeof item?.component === 'string' ? item.component.trim() : ''
+      if (
+        componentPath &&
+        componentPath !== '/' &&
+        componentPath !== HOME_COMPONENT_PATH &&
+        !visited.has(componentPath) &&
+        !item?.meta?.isFirstLevel
+      ) {
+        visited.add(componentPath)
+        paths.push(componentPath)
+      }
+      if (Array.isArray(item?.children) && item.children.length > 0) {
+        walk(item.children)
+      }
+    }
+  }
+  walk(menuList)
+  return paths
+}
+async function warmSequentially(paths, componentLoader) {
+  for (const componentPath of paths) {
+    await componentLoader.warm(componentPath)
+  }
 }
 export { RouteRegistry }
diff --git a/rsf-design/src/router/guards/beforeEach.js b/rsf-design/src/router/guards/beforeEach.js
index 8a71148..ff63662 100644
--- a/rsf-design/src/router/guards/beforeEach.js
+++ b/rsf-design/src/router/guards/beforeEach.js
@@ -151,6 +151,7 @@
       throw new Error('鑾峰彇鑿滃崟鍒楄〃澶辫触锛岃閲嶆柊鐧诲綍')
     }
     routeRegistry?.register(menuList)
+    routeRegistry?.warm(menuList)
     const menuStore = useMenuStore()
     menuStore.setMenuList(menuList)
     menuStore.addRemoveRouteFns(routeRegistry?.getRemoveRouteFns() || [])

--
Gitblit v1.9.1