接入前端登录权限底座

This commit is contained in:
andy
2026-07-09 20:42:16 +08:00
parent c190d5e3a8
commit e49b2ccc1d
27 changed files with 1745 additions and 68 deletions

View File

@@ -1,11 +1,42 @@
import { createRouter, createWebHistory } from 'vue-router'
import { createRouter, createWebHistory, type RouteLocationNormalized } from 'vue-router'
import { useAuthStore } from '@/stores/authStore'
import type { AuthPermissionCode } from '@/types/auth'
type AuthStore = ReturnType<typeof useAuthStore>
type AppRouteMeta = {
public?: boolean
layout?: 'plain' | 'app'
permission?: AuthPermissionCode
}
export const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/reservation/orders',
redirect: () => {
const authStore = useAuthStore()
return authStore.firstMenuPath ?? '/reservation/orders'
},
},
{
path: '/login',
name: 'login',
component: () => import('@/views/auth/LoginView.vue'),
meta: {
public: true,
layout: 'plain',
titleKey: 'auth.loginTitle',
},
},
{
path: '/unauthorized',
name: 'unauthorized',
component: () => import('@/views/auth/UnauthorizedView.vue'),
meta: {
titleKey: 'auth.unauthorizedTitle',
},
},
{
path: '/reservation/workbench',
@@ -17,6 +48,7 @@ export const router = createRouter({
component: () => import('@/views/reservation/ReservationOrderListView.vue'),
meta: {
titleKey: 'nav.orders',
permission: 'RESERVATION_ORDER_READ',
},
},
{
@@ -25,6 +57,7 @@ export const router = createRouter({
component: () => import('@/views/reservation/ReservationTaskListView.vue'),
meta: {
titleKey: 'nav.taskQueue',
permission: 'RESERVATION_TASK_READ',
},
},
{
@@ -33,6 +66,7 @@ export const router = createRouter({
component: () => import('@/views/reservation/ReservationOrderDetailView.vue'),
meta: {
titleKey: 'nav.orderDetail',
permission: 'RESERVATION_ORDER_READ',
},
},
{
@@ -41,6 +75,7 @@ export const router = createRouter({
component: () => import('@/views/reservation/ReservationTaskDetailView.vue'),
meta: {
titleKey: 'nav.taskDetail',
permission: 'RESERVATION_TASK_READ',
},
},
{
@@ -49,6 +84,7 @@ export const router = createRouter({
component: () => import('@/views/reservation/ReservationSourceMessageConversationView.vue'),
meta: {
titleKey: 'nav.sourceMessageConversation',
permission: 'SOURCE_MESSAGE_ORIGINAL_READ',
},
},
{
@@ -57,7 +93,55 @@ export const router = createRouter({
component: () => import('@/views/debug/DebugEmlSuperAgentRunView.vue'),
meta: {
titleKey: 'debugEml.title',
permission: 'SYSTEM_DEBUG_EML_RUN',
},
},
],
})
router.beforeEach(createAuthGuard())
export function createAuthGuard(resolveAuthStore: () => AuthStore = () => useAuthStore()) {
return async (to: RouteLocationNormalized) => {
const authStore = resolveAuthStore()
const meta = to.meta as AppRouteMeta
if (!authStore.restoreAttempted && authStore.hasStoredToken()) {
await authStore.restoreFromSession()
}
if (meta.public) {
if (to.name === 'login' && authStore.isAuthenticated) {
return safeRedirectPath(to.query.redirect) ?? authStore.firstMenuPath ?? '/reservation/orders'
}
return true
}
if (!authStore.isAuthenticated) {
return {
path: '/login',
query: {
redirect: to.fullPath,
},
}
}
if (meta.permission && !authStore.hasPermission(meta.permission)) {
return {
name: 'unauthorized',
}
}
return true
}
}
function safeRedirectPath(redirect: unknown): string | null {
if (typeof redirect !== 'string') {
return null
}
if (!redirect.startsWith('/') || redirect.startsWith('//') || redirect.startsWith('/login')) {
return null
}
return redirect
}