@@ -2,11 +2,15 @@
import { onMounted , reactive , ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Printer , Search } from '@lucide/vue'
import { ApiError } from '@/api/types'
import { getMerchantWarehouses , type MerchantWarehouse } from '@/modules/first-leg/public'
import {
getReturnInventoryCarton ,
getReturnInventoryLabel ,
listReturnInventory ,
listReturnInventorySummary ,
listReturnInventoryCartons ,
type ReturnInventoryCartonSummary ,
type ReturnInventoryCartonDetail ,
type ReturnInventoryItem ,
type ReturnInventorySummaryItem ,
@@ -16,8 +20,33 @@ const activeTab = ref<'summary' | 'cartons'>('summary')
const props = defineProps < { cartonOnly ? : boolean } > ( )
if ( props . cartonOnly ) activeTab . value = 'cartons'
const loading = ref ( false )
const loadError = ref ( '' )
const warehouses = ref < MerchantWarehouse [ ] > ( [ ] )
const warehouseError = ref ( '' )
const warehousesLoading = ref ( false )
function warehouseLabel ( warehouseId : string ) {
const warehouse = warehouses . value . find ( item => String ( item . id ) === String ( warehouseId ) )
return warehouse ? ` ${ warehouse . warehouseName } · ${ warehouse . warehouseCode } ` : '—'
}
async function loadWarehouses ( ) {
warehousesLoading . value = true
warehouseError . value = ''
try {
const result : MerchantWarehouse [ ] = [ ]
for ( let page = 1 ; ; page ++ ) {
const response = await getMerchantWarehouses ( { warehouseType : 'OVERSEAS' , page , size : 200 } )
result . push ( ... response . records )
if ( ! response . records . length || result . length >= Number ( response . total ) ) break
}
warehouses . value = result
} catch ( error ) {
warehouses . value = [ ]
warehouseError . value = error instanceof Error ? error . message : '海外仓目录加载失败,请重试'
} finally { warehousesLoading . value = false }
}
const summaryRows = ref < ReturnInventorySummaryItem [ ] > ( [ ] )
const cartonRows = ref < ReturnInventoryItem [ ] > ( [ ] )
const cartonSummaryRows = ref < ReturnInventoryCartonSummary [ ] > ( [ ] )
const total = ref ( 0 )
const showCartonDetail = ref ( false )
const cartonDetail = ref < ReturnInventoryCartonDetail > ( )
@@ -37,16 +66,35 @@ function params(includeCartonMark: boolean) {
async function load ( ) {
loading . value = true
loadError . value = ''
try {
if ( props . cartonOnly ) {
const page = await listReturnInventoryCartons ( params ( true ) )
cartonSummaryRows . value = page . records
total . value = Number ( page . total )
return
}
if ( activeTab . value === 'summary' ) {
const page = await listReturnInventorySummary ( params ( false ) )
summaryRows . value = page . records
total . value = page . total
total . value = Number ( page . total )
return
}
const page = await listReturnInventory ( params ( true ) )
cartonRows . value = page . records
total . value = page . total
total . value = Number ( page . total )
} catch ( error ) {
summaryRows . value = [ ]
cartonRows . value = [ ]
cartonSummaryRows . value = [ ]
total . value = 0
const message = error instanceof ApiError && ( error . code === 'A0403' || error . status === 403 )
? '当前账号无权访问退货库存,请联系管理员核对海外库存权限。'
: error instanceof Error ? error . message : '退货库存加载失败,请重试'
const diagnostics = error instanceof ApiError
? [ error . code , error . requestId && ` requestId: ${ error . requestId } ` , error . traceId && ` traceId: ${ error . traceId } ` ] . filter ( Boolean ) . join ( ' · ' )
: ''
loadError . value = diagnostics ? ` ${ message } ( ${ diagnostics } ) ` : message
} finally { loading . value = false }
}
@@ -67,51 +115,71 @@ async function openCarton(cartonMark: string) {
}
async function printLabel ( cartonMark : string ) {
const printWindow = window . open ( '' , '_blank' )
if ( ! printWindow ) { ElMessage . warning ( '浏览器阻止了打印窗口,请允许弹窗后重试' ) ; return }
printWindow . opener = null
printWindow . document . title = '正在加载退货箱唛'
printWindow . document . body . textContent = '正在加载退货箱唛,请稍候…'
try {
const label = await getReturnInventoryLabel ( cartonMark )
const printWindow = window . open ( '' , '_blank' )
if ( ! printWindow ) { ElMessage . warning ( '浏览器阻止了打印窗口,请允许弹窗后重试' ) ; return }
printWindow . opener = null
if ( printWindow . closed ) return
printWindow . document . open ( )
printWindow . document . write ( label . printableHtml )
printWindow . document . close ( )
printWindow . focus ( )
printWindow . print ( )
} catch ( error ) { ElMessage . error ( error instanceof Error ? error . message : '箱唛加载失败' ) }
} catch ( error ) {
printWindow . close ( )
ElMessage . error ( error instanceof Error ? error . message : '箱唛加载失败' )
}
}
onMounted ( load )
onMounted ( ( ) => { void load ( ) ; void loadWarehouses ( ) } )
< / script >
< template >
< section class = "return-inventory-page" >
< div class = "page-heading" > < div > < h2 > { { props . cartonOnly ? '退货箱号管理' : '退货库存管理' } } < / h2 > < p > { { props . cartonOnly ? '查询合箱完成后 的退货箱号、箱规和箱内 SKU 明细。' : '库存数量以仓库 PDA 实收并上架的 数量为准。' } } < / p > < / div > < / div >
< div class = "page-heading" > < div > < h2 > { { props . cartonOnly ? '退货箱号管理' : '退货库存管理' } } < / h2 > < p > { { props . cartonOnly ? '查询签收及合箱生成 的退货箱号、箱规和箱内 SKU 明细。' : '库存数量以仓库 PDA 确认实收后的入账 数量为准。' } } < / p > < / div > < / div >
< el-tabs v-if = "!props.cartonOnly" v-model="activeTab" @tab-change="changeTab"><el-tab-pane label="SKU 汇总库存" name="summary" /><el-tab-pane label="箱号明细" name="cartons" / > < / el-tabs >
< div class = "filter-bar" >
< el-input v-model = "query.overseasWarehouseId" clearable placeholder="海外仓 ID" @keyup.enter="search" / >
< el-select v-model = "query.overseasWarehouseId" clearable filterable placeholder="海外仓" :loading="warehousesLoading"><el-option v-for="warehouse in warehouses" :key="warehouse.id" :label="warehouseLabel(String(warehouse.id))" :value="String(warehouse.id)" / > < / el-select >
< el-input v-model = "query.skuCode" clearable placeholder="SKU" @keyup.enter="search" / >
< el-input v-if = "activeTab === 'cartons'" v-model="query.cartonMark" clearable placeholder="箱号 / 箱唛" @keyup.enter="search" / >
< el-input v-if = "activeTab === 'cartons'" v-model="query.returnNo" clearable placeholder="退货单号" @keyup.enter="search" / >
< el-select v-if = "activeTab === 'cartons'" v-model="query.status" clearable placeholder="箱状态"><el-option label="可用" value="AVAILABLE" /><el-option label="已合箱" value="MERGED" / > < / el-select >
< el-button type = "primary" @click ="search" > < Search :size = "16" / > 查询 < / el-button >
< / div >
< el-table v-if = "activeTab === 'summary'" v-loading="loading" :data="summaryRows" >
< el -table -column prop = "overseasWarehouseId" label = "海外仓" min -width = " 150 " / > < el-table-column prop = "skuCode" label = "SKU" min -width = " 180 " / >
< el-alert v-if = "loadError" :title="loadError" type="error" show-icon :closable="false" / >
< el-alert v-if = "warehouseError" :title="warehouseError" type="warning" show-icon :closable="false"><el-button link type="primary" @click="loadWarehouses" > 重试加载海外仓 < / el -button > < / el-alert >
< el-table v-if = "activeTab === 'summary'" v-loading="loading" :data="summaryRows" :empty-text="loadError ? '库存加载失败,请核对权限或重试' : '暂无数据'" >
< el -table -column label = "操作" width = "110" fixed = "left" > < template # default = "{ row }" > < el-button link type = "primary" @click ="openSkuCartons(row.skuCode)" > 查看箱号 < / el -button > < / template > < / el-table-column >
< el-table-column label = "海外仓" min -width = " 150 " > < template # default = "{ row }" > { { warehouseLabel ( row . overseasWarehouseId ) } } < / template > < / el-table-column > < el-table-column prop = "skuCode" label = "SKU" min -width = " 180 " / >
< el-table-column prop = "productNameZh" label = "中文品名" min -width = " 190 " show -overflow -tooltip / > < el-table-column prop = "availableQuantity" label = "可用库存" min -width = " 120 " align = "right" / >
< el-table-column prop = "cartonQuantity" label = "所在箱数" min -width = " 120 " align = "right" / > < el-table-column prop = "updatedAt" label = "最近入 库时间" min -width = " 180 " / >
< el-table-column label = "操作" width = "110" fixed = "right" > < template # default = "{ row }" > < el-button link type = "primary" @click ="openSkuCartons(row.skuCode)" > 查看箱号 < / el -button > < / template > < / el-table-column >
< el-table-column prop = "cartonQuantity" label = "所在箱数" min -width = " 120 " align = "right" / > < el-table-column prop = "updatedAt" label = "库存更新 时间" min -width = " 180 " / >
< / el-table >
< el-table v-else v-loading = "loading" :data="cartonRows ">
< el-table v-else-if = "props.cartonOnly" v-loading="loading" :data="cartonSummaryRows" row-key="cartonMark" :empty-text="loadError ? '库存加载失败,请核对权限或重试' : '暂无数据' ">
< el -table -column label = "操作" width = "150" fixed = "left" > < template # default = "{ row }" > < el-button link type = "primary" @click ="openCarton(row.cartonMark)" > 详情 < / el -button > < el-button link type = "primary" @click ="printLabel(row.cartonMark)" > < Printer :size = "14" / > 箱唛 < / el-button > < / template > < / el-table-column >
< el-table-column prop = "cartonMark" label = "箱号" min -width = " 235 " / >
< el-table-column label = "海外仓" min -width = " 150 " > < template # default = "{ row }" > { { warehouseLabel ( row . overseasWarehouseId ) } } < / template > < / el-table-column >
< el-table-column prop = "skuTypes" label = "箱内 SKU 种类" min -width = " 130 " align = "right" / >
< el-table-column prop = "totalQuantity" label = "箱内总件数" min -width = " 115 " align = "right" / >
< el-table-column prop = "sourceReturnNo" label = "来源退货单" min -width = " 235 " / >
< el-table-column prop = "sourceBatchNo" label = "签收批次" min -width = " 190 " / >
< el-table-column label = "箱规" min -width = " 160 " > < template # default = "{ row }" > { { row . length && row . width && row . height ? ` ${ row . length } × ${ row . width } × ${ row . height } cm ` : '—' } } < / template > < / el-table-column >
< el-table-column label = "重量" min -width = " 100 " > < template # default = "{ row }" > { { row . weight ? ` ${ row . weight } kg ` : '—' } } < / template > < / el-table-column >
< el-table-column prop = "createdAt" label = "箱号生成时间" min -width = " 175 " / >
< / el-table >
< el-table v-else v-loading = "loading" :data="cartonRows" :empty-text="loadError ? '库存加载失败,请核对权限或重试' : '暂无数据'" >
< el -table -column label = "操作" width = "150" fixed = "left" > < template # default = "{ row }" > < el-button link type = "primary" @click ="openCarton(row.cartonMark)" > 详情 < / el -button > < el-button link type = "primary" @click ="printLabel(row.cartonMark)" > < Printer :size = "14" / > 箱唛 < / el-button > < / template > < / el-table-column >
< el-table-column prop = "cartonMark" label = "箱号" min -width = " 235 " > < template # default = "{ row }" > < el-button link type = "primary" @click ="openCarton(row.cartonMark)" > {{ row.cartonMark }} < / el -button > < / template > < / el-table-column >
< el-table-column prop = "overseasWarehouseId" label = "海外仓" min -width = " 130 " / > < el-table-column prop = "skuCode" label = "SKU" min -width = " 150 " / >
< el-table-column label = "海外仓" min -width = " 150 " > < template # default = "{ row }" > { { warehouseLabel ( row . overseasWarehouseId ) } } < / template > < / el-table-column > < el-table-column prop = "skuCode" label = "SKU" min -width = " 150 " / >
< el-table-column prop = "productNameZh" label = "中文品名" min -width = " 170 " show -overflow -tooltip / > < el-table-column prop = "quantity" label = "本箱 SKU 数量" min -width = " 135 " align = "right" / >
< el-table-column prop = "cartonSkuTypes" label = "箱内 SKU 种类" min -width = " 130 " align = "right" / > < el-table-column prop = "cartonTotalQuantity" label = "箱内总件数" min -width = " 115 " align = "right" / >
< el-table-column prop = "status" label = "箱状态" min -width = " 100 " / > < el-table-column prop = "sourceBatchNo" label = "签收批次" min -width = " 190 " / > < el-table-column prop = "receivedAt" label = "签收时间" min -width = " 175 " / >
< el-table-column label = "操作" width = "150" fixed = "right" > < template # default = "{ row }" > < el-button link type = "primary" @click ="openCarton(row.cartonMark)" > 详情 < / el -button > < el-button link type = "primary" @click ="printLabel(row.cartonMark)" > < Printer :size = "14" / > 箱唛 < / el-button > < / template > < / el-table-column >
< / el-table >
< el-pagination v -model :current-page = "query.page" v -model :page-size = "query.size" :total = "total" layout = "total, prev, pager, next" @ current -change = " load " / >
< el-drawer v-model = "showCartonDetail" title="退货箱明细" size="680px"><template v-if="cartonDetail" >
< el -descriptions :column = "2" border > < el-descriptions-item label = "箱号" > { { cartonDetail . cartonMark } } < / el-descriptions-item > < el-descriptions-item label = "海外仓" > { { cartonDetail . overseasWarehouseId } } < / el-descriptions-item > < el-descriptions-item label = "来源退货单" > { { cartonDetail . sourceReturnNo || '—' } } < / el-descriptions-item > < el-descriptions-item label = "签收批次" > { { cartonDetail . sourceBatchNo || '—' } } < / el-descriptions-item > < el-descriptions-item label = "状态" > { { cartonDetail . status } } < / el-descriptions-item > < el-descriptions-item label = "SKU 种类" > { { cartonDetail . skuTypes } } < / el-descriptions-item > < el-descriptions-item label = "箱内总件数" > { { cartonDetail . totalQuantity } } < / el-descriptions-item > < el-descriptions-item label = "箱规" > { { cartonDetail . length && cartonDetail . width && cartonDetail . height ? ` ${ cartonDetail . length } × ${ cartonDetail . width } × ${ cartonDetail . height } cm ` : '—' } } < / el-descriptions-item > < el-descriptions-item label = "重量" > { { cartonDetail . weight ? ` ${ cartonDetail . weight } kg ` : '—' } } < / el-descriptions-item > < / el-descriptions >
< el -descriptions :column = "2" border > < el-descriptions-item label = "箱号" > { { cartonDetail . cartonMark } } < / el-descriptions-item > < el-descriptions-item label = "海外仓" > { { warehouseLabel ( cartonDetail . overseasWarehouseId ) } } < / el-descriptions-item > < el-descriptions-item label = "来源退货单" > { { cartonDetail . sourceReturnNo || '—' } } < / el-descriptions-item > < el-descriptions-item label = "签收批次" > { { cartonDetail . sourceBatchNo || '—' } } < / el-descriptions-item > < el-descriptions-item label = "状态" > { { cartonDetail . status } } < / el-descriptions-item > < el-descriptions-item label = "SKU 种类" > { { cartonDetail . skuTypes } } < / el-descriptions-item > < el-descriptions-item label = "箱内总件数" > { { cartonDetail . totalQuantity } } < / el-descriptions-item > < el-descriptions-item label = "箱规" > { { cartonDetail . length && cartonDetail . width && cartonDetail . height ? ` ${ cartonDetail . length } × ${ cartonDetail . width } × ${ cartonDetail . height } cm ` : '—' } } < / el-descriptions-item > < el-descriptions-item label = "重量" > { { cartonDetail . weight ? ` ${ cartonDetail . weight } kg ` : '—' } } < / el-descriptions-item > < / el-descriptions >
< div class = "drawer-heading" > < h3 > 箱内 SKU < / h3 > < el-button type = "primary" @click ="printLabel(cartonDetail.cartonMark)" > < Printer :size = "16" / > 打印箱唛 < / el-button > < / div >
< el-table :data = "cartonDetail.items" size = "small" > < el-table-column prop = "skuCode" label = "SKU" min -width = " 170 " / > < el-table-column prop = "productNameZh" label = "中文品名" min -width = " 180 " / > < el-table-column prop = "quantity" label = "数量" min -width = " 100 " align = "right" / > < / el-table >
< / template > < / el-drawer >