优化
This commit is contained in:
@@ -4,11 +4,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { getOutbound } from '../api/outbounds'
|
import { getOutbound } from '../api/outbounds'
|
||||||
import OutboundDetailView from './OutboundDetailView.vue'
|
import OutboundDetailView from './OutboundDetailView.vue'
|
||||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||||
|
import { getPlatformWarehouse } from '@/modules/warehouses'
|
||||||
|
|
||||||
const canPermission = vi.hoisted(() => vi.fn((_code: string) => false))
|
const canPermission = vi.hoisted(() => vi.fn((_code: string) => false))
|
||||||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) }))
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) }))
|
||||||
vi.mock('vue-router', () => ({ useRoute: () => ({ params: { outboundNo: 'OUT/1' } }), useRouter: () => ({ push: vi.fn() }) }))
|
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('../api/outbounds', () => ({ getOutbound: vi.fn(), cancelOutbound: vi.fn() }))
|
||||||
|
vi.mock('@/modules/warehouses', () => ({ getPlatformWarehouse: vi.fn() }))
|
||||||
|
|
||||||
describe('OutboundDetailView', () => {
|
describe('OutboundDetailView', () => {
|
||||||
it.each([
|
it.each([
|
||||||
@@ -25,6 +27,7 @@ describe('OutboundDetailView', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
vi.mocked(getPlatformWarehouse).mockReset().mockRejectedValue(new Error('Warehouse unavailable'))
|
||||||
canPermission.mockReset().mockReturnValue(false)
|
canPermission.mockReset().mockReturnValue(false)
|
||||||
vi.mocked(getOutbound).mockReset().mockResolvedValue({
|
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' },
|
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()
|
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([
|
it.each([
|
||||||
['warehouse-internal-id', '洛杉矶仓', '洛杉矶仓'],
|
['warehouse-internal-id', '洛杉矶仓', '洛杉矶仓'],
|
||||||
['warehouse-internal-id', null, '—'],
|
['warehouse-internal-id', null, '—'],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
|||||||
import OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
|
import OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
|
||||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||||
|
import { getPlatformWarehouse } from '@/modules/warehouses'
|
||||||
import { cancelOutbound, getOutbound, type OutboundDetail } from '../api/outbounds'
|
import { cancelOutbound, getOutbound, type OutboundDetail } from '../api/outbounds'
|
||||||
import { formatOverseasDateTime } from '../lastMilePresentation'
|
import { formatOverseasDateTime } from '../lastMilePresentation'
|
||||||
import { overseasWarehousePermissions as permission } from '../permissions'
|
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 outboundNo = computed(() => String(route.params.outboundNo || '').trim())
|
||||||
const detail = ref<OutboundDetail | null>(null); const loading = ref(false); const cancelling = ref(false)
|
const detail = ref<OutboundDetail | null>(null); const loading = ref(false); const cancelling = ref(false)
|
||||||
const loadError = ref<ApiErrorDetails | null>(null); const mutationError = ref<ApiErrorDetails | null>(null)
|
const loadError = ref<ApiErrorDetails | null>(null); const mutationError = ref<ApiErrorDetails | null>(null)
|
||||||
|
const warehouseName = ref('')
|
||||||
const statusLabels: Record<string, string> = { WAIT_PICK: '待拣货', WAIT_OUTBOUND: '待出库', PICKING: '拣货中', PICKED: '已拣货', OUTBOUNDED: '已出库', CANCELLED: '已取消' }
|
const statusLabels: Record<string, string> = { WAIT_PICK: '待拣货', WAIT_OUTBOUND: '待出库', PICKING: '拣货中', PICKED: '已拣货', OUTBOUNDED: '已出库', CANCELLED: '已取消' }
|
||||||
const palletColumns: DataTableColumn[] = [
|
const palletColumns: DataTableColumn[] = [
|
||||||
{ key: 'palletNo', label: '托盘码', width: 220 },
|
{ 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.
|
// A warehouse ID is only a join key; never render it as a warehouse name.
|
||||||
const warehouseLabel = computed(() => {
|
const warehouseLabel = computed(() => {
|
||||||
const outboundWarehouseId = detail.value?.outbound.overseasWarehouseId
|
const outboundWarehouseId = detail.value?.outbound.overseasWarehouseId
|
||||||
|
if (!outboundWarehouseId) return '—'
|
||||||
const delivery = detail.value?.deliveries?.find(item => item.overseasWarehouseId === outboundWarehouseId)
|
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 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) }
|
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) }
|
||||||
|
|||||||
Reference in New Issue
Block a user