修改菜单问题
This commit is contained in:
@@ -9,7 +9,8 @@ VITE_PORT = 3006
|
|||||||
VITE_BASE_URL = /
|
VITE_BASE_URL = /
|
||||||
|
|
||||||
# API 地址前缀
|
# API 地址前缀
|
||||||
VITE_API_URL = https://mzl.dev.hnxinyouhe.com
|
# VITE_API_URL = https://mzl.dev.hnxinyouhe.com
|
||||||
|
VITE_API_URL = https://x-server.hnxinyouhe.com
|
||||||
|
|
||||||
# 权限模式( frontend | backend )
|
# 权限模式( frontend | backend )
|
||||||
VITE_ACCESS_MODE = frontend
|
VITE_ACCESS_MODE = frontend
|
||||||
|
|||||||
+2
-1
@@ -7,7 +7,8 @@ VITE_BASE_URL = /
|
|||||||
# VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
|
# VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
|
||||||
# VITE_API_URL = http://219.157.255.213:25702
|
# VITE_API_URL = http://219.157.255.213:25702
|
||||||
# VITE_API_URL = https://mzl.dev.hnxinyouhe.com
|
# VITE_API_URL = https://mzl.dev.hnxinyouhe.com
|
||||||
VITE_API_URL = https://fdesk.hnxinyouhe.com
|
# VITE_API_URL = https://fdesk.hnxinyouhe.com
|
||||||
|
VITE_API_URL = https://x-server.hnxinyouhe.com
|
||||||
|
|
||||||
|
|
||||||
# Delete console
|
# Delete console
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,8 @@
|
|||||||
VITE_BASE_URL = /
|
VITE_BASE_URL = /
|
||||||
|
|
||||||
# API 地址前缀
|
# API 地址前缀
|
||||||
VITE_API_URL = https://fdesk.hnxinyouhe.com
|
# VITE_API_URL = https://fdesk.hnxinyouhe.com
|
||||||
|
VITE_API_URL = https://x-server.hnxinyouhe.com
|
||||||
|
|
||||||
# Delete console
|
# Delete console
|
||||||
VITE_DROP_CONSOLE = true
|
VITE_DROP_CONSOLE = true
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# 菜单权限控制问题修复方案
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
在后端控制模式下,当普通用户登录后再退出登录管理员时,管理员特有的菜单会出现404错误。
|
||||||
|
|
||||||
|
## 问题原因
|
||||||
|
|
||||||
|
1. **路由注册状态未正确重置**:`isRouteRegistered` 标志位在用户切换时没有正确重置
|
||||||
|
2. **动态路由清理不完整**:`resetRouterState` 函数只清理了带有 `meta.dynamic` 标记的路由
|
||||||
|
3. **工作台标签页验证时机问题**:`validateWorktabs` 在路由重新注册后执行,但可能没有正确清理无效的标签页
|
||||||
|
4. **用户信息变化检测缺失**:没有检测用户切换事件,导致路由状态没有及时更新
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
### 1. 改进路由状态重置 (`src/router/guards/beforeEach.ts`)
|
||||||
|
|
||||||
|
- 修改 `resetRouterState` 函数,清理所有动态注册的路由(不仅仅是带有dynamic标记的)
|
||||||
|
- 添加工作台标签页的清理
|
||||||
|
- 使用菜单store的 `clearMenuList` 方法
|
||||||
|
|
||||||
|
### 2. 添加用户切换检测 (`src/router/guards/beforeEach.ts`)
|
||||||
|
|
||||||
|
- 在 `handleRouteGuard` 函数中添加用户信息变化检测
|
||||||
|
- 使用 `sessionStorage` 缓存用户信息,检测用户切换
|
||||||
|
- 当检测到用户信息变化时,强制重置路由状态
|
||||||
|
|
||||||
|
### 3. 改进菜单API (`src/api/menuApi.ts`)
|
||||||
|
|
||||||
|
- 添加用户登录状态验证
|
||||||
|
- 改进菜单数据验证逻辑
|
||||||
|
- 添加更详细的错误处理和日志
|
||||||
|
|
||||||
|
### 4. 完善用户登出逻辑 (`src/store/modules/user.ts`)
|
||||||
|
|
||||||
|
- 在 `logOut` 方法中添加更完整的状态清理
|
||||||
|
- 清理用户信息缓存
|
||||||
|
- 确保工作台标签页被正确清理
|
||||||
|
|
||||||
|
### 5. 增强菜单Store (`src/store/modules/menu.ts`)
|
||||||
|
|
||||||
|
- 添加 `clearMenuList` 方法用于清理菜单数据
|
||||||
|
- 提供更完整的状态管理
|
||||||
|
|
||||||
|
### 6. 改进错误处理 (`src/router/guards/beforeEach.ts`)
|
||||||
|
|
||||||
|
- 修改 `handleMenuError` 函数,确保在菜单处理失败时正确清理所有状态
|
||||||
|
- 添加更详细的状态清理逻辑
|
||||||
|
|
||||||
|
## 测试工具
|
||||||
|
|
||||||
|
创建了 `src/utils/menuPermissionTest.ts` 测试工具,包含:
|
||||||
|
|
||||||
|
- `testMenuPermissionSwitch()`: 测试菜单权限切换的正确性
|
||||||
|
- `simulateUserSwitch()`: 模拟用户切换过程
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 在浏览器控制台中调用 `testMenuPermissionSwitch()` 检查当前状态
|
||||||
|
2. 在用户切换后调用 `simulateUserSwitch()` 模拟切换过程
|
||||||
|
3. 观察控制台输出,确认路由和菜单状态正确
|
||||||
|
|
||||||
|
## 预期效果
|
||||||
|
|
||||||
|
修复后,当用户切换时:
|
||||||
|
|
||||||
|
1. 所有动态路由会被正确清理
|
||||||
|
2. 菜单数据会被重新加载
|
||||||
|
3. 工作台标签页会被验证和清理
|
||||||
|
4. 不会出现404错误
|
||||||
|
5. 用户权限会正确应用
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 确保后端返回的菜单数据与用户权限匹配
|
||||||
|
- 在开发环境中可以使用测试工具验证修复效果
|
||||||
|
- 建议在生产环境中添加更多的日志记录用于问题排查
|
||||||
+19
-5
@@ -11,23 +11,37 @@ export const menuService = {
|
|||||||
async getMenuList(delay = 300): Promise<MenuResponse> {
|
async getMenuList(delay = 300): Promise<MenuResponse> {
|
||||||
try {
|
try {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 验证用户登录状态
|
||||||
|
if (!userStore.isLogin) {
|
||||||
|
throw new Error('用户未登录')
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟接口返回的菜单数据
|
// 模拟接口返回的菜单数据
|
||||||
const menuData = userStore.info.menu
|
const menuData = userStore.info.menu
|
||||||
if(menuData.length==0){
|
|
||||||
|
// 验证菜单数据
|
||||||
|
if (!menuData || !Array.isArray(menuData) || menuData.length === 0) {
|
||||||
ElMessage.error('暂无系统菜单权限')
|
ElMessage.error('暂无系统菜单权限')
|
||||||
|
return { menuList: [] }
|
||||||
}
|
}
|
||||||
const menuData1 = asyncRoutes
|
|
||||||
// 处理菜单数据
|
// 处理菜单数据
|
||||||
// const menuList1 = menuData1.map((route) => menuDataToRouter(route))
|
|
||||||
const menuList = menuData.map((route: any) => menuDataToRouter(route))
|
const menuList = menuData.map((route: any) => menuDataToRouter(route))
|
||||||
// console.log(menuList1)
|
|
||||||
// console.log(menuList)
|
// 验证处理后的菜单列表
|
||||||
|
if (!menuList || menuList.length === 0) {
|
||||||
|
ElMessage.error('菜单数据处理失败')
|
||||||
|
return { menuList: [] }
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟接口延迟
|
// 模拟接口延迟
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay))
|
await new Promise((resolve) => setTimeout(resolve, delay))
|
||||||
|
|
||||||
return { menuList }
|
return { menuList }
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('获取菜单失败:', error)
|
||||||
throw error instanceof Error ? error : new Error('获取菜单失败')
|
throw error instanceof Error ? error : new Error('获取菜单失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,7 +197,22 @@ async function registerAndStoreMenu(
|
|||||||
*/
|
*/
|
||||||
function handleMenuError(error: unknown): void {
|
function handleMenuError(error: unknown): void {
|
||||||
console.error('菜单处理失败:', error)
|
console.error('菜单处理失败:', error)
|
||||||
useUserStore().logOut()
|
|
||||||
|
// 清理所有状态
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const menuStore = useMenuStore()
|
||||||
|
const worktabStore = useWorktabStore()
|
||||||
|
|
||||||
|
// 清空菜单和工作台
|
||||||
|
menuStore.clearMenuList()
|
||||||
|
worktabStore.clearAll()
|
||||||
|
|
||||||
|
// 重置路由注册状态
|
||||||
|
isRouteRegistered.value = false
|
||||||
|
|
||||||
|
// 登出用户
|
||||||
|
userStore.logOut()
|
||||||
|
|
||||||
throw error instanceof Error ? error : new Error('获取菜单列表失败,请重新登录')
|
throw error instanceof Error ? error : new Error('获取菜单列表失败,请重新登录')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,13 +248,34 @@ function isValidMenuList(menuList: AppRouteRecord[]): boolean {
|
|||||||
*/
|
*/
|
||||||
export function resetRouterState(router: Router): void {
|
export function resetRouterState(router: Router): void {
|
||||||
isRouteRegistered.value = false
|
isRouteRegistered.value = false
|
||||||
// 清理动态注册的路由
|
|
||||||
|
// 清理所有动态注册的路由(不仅仅是带有dynamic标记的)
|
||||||
|
const routesToRemove: string[] = []
|
||||||
router.getRoutes().forEach((route) => {
|
router.getRoutes().forEach((route) => {
|
||||||
if (route.meta?.dynamic) {
|
// 排除静态路由和登录页面
|
||||||
router.removeRoute(route.name as string)
|
if (route.name &&
|
||||||
|
route.name !== 'Login' &&
|
||||||
|
route.name !== 'Exception404' &&
|
||||||
|
route.name !== 'Exception500' &&
|
||||||
|
!route.path.startsWith('/exception/')) {
|
||||||
|
routesToRemove.push(route.name as string)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 移除动态路由
|
||||||
|
routesToRemove.forEach((routeName) => {
|
||||||
|
try {
|
||||||
|
router.removeRoute(routeName)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`移除路由失败: ${routeName}`, error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 清空菜单数据
|
// 清空菜单数据
|
||||||
const menuStore = useMenuStore()
|
const menuStore = useMenuStore()
|
||||||
menuStore.setMenuList([])
|
menuStore.clearMenuList()
|
||||||
|
|
||||||
|
// 清空工作台标签页
|
||||||
|
const worktabStore = useWorktabStore()
|
||||||
|
worktabStore.clearAll()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ export const useMenuStore = defineStore('menuStore', () => {
|
|||||||
const setMenuList = (list: AppRouteRecord[]) => (menuList.value = list)
|
const setMenuList = (list: AppRouteRecord[]) => (menuList.value = list)
|
||||||
|
|
||||||
const setMenuWidth = (width: string) => (menuWidth.value = width)
|
const setMenuWidth = (width: string) => (menuWidth.value = width)
|
||||||
|
|
||||||
|
const clearMenuList = () => {
|
||||||
|
menuList.value = []
|
||||||
|
menuWidth.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuList,
|
menuList,
|
||||||
menuWidth,
|
menuWidth,
|
||||||
setMenuList,
|
setMenuList,
|
||||||
setMenuWidth
|
setMenuWidth,
|
||||||
|
clearMenuList
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,9 +110,18 @@ export const useUserStore = defineStore(
|
|||||||
accessToken.value = ''
|
accessToken.value = ''
|
||||||
refreshToken.value = ''
|
refreshToken.value = ''
|
||||||
if (refreshTimeout) clearTimeout(refreshTimeout) // 清理定时器
|
if (refreshTimeout) clearTimeout(refreshTimeout) // 清理定时器
|
||||||
useWorktabStore().opened = []
|
|
||||||
|
// 清理工作台标签页
|
||||||
|
useWorktabStore().clearAll()
|
||||||
|
|
||||||
|
// 清理sessionStorage中的用户信息缓存
|
||||||
|
sessionStorage.removeItem('lastUserInfo')
|
||||||
sessionStorage.removeItem('iframeRoutes')
|
sessionStorage.removeItem('iframeRoutes')
|
||||||
|
|
||||||
|
// 重置路由状态
|
||||||
resetRouterState(router)
|
resetRouterState(router)
|
||||||
|
|
||||||
|
// 跳转到登录页
|
||||||
router.push(RoutesAlias.Login)
|
router.push(RoutesAlias.Login)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,15 @@ axiosInstance.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
const { code, msg } = error.response.data
|
||||||
return Promise.reject(handleError(error))
|
switch (code) {
|
||||||
|
case ApiStatus.unauthorized:
|
||||||
|
logOut()
|
||||||
|
throw new HttpError(msg || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
|
||||||
|
default:
|
||||||
|
throw new HttpError(msg || $t('httpMsg.requestFailed'), code)
|
||||||
|
}
|
||||||
|
// return Promise.reject(handleError(error))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,2 @@
|
|||||||
// ANSI 转义码生成网站 https://patorjk.com/software/taag/#p=display&f=Big&t=ABB%0A
|
// ANSI 转义码生成网站 https://patorjk.com/software/taag/#p=display&f=Big&t=ABB%0A
|
||||||
const asciiArt = `
|
console.log('')
|
||||||
\x1b[32m✨ 你好!欢迎使用 Art Design Pro!
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[36m💡 如果您觉得项目对您有帮助,请点击下面的链接为我点个 ★Star 支持一下!祝您使用愉快!
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[33m🌟 GitHub: https://github.com/Daymychen/art-design-pro
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[31m✨ 技术支持(QQ群): 821834289,如果你有任何问题,请加入QQ群,我们会在第一时间为你解答!
|
|
||||||
\x1b[0m
|
|
||||||
|
|
||||||
\x1b[36m _ _______ _________ ______ ________ ______ _____ ______ ____ _____ _______ _______ ___
|
|
||||||
\x1b[36m / \\ |_ __ \\ | _ _ | |_ _ \`.|_ __ |.' ____ \\ |_ _|.' ___ ||_ \\|_ _| |_ __ \\|_ __ \\ .' \`.
|
|
||||||
\x1b[36m / _ \\ | |__) ||_/ | | \\_| | | \`. \\ | |_ \\_|| (___ \\_| | | / .' \\_| | \\ | | | |__) | | |__) | / .-. \\
|
|
||||||
\x1b[36m / ___ \\ | __ / | | | | | | | _| _ _.____\`. | | | | ____ | |\\ \\| | | ___/ | __ / | | | |
|
|
||||||
\x1b[36m _/ / \\ \\_ _| | \\ \\_ _| |_ _| |_.' /_| |__/ || \\____) | _| |_\\ \`.___] |_| |_\\ |_ _| |_ _| | \\ \\_\\ \`-' /
|
|
||||||
\x1b[36m|____| |____||____| |___||_____| |______.'|________| \\______.'|_____|\\\`._____.'|_____|\\____| |_____| |____| |___|\\\`.___.'
|
|
||||||
\x1b[0m
|
|
||||||
`
|
|
||||||
|
|
||||||
console.log(asciiArt)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user