fix(admin): restore manifest menu and overseas container access
This commit is contained in:
@@ -39,11 +39,11 @@ describe('ContainerDetailView customs integration', () => {
|
||||
expect(source).toContain(':sections="[\'pda\']"')
|
||||
})
|
||||
|
||||
it('keeps the confirmed loading list inside the domestic container detail only', () => {
|
||||
it('keeps the container detail entry alongside the standalone manifest', () => {
|
||||
expect(routeSource).toContain("name: 'domestic-container-detail'")
|
||||
expect(routeSource).toContain("name: 'overseas-container-detail'")
|
||||
expect(loadingRouteSource).not.toContain("name: 'loading-lists'")
|
||||
expect(navigationSource).not.toContain("title: '装柜清单'")
|
||||
expect(loadingRouteSource).toContain("name: 'loading-lists'")
|
||||
expect(navigationSource).toContain("title: '装柜清单'")
|
||||
expect(source).toContain('<el-tabs v-model="activeTab"')
|
||||
expect(source).toContain("v-if=\"isDomestic && activeTab === 'loading-list'\"")
|
||||
expect(source).toContain("loadingDetail.value = await getConfirmedLoadingList(aggregate.loadingPlanId, resource.warehouseId)")
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import ContainerWorkbenchView from './ContainerWorkbenchView.vue'
|
||||
|
||||
const mocks = vi.hoisted(() => ({ permissions: new Set<string>(), push: vi.fn() }))
|
||||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (permission: string) => mocks.permissions.has(permission) }) }))
|
||||
vi.mock('vue-router', () => ({ useRouter: () => ({ push: mocks.push }) }))
|
||||
vi.mock('./ContainersView.vue', () => ({ default: { props: ['workbench'], template: '<div data-testid="workbench">{{ workbench }}</div>' } }))
|
||||
|
||||
function render(stage: 'DOMESTIC' | 'OVERSEAS' = 'DOMESTIC') {
|
||||
return mount(ContainerWorkbenchView, { props: { stage }, global: { stubs: { ElButton: { template: '<button><slot /></button>' } } } })
|
||||
}
|
||||
|
||||
describe('container stage entry permissions', () => {
|
||||
beforeEach(() => { mocks.permissions.clear(); mocks.push.mockReset() })
|
||||
it('opens overseas operations for an overseas-only user entering the common menu', () => {
|
||||
mocks.permissions.add('platform:firstleg:terminal-drayage:list')
|
||||
const wrapper = render()
|
||||
expect(wrapper.get('[data-testid="workbench"]').text()).toBe('OVERSEAS')
|
||||
expect(wrapper.text()).not.toContain('国内装柜')
|
||||
})
|
||||
it('preserves a direct overseas visit and allows switching back to domestic', async () => {
|
||||
mocks.permissions.add('platform:firstleg:terminal-drayage:list')
|
||||
mocks.permissions.add('platform:firstleg:container:list')
|
||||
const wrapper = render('OVERSEAS')
|
||||
expect(wrapper.get('[data-testid="workbench"]').text()).toBe('OVERSEAS')
|
||||
await wrapper.findAll('button')[0]!.trigger('click')
|
||||
expect(mocks.push).toHaveBeenCalledWith({ name: 'domestic-container-workbench' })
|
||||
})
|
||||
it('does not mount data consumers without either list permission', () => {
|
||||
expect(render().find('[data-testid="workbench"]').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import '@/styles/adminDensity.css'
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import ContainersView from './ContainersView.vue'
|
||||
|
||||
const props = defineProps<{ stage: 'DOMESTIC' | 'OVERSEAS' }>()
|
||||
const access = useAccessPreviewStore()
|
||||
const router = useRouter()
|
||||
const canDomestic = computed(() => access.can('platform:firstleg:container:list'))
|
||||
const canOverseas = computed(() => access.can('platform:firstleg:terminal-drayage:list'))
|
||||
const current = computed(() => props.stage === 'DOMESTIC' && !canDomestic.value && canOverseas.value ? 'OVERSEAS' : props.stage)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="admin-density">
|
||||
<nav class="density-status-tabs" aria-label="货柜业务阶段">
|
||||
<el-button v-if="canDomestic" :class="{ 'is-active': current === 'DOMESTIC' }" :aria-current="current === 'DOMESTIC' ? 'page' : undefined" @click="router.push({ name: 'domestic-container-workbench' })">📦 国内装柜</el-button>
|
||||
<el-button v-if="canOverseas" :class="{ 'is-active': current === 'OVERSEAS' }" :aria-current="current === 'OVERSEAS' ? 'page' : undefined" @click="router.push({ name: 'overseas-container-workbench' })">🚢 海外清关提柜</el-button>
|
||||
</nav>
|
||||
<ContainersView v-if="canDomestic || canOverseas" :key="current" :workbench="current" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -2,7 +2,7 @@
|
||||
import '@/styles/adminDensity.css'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import ContainerSimulationCreate from '../components/ContainerSimulationCreate.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
@@ -46,6 +46,7 @@ type ContainerForm = ContainerSaveRequest & Pick<ContainerBookingResource, 'cabi
|
||||
const props = withDefaults(defineProps<{ workbench?: 'DOMESTIC' | 'OVERSEAS' }>(), { workbench: 'DOMESTIC' })
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const access = useAccessPreviewStore()
|
||||
const rows = ref<ContainerWorkbenchRow[]>([])
|
||||
const customers = ref<PlatformTenantResource[]>([])
|
||||
@@ -67,7 +68,7 @@ const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
const scope = ref<'ACTIVE' | 'HISTORY'>('ACTIVE')
|
||||
const filters = ref<Filters>({ customerId: '', keyword: '', containerType: '', status: '', sailingRange: [] })
|
||||
const filters = ref<Filters>({ customerId: '', keyword: typeof route.query.keyword === 'string' ? route.query.keyword : '', containerType: '', status: '', sailingRange: [] })
|
||||
const applied = ref<Filters>({ ...filters.value })
|
||||
const detail = ref<ContainerBookingResource | null>(null)
|
||||
const editorOpen = ref(false)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import ContainersView from './ContainersView.vue'
|
||||
import ContainerWorkbenchView from './ContainerWorkbenchView.vue'
|
||||
</script>
|
||||
|
||||
<template><ContainersView workbench="DOMESTIC" /></template>
|
||||
<template><ContainerWorkbenchView stage="DOMESTIC" /></template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import ContainersView from './ContainersView.vue'
|
||||
import ContainerWorkbenchView from './ContainerWorkbenchView.vue'
|
||||
</script>
|
||||
|
||||
<template><ContainersView workbench="OVERSEAS" /></template>
|
||||
<template><ContainerWorkbenchView stage="OVERSEAS" /></template>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const containerLoadingRoutes = [
|
||||
path: 'container-loading/domestic',
|
||||
name: 'domestic-container-workbench',
|
||||
component: () => import('./pages/DomesticContainersView.vue'),
|
||||
meta: { showPageHeading: false, title: '货柜列表', description: '管理国内送柜、装柜、出口报关与出仓', permission: 'platform:firstleg:container:list' },
|
||||
meta: { showPageHeading: false, title: '货柜列表', description: '管理国内装柜、海外清关提柜与到仓', permission: 'platform:firstleg:container:list', permissionAnyOf: ['platform:firstleg:terminal-drayage:list'] },
|
||||
},
|
||||
{
|
||||
path: 'container-loading/domestic/:containerId',
|
||||
|
||||
Reference in New Issue
Block a user