1
This commit is contained in:
427
demo-project/首页路由梳理说明.md
Normal file
427
demo-project/首页路由梳理说明.md
Normal 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 的最佳实践
|
||||
|
||||
---
|
||||
|
||||
**首页路由梳理完成!** 🎉
|
||||
|
||||
现在路由创建流程清晰、简洁、可靠。
|
||||
|
||||
Reference in New Issue
Block a user