import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const view = readFileSync(new URL('./WarehouseServiceView.vue', import.meta.url), 'utf8') const api = readFileSync(new URL('../api/warehouseServices.ts', import.meta.url), 'utf8') test('仓储转运先选择入仓方式并按方式切换地址信息', () => { assert.match(view, /value="WAREHOUSE_PICKUP">仓库上门提货/) assert.match(view, /value="CUSTOMER_DELIVERY">客户送货到仓/) assert.match(view, /form\.deliveryMethod === 'WAREHOUSE_PICKUP'.*提货地址/s) assert.match(view, /form\.deliveryMethod === 'CUSTOMER_DELIVERY'.*送货地址.*收货联系人.*联系电话/s) assert.match(view, /getMerchantWarehouseDetail\(form\.warehouse\)/) }) test('仓储转运支持无目的地、FBA 仓和私人仓并显式选择是否换标', () => { assert.match(view, /是否有目的去向/) assert.match(view, /value="NONE">暂未确定/) assert.match(view, /value="FBA">FBA 仓/) assert.match(view, /value="PRIVATE">私人仓/) assert.match(view, /transferDestinationType === 'PRIVATE'.*私人仓地址.*邮编.*私人仓联系人.*联系电话/s) assert.match(view, /是否需要换标/) assert.match(view, /:value="false">不需要换标/) assert.match(view, /:value="true">需要换标/) assert.match(api, /fbaWarehouseCode\?: string/) assert.match(api, /privateDestinationAddress\?: string; privateDestinationPostalCode\?: string/) assert.match(api, /fbaShipmentNo\?: string/) assert.doesNotMatch(view, /换标费用(CNY)/) assert.doesNotMatch(view, /relabelChargeAmount:/) }) test('仓储转运换标整票只允许一个目的仓', () => { assert.match(view, /props\.service === 'transfer' && transferDestinationType === 'FBA'[^\n]+未换标货物目的 FBA 仓/) assert.match(view, /此处只表示不换标箱子的去向/) assert.match(view, /FBA 货件编号<\/span>')[1]?.split('warehouse-transfer-submit-bar')[0] || '' assert.doesNotMatch(transferRelabelSection, /v-model="row\.destinationWarehouseId"/) assert.doesNotMatch(transferRelabelSection, /FBA 目的仓 \*<\/th>/) assert.doesNotMatch(view, /applyTransferRelabelDestination/) assert.match(view, /destinationWarehouseId: binding\.destinationWarehouseCode \|\| ''/) assert.match(view, /props\.service === 'transfer' \? '客户箱唛号' : '仓库唛头'/) assert.match(view, /已导入 \$\{importedRows\.length\} 条仓储转运换标明细/) assert.doesNotMatch(transferRelabelSection, /新 FBA 箱唛<\/th>FBA 目的仓<\/th>/) assert.match(view, /const includeDestination = false/) }) test('仓储转运不要求客户填写内部入仓号和我方箱唛,但换标时填写客户箱唛', () => { const transferConfig = view.match(/transfer:\s*\{[^\n]+/)?.[0] || '' const transferSubmit = view.match(/const payload: TransferCreate = \{[^\n]+/)?.[0] || '' assert.doesNotMatch(transferConfig, /进仓编号/) assert.doesNotMatch(transferSubmit, /inboundCode|sourceLastlegOrderIds/) assert.match(view, /货物明细/) assert.match(view, /客户只填写自己的箱唛,我方箱唛由系统生成/) assert.match(view, /downloadTransferCartonMarkTemplate/) assert.match(view, /importTransferCartonMarks/) assert.match(view, /下载 SKU 关联模板/) assert.match(view, /上传 SKU 关联/) assert.match(view, /下载箱号关联模板/) assert.match(view, /上传箱号关联/) assert.match(view, /下载 SKU 混箱模板/) assert.match(view, /上传 SKU 混箱/) assert.match(view, /换标类型/) assert.match(view, /v-if="labelingMode === 'carton'"/) assert.match(view, /v-else-if="labelingMode === 'sku'"/) assert.match(view, /v-else class="warehouse-table-scroll"/) assert.match(view, /添加 SKU 明细/) assert.match(view, /绑定明细/) assert.match(view, /旧 SKU \*.*新 SKU \*.*换标数量(件)\*.*新 FBA 箱唛 \*.*备注/) assert.match(transferSubmit, /fbaBoxNos: customerCartonMarks/) assert.match(transferSubmit, /shippingMark: transferCustomerMarks\(item\)/) assert.match(transferSubmit, /relabelItems: form\.relabel/) assert.match(view, /props\.service === 'disposal' \? '本次废弃箱数' : '预计箱数'/) assert.match(view, /FNSKU \/ 箱唛<\/th>/) assert.doesNotMatch(view, /FNSKU \/ 箱唛<\/th>/) }) test('上门提货不提交物流号,需要换标时随转运单登记申请', () => { assert.match(view, /trackingNo:\s*form\.deliveryMethod === 'CUSTOMER_DELIVERY'\s*\?[^:]+:\s*undefined/) assert.match(view, /本次提交将同时登记换标申请/) assert.match(view, /form\.deliveryMethod === 'CUSTOMER_DELIVERY'.*物流跟踪号/s) assert.match(view, /提货联系人/) assert.match(view, /联系人电话/) assert.match(view, /换标联系人/) assert.match(view, /form\.pickupContact/) assert.match(view, /form\.pickupPhone/) assert.match(view, /form\.relabelContact/) assert.match(view, /form\.relabelPhone/) assert.match(view, /选择海外仓后自动带出,也可手动填写/) assert.match(view, /pickupContactName: form\.deliveryMethod === 'WAREHOUSE_PICKUP' \? form\.pickupContact : undefined/) assert.match(view, /pickupContactPhone: form\.deliveryMethod === 'WAREHOUSE_PICKUP' \? form\.pickupPhone : undefined/) assert.match(view, /relabelContactName: form\.relabel \? form\.relabelContact/) assert.match(api, /pickupContactName\?: string; pickupContactPhone\?: string/) }) test('仓储转运列表操作仓使用海外仓接口数据', () => { assert.match(view, /loadAllWarehouseRecords\('OVERSEAS', getMerchantWarehouses\)/) assert.match(view, /warehouseDisplay\(item\.operatingWarehouseId\)/) assert.match(view, /\['OVERSEAS', 'OVERSEAS_PARTNER'\]/) assert.match(view, /v-for="warehouse in \(props\.service === 'relabel' \? fbaWarehouses : operationWarehouses\)"/) assert.doesNotMatch(view, /美西洛杉矶仓/) }) test('仓储转运预计发货时间可选并只读展示实际发货时间', () => { assert.match(view, /预计发货时间/) assert.match(view, /v-model="form\.estimatedShipAt"/) assert.match(view, /选填,可线下确认后补充/) assert.doesNotMatch(view, /!form\.estimatedShipAt && '预计发货时间'/) assert.match(view, /estimatedShipAt: form\.estimatedShipAt/) assert.match(view, /实际发货时间由仓库出库或 PDA 完成后回填/) assert.match(api, /estimatedShipAt\?: string/) }) test('客户仓储转运展示每个实际流转状态的中文名称', () => { assert.match(view, /label: '草稿', value: 'DRAFT'/) assert.match(view, /label: '待审核', value: 'PENDING_REVIEW'/) assert.match(view, /label: '入库测量中', value: 'INBOUND_MEASURING'/) assert.match(view, /label: '待客户确认实测', value: 'WAIT_CUSTOMER_CONFIRMATION'/) assert.match(view, /label: '待确认目的地', value: 'WAIT_FBA_DESTINATION'/) assert.match(view, /label: '待排车', value: 'READY_FOR_DISPATCH'/) assert.match(view, /WAIT_OUTBOUND: '待出库'/) assert.match(view, /OUTBOUNDED: '已出库'/) }) test('PDA 打托完成后客户可从列表或详情确认并查看实测数据', () => { assert.match(view, /row\.__status === 'WAIT_CUSTOMER_CONFIRMATION'/) assert.match(view, /confirmTransferFromList\(row\['0'\]\)/) assert.match(view, /selectedDetail\?\.status === 'WAIT_CUSTOMER_CONFIRMATION'/) assert.match(view, /PDA 打托实测数据/) for (const label of ['托盘号', '库位', '箱数', '长 × 宽 × 高(cm)', '毛重(kg)', '体积(m³)', '完成时间']) assert.equal(view.includes(label), true) assert.match(view, /pallet\.warehouseLocationCode/) assert.match(view, /pallet\.cartonCount/) assert.match(view, /pallet\.grossWeightKg/) assert.match(view, /pallet\.volumeCbm/) assert.doesNotMatch(view, /pallet\.items/) }) test('仓储转运详情自动加载 PDA 测量照片并释放 Blob URL', () => { assert.match(api, /downloadTransferMeasurementPhoto/) assert.match(api, /measurement-photos\/files\/\$\{encodeURIComponent\(fileId\)\}\/download/) assert.match(view, /measurementPhotos\?: TransferMeasurementPhoto\[\]/) assert.match(view, /fileId: string \| number/) assert.match(view, /if \(props\.service === 'transfer'\) void loadMeasurementPhotos\(\)/) assert.match(view, /测量打托现场照片/) assert.match(view, /:preview-src-list="measurementPhotoPreviewUrls"/) assert.match(view, /加载失败,点击重试/) assert.match(view, /URL\.createObjectURL\(blob\)/) assert.match(view, /URL\.revokeObjectURL\(url\)/) assert.match(view, /onBeforeUnmount\(\(\) => \{ clearCompletionPhotos\(\); clearMeasurementPhotos\(\) \}\)/) }) test('PDA 完成后主单显示入库完成并保留客户确认测量入口', () => { assert.match(view, /row\.__status === 'WAIT_CUSTOMER_CONFIRMATION'.*确认测量数据/s) assert.match(view, /selectedDetail\?\.status === 'WAIT_CUSTOMER_CONFIRMATION'.*确认测量数据/s) assert.doesNotMatch(view, /确认实测并入库/) }) test('仓储转运可在新建页直接提交,也可保存草稿后编辑提交', () => { assert.match(api, /function submitTransfer\(transferNo: string\)/) assert.match(api, /function updateTransfer\(transferNo: string, payload: TransferCreate\)/) assert.match(api, /warehouse-transfers\/\$\{encodeURIComponent\(transferNo\)\}\/save-draft/) assert.match(api, /warehouse-transfers\/\$\{encodeURIComponent\(transferNo\)\}\/submit/) assert.match(view, /submit\('draft'\).*保存草稿/s) assert.match(view, /async function openEdit\(transferNo: string\)/) assert.match(view, /row\.__status === 'DRAFT'[^>]*[\s\S]*?@click="openEdit\(row\['0'\]\)"[^>]*>编辑/) assert.match(view, /v-if="props\.service === 'transfer'"[^>]*type="primary"[^>]*native-type="submit"[^>]*>提交订单/) assert.match(view, /await updateTransfer\(editingTransferNo\.value, payload\)/) assert.match(view, /await submitTransfer\(transferNo\)/) assert.match(view, /
]*@submit\.prevent="submit\('submit'\)"/) assert.match(view, /selectedDetail\?\.status !== 'DRAFT'[^>]*[\s\S]*?printTransferCartons[^>]*[\s\S]*?打印箱唛/) assert.doesNotMatch(view, /row\.__status !== 'DRAFT'.*printTransferCartons\(row\['0'\]\)/s) assert.doesNotMatch(view, /selectedDetail\?\.status === 'DRAFT'[^>]*[\s\S]*?>提交订单/) assert.match(view, /native-type="button"[^>]*:loading="saving"[^>]*@click="submit\('draft'\)"/) assert.match(view, /DRAFT: '草稿'/) assert.doesNotMatch(view, /

箱唛明细<\/h3>/) }) test('仓储转运基础货物不强制 SKU 和中文品名,只换箱唛不强制旧 SKU', () => { assert.match(view, /商品 SKU\{\{ props\.service === 'transfer' \? '' : ' \*' \}\}/) assert.match(view, /中文品名\{\{ props\.service === 'transfer' \? '' : ' \*' \}\}/) assert.match(view, /props\.service === 'transfer' \? '选填' : '请输入商品 SKU'/) assert.match(view, /v-if="labelingMode === 'carton'"[^\n]+旧 SKU<\/th>/) assert.match(view, /labelingMode\.value !== 'carton' && \(!row\.sku\.trim\(\) \|\| !row\.newSkuCode\.trim\(\)\)/) assert.match(api, /skuCode\?: string; productNameZh\?: string/) assert.match(api, /originalSku\?: string/) })