This commit is contained in:
2026-08-04 21:01:39 +08:00
parent 88bd939170
commit 5cb1ca307d
77 changed files with 9914 additions and 131 deletions
+25
View File
@@ -129,6 +129,31 @@ describe('admin auth session', () => {
expect(auth.isAuthenticated).toBe(true)
})
it('refreshes a valid persisted session so newly granted permissions appear after reload', async () => {
localStorage.setItem('cc-admin-token', token())
localStorage.setItem('cc-admin-refresh-token', 'refresh-old')
localStorage.setItem('cc-admin-permission-codes', '["platform:warehouse:list"]')
const refreshedToken = token({
permissions: ['platform:warehouse:list', 'platform:firstleg:inbound:list'],
})
mocks.post.mockResolvedValue(loginResponse(refreshedToken, 'refresh-rotated'))
mocks.listCurrentPlatformPermissionCodes.mockResolvedValue([
'platform:warehouse:list',
'platform:firstleg:inbound:list',
])
const auth = useAuthStore()
await expect(auth.restoreSession()).resolves.toBe(true)
expect(mocks.post).toHaveBeenCalledWith('/auth/refresh', { refreshToken: 'refresh-old' })
expect([...auth.permissionCodes]).toEqual([
'platform:warehouse:list',
'platform:firstleg:inbound:list',
])
expect(localStorage.getItem('cc-admin-permission-codes'))
.toBe('["platform:warehouse:list","platform:firstleg:inbound:list"]')
})
it('does not let an in-flight refresh restore a session after logout', async () => {
localStorage.setItem('cc-admin-token', token())
localStorage.setItem('cc-admin-refresh-token', 'refresh-old')
+9 -1
View File
@@ -162,7 +162,15 @@ export const useAuthStore = defineStore('admin-auth', () => {
}
async function restoreSession() {
if (isAuthenticated.value) return true
if (isAuthenticated.value) {
if (!refreshToken.value) return true
try {
await refreshSession()
return true
} catch {
return false
}
}
if (!refreshToken.value) return false
try {
await refreshSession()
+12 -1
View File
@@ -8,15 +8,26 @@ import {
} from './adminNavigation'
describe('adminNavigation', () => {
it('keeps only implemented phase-one menus visible while retaining prototype nodes as comments', () => {
it('exposes implemented phase-one and phase-two menus while retaining future prototype nodes as comments', () => {
expect(adminNavigation.map((item) => item.title)).toEqual([
'客户管理',
'国内仓',
'头程订单',
'头程计费',
'通用',
'系统管理',
])
expect(adminNavigation[0].children?.map((item) => item.title)).toEqual(['客户列表'])
expect(adminNavigation.find((item) => item.key === 'domestic-warehouse')?.children?.map((item) => item.title)).toEqual([
'仓库管理',
'入库单',
'库位管理',
'托盘管理',
])
expect(adminNavigation.find((item) => item.key === 'first-leg-pricing')?.children?.map((item) => item.title)).toEqual([
'客户计费规则',
'船司 FBA 价格',
])
expect(adminNavigation.find((item) => item.key === 'general')?.children?.map((item) => item.title)).toContain('快递公司')
expect(adminNavigation.find((item) => item.key === 'general')?.children?.map((item) => item.title)).toContain('下载中心')
expect(adminNavigation.find((item) => item.key === 'system')?.children?.map((item) => item.title)).toEqual([
+14 -1
View File
@@ -3,6 +3,7 @@ import {
Building2,
ContactRound,
ClipboardList,
CircleDollarSign,
Database,
Download,
KeyRound,
@@ -52,7 +53,9 @@ export const adminNavigation: AdminNavigationNode[] = [
iconColor: '#52c41a',
children: [
{ key: 'warehouses', title: '仓库管理', icon: WarehouseIcon, path: '/warehouses', permission: 'platform:warehouse:list' },
// { key: 'inbound-list', title: '入库单列表', icon: ClipboardList, path: '/prototype/domestic-warehouse/inbound-list' },
{ key: 'inbound-list', title: '入库单', icon: ClipboardList, path: '/inbounds', permission: 'platform:firstleg:inbound:list' },
{ key: 'locations', title: '库位管理', icon: WarehouseIcon, path: '/master-data/locations', permission: 'platform:location:list' },
{ key: 'pallets', title: '托盘管理', icon: WarehouseIcon, path: '/master-data/pallets', permission: 'platform:pallet:list' },
// { key: 'inbound-dedup', title: '入库单测量', icon: ClipboardList, path: '/prototype/domestic-warehouse/inbound-dedup' },
// { key: 'inbound-measure', title: '测量数据审核', icon: FileCheck2, path: '/prototype/domestic-warehouse/inbound-measure' },
// { key: 'inbound-receive', title: '接收审核', icon: FileCheck2, path: '/prototype/domestic-warehouse/inbound-receive' },
@@ -68,6 +71,16 @@ export const adminNavigation: AdminNavigationNode[] = [
path: '/orders/first-leg',
permission: 'platform:order:list',
},
{
key: 'first-leg-pricing',
title: '头程计费',
icon: CircleDollarSign,
iconColor: '#13a8a8',
children: [
{ key: 'fba-customer-rules', title: '客户计费规则', icon: Database, path: '/first-leg-pricing/customer-rules', permission: 'firstleg:fba-pricing:rule:view' },
{ key: 'fba-carrier-rates', title: '船司 FBA 价格', icon: CircleDollarSign, path: '/first-leg-pricing/carrier-rates', permission: 'firstleg:fba-pricing:rate:view' },
],
},
/* {
key: 'overseas-delivery',
title: '海外派送',