guards.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /**
  2. * 路由守卫
  3. */
  4. import type { Router } from 'vue-router'
  5. import { useUserStore, type UserRole } from '@/store/modules/user'
  6. import { useAppStore } from '@/store/modules/app'
  7. import { showToast } from 'vant'
  8. import i18n from '@/locale'
  9. const { t } = i18n.global
  10. /**
  11. * 前置守卫
  12. */
  13. export function setupRouterGuards(router: Router) {
  14. router.beforeEach(async (to, from, next) => {
  15. const userStore = useUserStore()
  16. const appStore = useAppStore()
  17. // 0. 模式守卫
  18. if (to.path === '/index') {
  19. // 进入首页,切换为平台模式
  20. if (!appStore.isPlatformMode) {
  21. appStore.enterPlatformMode()
  22. }
  23. } else if (to.path === '/menu') {
  24. // 进入菜单页,检查是否有店铺上下文
  25. const queryShopId = to.query.shopId as string
  26. // 如果URL有shopId,优先使用(处理直接访问)
  27. if (queryShopId && (!appStore.currentShop || appStore.currentShop.id !== queryShopId)) {
  28. // TODO: 这里应该从API加载店铺信息
  29. // 暂时只设置简单的上下文
  30. appStore.enterShopMode({ id: queryShopId, name: '加载中...', status: 'operating', companyId: '', address: '' })
  31. }
  32. // 如果没有context且没有query参数,重定向回首页
  33. else if (!appStore.currentShop) {
  34. showToast('请先选择店铺')
  35. next('/index')
  36. return
  37. }
  38. }
  39. // 1. 检查是否需要登录
  40. // guest 用户允许访问菜单和购物车,但不能访问需要登录的页面
  41. if (to.meta.requiresAuth) {
  42. // 检查是否登录或有有效会话
  43. if (!userStore.isLogin && !userStore.isGuest) {
  44. // 特殊处理:如果是Shop Mode下的购物车/结算,提示登录
  45. if (appStore.isShopMode && ['/cart', '/payment'].some(p => to.path.startsWith(p))) {
  46. // 允许访问,但在页面内处理登录逻辑
  47. // 或者跳转登录
  48. showToast(t('common.pleaseLogin'))
  49. next({
  50. path: '/login',
  51. query: { redirect: to.fullPath }
  52. })
  53. return
  54. }
  55. showToast(t('common.pleaseLogin'))
  56. next({
  57. path: '/login',
  58. query: { redirect: to.fullPath }
  59. })
  60. return
  61. }
  62. // guest 用户尝试访问需要登录的页面
  63. if (userStore.isGuest && to.meta.requiresAuth) {
  64. // 允许 guest 访问部分页面(订单创建相关)
  65. const guestAllowedPaths = ['/cart', '/payment', '/order/detail']
  66. if (!guestAllowedPaths.some(p => to.path.startsWith(p))) {
  67. showToast(t('common.pleaseLogin'))
  68. next({
  69. path: '/login',
  70. query: { redirect: to.fullPath }
  71. })
  72. return
  73. }
  74. }
  75. }
  76. // 2. 检查角色要求
  77. if (to.meta.requiresRole) {
  78. const requiredRoles = Array.isArray(to.meta.requiresRole)
  79. ? to.meta.requiresRole as UserRole[]
  80. : [to.meta.requiresRole] as UserRole[]
  81. const hasRole = requiredRoles.some(role =>
  82. userStore.availableRoles.includes(role)
  83. )
  84. if (!hasRole) {
  85. showToast(t('role.noPermission'))
  86. next(false)
  87. return
  88. }
  89. // 3. 自动切换角色(如果当前角色不匹配)
  90. if (!requiredRoles.includes(userStore.currentRole)) {
  91. // 如果当前角色不匹配,但用户拥有所需角色,自动切换
  92. const targetRole = requiredRoles.find(role =>
  93. userStore.availableRoles.includes(role)
  94. )
  95. if (targetRole) {
  96. userStore.setCurrentRole(targetRole)
  97. }
  98. }
  99. }
  100. // 4. 检查权限(可选)
  101. if (to.meta.requiresPermission) {
  102. const permission = to.meta.requiresPermission as string
  103. if (!userStore.can(permission)) {
  104. showToast(t('role.noPermission'))
  105. next(false)
  106. return
  107. }
  108. }
  109. next()
  110. })
  111. router.afterEach((to) => {
  112. // 设置页面标题(支持国际化)
  113. if (to.meta.title) {
  114. const title = typeof to.meta.title === 'string'
  115. ? t(to.meta.title)
  116. : to.meta.title
  117. document.title = typeof title === 'string' ? title : 'FastEat'
  118. }
  119. })
  120. }