import '@/styles/adminDensity.css' import { createRouter, createWebHistory } from 'vue-router' import { ElMessageBox } from 'element-plus' import AppLayout from '@/app/layouts/AppLayout.vue' import { decideRouteAccess } from '@/core/routing/accessDecision' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { downloadRoutes } from '@/modules/downloads/routes' import { inboundRoutes } from '@/modules/inbounds/routes' import { unifiedOrderRoutes } from '@/modules/unified-orders/routes' import { fbaPricingRoutes } from '@/modules/fba-pricing/routes' import { iamRoutes } from '@/modules/iam/routes' import { masterDataRoutes } from '@/modules/master-data/routes' import { warehouseResourceRoutes } from '@/modules/warehouse-resources/routes' import { useAuthStore } from '@/core/auth/auth' import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork' import { canAccessWorkspaceRoute, isWorkspaceRoute, workspaceRouteId } from '@/core/workspace-tabs/routeWorkspace' import { readStoredActiveWorkspacePath, useWorkspaceTabsStore } from '@/core/workspace-tabs/workspaceTabs' import { customerRoutes } from '@/modules/customers/routes' import { dashboardRoutes } from '@/modules/dashboard/routes' import { warehouseRoutes } from '@/modules/warehouses/routes' import { prototypeModuleRoutes } from '@/prototype/routes' import { oceanBookingRoutes } from '@/modules/ocean-bookings/routes' import { truckingRoutes } from '@/modules/trucking/routes' import { feeItemRoutes } from '@/modules/fee-items/routes' import { containerLoadingRoutes } from '@/modules/container-loading/routes' import { loadingListRoutes } from '@/modules/loading-lists/routes' import { financeRoutes } from '@/modules/finance/routes' import { notificationRoutes } from '@/modules/notifications/routes' import { domesticContainerRoutes } from '@/modules/domestic-containers/routes' import { supplierRoutes } from '@/modules/suppliers/routes' import { overseasWarehouseRoutes } from '@/modules/overseas-warehouse/routes' import { overseasBusinessRoutes } from '@/modules/overseas-business/routes' import { overseasMasterDataRoutes } from '@/modules/overseas-master-data/routes' import { pdaReleaseRoutes } from '@/modules/pda-releases/routes' import { sailingCalendarRoutes } from '@/modules/sailing-calendar/routes' import { appointmentCalendarRoutes } from '@/modules/appointment-calendar/routes' import { selfOperatedRoutes } from '@/modules/self-operated/routes' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/login', name: 'login', component: () => import('@/modules/auth/pages/LoginView.vue'), meta: { title: '登录' }, }, { path: '/', component: AppLayout, meta: { requiresAuth: true }, children: [ { path: '', name: 'home', component: () => import('@/app/pages/ModuleView.vue'), meta: { title: '进入管理后台', description: '正在进入当前角色可访问的业务页面', workspaceTab: false, keepAlive: false }, }, ...dashboardRoutes, ...prototypeModuleRoutes, ...oceanBookingRoutes, ...truckingRoutes, ...containerLoadingRoutes, ...loadingListRoutes, ...financeRoutes, ...notificationRoutes, ...domesticContainerRoutes, ...supplierRoutes, ...overseasWarehouseRoutes, ...overseasBusinessRoutes, ...overseasMasterDataRoutes, ...customerRoutes, ...warehouseRoutes, ...unifiedOrderRoutes, ...inboundRoutes, ...fbaPricingRoutes, ...warehouseResourceRoutes, ...feeItemRoutes, ...masterDataRoutes, ...downloadRoutes, ...iamRoutes, ...pdaReleaseRoutes, ...sailingCalendarRoutes, ...appointmentCalendarRoutes, ...selfOperatedRoutes, { path: '403', name: 'forbidden', component: () => import('@/app/pages/ForbiddenView.vue'), meta: { title: '权限不足', description: '当前登录账号没有访问此页面的权限', workspaceTab: false, keepAlive: false }, }, ], }, { path: '/:pathMatch(.*)*', redirect: '/' }, ], }) router.beforeEach(async (to, from) => { const fromId = workspaceRouteId(from) if (!fromId || to.fullPath === from.fullPath) return true const isNonDestructiveWorkspaceSwitch = isWorkspaceRoute(to) && workspaceRouteId(to) !== fromId if (isNonDestructiveWorkspaceSwitch) return true const unsaved = useUnsavedWorkStore() const blocking = unsaved.findForWorkspace(fromId) if (blocking.length === 0) return true try { await ElMessageBox.confirm( `当前页面还有未保存内容:${[...new Set(blocking.map((entry) => entry.label))].join('、')}。继续后将放弃这些修改。`, '放弃未保存的修改?', { confirmButtonText: '放弃并继续', cancelButtonText: '继续编辑', type: 'warning', customClass: 'admin-density-confirm' }, ) unsaved.removeWorkspaces([fromId]) useWorkspaceTabsStore().bumpCacheEpoch(fromId) return true } catch { return false } }) router.beforeEach((to) => { document.title = to.meta.title ? `${to.meta.title} - 柏悦物流管理后台` : '柏悦物流管理后台' const auth = useAuthStore() if (to.name === 'login' && auth.isAuthenticated) { return { name: 'home' } } if (!auth.isAuthenticated && !to.meta.requiresAuth) return true const access = useAccessPreviewStore() if (auth.isAuthenticated) access.initializeAfterLogin() const alternativePermissions = to.meta.permissionAnyOf ?? [] const requiresRoutePermission = Boolean(to.meta.permission || alternativePermissions.length) const hasRoutePermission = !requiresRoutePermission || Boolean( (to.meta.permission && access.can(to.meta.permission)) || alternativePermissions.some((permission) => access.can(permission)), ) const decision = decideRouteAccess({ requiresAuth: to.meta.requiresAuth, isAuthenticated: auth.isAuthenticated, fullPath: to.fullPath, permission: to.meta.permission ?? alternativePermissions[0], hasPermission: hasRoutePermission, }) if (decision.type === 'login') { return { name: 'login', query: { redirect: decision.redirect } } } if (decision.type === 'forbidden') { return { name: 'forbidden', query: { from: decision.from }, replace: true } } if (to.name === 'home') { const restoredPath = auth.user?.id ? readStoredActiveWorkspacePath(auth.user.id) : null if (restoredPath) { const restoredRoute = router.resolve(restoredPath) if (canAccessWorkspaceRoute(restoredRoute, access.permissionCodes)) { return { path: restoredPath, replace: true } } } return access.firstAccessiblePath ? { path: access.firstAccessiblePath, replace: true } : { name: 'forbidden', replace: true } } return true }) export default router