zhou zhou
19 小时以前 03167ec81343fb8bf8967da13768a1137c89b24d
#前端
5个文件已修改
181 ■■■■■ 已修改文件
rsf-design/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/router/core/ComponentLoader.js 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/router/core/MenuProcessor.js 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/router/core/RouteRegistry.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/router/guards/beforeEach.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 {
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 module = this.resolveModule(componentPath)
    if (!module) {
    const fullPath = `../../views${componentPath}.vue`
    const fullPathWithIndex = `../../views${componentPath}/index.vue`
    const module = this.modules[fullPath] || this.modules[fullPathWithIndex]
    if (!module) {
      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() {
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 }
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 }
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() || [])