@@ -10,7 +10,7 @@ import { useAuthStore } from '@/modules/auth/public'
import { getMerchantWarehouses , type MerchantWarehouse } from '@/modules/first-leg/public'
import { createReturnOrder , downloadReturnPhoto , getReturnOrder , listReturnOrders , updateReturnOrder , type ReturnOrderDetail , type ReturnOrderSummary , type ReturnReceipt } from '../api/returnOrders'
const props = withDefaults ( defineProps < { detailPage ? : boolean ; returnNo ? : string } > ( ) , { detailPage : false , returnNo : '' } )
const props = withDefaults ( defineProps < { createPage ? : boolean ; detailPage ? : boolean ; returnNo ? : string } > ( ) , { createPage : false , detailPage : false , returnNo : '' } )
const router = useRouter ( )
const route = useRoute ( )
const detailReturnNo = computed ( ( ) => props . returnNo || String ( route . params . returnNo || '' ) )
@@ -110,10 +110,7 @@ async function loadOperationWarehouses() {
} finally { operationWarehousesLoading . value = false }
}
function openCreate ( ) {
editingReturnNo . value = ''
resetForm ( )
showCreate . value = true
if ( ! operationWarehouses . value . length && ! operationWarehousesLoading . value ) void loadOperationWarehouses ( )
void router . push ( { name : 'overseas-return-relabel-create' } )
}
async function openEdit ( returnNo : string ) {
const detail = await getReturnOrder ( returnNo )
@@ -176,6 +173,7 @@ function previewPhoto(fileId: string | number) {
}
function openDetail ( returnNo : string ) { void router . push ( { name : 'overseas-return-relabel-detail' , params : { returnNo } } ) }
function returnToList ( ) { void router . push ( { name : 'overseas-return-relabel' } ) }
function cancelCreate ( ) { resetForm ( ) ; void router . push ( { name : 'overseas-return-relabel' } ) }
function canApplyRelabel ( order : { status ? : unknown ; needRelabel ? : unknown ; relabelNo ? : unknown } ) { return order . status === 'COMPLETED' && order . needRelabel === true && ! order . relabelNo }
function openRelabel ( order : Pick < ReturnOrderSummary , ' returnNo ' | ' overseasWarehouseId ' > ) {
void router . push ( { name : 'overseas-labeling-create' , query : { returnNo : order . returnNo , warehouseId : String ( order . overseasWarehouseId ) } } )
@@ -218,19 +216,48 @@ function formatDateTime(value?: string | null) {
return new Intl . DateTimeFormat ( 'zh-CN' , { year : 'numeric' , month : '2-digit' , day : '2-digit' , hour : '2-digit' , minute : '2-digit' , second : '2-digit' , hour12 : false } ) . format ( date )
}
onMounted ( ( ) => {
if ( props . detailPage ) void Promise . all ( [ loadDetail ( ) , loadOperationWarehouses ( ) ] )
if ( props . createPage ) { resetForm ( ) ; void loadOperationWarehouses ( ) }
else if ( props . detailPage ) void Promise . all ( [ loadDetail ( ) , loadOperationWarehouses ( ) ] )
else void Promise . all ( [ load ( ) , loadOperationWarehouses ( ) ] )
} )
watch ( ( ) => [ query . page , query . size ] as const , ( [ nextPage , nextSize ] , [ previousPage , previousSize ] ) => {
if ( nextSize !== previousSize && nextPage !== 1 ) { query . page = 1 ; return }
if ( ! props . detailPage ) void load ( )
if ( ! props . createPage && ! props . detailPage ) void load ( )
} )
onBeforeUnmount ( clearPhotoUrls )
< / script >
< template >
< section class = "return-orders-page" >
< template v-if = "! props.detail Page" >
< template v-if = "props.create Page" >
< article class = "return-create-surface" >
< header class = "return-create-header" >
< div > < h2 > 新建退货预报 < / h2 > < p > 填写预计退回的 SKU 和数量 , 仓库签收后将自动更新实收结果 。 < / p > < / div >
< el-button @click ="returnToList" > < ArrowLeft :size = "16" / > 返回列表 < / el-button >
< / header >
< div class = "return-create-body" >
< el-form label -width = " 150px " class = "return-create-form" >
< el-form-item label = "海外仓" required class = "return-form-wide" > < el-select v-model = "form.overseasWarehouseId" filterable clearable :loading="operationWarehousesLoading" :disabled="Boolean(operationWarehousesError)" placeholder="请选择海外仓" @change="selectOperationWarehouse"><el-option v-for="warehouse in operationWarehouses" :key="warehouse.id" :label="warehouseLabel(warehouse)" :value="String(warehouse.id)" /></el-select><div v-if="operationWarehousesError" class="warehouse-error" > {{ operationWarehousesError }} < / div > < / el -form -item >
< el-form-item label = "海外仓联系人" required > < el-input v-model = "form.warehouseContactName" :readonly="Boolean(selectedOperationWarehouse?.contactName)" :placeholder="selectedOperationWarehouse?.contactName ? '已从海外仓资料带出' : '该海外仓未维护,请手动填写'" / > < / el-form-item >
< el-form-item label = "海外仓联系电话" required > < el-input v-model = "form.warehouseContactPhone" :readonly="Boolean(selectedOperationWarehouse?.contactPhone)" :placeholder="selectedOperationWarehouse?.contactPhone ? '已从海外仓资料带出' : '该海外仓未维护,请手动填写'" / > < / el-form-item >
< el-form-item label = "客户参考号" class = "return-form-wide" > < el-input v-model = "form.customerReferenceNo" placeholder="可填写客户侧参考号" / > < / el-form-item >
< el-form-item label = "备注" class = "return-form-wide" > < el-input v-model = "form.remark" type="textarea" :rows="3" placeholder="填写退货说明(选填)" / > < / el-form-item >
< / el-form >
< div class = "items-title" > < span > SKU 预报明细 < / span > < el-button text type = "primary" @click ="addItem" > < Plus :size = "15" / > 添加 SKU < / el-button > < / div >
< div class = "return-relabel-option" > < span > 换标 < / span > < el-checkbox v-model = "form.needRelabel" > 需要换标 < / el -checkbox > < / div >
< div class = "items-hint" > 客户只需填写预计退回的 SKU 和数量 ; 快递单号由仓库签收时按实际包裹扫描绑定 。 < / div >
< el-table :data = "form.items" row -key = " id " size = "small" border >
< el-table-column label = "操作" width = "80" fixed = "left" > < template # default = "{ $index }" > < el-button link type = "danger" : disabled = "form.items.length === 1" @click ="removeItem($index)" > 删除 < / el -button > < / template > < / el-table-column >
< el-table-column label = "SKU" min -width = " 240 " > < template # default = "{ row }" > < div > < el-input v-model = "row.skuCode" :class="{ 'duplicate-sku-input': duplicateSkuIds.has(row.id) }" placeholder="填写 SKU" @blur="row.skuCode = row.skuCode.trim()" /><div v-if="duplicateSkuIds.has(row.id)" class="duplicate-sku-error" > 该 SKU 已在明细中填写 < / div > < / div > < / template > < / el -table -column >
< el-table-column label = "中文品名" min -width = " 320 " > < template # default = "{ row }" > < el-input v-model = "row.productNameZh" placeholder="填写中文品名(选填)" / > < / template > < / el-table-column >
< el-table-column label = "预报数量" width = "180" > < template # default = "{ row }" > < el-input-number v-model = "row.expectedQuantity" :min="1" controls-position="right" placeholder="数量" / > < / template > < / el-table-column >
< / el-table >
< / div >
< footer class = "return-create-actions" > < el-button @click ="cancelCreate" > 取消 < / el -button > < el-button v-if = "canCreate" type="primary" :loading="creating" @click="submit" > 提交预报 < / el -button > < / footer >
< / article >
< / template >
< template v-else-if = "!props.detailPage" >
< QueryToolbar v-model = "queryModel" layout="grid" ariaLabel="退货签收筛选" :fields="queryFields" @search="search" / >
< div class = "return-table-card" >
< DataTable v -model :page = "query.page" v -model :page-size = "query.size" :data = "rows" :columns = "tableColumns" :loading = "loading" :total = "total" : page -sizes = " [ 15 , 30 , 50 , 100 ] " :row-actions = "rowActions" row -key = " returnNo " empty -text = " 暂无退货预报 " >
@@ -330,6 +357,15 @@ onBeforeUnmount(clearPhotoUrls)
< style scoped >
. return - orders - page { display : grid ; gap : 12 px ; } . return - create - form { display : grid ; grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; column - gap : 20 px ; } . return - form - wide { grid - column : 1 / - 1 ; } . items - title { display : flex ; justify - content : space - between ; align - items : center ; margin : 16 px 0 4 px ; font - weight : 600 ; } . items - hint { margin - bottom : 10 px ; color : var ( -- el - text - color - secondary ) ; font - size : 12 px ; } h3 { margin : 24 px 0 10 px ; font - size : 16 px ; } h4 { margin : 16 px 0 8 px ; font - size : 14 px ; } . remark { white - space : pre - wrap ; } . warehouse - error , . duplicate - sku - error { margin - top : 6 px ; color : var ( -- el - color - danger ) ; font - size : 12 px ; } : deep ( . duplicate - sku - input . el - input _ _wrapper ) { box - shadow : 0 0 0 1 px var ( -- el - color - danger ) inset ; } : deep ( . el - dialog . el - select ) { width : 100 % ; }
. return - create - surface { overflow : hidden ; border : 1 px solid var ( -- client - border ) ; border - radius : var ( -- client - radius ) ; background : # fff ; }
. return - create - header { display : flex ; min - height : 68 px ; align - items : center ; justify - content : space - between ; gap : 20 px ; padding : 12 px 20 px ; border - bottom : 1 px solid var ( -- client - border ) ; }
. return - create - header h2 { margin : 0 ; color : # 1 f2d45 ; font - size : 20 px ; }
. return - create - header p { margin : 4 px 0 0 ; color : # 7 c899c ; font - size : 12 px ; }
. return - create - body { padding : 18 px 20 px 14 px ; }
. return - create - body : deep ( . el - select ) , . return - create - body : deep ( . el - input - number ) { width : 100 % ; }
. return - create - body . items - title { margin - top : 8 px ; }
. return - relabel - option { display : flex ; align - items : center ; gap : 16 px ; margin : 8 px 0 ; color : # 526176 ; }
. return - create - actions { display : flex ; align - items : center ; justify - content : flex - end ; gap : 10 px ; padding : 12 px 20 px ; border - top : 1 px solid var ( -- client - border ) ; background : # fbfcfe ; }
. return - table - card { overflow : hidden ; border : 1 px solid var ( -- client - border ) ; border - radius : var ( -- client - radius ) ; background : # fff ; box - shadow : none ; }
. return - table - card : deep ( . el - table ) { width : 100 % ; }
. return - status -- pending - review { -- el - tag - bg - color : # fff7e6 ; -- el - tag - border - color : # f4c26b ; -- el - tag - text - color : # a86100 ; }