2026-08-06 13:56:04 +08:00
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe , expect , it } from 'vitest'
import { unifiedOrderStatusOptions } from '../api/unifiedOrders'
2026-09-04 12:41:58 +08:00
const source = readFileSync ( resolve ( process . cwd (), 'src/modules/unified-orders/pages/UnifiedOrdersView.vue' ), 'utf8' ). replace ( /\r\n/g , '\n' )
const routeSource = readFileSync ( resolve ( process . cwd (), 'src/modules/unified-orders/routes.ts' ), 'utf8' ). replace ( /\r\n/g , '\n' )
2026-08-06 13:56:04 +08:00
describe ( 'UnifiedOrdersView contract' , () => {
2026-09-04 11:50:34 +08:00
it ( 'shows the server confirmation lock without reading a stale fee snapshot' , () => {
const openDialogSource = source . slice ( source . indexOf ( 'async function openFeeConfirmDialog()' ), source . indexOf ( 'async function executeConfirmAndSendFee()' ))
const sendFeeSource = source . slice ( source . indexOf ( 'async function executeConfirmAndSendFee()' ), source . indexOf ( 'function handleOrderSelectionChange' ))
expect ( openDialogSource ). not . toContain ( 'getLatestInboundFee' )
expect ( sendFeeSource ). toContain ( "ElMessage.error(getApiErrorDetails(error, '确认费用发送失败').message)" )
2026-09-03 17:49:01 +08:00
})
2026-08-13 14:37:59 +08:00
it ( 'only exposes the six product main statuses' , () => {
2026-08-06 13:56:04 +08:00
expect ( unifiedOrderStatusOptions . map (( item ) => item . label )). toEqual ([
2026-08-13 14:37:59 +08:00
'待审核' , '待测量数据' , '待分柜' , '装柜中' , '装柜完成' , '派送完成' ,
2026-08-06 13:56:04 +08:00
])
for ( const legacyLabel of [ '待客户确认' , '差异处理中' , '派送中' , '进入分柜' ]) {
expect ( source ). not . toContain ( legacyLabel )
}
})
2026-08-06 15:32:02 +08:00
it ( 'renders accessible current-status tabs with keyboard navigation' , () => {
expect ( source ). toContain ( 'role="tablist"' )
expect ( source ). toContain ( 'role="tab"' )
2026-08-11 18:06:35 +08:00
expect ( source ). toContain ( ':aria-selected="selectedStatus === tab.value"' )
2026-08-06 15:32:02 +08:00
expect ( source ). toContain ( "event.key === 'ArrowRight'" )
expect ( source ). toContain ( "event.key === 'ArrowLeft'" )
expect ( source ). toContain ( "event.key === 'Home'" )
expect ( source ). toContain ( "event.key === 'End'" )
})
it ( 'provides labeled prototype-aligned advanced filters backed by real query fields' , () => {
for ( const key of [
2026-08-10 17:42:17 +08:00
'receivingWarehouseId' , 'destinationWarehouseId' , 'inboundCode' , 'expectedArrivalDate' ,
2026-08-06 15:32:02 +08:00
'feeReviewStatus' , 'containerNo' , 'cabinetNo' , 'channelName' , 'sailingDate' , 'loaded' , 'fullContainer' ,
'measurementReviewStatus' , 'fbaCode' , 'createdRange' ,
]) expect ( source ). toContain ( `key: ' ${ key } '` )
2026-08-12 12:19:02 +08:00
for ( const label of [ '定位仓库' , '交货仓' , '费用审核状态' , '是否已配载' , '是否整柜' , '测量数据审核状态' , '商品 SKU 编码' ]) {
2026-08-06 15:32:02 +08:00
expect ( source ). toContain ( `label: ' ${ label } '` )
}
expect ( source ). toContain ( 'listAllPlatformWarehouses' )
expect ( source ). toContain ( "{ status: 'ACTIVE' }" )
expect ( source ). toContain ( "warehouse.warehouseType === 'DOMESTIC'" )
2026-08-11 18:06:35 +08:00
expect ( source ). toContain ( "loaded: optionalBoolean(activeFilters.loaded)" )
expect ( source ). toContain ( "fullContainer: optionalBoolean(activeFilters.fullContainer)" )
2026-08-06 15:36:28 +08:00
expect ( source ). toContain ( "{ value: 'UNRECORDED', label: '未录入' }" )
expect ( source ). not . toContain ( "{ value: 'DRAFT', label: '草稿' }" )
expect ( source ). not . toContain ( "{ value: 'CANCELLED', label: '已取消' }" )
2026-08-07 18:44:55 +08:00
expect ( source . match ( /key: 'inboundCode', type: 'input'/g )). toHaveLength ( 1 )
2026-08-12 10:59:49 +08:00
expect ( source ). not . toContain ( "key: 'orderNo', type:" )
2026-08-10 17:42:17 +08:00
expect ( source ). not . toContain ( 'internalOrderStatus: filters.value.internalOrderStatus' )
2026-08-07 18:44:55 +08:00
expect ( source ). not . toContain ( "key: 'keyword'" )
const prototypeFieldOrder = [
2026-08-10 17:42:17 +08:00
'receivingWarehouseId' , 'destinationWarehouseId' , 'inboundCode' , 'expectedArrivalDate' ,
2026-08-07 18:44:55 +08:00
'feeReviewStatus' , 'tenantId' , 'containerNo' , 'cabinetNo' , 'channelName' , 'sailingDate' , 'loaded' ,
'fullContainer' , 'measurementReviewStatus' , 'fbaCode' , 'createdRange' ,
]
const fieldPositions = prototypeFieldOrder . map (( key ) => source . indexOf ( `key: ' ${ key } '` ))
expect ( fieldPositions ). toEqual ([... fieldPositions ]. sort (( left , right ) => left - right ))
expect ( source ). toContain ( 'preference-key="unified-orders-query-v2"' )
2026-08-06 15:32:02 +08:00
})
2026-08-12 12:19:02 +08:00
it ( 'keeps unified filters aligned with the formal query contract and does not expose hold filters' , () => {
const filterSource = source . slice ( source . indexOf ( 'const primaryQueryFields' ), source . indexOf ( 'const columns:' ))
for ( const key of [ 'expectedArrivalDate' , 'feeReviewStatus' , 'measurementReviewStatus' , 'fbaCode' , 'loaded' , 'fullContainer' ]) {
expect ( filterSource ). toContain ( `key: ' ${ key } '` )
}
expect ( filterSource ). not . toContain ( "key: 'holdCargo'" )
expect ( filterSource ). not . toContain ( "key: 'holdReason'" )
expect ( filterSource ). toContain ( "key: 'fbaCode', type: 'input', width: 220, label: '商品 SKU 编码'" )
expect ( filterSource ). not . toContain ( "label: '商品 FBA 编码'" )
expect ( source ). toContain ( 'expectedArrivalDate: activeFilters.expectedArrivalDate || undefined' )
expect ( source ). toContain ( 'feeReviewStatus: activeFilters.feeReviewStatus || undefined' )
expect ( source ). toContain ( 'measurementReviewStatus: activeFilters.measurementReviewStatus || undefined' )
expect ( source ). toContain ( 'fbaCode: trimmed(activeFilters.fbaCode)' )
})
2026-08-10 17:42:17 +08:00
it ( 'uses tabs as the only order-status filter and keeps advanced controls inside QueryToolbar' , () => {
2026-08-11 18:06:35 +08:00
expect ( source ). toContain ( 'mainStatus: selectedStatus.value || undefined' )
2026-08-10 17:42:17 +08:00
expect ( source ). toContain ( '<template #actions>' )
expect ( source ). toContain ( ':aria-expanded="showAdvancedFilters"' )
expect ( source ). not . toContain ( 'class="filter-mode-actions"' )
expect ( source ). not . toContain ( '.filter-mode-actions' )
expect ( source ). not . toContain ( '请选择订单状态' )
})
2026-08-06 13:56:04 +08:00
it ( 'covers real list states and keeps operation column first and left-fixed' , () => {
2026-09-04 11:50:34 +08:00
expect ( source ). toContain ( "{ key: 'actions', label: '操作', width: 270, fixed: 'left'" )
2026-08-17 18:00:42 +08:00
expect ( source ). toContain ( "key: 'selection', type: 'selection'" )
2026-08-11 19:59:15 +08:00
expect ( source ). not . toContain ( '批量审核接口待接入' )
2026-08-29 10:57:01 +08:00
expect ( source ). toContain ( '@selection-change="handleOrderSelectionChange"' )
2026-08-17 18:00:42 +08:00
expect ( source ). toContain ( 'batchApprovePlatformUnifiedOrders' )
expect ( source ). toContain ( '批量审核通过' )
2026-08-06 13:56:04 +08:00
expect ( source ). toContain ( 'title="订单列表暂时无法加载"' )
expect ( source ). toContain ( 'empty-text="暂无符合条件的订单"' )
expect ( source ). toContain ( ':loading="loading"' )
expect ( source ). toContain ( 'v-model:page="page"' )
expect ( source ). toContain ( 'v-model:page-size="pageSize"' )
2026-08-17 18:00:42 +08:00
const columnSource = source . slice ( source . indexOf ( 'const columns =' ), source . indexOf ( 'function emptyFilters' ))
2026-08-12 10:59:49 +08:00
const expectedColumnKeys = [
2026-08-31 10:09:17 +08:00
'actions' , 'inboundCode' , 'mainStatus' , 'customerName' , 'receivingWarehouseName' ,
2026-08-17 19:52:04 +08:00
'destinationWarehouseName' , 'channelName' , 'totalPackageCount' , 'totalPalletCount' , 'requestedPickupAt' , 'locationCodes' , 'measurementReviewStatus' , 'actualGrossWeightKg' ,
'actualVolumeCbm' , 'totalNetWeightKg' , 'chargeableWeightKg' , 'chargeableVolumeCbm' , 'expectedShipDate' ,
2026-09-03 11:47:32 +08:00
'estimatedVolumeCbm' , 'expectedVolumeCbm' , 'expectedArrivalAt' , 'feeReviewStatus' , 'skuNos' , 'totalQuantity' , 'expressCompany' , 'expressTrackingNo' ,
2026-08-17 19:52:04 +08:00
'shipmentId' , 'referenceId' , 'containerNo' , 'cabinetNo' ,
2026-08-18 11:33:01 +08:00
'effectiveSailingAt' , 'effectivePortArrivalAt' , 'customerRemark' , 'latestInternalNote' , 'createdAt' , 'updatedAt' ,
2026-08-12 10:59:49 +08:00
]
for ( const key of expectedColumnKeys ) expect ( columnSource ). toContain ( `key: ' ${ key } '` )
for ( const uncontractedKey of [
2026-08-17 19:52:04 +08:00
'internalOrderStatus' , 'holdCargo' , 'companyName' , 'pickupMethod' , 'associatedFiles' , 'createdBy' ,
2026-08-12 10:59:49 +08:00
]) expect ( columnSource ). not . toContain ( `key: ' ${ uncontractedKey } '` )
2026-08-31 10:09:17 +08:00
expect ( columnSource . indexOf ( "key: 'actions'" )). toBeLessThan ( columnSource . indexOf ( "key: 'inboundCode'" ))
2026-08-30 15:49:01 +08:00
expect ( columnSource ). not . toContain ( "key: 'coLoadGroup'" )
2026-08-31 10:09:17 +08:00
expect ( columnSource ). not . toContain ( "key: 'orderNo'" )
2026-09-04 11:50:34 +08:00
expect ( columnSource ). toContain ( "key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 160, slotName: 'inboundCode'" )
2026-08-12 10:59:49 +08:00
expect ( columnSource ). toContain ( "{ key: 'mainStatus', prop: 'mainStatus', label: '订单主状态'" )
2026-08-17 19:52:04 +08:00
expect ( columnSource ). not . toContain ( "key: 'loadingStatus'" )
expect ( columnSource ). not . toContain ( "key: 'businessDate'" )
expect ( columnSource ). not . toContain ( "label: '业务日期'" )
expect ( columnSource ). not . toContain ( "label: '配载状态'" )
expect ( columnSource ). not . toContain ( "key: 'recommendedPalletType'" )
expect ( columnSource ). toContain ( "key: 'requestedPickupAt', prop: 'requestedPickupAt', label: '预约提货时间'" )
2026-09-01 21:09:39 +08:00
expect ( columnSource . match ( /defaultVisible: false/g )). toHaveLength ( 13 )
2026-09-04 11:50:34 +08:00
expect ( columnSource ). toContain ( "key: 'expectedArrivalAt', prop: 'expectedArrivalAt', label: '预计到货时间', minWidth: 170, slotName: 'expectedArrival' }" )
expect ( columnSource ). toContain ( "key: 'estimatedVolumeCbm', prop: 'estimatedVolumeCbm', label: '预估体积(m³)', minWidth: 120, slotName: 'estimatedVolume' }" )
2026-09-03 11:47:32 +08:00
expect ( columnSource ). toContain ( "key: 'expectedVolumeCbm', prop: 'expectedVolumeCbm', label: '下单体积(m³)', minWidth: 130, slotName: 'field' }" )
const planningColumnPositions = [
columnSource . indexOf ( "key: 'expectedArrivalAt'" ),
columnSource . indexOf ( "key: 'estimatedVolumeCbm'" ),
columnSource . indexOf ( "key: 'expectedVolumeCbm'" ),
columnSource . indexOf ( "key: 'actualVolumeCbm'" ),
]
expect ( planningColumnPositions ). toEqual ([... planningColumnPositions ]. sort (( left , right ) => left - right ))
expect ( source ). toContain ( "can('platform:order:declaration:edit')" )
expect ( source ). toContain ( 'updatePlatformUnifiedOrderPlanning(String(row.tenantId), String(row.orderId)' )
expect ( source ). toContain ( 'expectedVersion: row.orderVersion!' )
expect ( source ). toContain ( '<ApiErrorMeta :details="planningDraft(row as UnifiedOrderResource).error!" />' )
2026-08-18 11:33:01 +08:00
expect ( columnSource ). toContain ( "key: 'effectiveSailingAt', prop: 'effectiveSailingAt', label: '开船时间', minWidth: 180, slotName: 'field' }" )
expect ( columnSource ). toContain ( "key: 'effectivePortArrivalAt', prop: 'effectivePortArrivalAt', label: '到港时间', minWidth: 180, slotName: 'field' }" )
expect ( source ). toContain ( 'preference-key="unified-orders-table-v2"' )
2026-08-17 19:52:04 +08:00
expect ( source ). toContain ( "property === 'requestedPickupAt' && (row.pickupMethod !== 'PICKUP' || !value)" )
expect ( source ). toContain ( "property === 'skuNos' ? value.join('、')" )
expect ( source ). not . toContain ( '<dt>推荐托盘类型</dt>' )
2026-08-18 11:33:01 +08:00
expect ( source ). toContain ( "estimated === true ? '预计 ' : ''" )
expect ( source ). toContain ( 'row.transport?.effectiveSailingAtEstimated' )
expect ( source ). toContain ( 'row.transport?.effectivePortArrivalAtEstimated' )
expect ( source ). not . toContain ( "transport.sailingAt" )
expect ( source ). not . toContain ( "transport.portArrivalAt" )
2026-08-12 12:19:02 +08:00
expect ( source ). toContain ( '.table-card { overflow: hidden; }' )
2026-08-07 10:45:19 +08:00
expect ( source ). toContain ( 'slotName: \'measurementReviewStatus\'' )
2026-08-11 19:35:28 +08:00
expect ( source ). toContain ( 'unifiedOrderReviewStatusLabel(value, \'未录入\')' )
2026-08-12 10:59:49 +08:00
expect ( source ). toContain ( "{ key: 'customerRemark', prop: 'customerRemark', label: '客户备注'" )
expect ( source ). toContain ( "{ key: 'latestInternalNote', prop: 'latestInternalNote', label: '内部备注'" )
2026-08-07 18:44:55 +08:00
expect ( source . match ( /<LineClampText v-if="column\.key === 'remark'"/g )). toHaveLength ( 3 )
2026-08-27 21:48:15 +08:00
expect ( source ). toContain ( "detail.summary.internalRemark || detail.summary.latestInternalNote || '—'" )
2026-08-06 13:56:04 +08:00
})
2026-08-10 17:42:17 +08:00
it ( 'uses ListPageLayout regions for one 16px query-to-table gap' , () => {
expect ( source ). toContain ( '<ListPageLayout ariaLabel="统一订单列表">' )
expect ( source ). toContain ( '<template #toolbar>' )
expect ( source ). toContain ( '<template v-if="loadError" #state>' )
expect ( source ). toContain ( '<template #default>' )
expect ( source ). toContain ( 'v-if="!loadError" id="unified-orders-results" class="surface-panel table-panel table-card"' )
expect ( source ). not . toContain ( 'class="orders-load-error"' )
expect ( source ). not . toContain ( '.orders-load-error { margin-top: 0; }' )
})
it ( 'counts detail in the three directly visible row actions' , () => {
expect ( source ). toContain ( 'unifiedOrderSecondaryActions(row).slice(0, 2)' )
expect ( source ). toContain ( 'unifiedOrderSecondaryActions(row).slice(2)' )
expect ( source ). toContain ( '<Eye :size="15" />详情' )
expect ( source ). toContain ( 'v-if="moreUnifiedOrderActions(row as UnifiedOrderResource).length"' )
})
2026-08-14 19:02:34 +08:00
it ( 'opens smart loading from the order table toolbar with its own permission' , () => {
expect ( source ). toContain ( "can('platform:firstleg:smart-loading:list')" )
expect ( source ). toContain ( '#toolbar-actions' )
expect ( source ). toContain ( '@click="smartLoadingOpen = true"' )
expect ( source ). toContain ( '<SmartLoadingDialog v-model="smartLoadingOpen" />' )
})
2026-09-01 21:09:39 +08:00
it ( 'uses one asynchronous export entry for either selected orders or the applied filter snapshot' , () => {
expect ( source ). toContain ( "exportType: 'DOMESTIC_WAREHOUSE_ORDER_LIST'" )
expect ( source ). toContain ( "parameters: selectedOrderNos.length" )
expect ( source ). toContain ( '? { selectedOrderNos }' )
expect ( source ). toContain ( ': exportFilterParameters()' )
expect ( source ). toContain ( "can('platform:order:export') && can('platform:download:list')" )
expect ( source ). not . toContain ( 'page: page.value,\n size: pageSize.value,\n })\n const task = await createPlatformExportTask' )
})
2026-08-30 15:49:01 +08:00
it ( 'states only the domestic warehouse and channel qualification without local destination or split blocking' , () => {
const createSource = source . slice ( source . indexOf ( 'function openCoLoadCreate' ), source . indexOf ( 'async function batchApproveOrders' ))
const dialogSource = source . slice ( source . indexOf ( '<AppDialog v-model="coLoadCreateOpen"' ), source . indexOf ( '<AppDrawer v-model="relationOpen"' ))
expect ( dialogSource ). toContain ( '同一国内仓、同一渠道' )
expect ( dialogSource ). toContain ( '目的仓或目的港可以不同' )
expect ( dialogSource ). toContain ( '资格由服务端按完整入仓片段校验' )
expect ( dialogSource ). not . toContain ( '拆子单' )
expect ( createSource ). not . toContain ( 'destinationWarehouse' )
expect ( createSource ). not . toContain ( 'destinationPort' )
expect ( createSource ). not . toContain ( 'splitStatus' )
expect ( createSource ). not . toContain ( 'relation' )
expect ( createSource ). toContain ( 'createPlatformCoLoadGroup(selectedOrders.value.map((row) => String(row.orderId)))' )
2026-08-31 14:54:57 +08:00
expect ( source ). toContain ( ':disabled="loading || !canCreateCoLoadGroup"' )
2026-08-31 15:50:05 +08:00
expect ( source ). toContain ( '所选订单的平台收货仓不同:' )
expect ( source ). toContain ( '所选订单的渠道不同:' )
expect ( source ). toContain ( 'row.channelCode!.trim()' )
expect ( source ). not . toContain ( 'String(row.channelName || \'\').trim()' )
2026-08-31 16:37:36 +08:00
expect ( source ). not . toContain ( '请至少勾选 2 票订单后再设为同柜组' )
expect ( source ). not . toContain ( '当前仅选择 1 票:' )
expect ( source ). toContain ( '以下入仓码已有同柜组:' )
expect ( source ). toContain ( 'selectable: () => !batchApprovalSaving.value && !coLoadSaving.value' )
expect ( source ). not . toContain ( 'isCoLoadRowSelectable' )
expect ( source ). not . toContain ( '<small v-if="coLoadSelectionProblem"' )
2026-08-30 15:49:01 +08:00
})
2026-08-31 10:09:17 +08:00
it ( 'uses inbound code as the only list identifier and opens a business-only group dialog' , () => {
2026-08-30 15:49:01 +08:00
expect ( source ). not . toContain ( "{ key: 'coLoadGroup', label: '同柜组'" )
2026-08-31 10:09:17 +08:00
expect ( source ). not . toContain ( '<template #orderNo=' )
expect ( source ). toContain ( '<template #inboundCode="{ row }">' )
expect ( source ). toContain ( "inboundCode || '待生成入仓码'" )
2026-08-30 15:49:01 +08:00
expect ( source ). toContain ( 'class="coload-tag-trigger"' )
2026-08-31 10:09:17 +08:00
expect ( source ). toContain ( 'coLoadGroupSummary(coLoadGroup(row as UnifiedOrderResource)!)' )
2026-08-31 10:48:23 +08:00
expect ( source ). not . toContain ( 'coLoadDetailGroup' )
2026-08-30 15:49:01 +08:00
expect ( source ). not . toContain ( '<span v-else>—</span></template>\n <template #orderStatus' )
2026-08-31 10:09:17 +08:00
expect ( source ). toContain ( 'v-for="member in coLoadDetailMembers"' )
2026-08-31 10:48:23 +08:00
expect ( source ). toContain ( '<thead><tr><th>入仓码</th></tr></thead>' )
2026-08-31 10:09:17 +08:00
expect ( source ). toContain ( '{{ member.inboundCode }}' )
2026-08-31 10:31:53 +08:00
expect ( source ). toContain ( '同柜组成员入仓码尚未返回,请刷新后重试' )
expect ( source ). toContain ( 'coLoadDetailHasMissingInboundCodes' )
2026-08-31 10:09:17 +08:00
expect ( source ). not . toContain ( 'visibleInboundCode' )
expect ( source ). not . toContain ( 'visibleOrderNo' )
expect ( source ). not . toContain ( '<th>订单编码</th>' )
2026-08-30 17:43:03 +08:00
expect ( source ). not . toContain ( '客户 ID' )
2026-08-31 14:54:57 +08:00
expect ( source ). toContain ( "coLoadDetailSummary?.status === 'ACTIVE'" )
expect ( source ). toContain ( '解除同柜组' )
expect ( source ). toContain ( 'cancelPlatformCoLoadGroup(inboundCode' )
expect ( source ). toContain ( '<ApiErrorMeta :details="coLoadCancelError"' )
2026-08-30 15:49:01 +08:00
})
2026-08-31 10:48:23 +08:00
it ( 'does not expose a generic relationship probe in list rows' , () => {
expect ( source ). not . toContain ( '>履约详情</el-button>' )
2026-08-31 10:31:53 +08:00
expect ( source ). not . toContain ( '>关联数据</el-button>' )
2026-08-31 10:48:23 +08:00
expect ( source ). not . toContain ( 'openInboundRelationship' )
expect ( source ). not . toContain ( 'inboundRelationshipDialogOpen' )
expect ( source ). not . toContain ( '<InboundCodeRelationshipDialog' )
2026-08-31 10:31:53 +08:00
})
2026-08-11 18:06:35 +08:00
it ( 'keeps editable query values separate from the applied list request' , () => {
expect ( source ). toContain ( 'const appliedFilters = ref<FilterModel>(emptyFilters())' )
expect ( source ). toContain ( 'const activeFilters = appliedFilters.value' )
expect ( source ). toContain ( 'appliedFilters.value = cloneFilters(filters.value)' )
expect ( source ). toContain ( 'appliedFilters.value = emptyFilters()' )
expect ( source ). not . toContain ( 'filters.value.mainStatus' )
})
2026-08-06 20:14:56 +08:00
it ( 'keeps only confirmed business actions as explicit boundaries' , () => {
2026-08-06 13:56:04 +08:00
expect ( source ). toContain ( 'nextActionBoundary' )
2026-08-07 20:07:37 +08:00
expect ( source ). not . toContain ( 'class="action-boundary"' )
2026-08-17 17:49:10 +08:00
expect ( source ). toContain ( "can('platform:firstleg:inbound:measure:view')" )
2026-08-06 13:56:04 +08:00
expect ( source ). toContain ( 'openMeasurement' )
2026-08-17 17:49:10 +08:00
expect ( source ). toContain ( '<InboundMeasurementDialog' )
2026-08-13 19:00:37 +08:00
expect ( source ). toContain ( 'measurementDialogOpen.value = true' )
2026-08-17 17:49:10 +08:00
expect ( source ). toContain ( '@updated="handleMeasurementUpdated"' )
expect ( source ). toContain ( '@submitted="handleMeasurementSubmitted"' )
expect ( source ). not . toContain ( '<InboundMeasurementPanel' )
expect ( source ). not . toContain ( 'class="inbound-task-dialog"' )
2026-08-13 20:00:38 +08:00
expect ( source ). not . toContain ( "name: 'inbound-order-detail'" )
expect ( source ). not . toContain ( 'openInboundFromDetail' )
expect ( source ). not . toContain ( 'inbound-code-link' )
2026-08-06 20:14:56 +08:00
expect ( source ). not . toContain ( '待接入:智能分柜' )
2026-08-06 13:56:04 +08:00
expect ( source ). not . toContain ( 'updateMainStatus' )
expect ( source ). not . toContain ( '人工进入分柜' )
expect ( source ). not . toContain ( 'performPlatformUnifiedOrderAction' )
expect ( source ). not . toContain ( '/actions/' )
})
2026-08-12 14:08:33 +08:00
it ( 'treats WAIT_SPLIT as a display/filter state without inventing a split action' , () => {
expect ( source ). toContain ( '...unifiedOrderStatusOptions' )
expect ( source ). toContain ( 'function nextActionBoundary(row: UnifiedOrderResource)' )
expect ( source ). toContain ( "return row.nextAction === 'CONFIRM_MEASUREMENT' ? '待完成测量' : ''" )
expect ( source ). not . toContain ( '人工进入分柜' )
expect ( source ). not . toContain ( "action === 'split'" )
expect ( source ). not . toContain ( "nextAction === 'SMART_SPLIT'" )
})
2026-08-07 14:47:11 +08:00
it ( 'approves pending review orders without exposing an order carrier assignment' , () => {
2026-08-06 18:02:04 +08:00
expect ( source ). toContain ( "row.nextAction === 'APPROVE_ORDER'" )
expect ( source ). toContain ( "can('platform:order:approve')" )
expect ( source ). toContain ( 'await approvePlatformUnifiedOrder(tenantId, orderId)' )
2026-08-07 10:45:19 +08:00
expect ( source ). toContain ( 'title="审核订单"' )
expect ( source ). toContain ( 'confirm-text="审核通过"' )
expect ( source ). not . toContain ( 'assignPlatformUnifiedOrderCarrier' )
expect ( source ). not . toContain ( 'listPlatformPartners' )
2026-08-07 14:47:11 +08:00
expect ( source ). not . toContain ( '指派承运商' )
expect ( source ). not . toContain ( '待平台指派' )
2026-08-06 18:02:04 +08:00
})
2026-08-06 13:56:04 +08:00
it ( 'provides unified detail, history and internal note states' , () => {
expect ( source ). toContain ( 'getPlatformUnifiedOrder' )
expect ( source ). toContain ( 'aria-label="状态历史"' )
expect ( source ). toContain ( 'aria-label="内部备注"' )
expect ( source ). toContain ( '订单详情暂时无法加载' )
2026-08-07 14:47:11 +08:00
expect ( source ). toContain ( '一、客户下单数据' )
expect ( source ). toContain ( '二、外箱规数据' )
expect ( source ). toContain ( '三、实际测量数据' )
expect ( source ). toContain ( 'measurementCollapsed = reactive({ customer: false, outer: false, actual: false })' )
expect ( source ). toContain ( 'function toggleMeasurementSection(section: keyof typeof measurementCollapsed)' )
expect ( source ). toContain ( ':aria-expanded="!measurementCollapsed.customer"' )
expect ( source ). toContain ( ':aria-expanded="!measurementCollapsed.outer"' )
expect ( source ). toContain ( ':aria-expanded="!measurementCollapsed.actual"' )
expect ( source ). toContain ( 'class="measurement-section-summary"' )
expect ( source ). toContain ( '<ChevronUp v-if="!measurementCollapsed.customer"' )
expect ( source ). toContain ( '<ChevronDown v-else :size="18" />' )
expect ( source ). toContain ( "{ key: 'girthCm', label: '箱规周长(cm)' }" )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( 'label="#" prop="itemNo"' )
expect ( source ). toContain ( "const actualMeasuredColumns = measuredColumns.filter((column) => column.key !== 'itemNo')" )
expect ( source ). toContain ( 'v-for="column in actualMeasuredColumns"' )
expect ( source ). toContain ( '仓库修改外箱(长)' )
expect ( source ). toContain ( '仓库修改外箱(宽)' )
expect ( source ). toContain ( '仓库修改外箱(高)' )
expect ( source ). toContain ( 'saveWarehouseOuterDimensions' )
2026-09-03 21:09:40 +08:00
expect ( source ). not . toContain ( '请至少输入一项需要修改的外箱尺寸' )
expect ( source ). toContain ( ".map((value) => value == null || value === '' ? null : Number(value))" )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( '相关数值已重新计算' )
2026-08-06 20:14:56 +08:00
expect ( source ). toContain ( 'runMoreAction($event, row as UnifiedOrderResource)' )
2026-08-10 17:42:17 +08:00
expect ( source ). toContain ( ':aria-label="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"' )
expect ( source ). toContain ( ':title="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"' )
2026-08-06 20:14:56 +08:00
expect ( source ). toContain ( "can('platform:order:note')" )
2026-08-07 14:47:11 +08:00
expect ( source ). toContain ( 'await loadOrders()' )
2026-08-11 19:35:28 +08:00
expect ( source ). toContain ( 'orderStatusLabel(detail.summary.internalOrderStatus || detail.summary.mainStatus)' )
expect ( source ). toContain ( 'feeReviewLabel(detail.summary.feeReviewStatus)' )
expect ( source ). toContain ( 'unifiedOrderLoadingStatusLabel(detail.summary.transport?.loadingStatus)' )
expect ( source ). not . toContain ( "value.replaceAll('_', ' ')" )
2026-08-06 13:56:04 +08:00
})
2026-09-03 21:09:40 +08:00
it ( 'keeps the customer order package count independent from measured inbound totals' , () => {
expect ( source ). toContain ( "{ source: '客户下单数据', count: customerTotals.count" )
expect ( source ). not . toContain ( "{ source: '客户下单数据', count: summary?.totalPackageCount" )
})
2026-08-27 21:48:15 +08:00
it ( 'uses the inbound measurement girth formula for cargo dimensions' , () => {
expect ( source ). toContain ( 'formatNumber(length + 2 * (width + height))' )
expect ( source ). not . toContain ( 'formatNumber(2 * (width + height))' )
})
2026-09-03 10:46:07 +08:00
it ( 'shows the matched dimension addition and enabled multiplier in calculation choices' , () => {
expect ( source ). toContain ( 'getCustomerDimensionConfig' )
expect ( source ). toContain ( 'matchingDimensionRule' )
2026-09-03 15:05:30 +08:00
expect ( source ). toContain ( '|| rules[rules.length - 1]' )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( '`原尺寸 + ${Number(values[0]) * 2}cm(单边 +${values[0]}cm) `' )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( '`原尺寸 × ${rule.lengthFactor}`' )
expect ( source ). toContain ( 'label="箱规尺寸" value="ADD"' )
expect ( source ). toContain ( 'label="箱规系数" value="MULTIPLY"' )
})
2026-09-03 17:49:01 +08:00
it ( 'keeps preview modes independent and persists both outer-carton selectors immediately' , () => {
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( "measurementSectionModes = reactive<Record<MeasurementSection, MeasurementCalculationMode>>" )
expect ( source ). toContain ( ':model-value="measurementSectionModes.customer"' )
expect ( source ). toContain ( ':model-value="measurementSectionModes.outer"' )
expect ( source ). toContain ( ':model-value="measurementSectionModes.actual"' )
expect ( source ). toContain ( "updateMeasurementSectionMode('customer', $event)" )
expect ( source ). toContain ( "updateMeasurementSectionMode('actual', $event)" )
2026-09-03 17:49:01 +08:00
expect ( source ). not . toContain ( "updateMeasurementSectionMode('outer', $event)" )
expect ( source . match ( /@change="selectAndSaveOuterMeasurementMode\(\$event\)"/g )). toHaveLength ( 2 )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( '@visible-change="handleOuterModeDropdownVisibleChange($event)"' )
2026-09-03 17:49:01 +08:00
expect ( source . match ( /:disabled="Boolean\(measurementStrategySavingId\) \|\| !can\('platform:firstleg:inbound:measure:edit'\)"/g )). toHaveLength ( 2 )
expect ( source ). toContain ( 'const previousMode = measurementSectionModes.outer' )
expect ( source ). toContain ( 'if (!saved) measurementSectionModes.outer = previousMode' )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( 'const selectedSameMode = outerModeAtDropdownOpen.value === measurementSectionModes.outer' )
expect ( source ). toContain ( 'await nextTick()' )
expect ( source ). toContain ( 'const saved = await updateMeasurementCalculationMode(null, mode, ruleId)' )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( 'calculatedMeasurementRow(row, measurementSectionModes.customer, true)' )
2026-09-03 12:00:07 +08:00
expect ( source ). toContain ( 'calculatedMeasurementRow(source, measurementSectionModes.outer)' )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( 'calculatedMeasurementRow(row as unknown as Record<string, unknown>, measurementSectionModes.actual, false, false)' )
2026-09-03 15:05:30 +08:00
expect ( source ). toContain ( "return Number(hasOuterOverride && override != null && override !== '' ? override : value)" )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( "const hasOuterOverride = useWarehouseOuterOverrides && !customerSource" )
expect ( source ). not . toContain ( "!customerSource && mode !== 'ACTUAL'" )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'rawDimensions.map((value) => value + rule.edgeAdditionCm * 2)' )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( 'matchedSpecificationCoefficient(chargeRule, calculationSource)' )
expect ( source ). toContain ( 'listCustomerChargeRules(customerId, tenantId)' )
expect ( source ). toContain ( 'matchedDimensions.map((value) => roundedUp(value * specificationCoefficient, 3))' )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'const selectedMode: MeasurementCalculationMode = measurementSectionModes.outer' )
2026-09-03 10:46:07 +08:00
expect ( source ). not . toContain ( '[chargeRule.lengthFactor, chargeRule.widthFactor, chargeRule.heightFactor]' )
expect ( source ). not . toContain ( 'column.key === \'coefficientMode\'' )
expect ( source ). not . toContain ( '@change="updateMeasurementCalculationMode(null, $event)"' )
})
2026-09-03 17:49:01 +08:00
it ( 'confirms customer billing from the saved outer-carton totals' , () => {
expect ( source ). toContain ( 'const outerTotals = measurementTotals(outerMeasurementRows.value)' )
expect ( source ). toContain ( "ElMessage.warning('外箱规数据不完整,请先保存外箱规尺寸和计算规则')" )
2026-09-04 11:50:34 +08:00
expect ( source ). toContain ( "weightSource: 'ACTUAL'" )
expect ( source ). toContain ( "volumeSource: 'ACTUAL'" )
2026-09-03 21:09:40 +08:00
expect ( source ). not . toContain ( '测量审核通过后才能确认并发送费用' )
expect ( source ). not . toContain ( "reviewStatus !== 'APPROVED'" )
expect ( source ). toContain ( 'const hasActualMeasurementData = computed(() => (measurementDetail.value?.items?.length ?? 0) > 0)' )
expect ( source ). toContain ( "暂无实际测量数据,不能确认并发送费用" )
2026-09-04 11:50:34 +08:00
expect ( source ). not . toContain ( ':disabled="confirmFeeSending || measurementLoading || !hasActualMeasurementData"' )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'selectAndSaveOuterMeasurementMode($event)' )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( 'const saved = await updateMeasurementCalculationMode(null, mode, ruleId)' )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'coefficientMode: targetIds.has(String(item.id)) ? mode' )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( 'outerLengthCm: item.outerLengthCm' )
expect ( source ). toContain ( 'outerWidthCm: item.outerWidthCm' )
expect ( source ). toContain ( 'outerHeightCm: item.outerHeightCm' )
expect ( source ). toContain ( "String(item.chargeRuleId) === String(activeRule!.id)" )
expect ( source ). toContain ( 'item.calculatedOuterLengthCm, item.calculatedOuterWidthCm, item.calculatedOuterHeightCm' )
expect ( source ). toContain ( 'if (dimensions.every((dimension) => dimension == null)) return true' )
expect ( source ). toContain ( '已保存并绑定计算规则,体积已重新计算' )
2026-09-04 11:50:34 +08:00
expect ( source ). toContain ( 'const measuredGrossWeight = Number(measurementDetail.value?.actualGrossWeightKg)' )
expect ( source ). toContain ( 'const measuredNetWeight = Number(measurementDetail.value?.actualNetWeightKg)' )
expect ( source ). toContain ( 'const actualVolume = Number(measurementDetail.value?.actualVolumeCbm)' )
expect ( source ). toContain ( 'feeConfirmWeight.value = measuredGrossWeight > 0' )
expect ( source ). toContain ( 'feeConfirmVolume.value = actualVolume > 0' )
2026-09-03 17:49:01 +08:00
const sendFeeSource = source . slice ( source . indexOf ( 'async function executeConfirmAndSendFee()' ), source . indexOf ( 'function handleOrderSelectionChange' ))
expect ( sendFeeSource ). not . toContain ( 'saveInboundMeasurementDraft' )
expect ( sendFeeSource ). not . toContain ( 'updateMeasurementCalculationMode' )
2026-09-03 21:09:40 +08:00
expect ( source . slice ( source . indexOf ( 'async function openFeeConfirmDialog()' ), source . indexOf ( 'async function executeConfirmAndSendFee()' ))). not . toContain ( 'getLatestInboundFee' )
2026-09-03 17:49:01 +08:00
})
2026-09-04 11:50:34 +08:00
it ( 'clears an invalidated measurement loading state before refreshing order details' , () => {
const detailLoaderSource = source . slice ( source . indexOf ( 'async function loadDetail(force = false)' ), source . indexOf ( 'watch([page, pageSize]' ))
expect ( detailLoaderSource ). toContain ( 'measurementRequestSerial += 1\n measurementLoading.value = false' )
})
2026-09-03 17:49:01 +08:00
it ( 'restores the saved outer-carton rule and server-calculated dimensions on re-entry' , () => {
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( 'const savedModes = [...new Set(response.items.map((item) => item.coefficientMode)' )
expect ( source ). toContain ( 'measurementSectionModes.outer = savedModes.length === 1' )
expect ( source ). toContain ( '? savedModes[0]!' )
expect ( source ). toContain ( "configuredMode === 'ADD' ? 'ADD' : 'ACTUAL'" )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'row.calculatedOuterLengthCm' )
expect ( source ). toContain ( 'measurementCalculationModeForRow(source) === measurementSectionModes.outer' )
2026-09-03 21:09:40 +08:00
expect ( source ). toContain ( "String(row.calculationRuleVersion || '').startsWith('WAREHOUSE_OUTER_OVERRIDE_')" )
expect ( source ). toContain ( 'warehouseOuterOverride' )
expect ( source ). toContain ( 'Math.abs(Number(value) - Number(recalculatedDimensions[index])) < 0.001' )
2026-09-03 17:49:01 +08:00
expect ( source ). toContain ( 'lengthCm: Number(savedDimensions[0])' )
})
2026-08-17 18:00:42 +08:00
it ( 'binds dimension and weight evidence to their formal server records without mixing galleries' , () => {
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( '<el-table :data="actualMeasurementRows" row-key="id"' )
2026-08-21 16:28:59 +08:00
expect ( source ). toContain ( '<el-table-column label="箱号" prop="boxCode"' )
expect ( source ). toContain ( "row.boxCode || '—'" )
2026-08-30 17:43:03 +08:00
expect ( source ). toContain ( "`箱号 ${row.boxCode || row.itemNo || '箱规编码待补充'} 的尺寸图片`" )
expect ( source ). not . toContain ( 'row.boxCode || row.itemNo || row.id' )
2026-08-17 18:00:42 +08:00
expect ( source ). toContain ( 'label="尺寸图片"' )
expect ( source ). toContain ( ':file-id="row.intelligentImageFileId"' )
expect ( source ). toContain ( 'openMeasurementEvidence(row.intelligentImageFileId' )
expect ( source ). toContain ( 'v-for="weightResult in measurementDetail.weightResults"' )
expect ( source ). toContain ( ':file-id="weightResult.fileId"' )
expect ( source ). toContain ( 'openMeasurementEvidence(weightResult.fileId' )
expect ( source ). toContain ( 'measurementDetail?.actualGrossWeightKg' )
expect ( source ). toContain ( 'weightResult.grossWeightKg' )
expect ( source ). toContain ( 'weightResult.netWeightKg' )
expect ( source ). toContain ( "weightResult.palletType || '未记录类型'" )
expect ( source ). not . toContain ( 'weightResult.palletCode' )
expect ( source ). not . toContain ( 'measurementImageUrls' )
expect ( source ). not . toContain ( 'const fileIds = [...measurementDetail.value.items' )
})
it ( 'keeps authenticated evidence loading, original preview, permission and error states' , () => {
expect ( source ). toContain ( '<MeasurementEvidenceThumbnail' )
expect ( source ). toContain ( "downloadWeightImage(String(summary.tenantId), String(summary.inboundId), fileId)" )
expect ( source ). toContain ( 'measurementPreviewError' )
expect ( source ). toContain ( '测量原图暂时无法读取' )
expect ( source ). toContain ( "can('platform:firstleg:inbound:measure:view')" )
2026-08-17 12:31:27 +08:00
})
2026-08-12 09:25:55 +08:00
it ( 'shows parent-child markers only for related rows and keeps the aggregate in detail' , () => {
expect ( source ). not . toContain ( "type: 'expand'" )
expect ( source ). toContain ( 'v-if="(row as UnifiedOrderResource).relation"' )
expect ( source ). toContain ( '父单 · ${row.relation.childCount} 个子单' )
expect ( source ). toContain ( '子单 · 第 ${row.relation.splitSequence} 次' )
expect ( source ). toContain ( 'title="父子订单关联"' )
expect ( source ). toContain ( '@click="openOrderRelation(row as UnifiedOrderResource)"' )
2026-08-12 16:40:09 +08:00
expect ( source ). toContain ( "{ key: 'fulfillment', label: '拆分履约', icon: OrderFulfillmentIcon }" )
2026-08-11 09:16:58 +08:00
expect ( source ). toContain ( "activeDetailTab === 'fulfillment'" )
expect ( source ). toContain ( '根单、已完成分片与当前余量' )
})
2026-08-10 17:42:17 +08:00
it ( 'distinguishes overflow action buttons by row and keeps a stable missing-field fallback' , () => {
expect ( source ). toContain ( 'function moreUnifiedOrderActionsLabel(row: UnifiedOrderResource)' )
2026-08-14 19:59:32 +08:00
expect ( source ). toContain ( "String(row.inboundCode || '').trim() || '当前入仓单'" )
expect ( source ). not . toContain ( 'compatibleOrder.orderNo' )
2026-08-10 17:42:17 +08:00
expect ( source ). not . toContain ( 'aria-label="更多订单操作"' )
expect ( source ). not . toContain ( 'title="更多操作"' )
})
2026-08-06 19:03:13 +08:00
it ( 'renders order detail as a routed full page instead of a drawer' , () => {
2026-09-01 21:09:39 +08:00
expect ( source ). toContain ( '<template v-if="isDetailRoute">' )
2026-08-06 19:03:13 +08:00
expect ( source ). toContain ( 'class="order-detail-header"' )
expect ( source ). toContain ( 'aria-label="订单详情模块"' )
expect ( source ). toContain ( "{ key: 'basic', label: '基本信息'" )
expect ( source ). toContain ( "{ key: 'measurement', label: '测量数据'" )
expect ( source ). not . toContain ( 'detailOpen' )
2026-08-07 20:07:37 +08:00
expect ( source ). toContain ( 'watch(detailRouteKey,' )
expect ( source ). toContain ( 'requestKey !== detailRouteKey.value' )
2026-08-06 19:03:13 +08:00
})
2026-08-07 10:45:19 +08:00
it ( 'keeps the basic information compact and maps available order fields' , () => {
expect ( source ). toContain ( 'grid-template-columns: repeat(4, minmax(0, 1fr))' )
2026-08-22 10:02:13 +08:00
for ( const label of [ '业务类型' , '买单 / 退税' , '提货方式' , '配载状态' , '目的仓类型' , '客户申报净重' , '客户申报总体积' , '商检验' , '提货联系人' , '目的地地址' ]) {
2026-08-07 10:45:19 +08:00
expect ( source ). toContain ( `<dt> ${ label } </dt>` )
}
2026-08-22 10:02:13 +08:00
expect ( source ). toContain ( "['actualVolumeCbm', 'chargeableVolumeCbm'].includes(property)" )
2026-08-07 10:45:19 +08:00
expect ( source ). toContain ( "partyValue(detail.order.parties, 'SENDER', 'phone')" )
2026-08-07 14:47:11 +08:00
expect ( source ). toContain ( "partyValue(detail.order.parties, 'RECEIVER', 'phone')" )
expect ( source ). not . toContain ( "partyValue(detail.order.parties, 'CONSIGNEE'" )
2026-08-07 10:45:19 +08:00
expect ( source ). toContain ( "cargoTotal(detail.order.cargoes, 'netWeightKg')" )
2026-08-17 18:32:41 +08:00
expect ( source ). toContain ( 'InboundFeePanel' )
2026-08-17 18:43:12 +08:00
expect ( source ). toContain ( 'InboundFeeDetailManager' )
2026-08-17 18:32:41 +08:00
expect ( source ). toContain ( ':customer-id="String(detail.summary.tenantId)"' )
2026-08-17 19:13:25 +08:00
expect ( source ). toContain ( 'compact' )
2026-09-04 11:50:34 +08:00
expect ( source ). toContain ( 'display-only' )
expect ( source ). toContain ( ':key="`fee-${normalizedInboundId}-${feePanelRevision}`"' )
expect ( source ). toContain ( 'feePanelRevision.value += 1' )
2026-08-17 18:43:12 +08:00
expect ( source ). toContain ( '头程自动初算' )
expect ( source ). toContain ( '其他费用明细' )
2026-08-12 16:31:46 +08:00
expect ( source ). toContain ( '@changed="loadDetail(true)"' )
2026-08-11 19:35:28 +08:00
expect ( source ). toContain ( 'firstLegTransportModeLabel(detail.order.order?.transportMode)' )
expect ( source ). toContain ( 'firstLegPickupMethodLabel(detail.order.order?.pickupMethod)' )
expect ( source ). toContain ( 'firstLegDestinationTypeLabel(detail.order.order?.destinationType)' )
2026-08-07 10:45:19 +08:00
})
2026-08-18 16:09:43 +08:00
it ( 'does not mount fee consumers until a real inboundId exists' , () => {
const feeSection = source . slice ( source . indexOf ( "activeDetailTab === 'fees'" ), source . indexOf ( "activeDetailTab === 'history'" ))
expect ( source ). toContain ( 'const normalizedInboundId = computed(() => normalizedIdentifier(detail.value?.summary.inboundId))' )
expect ( feeSection ). toContain ( 'v-if="!normalizedInboundId" description="订单尚未生成入库单,暂无入仓费用"' )
expect ( feeSection . match ( /:inbound-id="normalizedInboundId"/g )). toHaveLength ( 2 )
expect ( feeSection ). not . toContain ( 'String(detail.summary.inboundId)' )
})
2026-08-10 09:12:06 +08:00
it ( 'mounts downstream progress inside the existing order detail without opening unsafe writes' , () => {
expect ( source ). toContain ( 'UnifiedOrderWorkflowPanel' )
2026-08-12 16:40:09 +08:00
expect ( source ). toContain ( "{ key: 'workflow', label: '后半程作业', icon: OrderFulfillmentIcon }" )
2026-08-10 12:05:20 +08:00
expect ( source ). toContain ( ':order-id="String(detail.summary.orderId)"' )
expect ( source ). not . toContain ( ':show-delivery=' )
2026-08-10 09:12:06 +08:00
expect ( source ). toContain ( "detail.value?.summary.inboundId" )
2026-08-06 20:14:56 +08:00
expect ( routeSource ). not . toContain ( 'unified-order-workflow' )
2026-08-06 13:56:04 +08:00
})
2026-08-12 16:40:09 +08:00
2026-09-03 11:54:20 +08:00
it ( 'only exposes declaration editing before loading and marks channel changes' , () => {
expect ( source ). toContain ( "new Set<UnifiedOrderMainStatus>(['PENDING_REVIEW', 'WAIT_MEASURE', 'WAIT_SPLIT'])" )
expect ( source ). toContain ( 'declarationEditableStatuses.has(detail.value.summary.mainStatus)' )
expect ( source ). not . toContain ( "detail.value.summary.mainStatus === 'WAIT_MEASURE'" )
2026-09-02 10:24:07 +08:00
expect ( source ). toContain ( "can('platform:order:declaration:edit')" )
expect ( source ). toContain ( '编辑客户申报资料' )
expect ( source ). toContain ( '<OrderDeclarationEditorDialog' )
expect ( source ). toContain ( '曾变更渠道' )
expect ( source ). toContain ( '@updated="handleDeclarationUpdated"' )
})
2026-09-01 21:09:39 +08:00
it ( 'supports the measurement-review deep link with an inbound-code lookup and recoverable return path' , () => {
expect ( routeSource ). toContain ( "name: 'unified-order-detail-by-inbound-code'" )
expect ( routeSource . indexOf ( "path: 'orders/inbound/:inboundCode'" )). toBeLessThan ( routeSource . indexOf ( "path: 'orders/:orderId'" ))
expect ( source ). toContain ( "route.query.tab === 'measurement'" )
expect ( source ). toContain ( "route.query.from === 'measurement-review'" )
expect ( source ). toContain ( 'getPlatformInboundCodeAssociation(inboundCode)' )
expect ( source ). toContain ( "activeDetailTab.value = 'measurement'" )
expect ( source ). toContain ( 'await loadMeasurementDetails()' )
expect ( source ). toContain ( "router.push({ name: 'inbound-measurement-reviews' })" )
expect ( source ). toContain ( 'router.back()' )
expect ( source ). toContain ( '可能无权查看或目标已不存在' )
expect ( source ). toContain ( '返回测量审核列表' )
})
2026-08-12 16:40:09 +08:00
it ( 'keeps detail tabs compact on one horizontally scrollable row' , () => {
expect ( source ). toContain ( '.detail-tabs { display: flex; overflow-x: auto; overflow-y: hidden;' )
expect ( source ). toContain ( 'min-width: max-content; min-height: 56px; flex: 0 0 auto;' )
expect ( source ). not . toContain ( 'grid-template-columns: repeat(6, minmax(124px, 1fr))' )
expect ( source ). not . toContain ( '.detail-tabs { grid-template-columns: repeat(6, 132px); }' )
})
2026-08-12 17:29:35 +08:00
it ( 'matches the prototype table grid and distinguishes container identifiers' , () => {
expect ( source ). toContain ( 'row-key="orderId" border stripe' )
2026-09-03 10:46:07 +08:00
expect ( source ). toContain ( '<el-table :data="customerMeasurementRows" border stripe' )
expect ( source ). toContain ( '<el-table :data="outerMeasurementRows" border stripe' )
expect ( source ). toContain ( '<el-table :data="actualMeasurementRows" row-key="id" border stripe' )
expect ( source ). toContain ( '<el-table :data="customerMeasurementRows" border stripe empty-text="暂无客户下单数据"' )
2026-08-12 17:29:35 +08:00
expect ( source ). toContain ( '--el-table-border-color: #e5e7eb' )
expect ( source ). toContain ( '--el-table-row-hover-bg-color: #f3f7fb' )
expect ( source ). toContain ( '.el-table--striped .el-table__body tr.el-table__row--striped' )
expect ( source ). toContain ( "label: '真实集装箱号'" )
expect ( source ). toContain ( "label: '内部柜次号'" )
expect ( source ). toContain ( '<dt>真实集装箱号</dt>' )
expect ( source ). toContain ( '<dt>内部柜次号</dt>' )
})
2026-08-12 17:45:49 +08:00
it ( 'uses outlined semantic buttons for row business actions while keeping detail navigation explicit' , () => {
const rowActions = source . slice ( source . indexOf ( '<template #actions' ), source . indexOf ( '<template #inboundCode' ))
expect ( rowActions ). toContain ( 'size="small" plain :type="unifiedOrderActionType(action)"' )
expect ( rowActions ). not . toContain ( 'v-for="action in directUnifiedOrderActions(row as UnifiedOrderResource)" :key="action" link' )
expect ( rowActions ). toContain ( '<Eye :size="15" />详情' )
expect ( source ). toContain ( '<el-button link class="back-button"' )
})
2026-08-06 13:56:04 +08:00
})