优化
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { reactive } from 'vue'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import BusinessDocumentFees from '../components/BusinessDocumentFees.vue'
|
||||
import { getLocalLooseboardOrder } from '../api/platformOverseasBusiness'
|
||||
@@ -36,6 +36,22 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
describe('looseboard document fees', () => {
|
||||
it('refreshes the current status when returning to a cached document tab', async () => {
|
||||
const visible = ref(true)
|
||||
vi.mocked(getLocalLooseboardOrder).mockResolvedValueOnce(order({ status: 'WAITING_INBOUND' }))
|
||||
.mockResolvedValueOnce(order({ status: 'PENDING_DISPATCH' }))
|
||||
const wrapper = mount({ components: { LooseboardOrderDetailView }, setup: () => ({ visible }),
|
||||
template: '<KeepAlive><LooseboardOrderDetailView v-if="visible" /></KeepAlive>',
|
||||
}, { global: { plugins: [ElementPlus], stubs: { BusinessDocumentFees: true, FleetArrangementSummary: true, DataTable: true } } })
|
||||
await flushPromises()
|
||||
expect(getLocalLooseboardOrder).toHaveBeenCalledTimes(1)
|
||||
expect(wrapper.find('.status-badge').text()).toBe('待入仓')
|
||||
visible.value = false; await flushPromises()
|
||||
visible.value = true; await flushPromises()
|
||||
expect(getLocalLooseboardOrder).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.find('.status-badge').text()).toBe('待派车')
|
||||
})
|
||||
|
||||
it('loads the customer scoped document and passes its exact identity to fees', async () => {
|
||||
const wrapper = mountDetail(); await flushPromises()
|
||||
expect(getLocalLooseboardOrder).toHaveBeenCalledWith('LB-TEST', '9007199254741999')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import '@/styles/adminWorkbench.css'
|
||||
import '@/styles/adminDensity.css'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { computed, onActivated, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ArrowLeft } from '@lucide/vue'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
@@ -14,6 +14,7 @@ import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { getLocalLooseboardOrder } from '../api/platformOverseasBusiness'
|
||||
import BusinessDocumentFees from '../components/BusinessDocumentFees.vue'
|
||||
import FleetArrangementSummary from '../components/FleetArrangementSummary.vue'
|
||||
import TransferPickupProgressPanel from '@/modules/overseas-warehouse/components/TransferPickupProgressPanel.vue'
|
||||
import { poolOrderStatusLabels } from '../fleetArrangement'
|
||||
import type { LocalLooseboardDetail, OverseasBusinessHistory } from '../types'
|
||||
|
||||
@@ -58,6 +59,12 @@ async function load() {
|
||||
finally { if (sequence === requestSequence) loading.value = false }
|
||||
}
|
||||
watch([orderNo, customerId], load, { immediate: true })
|
||||
// 首次由路由加载;缓存标签重新打开时读取PDA和排车已推进的最新状态。
|
||||
let activatedOnce = false
|
||||
onActivated(() => {
|
||||
if (activatedOnce) void load()
|
||||
activatedOnce = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -73,6 +80,7 @@ watch([orderNo, customerId], load, { immediate: true })
|
||||
<el-skeleton v-else-if="loading" :rows="8" animated />
|
||||
<template v-else-if="detail">
|
||||
<section v-if="detail.fleetArrangement" class="looseboard-detail-section"><FleetArrangementSummary :arrangement="detail.fleetArrangement" /></section>
|
||||
<section v-if="detail.deliveryMode === 'WAREHOUSE_PICKUP' && detail.transferNo" class="looseboard-detail-section"><el-alert title="上门取货需在对应取货排车的作业节点上传取货及到仓资料。" type="info" :closable="false" /><TransferPickupProgressPanel :transfer-no="detail.transferNo" :customer-id="detail.customerId" /></section>
|
||||
<section class="looseboard-detail-section"><h2>订单资料</h2><el-descriptions class="density-descriptions" :column="3" border><el-descriptions-item v-for="[label, value] in fields" :key="String(label)" :label="String(label)">{{ value ?? '—' }}</el-descriptions-item></el-descriptions></section>
|
||||
<section class="looseboard-detail-section"><BusinessDocumentFees business-type="LOCAL_LOOSEBOARD" :business-no="detail.orderNo" :customer-id="detail.customerId" :business-id="detail.id" /></section>
|
||||
<section class="looseboard-detail-section"><h2>操作记录</h2><div class="workbench-table"><DataTable :data="detail.histories" :columns="historyColumns" empty-text="暂无操作记录" /></div></section>
|
||||
|
||||
@@ -60,7 +60,7 @@ async function load() {
|
||||
if (isLooseboard.value) {
|
||||
const detail = await getLocalLooseboardOrder(orderId.value, routeCustomerId.value)
|
||||
fleetArrangement.value = detail.fleetArrangement
|
||||
Object.assign(form, { customerId: detail.customerId, orderNo: detail.orderNo, productName: detail.productName || '', estimatedPalletCount: detail.estimatedPalletCount ?? 0, origin: detail.origin, destination: detail.destination, originAddress: detail.originAddress || '', destinationAddress: detail.destinationAddress || '', originContactName: detail.originContactName || '', originContactPhone: detail.originContactPhone || '', destinationContactName: detail.destinationContactName || '', destinationContactPhone: detail.destinationContactPhone || '', packageCount: detail.packageCount, totalWeightKg: Number(detail.totalWeightKg), totalVolumeCbm: Number(detail.totalVolumeCbm), budgetAmount: detail.budgetAmount == null ? undefined : Number(detail.budgetAmount), currency: detail.currency, pickupAt: detail.pickupAt || '', deliveryAt: detail.deliveryAt || '', remark: detail.remark || '', status: detail.status, version: detail.version })
|
||||
Object.assign(form, { deliveryMode: detail.deliveryMode || 'DELIVERY_TO_WAREHOUSE', operatingWarehouseId: detail.operatingWarehouseId || '', destinationType: detail.destinationType || 'PRIVATE', destinationPostalCode: detail.destinationPostalCode || '', deliveryContactName: detail.deliveryContactName || '', deliveryContactPhone: detail.deliveryContactPhone || '', estimatedArrivalAt: detail.estimatedArrivalAt || '', trackingNo: detail.trackingNo || '', customerId: detail.customerId, orderNo: detail.orderNo, productName: detail.productName || '', estimatedPalletCount: detail.estimatedPalletCount ?? 0, origin: detail.origin, destination: detail.destination, originAddress: detail.originAddress || '', destinationAddress: detail.destinationAddress || '', originContactName: detail.originContactName || '', originContactPhone: detail.originContactPhone || '', destinationContactName: detail.destinationContactName || '', destinationContactPhone: detail.destinationContactPhone || '', packageCount: detail.packageCount, totalWeightKg: Number(detail.totalWeightKg), totalVolumeCbm: Number(detail.totalVolumeCbm), budgetAmount: detail.budgetAmount == null ? undefined : Number(detail.budgetAmount), currency: detail.currency, pickupAt: detail.pickupAt || '', deliveryAt: detail.deliveryAt || '', remark: detail.remark || '', status: detail.status, version: detail.version })
|
||||
} else {
|
||||
const detail = await getUsTruckOrder(orderId.value, routeCustomerId.value)
|
||||
fleetArrangement.value = detail.fleetArrangement
|
||||
|
||||
@@ -53,10 +53,12 @@ describe('transport document entry', () => {
|
||||
expect(routing.push).toHaveBeenCalledWith({ name: 'overseas-business-looseboard-detail', params: { orderId: 'LB-TEST' }, query: { customerId: '9007199254741999' } })
|
||||
})
|
||||
|
||||
it('does not expose looseboard editing from the list even when permission exists', async () => {
|
||||
it('lets authorized users complete missing receiving information before review', async () => {
|
||||
const wrapper = mountList('looseboard', true); await flushPromises()
|
||||
const actions = wrapper.findComponent(RowActions)
|
||||
expect(actions.text()).not.toContain('编辑')
|
||||
expect(actions.text()).toContain('编辑')
|
||||
actions.vm.$emit('action', 'edit'); await flushPromises()
|
||||
expect(routing.push).toHaveBeenCalledWith({ name: 'overseas-business-looseboard-edit', params: { orderId: 'LB-TEST' }, query: { customerId: '9007199254741999' } })
|
||||
})
|
||||
|
||||
it('keeps truck mode on its own list API', async () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import RowActions, { type RowActionItem } from '@/shared/components/RowActions.vue'
|
||||
import { truckStatusLabels } from '../truckWorkflow'
|
||||
import FleetArrangementSummary from '../components/FleetArrangementSummary.vue'
|
||||
import LooseboardReviewDialog from '../components/LooseboardReviewDialog.vue'
|
||||
import { poolOrderStatusLabels, type FleetArrangement } from '../fleetArrangement'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
@@ -23,6 +24,7 @@ import { overseasBusinessPermissions } from '../permissions'
|
||||
import type { LocalLooseboardOrder, LocalLooseboardQuery, LocalLooseboardStatus, LocalTransportOrder, LocalTransportQuery, LocalTransportStatus, LocalTransportType } from '../types'
|
||||
|
||||
const props = defineProps<{ domain: 'truck' | 'looseboard' }>()
|
||||
const reviewOpen = ref(false), reviewOrder = ref<LocalLooseboardOrder | null>(null)
|
||||
const routeOpen = ref(false)
|
||||
const routeOrigin = ref<RouteLocation>(routeLocation('')), routeDestinations = ref<RouteLocation[]>([])
|
||||
function openRoute(row: LocalTransportOrder | LocalLooseboardOrder) {
|
||||
@@ -104,8 +106,8 @@ async function load() {
|
||||
}
|
||||
function search(model: QueryModel) { applied.value = { ...model }; if (page.value === 1) void load(); else page.value = 1 }
|
||||
function openCreate() { void router.push({ name: isLooseboard.value ? 'overseas-business-looseboard-new' : 'overseas-business-truck-new' }) }
|
||||
function canEdit(row: LocalTransportOrder | LocalLooseboardOrder) { return !isLooseboard.value && !row.fleetArrangement && row.status === 'PENDING_REVIEW' }
|
||||
function openEdit(row: LocalTransportOrder | LocalLooseboardOrder) { if (!canEdit(row)) return; void router.push({ name: 'overseas-business-truck-edit', params: { orderId: row.orderNo }, query: { customerId: row.customerId } }) }
|
||||
function canEdit(row: LocalTransportOrder | LocalLooseboardOrder) { return !row.fleetArrangement && (isLooseboard.value ? row.status === 'PENDING_ACCEPTANCE' && !(row as LocalLooseboardOrder).deliveryMode : row.status === 'PENDING_REVIEW') }
|
||||
function openEdit(row: LocalTransportOrder | LocalLooseboardOrder) { if (!canEdit(row)) return; void router.push({ name: isLooseboard.value ? 'overseas-business-looseboard-edit' : 'overseas-business-truck-edit', params: { orderId: row.orderNo }, query: { customerId: row.customerId } }) }
|
||||
/** 单据详情保留订单与客户范围,费用在原业务单中处理。 */
|
||||
function openDetail(row: LocalTransportOrder | LocalLooseboardOrder) {
|
||||
void router.push({ name: isLooseboard.value ? 'overseas-business-looseboard-detail' : 'overseas-business-truck-detail', params: { orderId: row.orderNo }, query: { customerId: row.customerId } })
|
||||
@@ -114,6 +116,8 @@ function openDetail(row: LocalTransportOrder | LocalLooseboardOrder) {
|
||||
function looseboardActions(row: LocalLooseboardOrder): RowActionItem[] {
|
||||
return [
|
||||
{ key: 'detail', label: '详情', priority: 10 },
|
||||
{ key: 'review', label: '审核', visible: row.status === 'PENDING_ACCEPTANCE' && access.can(overseasBusinessPermissions.looseboardStatus), priority: 20 },
|
||||
{ key: 'edit', label: '编辑', visible: row.status === 'PENDING_ACCEPTANCE' && !row.deliveryMode && access.can(overseasBusinessPermissions.looseboardEdit) },
|
||||
{ key: 'arrangement', label: '承运安排', visible: !!row.fleetArrangement },
|
||||
]
|
||||
}
|
||||
@@ -124,7 +128,8 @@ function truckActions(row: LocalTransportOrder): RowActionItem[] {
|
||||
]
|
||||
}
|
||||
function handleTruckAction(key: string, row: LocalTransportOrder | LocalLooseboardOrder) {
|
||||
if (key === 'edit') openEdit(row)
|
||||
if (key === 'review' && isLooseboard.value) { reviewOrder.value=row as LocalLooseboardOrder; reviewOpen.value=true }
|
||||
else if (key === 'edit') openEdit(row)
|
||||
else if (key === 'arrangement') showArrangement(row)
|
||||
else if (key === 'detail') openDetail(row)
|
||||
}
|
||||
@@ -134,6 +139,7 @@ watch([page, pageSize], () => { void load() }); watch([isLooseboard, () => route
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LooseboardReviewDialog v-model="reviewOpen" :order="reviewOrder" @saved="load" />
|
||||
<AdminPage class="admin-density admin-workbench"><ListPageLayout :aria-label="`${domainLabel}订单列表`">
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user