This commit is contained in:
张成
2025-10-08 19:20:24 +08:00
parent 845658f193
commit 7e888970d3
11 changed files with 1726 additions and 68 deletions

View File

@@ -0,0 +1,427 @@
# 首页路由梳理说明
## 🔍 问题分析
### 原来的问题
首页在多个地方被设置,导致路由混乱和错误:
1. **`src/utils/uiTool.js``getRoutes` 方法**
- 接收了 `HomePage` 参数但没有使用
- `children` 初始化为空数组
- 尝试查找不存在的 `homeRoute`,返回 `undefined`
- 使用 `undefined` 导致路由错误
2. **`src/index.js``install` 方法**
- 调用 `getRoutes` 创建路由
- 传入了 `HomePage` 但没有被使用
3. **`demo-project/src/main.js``mounted` 钩子**
- 又调用了 `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.js` 的 `getRoutes` 方法
**修改前**
```javascript
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
}
```
**修改后**
```javascript
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`
**修改前**
```javascript
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: ''
})
}
})
```
**修改后**
```javascript
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`
**修改前**
```javascript
// main.vue
...mapGetters({
sysFormModel: 'sysFormModel', // ❌ 缺少模块前缀
menuList: 'menuList',
userName: 'userName',
userAvator: 'avatorImgPath'
})
// user.vue
...mapActions(['handleLogOut']) // ❌ 缺少模块前缀
```
**修改后**
```javascript
// 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 的最佳实践
---
**首页路由梳理完成!** 🎉
现在路由创建流程清晰、简洁、可靠。