Skip to content

路由数据 转 菜单数据 ​

本节目标:理解“路由数据 转 菜单数据”的核心思路,并能把它用于实际开发或面试表达。 // store/useMenuStore.ts import { defineStore } from 'pinia' import { ref } from 'vue' import type { RouteRecordRaw } from 'vue-router' import type { MenuProps } from '@/types' import { routes } from '@/router' /** ========= 工具函数 ========= / // 复杂逻辑:菜单可见性——只看 hideInMenu/hiddenInMenu;有页面(component)或存在“可见子项”才展示 const isVisibleInMenu = (route: RouteRecordRaw): boolean => { const meta = route.meta || {} if (meta.hideInMenu === true || (meta as any).hiddenInMenu === true) return false const hasVisibleChild = (route.children || []).some((c) => isVisibleInMenu(c)) return Boolean((route as any).component || hasVisibleChild) } // 复杂逻辑:菜单文案优先 meta.title;没有则退化到基于 name 的 i18n key const getLabel = (route: RouteRecordRaw) => (route.meta?.title as string) || menu.${String(route.name || '')} // 复杂逻辑:菜单图标优先 meta.icon;没有则回退到 icon-menu.${routeName} const getIcon = (route: RouteRecordRaw) => (route.meta?.icon as string) || (route.name ? icon-menu.${String(route.name)} : undefined) // 复杂逻辑:仅保留“可见子项” const getVisibleChildren = (route: RouteRecordRaw) => (route.children || []).filter((c) => isVisibleInMenu(c)) // 复杂逻辑:只有一个子节点时扁平化,且把父级的顺序号沿用到子节点 const flattenIfSingle = (item: MenuProps): MenuProps => { if (item.children && item.children.length === 1) { const only = item.children[0] return { ...only, orderIndex: item.orderIndex } } return item } // 复杂逻辑:每次构建前动态获取“规范化后的”分区,避免初始化时序问题 const pickChildren = (name: string) => (routes.find((r) => r.name === name)?.children ?? []) as RouteRecordRaw[] // 复杂逻辑:按“路由声明顺序”构建菜单(不排序);过滤隐藏项;单子节点扁平化 function buildMenu(nodes: RouteRecordRaw[]): MenuProps[] { const result: MenuProps[] = [] let displayIndex = 0 for (const r of nodes) { if (!isVisibleInMenu(r)) continue const children = getVisibleChildren(r) const item: MenuProps = { key: String(r.name || r.path), label: getLabel(r), orderIndex: displayIndex++, // 复杂逻辑:严格遵循“声明顺序” } const icon = getIcon(r) if (icon) item.icon = icon if (children.length) item.children = buildMenu(children) result.push(flattenIfSingle(item)) } return result } /* ========= Store ========= */ export const useMenuStore = defineStore('menu', () => { const sideMenuList = ref<MenuProps[]>([]) const ibMenuList = ref<MenuProps[]>([]) const inited = ref(false) // 复杂逻辑:一次性从路由树构建两套菜单(公共区 + IB 区),完全使用“声明顺序” async function loadMenuData() { if (inited.value) return try { const publicChildren = pickChildren('root') const ibChildren = pickChildren('introducing-broker') sideMenuList.value = buildMenu(publicChildren) ibMenuList.value = buildMenu(ibChildren) inited.value = true } catch (error) { console.error('[menu] 加载菜单数据失败:', error) sideMenuList.value = [] ibMenuList.value = [] inited.value = false } } // 复杂逻辑:允许重建菜单(如登出/切换角色或语言后) function reset() { sideMenuList.value = [] ibMenuList.value = [] inited.value = false } return { inited, sideMenuList, ibMenuList, loadMenuData, reset, } }) export default useMenuStore

本站总访问 — 次