Files
admin_core/demo-project/_doc/首页路由梳理说明.md
张成 b27c047930 1
2025-10-08 20:01:26 +08:00

9.7 KiB
Raw Blame History

首页路由梳理说明

🔍 问题分析

原来的问题

首页在多个地方被设置,导致路由混乱和错误:

  1. src/utils/uiTool.jsgetRoutes 方法

    • 接收了 HomePage 参数但没有使用
    • children 初始化为空数组
    • 尝试查找不存在的 homeRoute,返回 undefined
    • 使用 undefined 导致路由错误
  2. src/index.jsinstall 方法

    • 调用 getRoutes 创建路由
    • 传入了 HomePage 但没有被使用
  3. demo-project/src/main.jsmounted 钩子

    • 又调用了 setAuthorityMenus 重新设置菜单
    • 导致路由被重复设置

错误信息

vue-router.esm.js:1396 Uncaught TypeError: Cannot read properties of undefined (reading 'path')
[vuex] unknown action type: getSysTitle
[vuex] unknown getter: sysFormModel

解决方案

设计原则

  1. 单一职责:每个方法只负责一件事
  2. 避免重复:路由只在一个地方创建和设置
  3. 清晰的流程:首页设置流程清晰可追踪

新的首页设置流程

应用启动
  ↓
框架 install 方法
  ↓
调用 getRoutes 创建主路由
  ↓
创建默认首页路由 (/home)
  ↓
检查 localStorage 中的权限菜单
  ↓
如果有权限菜单,合并到主路由
  ↓
创建 VueRouter 实例
  ↓
应用启动完成
  ↓
用户登录
  ↓
保存权限菜单到 localStorage
  ↓
刷新页面
  ↓
重新执行上述流程(此时有权限菜单)

📝 修改内容

1. 修复 src/utils/uiTool.jsgetRoutes 方法

修改前

static getRoutes(Main, ParentView, Page404, HomePage) {
  let mainRoute = {
    path: '/',
    name: '主视图',
    redirect: '/home',
    component: Main,
    meta: { title: '首页', notCache: true },
    children: []  // ❌ 空数组,没有默认首页
  }

  // ... 后续逻辑尝试使用不存在的 homeRoute
  const homeRoute = mainRoute.children.find(r => r.name === 'home')  // ❌ undefined
  mainRoute.children = [homeRoute, ...curRoutes]  // ❌ 使用 undefined
}

修改后

static getRoutes(Main, ParentView, Page404, HomePage) {
  // ✅ 创建默认的首页路由
  const defaultHomeRoute = {
    path: '/home',
    name: 'home',
    meta: { title: '首页', notCache: true },
    component: HomePage || { 
      render: h => h('div', { style: { padding: '20px' } }, '欢迎使用管理系统') 
    }
  }

  let mainRoute = {
    path: '/',
    name: '主视图',
    redirect: '/home',
    component: Main,
    meta: { title: '首页', notCache: true },
    children: [defaultHomeRoute]  // ✅ 包含默认首页
  }

  // 从 localStorage 读取权限菜单
  if (localStorage.authorityMenus && localStorage.authorityMenus !== 'undefined') {
    let authorityMenus = JSON.parse(localStorage.authorityMenus) || []

    if (authorityMenus && authorityMenus.length > 0) {
      let menus = uiTool.transformTree(authorityMenus)
      let curRoutes = uiTool.menuToRoute(menus, ParentView, Page404)

      // 检查权限路由中是否有 home
      const hasHome = curRoutes.some(r => r.name === 'home')
      
      if (hasHome) {
        // 如果权限路由中有 home使用权限路由的 home
        mainRoute.children = curRoutes
      } else {
        // 如果权限路由中没有 home保留默认 home 并添加其他路由
        mainRoute.children = [defaultHomeRoute, ...curRoutes]
      }
    }
  }

  return mainRoute
}

改进点

  • 使用传入的 HomePage 参数
  • 创建默认的首页路由
  • 正确处理权限菜单的合并
  • 避免使用 undefined

2. 简化 demo-project/src/main.js

修改前

window.rootVue = new Vue({
  el: '#app',
  router: AdminFramework.router,
  store: AdminFramework.store,
  render: h => h(App),
  mounted() {
    AdminFramework.uiTool.setRem()
    
    // ❌ 重复设置权限菜单
    this.$store.dispatch('user/setAuthorityMenus', {
      Main: AdminFramework.Main,
      ParentView: AdminFramework.ParentView,
      Page404: AdminFramework.Page404
    })
    
    this.$store.dispatch('app/getSysTitle', {
      defaultTitle: 'Demo 管理系统',
      defaultLogo: ''
    })
  }
})

修改后

window.rootVue = new Vue({
  el: '#app',
  router: AdminFramework.router,
  store: AdminFramework.store,
  render: h => h(App),
  mounted() {
    AdminFramework.uiTool.setRem()
    
    // ✅ 只获取系统标题,不重复设置菜单
    this.$store.dispatch('app/getSysTitle', {
      defaultTitle: 'Demo 管理系统',
      defaultLogo: ''
    })
  }
})

改进点

  • 移除了重复的 setAuthorityMenus 调用
  • 路由只在框架初始化时创建一次
  • 登录后通过刷新页面重新加载路由

