This commit is contained in:
2026-08-05 15:54:59 +08:00
parent eede351038
commit 4b31eb7786
43 changed files with 2117 additions and 851 deletions
+12 -3
View File
@@ -3,12 +3,14 @@ import { adminNavigation, getNavigationLeaves } from '@/core/navigation/adminNav
import router from './index'
describe('admin router access metadata', () => {
it('uses the root route only as a permission-aware entry point while retaining the dashboard prototype', () => {
it('uses the root route as a permission-aware entry point and registers the formal dashboard', () => {
const home = router.getRoutes().find((route) => route.name === 'home')
expect(home?.path).toBe('/')
expect(home?.meta.permission).toBeUndefined()
expect(router.getRoutes().some((route) => route.name === 'dashboard')).toBe(true)
const dashboard = router.getRoutes().find((route) => route.name === 'dashboard')
expect(dashboard?.path).toBe('/dashboard')
expect(dashboard?.meta.permission).toBeUndefined()
})
it('protects warehouse management with its stable list permission', () => {
@@ -22,7 +24,6 @@ describe('admin router access metadata', () => {
const expected = [
['inbound-orders', '/inbounds', 'platform:firstleg:inbound:list'],
['inbound-order-detail', '/inbounds/:inboundId', 'platform:firstleg:inbound:view'],
['fba-customer-rules', '/first-leg-pricing/customer-rules', 'firstleg:fba-pricing:rule:view'],
['fba-carrier-rates', '/first-leg-pricing/carrier-rates', 'firstleg:fba-pricing:rate:view'],
['locations', '/master-data/locations', 'platform:location:list'],
['pallets', '/master-data/pallets', 'platform:pallet:list'],
@@ -35,6 +36,14 @@ describe('admin router access metadata', () => {
}
})
it('does not register removed duplicate pricing and FBA placeholder pages', () => {
const removedRouteNames = ['fba-customer-rules', 'general-fba', 'general-voyage']
for (const name of removedRouteNames) {
expect(router.getRoutes().some((route) => route.name === name)).toBe(false)
}
})
it('protects express company records with a stable master-data permission', () => {
const route = router.getRoutes().find((item) => item.name === 'express-companies')
+38 -2
View File
@@ -1,4 +1,5 @@
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'
@@ -10,7 +11,11 @@ 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'
@@ -32,8 +37,9 @@ const router = createRouter({
path: '',
name: 'home',
component: () => import('@/app/pages/ModuleView.vue'),
meta: { title: '进入管理后台', description: '正在进入当前角色可访问的业务页面' },
meta: { title: '进入管理后台', description: '正在进入当前角色可访问的业务页面', workspaceTab: false, keepAlive: false },
},
...dashboardRoutes,
...prototypeModuleRoutes,
...customerRoutes,
...warehouseRoutes,
@@ -48,7 +54,7 @@ const router = createRouter({
path: '403',
name: 'forbidden',
component: () => import('@/app/pages/ForbiddenView.vue'),
meta: { title: '权限不足', description: '当前登录账号没有访问此页面的权限' },
meta: { title: '权限不足', description: '当前登录账号没有访问此页面的权限', workspaceTab: false, keepAlive: false },
},
],
},
@@ -56,6 +62,29 @@ const router = createRouter({
],
})
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' },
)
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()
@@ -87,6 +116,13 @@ router.beforeEach((to) => {
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 }