根因: router.beforeEach 在角色加载后 return true,不检查目标路由 是否在当前用户已注册的路由列表中。导致切换账户后,通过旧标签 或直接输入 URL 可访问前一个用户的页面。 修复: 在 return true 前增加 router.resolve() 检查,若目标路由 未注册(matched.length === 0)则拦截并提示无权访问。 数据库验证: 护士角色(role_id=201)确实没有住院医生工作站 (menu_id=288)的 sys_role_menu 权限,后端 getRouters 返回 正确。问题纯粹在前端路由守卫。
123 lines
3.8 KiB
JavaScript
Executable File
123 lines
3.8 KiB
JavaScript
Executable File
import router from './router'
|
|
import { ElMessage } from 'element-plus'
|
|
import NProgress from 'nprogress'
|
|
import 'nprogress/nprogress.css'
|
|
import { getToken } from '@/utils/auth'
|
|
import { isHttp, isPathMatch } from '@/utils/validate'
|
|
import { isRelogin } from '@/utils/request'
|
|
import useUserStore from '@/store/modules/user'
|
|
import useLockStore from '@/store/modules/lock'
|
|
import useSettingsStore from '@/store/modules/settings'
|
|
import usePermissionStore from '@/store/modules/permission'
|
|
import useNoticeStore from '@/store/modules/notice'
|
|
import useTagsViewStore from '@/store/modules/tagsView'
|
|
|
|
// 全局变量,用于控制公告弹窗只显示一次
|
|
let hasShownNoticePopup = false
|
|
|
|
NProgress.configure({ showSpinner: false })
|
|
|
|
const whiteList = ['/login', '/register']
|
|
|
|
const isWhiteList = (path) => {
|
|
return whiteList.some(pattern => isPathMatch(pattern, path))
|
|
}
|
|
|
|
router.beforeEach(async (to, from) => {
|
|
NProgress.start()
|
|
if (getToken()) {
|
|
to.meta.title && useSettingsStore().setTitle(to.meta.title)
|
|
const isLock = useLockStore().isLock
|
|
if (to.path === '/login') {
|
|
NProgress.done()
|
|
return { path: '/' }
|
|
}
|
|
if (isWhiteList(to.path)) {
|
|
return true
|
|
}
|
|
if (isLock && to.path !== '/lock') {
|
|
NProgress.done()
|
|
return { path: '/lock' }
|
|
}
|
|
if (!isLock && to.path === '/lock') {
|
|
NProgress.done()
|
|
return { path: '/' }
|
|
}
|
|
if (useUserStore().roles.length === 0) {
|
|
isRelogin.show = true
|
|
try {
|
|
await useUserStore().getInfo()
|
|
isRelogin.show = false
|
|
const accessRoutes = await usePermissionStore().generateRoutes()
|
|
accessRoutes.forEach(route => {
|
|
if (!isHttp(route.path)) {
|
|
if (!router.hasRoute(route.name)) {
|
|
try {
|
|
router.addRoute(route)
|
|
} catch (e) {
|
|
// 路由名重复时跳过,不影响其他路由加载
|
|
console.warn('路由添加跳过(名称重复):', route.name, e.message)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
useNoticeStore().startPolling()
|
|
useTagsViewStore().loadPersistedViews()
|
|
return { ...to, replace: true }
|
|
} catch (err) {
|
|
console.error('路由加载失败:', err)
|
|
await useUserStore().logOut()
|
|
ElMessage.error(err.message || '登录已过期')
|
|
return { path: '/login' }
|
|
}
|
|
}
|
|
// 铁律: 路由权限校验 — 目标路由必须在已注册的路由中存在
|
|
// 防止切换账户后,通过旧标签或直接输入 URL 访问无权限页面
|
|
const resolved = router.resolve(to)
|
|
if (resolved.matched.length === 0 || resolved.name === 'NotFound') {
|
|
// 路由不存在(未注册),拒绝导航
|
|
ElMessage.warning('无权访问该页面')
|
|
return { path: '/' }
|
|
}
|
|
return true
|
|
} else {
|
|
if (isWhiteList(to.path)) {
|
|
return true
|
|
}
|
|
NProgress.done()
|
|
return `/login?redirect=${to.fullPath}`
|
|
}
|
|
})
|
|
|
|
router.afterEach(() => {
|
|
NProgress.done()
|
|
|
|
// 登录成功后显示公告弹窗(仅限非登录页面且未显示过)
|
|
const token = getToken()
|
|
const isLoginPage = router.currentRoute.value.path === '/login'
|
|
|
|
if (token && !isLoginPage && !hasShownNoticePopup) {
|
|
setTimeout(() => {
|
|
showNoticePopupGlobally()
|
|
hasShownNoticePopup = true
|
|
}, 1500)
|
|
}
|
|
})
|
|
|
|
// 全局函数:显示公告弹窗
|
|
function showNoticePopupGlobally() {
|
|
try {
|
|
const layouts = document.querySelectorAll('.app-wrapper')
|
|
for (const layout of layouts) {
|
|
const noticePopupRef = layout.__vue_app__?.config.globalProperties.$refs?.noticePopupRef
|
|
if (noticePopupRef && noticePopupRef.showNotice) {
|
|
noticePopupRef.showNotice()
|
|
return
|
|
}
|
|
}
|
|
window.dispatchEvent(new CustomEvent('show-notice-popup'))
|
|
} catch (error) {
|
|
console.error('显示公告弹窗失败:', error)
|
|
}
|
|
}
|