3. 修复 Vuex 模块命名空间问题

修改的文件

  • src/components/main/main.vue
  • src/components/main/components/user/user.vue

修改前

// main.vue
...mapGetters({
  sysFormModel: 'sysFormModel',  // ❌ 缺少模块前缀
  menuList: 'menuList',
  userName: 'userName',
  userAvator: 'avatorImgPath'
})

// user.vue
...mapActions(['handleLogOut'])  // ❌ 缺少模块前缀

修改后

// main.vue
...mapGetters({
  sysFormModel: 'app/sysFormModel',  // ✅ 添加模块前缀
  menuList: 'user/menuList',
  userName: 'user/userName',
  userAvator: 'user/avatorImgPath'
})

// user.vue
...mapActions('user', ['handleLogOut'])  // ✅ 指定模块

🎯 完整的首页路由流程

首次访问(未登录)

1. 用户访问 http://localhost:8080
   ↓
2. 框架初始化
   ↓
3. getRoutes 创建主路由
   - localStorage 中没有 authorityMenus
   - 使用默认首页路由
   ↓
4. 路由配置:
   {
     path: '/',
     component: Main,
     children: [
       { path: '/home', name: 'home', component: HomePage }
     ]
   }
   ↓
5. 显示登录页面

登录流程

1. 用户输入用户名密码
   ↓
2. 调用登录接口
   ↓
3. 保存 token 和用户信息
   ↓
4. 调用 setAuthorityMenus
   - 尝试获取权限菜单(可能失败)
   - 使用默认菜单配置
   - 保存到 localStorage
   ↓
5. 刷新页面 (window.location.reload())
   ↓
6. 重新执行首次访问流程
   - 此时 localStorage 中有 authorityMenus
   - 根据权限菜单生成路由

登录后访问

1. 用户访问 http://localhost:8080
   ↓
2. 框架初始化
   ↓
3. getRoutes 创建主路由
   - localStorage 中有 authorityMenus
   - 解析权限菜单
   - 生成权限路由
   ↓
4. 路由配置:
   {
     path: '/',
     component: Main,
     children: [
       { path: '/home', name: 'home', component: HomePage },
       { path: '/system/user', name: 'sys_user', component: SysUser },
       { path: '/system/role', name: 'sys_role', component: SysRole },
       // ... 其他权限路由
     ]
   }
   ↓
5. 显示系统首页

📊 路由结构

完整的路由树

routes: [
  // 登录页面
  {
    path: '/login',
    name: 'login',
    component: LoginPage
  },
  
  // 主路由
  {
    path: '/',
    name: '主视图',
    redirect: '/home',
    component: Main,
    children: [
      // 默认首页
      {
        path: '/home',
        name: 'home',
        component: HomePage,
        meta: { title: '首页', notCache: true }
      },
      
      // 权限路由(从 authorityMenus 生成)
      {
        path: '/system/user',
        name: 'sys_user',
        component: SysUser,
        meta: { title: '用户管理' }
      },
      {
        path: '/system/role',
        name: 'sys_role',
        component: SysRole,
        meta: { title: '角色管理' }
      },
      // ... 更多权限路由
    ]
  },
  
  // 错误页面
  {
    path: '/401',
    name: 'error_401',
    component: Page401
  },
  {
    path: '/500',
    name: 'error_500',
    component: Page500
  },
  {
    path: '*',
    name: 'error_404',
    component: Page404
  }
]

验证清单

功能验证

  • 首次访问显示登录页面
  • 登录成功后跳转到首页
  • 首页正确显示
  • 左侧菜单正确显示
  • 系统标题正确显示
  • 用户名正确显示
  • 页面跳转正常
  • 刷新页面后状态保持

错误修复

  • 修复了 Cannot read properties of undefined (reading 'path') 错误
  • 修复了 [vuex] unknown action type: getSysTitle 错误
  • 修复了 [vuex] unknown getter: sysFormModel 错误
  • 修复了首页路由重复设置的问题

💡 最佳实践

1. 路由创建原则

  • 单一入口:路由只在框架初始化时创建一次
  • 数据驱动:根据 localStorage 中的数据动态生成路由
  • 默认兜底:始终提供默认的首页路由

2. 权限菜单处理

  • 登录时保存:登录成功后保存权限菜单到 localStorage
  • 启动时读取:应用启动时从 localStorage 读取权限菜单
  • 刷新更新:登录后刷新页面,重新生成路由

3. 避免的陷阱

  • 不要在多个地方创建或修改路由
  • 不要在 mounted 钩子中重复设置菜单
  • 不要忘记处理 undefined 的情况
  • 不要忘记 Vuex 模块的命名空间

🎉 总结

修复的问题

  1. 首页路由正确创建
  2. 权限菜单正确合并
  3. 避免了重复设置
  4. 修复了 Vuex 命名空间问题
  5. 流程清晰可维护

代码改进

  • 减少复杂度:移除了重复的菜单设置逻辑
  • 提高可读性:流程更清晰,易于理解
  • 增强健壮性:正确处理各种边界情况
  • 符合规范:遵循 Vue 和 Vuex 的最佳实践

首页路由梳理完成! 🎉

现在路由创建流程清晰、简洁、可靠。