diff --git a/src/modules/overseas-warehouse/pages/OutboundDetailView.test.ts b/src/modules/overseas-warehouse/pages/OutboundDetailView.test.ts index 1aed24a9..8209e1a3 100644 --- a/src/modules/overseas-warehouse/pages/OutboundDetailView.test.ts +++ b/src/modules/overseas-warehouse/pages/OutboundDetailView.test.ts @@ -4,11 +4,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { getOutbound } from '../api/outbounds' import OutboundDetailView from './OutboundDetailView.vue' import StatusBadge from '@/shared/components/StatusBadge.vue' +import { getPlatformWarehouse } from '@/modules/warehouses' const canPermission = vi.hoisted(() => vi.fn((_code: string) => false)) vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) })) vi.mock('vue-router', () => ({ useRoute: () => ({ params: { outboundNo: 'OUT/1' } }), useRouter: () => ({ push: vi.fn() }) })) vi.mock('../api/outbounds', () => ({ getOutbound: vi.fn(), cancelOutbound: vi.fn() })) +vi.mock('@/modules/warehouses', () => ({ getPlatformWarehouse: vi.fn() })) describe('OutboundDetailView', () => { it.each([ @@ -25,6 +27,7 @@ describe('OutboundDetailView', () => { }) beforeEach(() => { + vi.mocked(getPlatformWarehouse).mockReset().mockRejectedValue(new Error('Warehouse unavailable')) canPermission.mockReset().mockReturnValue(false) vi.mocked(getOutbound).mockReset().mockResolvedValue({ outbound: { id: '1', outboundNo: 'OUT/1', dispatchNo: 'PC-1', status: 'PICKED', palletCount: 2, packageCount: 20, createdAt: '2026-09-01T10:00:00' }, @@ -87,6 +90,24 @@ describe('OutboundDetailView', () => { wrapper.unmount() }) + it('resolves the warehouse for a discard outbound without deliveries', async () => { + vi.mocked(getOutbound).mockResolvedValueOnce({ + outbound: { id: '1', outboundNo: 'OB-WH-20261019-151681', outboundType: 'DISCARD_OUTBOUND', overseasWarehouseId: 'warehouse-internal-id', packageCount: 1 }, + deliveries: [], + }) + vi.mocked(getPlatformWarehouse).mockResolvedValueOnce({ + warehouse: { id: 'warehouse-internal-id', tenantId: '0', warehouseCode: 'LAX', internalCode: null, warehouseName: '洛杉矶仓', warehouseType: 'OVERSEAS', countryCode: 'US', timezone: 'America/Los_Angeles', contactName: null, contactPhone: null, contactEmail: null, status: 'ACTIVE', remark: null }, + addresses: [], capabilities: [], + }) + const wrapper = mount(OutboundDetailView, { global: { plugins: [ElementPlus] } }) + await flushPromises() + expect(getPlatformWarehouse).toHaveBeenCalledWith('warehouse-internal-id') + const label = wrapper.findAll('.el-descriptions__label').find(item => item.text() === '海外仓')! + expect(label.element.nextElementSibling?.textContent?.trim()).toBe('洛杉矶仓') + expect(wrapper.text()).not.toContain('warehouse-internal-id') + wrapper.unmount() + }) + it.each([ ['warehouse-internal-id', '洛杉矶仓', '洛杉矶仓'], ['warehouse-internal-id', null, '—'], diff --git a/src/modules/overseas-warehouse/pages/OutboundDetailView.vue b/src/modules/overseas-warehouse/pages/OutboundDetailView.vue index ad5cbdb4..5f62672a 100644 --- a/src/modules/overseas-warehouse/pages/OutboundDetailView.vue +++ b/src/modules/overseas-warehouse/pages/OutboundDetailView.vue @@ -13,6 +13,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue' import OperationErrorToast from '@/shared/components/OperationErrorToast.vue' import StatusBadge from '@/shared/components/StatusBadge.vue' import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http' +import { getPlatformWarehouse } from '@/modules/warehouses' import { cancelOutbound, getOutbound, type OutboundDetail } from '../api/outbounds' import { formatOverseasDateTime } from '../lastMilePresentation' import { overseasWarehousePermissions as permission } from '../permissions' @@ -21,6 +22,7 @@ const route = useRoute(); const router = useRouter(); const access = useAccessPr const outboundNo = computed(() => String(route.params.outboundNo || '').trim()) const detail = ref(null); const loading = ref(false); const cancelling = ref(false) const loadError = ref(null); const mutationError = ref(null) +const warehouseName = ref('') const statusLabels: Record = { WAIT_PICK: '待拣货', WAIT_OUTBOUND: '待出库', PICKING: '拣货中', PICKED: '已拣货', OUTBOUNDED: '已出库', CANCELLED: '已取消' } const palletColumns: DataTableColumn[] = [ { key: 'palletNo', label: '托盘码', width: 220 }, @@ -40,8 +42,23 @@ function show(value: unknown) { const text = String(value ?? '').trim(); return // A warehouse ID is only a join key; never render it as a warehouse name. const warehouseLabel = computed(() => { const outboundWarehouseId = detail.value?.outbound.overseasWarehouseId + if (!outboundWarehouseId) return '—' const delivery = detail.value?.deliveries?.find(item => item.overseasWarehouseId === outboundWarehouseId) - return outboundWarehouseId && delivery?.overseasWarehouseId === outboundWarehouseId ? show(delivery.overseasWarehouseName) : '—' + return show(delivery?.overseasWarehouseName?.trim() || warehouseName.value) +}) +// 废弃等出库没有关联派送单,直接从仓库主档补充名称。 +watch(() => detail.value, async (value, _previous, onCleanup) => { + warehouseName.value = '' + let current = true + onCleanup(() => { current = false }) + const warehouseId = value?.outbound.overseasWarehouseId + if (!warehouseId || value?.deliveries?.some(item => item.overseasWarehouseId === warehouseId && item.overseasWarehouseName?.trim())) return + try { + const result = await getPlatformWarehouse(warehouseId) + if (current && String(result.warehouse.id) === String(warehouseId)) warehouseName.value = result.warehouse.warehouseName + } catch { + // 主档不可用时保留占位,出库详情仍可查看,不展示内部 ID。 + } }) function statusLabel(value: unknown) { const code = String(value || ''); return statusLabels[code] || show(code) } function outboundTypeLabel(value?: string | null) { return value === 'RELABEL_OUTBOUND' ? '贴标出库' : value === 'DISCARD_OUTBOUND' ? '废弃出库' : value === 'WAREHOUSE_TRANSFER_OUTBOUND' ? '仓储转运出库' : value === 'ONE_CLICK_DROPSHIP' ? '一件代发出库' : value === 'RETURN_OUTBOUND' ? '退货出库' : value === 'DISPATCH_DELIVERY' ? '派送出库' : show(value) }