Files
admin-web/src/app/router/index.ts
T

167 lines
6.8 KiB
TypeScript
Raw Normal View History

2026-09-15 09:58:15 +08:00
import '@/styles/adminDensity.css'
2026-08-03 18:49:18 +08:00
import { createRouter, createWebHistory } from 'vue-router'
2026-08-05 15:54:59 +08:00
import { ElMessageBox } from 'element-plus'
2026-08-03 18:49:18 +08:00
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'
2026-08-04 21:01:39 +08:00
import { inboundRoutes } from '@/modules/inbounds/routes'
2026-08-06 13:56:04 +08:00
import { unifiedOrderRoutes } from '@/modules/unified-orders/routes'
2026-08-04 21:01:39 +08:00
import { fbaPricingRoutes } from '@/modules/fba-pricing/routes'
2026-08-03 18:49:18 +08:00
import { iamRoutes } from '@/modules/iam/routes'
import { masterDataRoutes } from '@/modules/master-data/routes'
2026-08-04 21:01:39 +08:00
import { warehouseResourceRoutes } from '@/modules/warehouse-resources/routes'
2026-08-03 18:49:18 +08:00
import { useAuthStore } from '@/core/auth/auth'
2026-08-05 15:54:59 +08:00
import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork'
import { canAccessWorkspaceRoute, isWorkspaceRoute, workspaceRouteId } from '@/core/workspace-tabs/routeWorkspace'
import { readStoredActiveWorkspacePath, useWorkspaceTabsStore } from '@/core/workspace-tabs/workspaceTabs'
2026-08-03 18:49:18 +08:00
import { customerRoutes } from '@/modules/customers/routes'
2026-08-05 15:54:59 +08:00
import { dashboardRoutes } from '@/modules/dashboard/routes'
2026-08-03 18:49:18 +08:00
import { warehouseRoutes } from '@/modules/warehouses/routes'
import { prototypeModuleRoutes } from '@/prototype/routes'
2026-08-08 13:32:27 +08:00
import { oceanBookingRoutes } from '@/modules/ocean-bookings/routes'
import { truckingRoutes } from '@/modules/trucking/routes'
2026-08-10 15:36:53 +08:00
import { feeItemRoutes } from '@/modules/fee-items/routes'
import { containerLoadingRoutes } from '@/modules/container-loading/routes'
import { loadingListRoutes } from '@/modules/loading-lists/routes'
2026-08-11 09:25:48 +08:00
import { financeRoutes } from '@/modules/finance/routes'
2026-08-12 18:24:05 +08:00
import { notificationRoutes } from '@/modules/notifications/routes'
2026-08-13 11:57:44 +08:00
import { domesticContainerRoutes } from '@/modules/domestic-containers/routes'
import { supplierRoutes } from '@/modules/suppliers/routes'
2026-08-19 16:45:51 +08:00
import { overseasWarehouseRoutes } from '@/modules/overseas-warehouse/routes'
import { overseasBusinessRoutes } from '@/modules/overseas-business/routes'
import { overseasMasterDataRoutes } from '@/modules/overseas-master-data/routes'
2026-09-20 14:22:12 +08:00
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'
2026-08-03 18:49:18 +08:00
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'),
2026-08-05 15:54:59 +08:00
meta: { title: '进入管理后台', description: '正在进入当前角色可访问的业务页面', workspaceTab: false, keepAlive: false },
2026-08-03 18:49:18 +08:00
},
2026-08-05 15:54:59 +08:00
...dashboardRoutes,
2026-08-03 18:49:18 +08:00
...prototypeModuleRoutes,
2026-08-08 13:32:27 +08:00
...oceanBookingRoutes,
...truckingRoutes,
2026-08-10 15:36:53 +08:00
...containerLoadingRoutes,
...loadingListRoutes,
2026-08-11 09:25:48 +08:00
...financeRoutes,
2026-08-12 18:24:05 +08:00
...notificationRoutes,
2026-08-13 11:57:44 +08:00
...domesticContainerRoutes,
...supplierRoutes,
2026-08-19 16:45:51 +08:00
...overseasWarehouseRoutes,
...overseasBusinessRoutes,
...overseasMasterDataRoutes,
2026-08-03 18:49:18 +08:00
...customerRoutes,
...warehouseRoutes,
2026-08-06 13:56:04 +08:00
...unifiedOrderRoutes,
2026-08-04 21:01:39 +08:00
...inboundRoutes,
...fbaPricingRoutes,
...warehouseResourceRoutes,
2026-08-10 15:36:53 +08:00
...feeItemRoutes,
2026-08-03 18:49:18 +08:00
...masterDataRoutes,
...downloadRoutes,
...iamRoutes,
2026-09-20 14:22:12 +08:00
...pdaReleaseRoutes,
...sailingCalendarRoutes,
...appointmentCalendarRoutes,
...selfOperatedRoutes,
2026-08-03 18:49:18 +08:00
{
path: '403',
name: 'forbidden',
component: () => import('@/app/pages/ForbiddenView.vue'),
2026-08-05 15:54:59 +08:00
meta: { title: '权限不足', description: '当前登录账号没有访问此页面的权限', workspaceTab: false, keepAlive: false },
2026-08-03 18:49:18 +08:00
},
],
},
{ path: '/:pathMatch(.*)*', redirect: '/' },
],
})
2026-08-05 15:54:59 +08:00
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('、')}。继续后将放弃这些修改。`,
'放弃未保存的修改?',
2026-09-15 09:58:15 +08:00
{ confirmButtonText: '放弃并继续', cancelButtonText: '继续编辑', type: 'warning', customClass: 'admin-density-confirm' },
2026-08-05 15:54:59 +08:00
)
unsaved.removeWorkspaces([fromId])
useWorkspaceTabsStore().bumpCacheEpoch(fromId)
return true
} catch {
return false
}
})
2026-08-03 18:49:18 +08:00
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()
2026-08-03 20:50:18 +08:00
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)),
)
2026-08-03 18:49:18 +08:00
const decision = decideRouteAccess({
requiresAuth: to.meta.requiresAuth,
isAuthenticated: auth.isAuthenticated,
fullPath: to.fullPath,
2026-08-03 20:50:18 +08:00
permission: to.meta.permission ?? alternativePermissions[0],
hasPermission: hasRoutePermission,
2026-08-03 18:49:18 +08:00
})
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') {
2026-08-05 15:54:59 +08:00
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 }
}
}
2026-08-03 18:49:18 +08:00
return access.firstAccessiblePath
? { path: access.firstAccessiblePath, replace: true }
: { name: 'forbidden', replace: true }
}
return true
})
export default router