@@ -8,7 +8,7 @@ import { useAccessPreviewStore } from '@/core/access/accessPreview'
import AdminPage from '@/shared/components/AdminPage.vue' ; import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue' ; import DataTable , { type DataTableColumn } from '@/shared/components/DataTable.vue' ; import ListPageLayout from '@/shared/components/ListPageLayout.vue' ; import LoadErrorState from '@/shared/components/LoadErrorState.vue' ; import QueryToolbar , { type QueryField , type QueryModel } from '@/shared/components/QueryToolbar.vue'
import InboundCodeRelationshipDialog from '@/shared/components/InboundCodeRelationshipDialog.vue'
import { saveCsv } from '@/shared/downloadCsv'
import { downloadInventoryLedger , listInventoryLedger } from '../api/inventory' ; import { formatOverseasDateTime } from '../lastMilePresentation' ; import { overseasWarehousePermissions as permission } from '../permissions' ; import type { InventoryTransactionListItem } from '../types'
import { downloadInventoryLedger , listInventoryLedger } from '../api/inventory' ; import { formatOverseasDateTime , inventoryBusinessTypeLabel } from '../lastMilePresentation' ; import { overseasWarehousePermissions as permission } from '../permissions' ; import type { InventoryTransactionListItem } from '../types'
const route = useRoute ( ) ; const router = useRouter ( ) ; const access = useAccessPreviewStore ( ) ; const rows = ref < InventoryTransactionListItem [ ] > ( [ ] ) ; const page = ref ( 1 ) ; const pageSize = ref ( 10 ) ; const total = ref ( 0 ) ; const loading = ref ( false ) ; const loadError = ref < ApiErrorDetails | null > ( null )
const exportLoading = ref ( false )
@@ -18,7 +18,12 @@ const fields = computed<QueryField[]>(() => [
{ key : 'occurredDate' , type : 'daterange' , label : '发生日期' , width : 260 } ,
{ key : 'customerName' , type : 'input' , label : '客户名称' , placeholder : '请输入客户名称' , width : 190 } ,
{ key : 'warehouse' , type : 'input' , label : '目的仓代码' , placeholder : '请输入目的仓代码' , width : 190 } , { key : 'sku' , type : 'input' , label : 'SKU编码' , placeholder : '请输入 SKU 编码' , width : 170 } ,
{ key : 'businessType' , type : 'inpu t' , label : '业务类型' , placeholder : '请输入 业务类型代码 ' , width : 170 } , { key : 'businessOrderNo' , type : 'input' , label : '业务订单号' , placeholder : '请输入业务订单号' , width : 180 } ,
{ key : 'businessType' , type : 'selec t' , label : '业务类型' , placeholder : '全部 业务类型' , width : 170 , options : [
{ label : '在途入账' , value : 'IN_TRANSIT' } , { label : '入库' , value : 'STOCK_IN' } , { label : '库存暂扣' , value : 'HOLD' } , { label : '解除暂扣' , value : 'RELEASE' } ,
{ label : '库存分配' , value : 'ALLOCATE' } , { label : '取消分配' , value : 'UNALLOCATE' } , { label : '拣货' , value : 'PICK' } , { label : '出库' , value : 'SHIP' } ,
{ label : '库存调整' , value : 'ADJUST' } , { label : '盘点调整' , value : 'STOCKTAKE_ADJUST' } , { label : '组托' , value : 'PALLET_ASSEMBLE' } ,
{ label : '拆托' , value : 'PALLET_DISASSEMBLE' } , { label : '换托' , value : 'PALLET_SWAP' } , { label : '解托' , value : 'PALLET_UNBIND' } ,
] } , { key : 'businessOrderNo' , type : 'input' , label : '业务订单号' , placeholder : '请输入业务订单号' , width : 180 } ,
{ key : 'inboundCode' , type : 'input' , label : '入仓码' , placeholder : '请输入入仓码' , width : 190 } , { key : 'inboundOrderNo' , type : 'input' , label : '入库单号' , placeholder : '请输入入库单号' , width : 180 } , { key : 'outboundOrderNo' , type : 'input' , label : '出库单号' , placeholder : '只读关联筛选' , width : 180 } ,
] )
const columns : DataTableColumn [ ] = [
@@ -31,14 +36,14 @@ const columns: DataTableColumn[] = [
]
function text ( value : unknown ) { return String ( value ? ? '' ) . trim ( ) || '—' }
function openRelationship ( row : InventoryTransactionListItem ) { if ( row . inboundCode === '—' ) return ; relationshipDialogInboundCode . value = row . inboundCode ; relationshipDialogOpen . value = true }
async function load ( model : QueryModel = filters . value ) { Object . assign ( filters . value , model ) ; const range = Array . isArray ( filters . value . occurredDate ) ? filters . value . occurredDate . map ( String ) : [ ] ; loading . value = true ; loadError . value = null ; try { const result = await listInventoryLedger ( { page : page . value , size : pageSize . value , customerName : String ( filters . value . customerName || '' ) . trim ( ) || undefined , destinationWarehouseCode : String ( filters . value . warehouse || '' ) . trim ( ) || undefined , skuCode : String ( filters . value . sku || '' ) . trim ( ) || undefined , businessType : String ( filters . value . businessType || '' ) . trim ( ) || undefined , businessOrderNo : String ( filters . value . businessOrderNo || '' ) . trim ( ) || undefined , inboundCode : String ( filters . value . inboundCode || '' ) . trim ( ) || undefined , inboundOrderNo : String ( filters . value . inboundOrderNo || '' ) . trim ( ) || undefined , outboundOrderNo : String ( filters . value . outboundOrderNo || '' ) . trim ( ) || undefined , occurredFrom : range [ 0 ] ? ` ${ range [ 0 ] } T00:00:00 ` : undefined , occurredTo : range [ 1 ] ? ` ${ range [ 1 ] } T23:59:59 ` : undefined } ) ; rows . value = result . records . map ( ( item ) => ( { id : String ( item . id ) , occurredAt : formatOverseasDateTime ( item . occurredAt ) , owner : text ( item . customerName || '客户信息待补充' ) , warehouse : text ( item . destinationWarehouseCode ) , sku : text ( item . skuCode ) , productName : '—' , businessType : text ( item . businessType ) , businessOrderNo : text ( item . businessOrderNo ) , inboundCode : text ( item . inboundCode ) , inboundOrderNo : text ( item . inboundOrderNo ) , outboundOrderNo : text ( item . outboundOrderNo ) , inboundQuantity : item . deltaPackages > 0 ? item . deltaPackages : 0 , outboundQuantity : item . deltaPackages < 0 ? Math . abs ( item . deltaPackages ) : 0 , balance : Number ( item . afterPackages || 0 ) , operator : item . operatorId ? '操作人名称待补充' : '—' , remark : text ( item . remark ) } ) ) ; total . value = result . total } catch ( error ) { rows . value = [ ] ; total . value = 0 ; loadError . value = getApiErrorDetails ( error , '进销存记录加载失败,请重试' ) } finally { loading . value = false } }
async function load ( model : QueryModel = filters . value ) { Object . assign ( filters . value , model ) ; const range = Array . isArray ( filters . value . occurredDate ) ? filters . value . occurredDate . map ( String ) : [ ] ; loading . value = true ; loadError . value = null ; try { const result = await listInventoryLedger ( { page : page . value , size : pageSize . value , customerName : String ( filters . value . customerName || '' ) . trim ( ) || undefined , destinationWarehouseCode : String ( filters . value . warehouse || '' ) . trim ( ) || undefined , skuCode : String ( filters . value . sku || '' ) . trim ( ) || undefined , businessType : String ( filters . value . businessType || '' ) . trim ( ) || undefined , businessOrderNo : String ( filters . value . businessOrderNo || '' ) . trim ( ) || undefined , inboundCode : String ( filters . value . inboundCode || '' ) . trim ( ) || undefined , inboundOrderNo : String ( filters . value . inboundOrderNo || '' ) . trim ( ) || undefined , outboundOrderNo : String ( filters . value . outboundOrderNo || '' ) . trim ( ) || undefined , occurredFrom : range [ 0 ] ? ` ${ range [ 0 ] } T00:00:00 ` : undefined , occurredTo : range [ 1 ] ? ` ${ range [ 1 ] } T23:59:59 ` : undefined } ) ; rows . value = result . records . map ( ( item ) => ( { id : String ( item . id ) , occurredAt : formatOverseasDateTime ( item . occurredAt ) , owner : text ( item . customerName ) , warehouse : text ( item . destinationWarehouseCode ) , sku : text ( item . skuCode ) , productName : '—' , businessType : inventoryBusinessTypeLabel ( item . businessType ) , businessOrderNo : text ( item . businessOrderNo ) , inboundCode : text ( item . inboundCode ) , inboundOrderNo : text ( item . inboundOrderNo ) , outboundOrderNo : text ( item . outboundOrderNo ) , inboundQuantity : item . deltaPackages > 0 ? item . deltaPackages : 0 , outboundQuantity : item . deltaPackages < 0 ? Math . abs ( item . deltaPackages ) : 0 , balance : Number ( item . afterPackages || 0 ) , operator : '—' , remark : text ( item . remark ) } ) ) ; total . value = result . total } catch ( error ) { rows . value = [ ] ; total . value = 0 ; loadError . value = getApiErrorDetails ( error , '进销存记录加载失败,请重试' ) } finally { loading . value = false } }
function search ( model : QueryModel ) { page . value = 1 ; void load ( model ) } ; function reset ( model : QueryModel ) { page . value = 1 ; void load ( model ) } ; function back ( ) { void router . push ( { name : 'overseas-inventory' } ) } ; onMounted ( load )
async function exportLedger ( ) { exportLoading . value = true ; try { const range = Array . isArray ( filters . value . occurredDate ) ? filters . value . occurredDate . map ( String ) : [ ] ; saveCsv ( await downloadInventoryLedger ( { customerName : String ( filters . value . customerName || '' ) . trim ( ) || undefined , destinationWarehouseCode : String ( filters . value . warehouse || '' ) . trim ( ) || undefined , skuCode : String ( filters . value . sku || '' ) . trim ( ) || undefined , businessType : String ( filters . value . businessType || '' ) . trim ( ) || undefined , businessOrderNo : String ( filters . value . businessOrderNo || '' ) . trim ( ) || undefined , inboundCode : String ( filters . value . inboundCode || '' ) . trim ( ) || undefined , inboundOrderNo : String ( filters . value . inboundOrderNo || '' ) . trim ( ) || undefined , outboundOrderNo : String ( filters . value . outboundOrderNo || '' ) . trim ( ) || undefined , occurredFrom : range [ 0 ] ? ` ${ range [ 0 ] } T00:00:00 ` : undefined , occurredTo : range [ 1 ] ? ` ${ range [ 1 ] } T23:59:59 ` : undefined } ) as Blob , '进销存记录.csv' ) } catch { ElMessage . error ( '进销存导出失败,请重试' ) } finally { exportLoading . value = false } }
< / script >
< template > < AdminPage > < ListPageLayout aria -label = " 进销存记录列表 " > < template # toolbar > < QueryToolbar v-model = "filters" class="surface-panel" :fields="fields" :loading="loading" ariaLabel="进销存记录筛选" preference-key="overseas-inventory-transactions-query" @search="search" @reset="reset" / > < / template >
< template v-if = "loadError" #state><LoadErrorState title="进销存记录加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" / > < / template > < / LoadErrorState > < / template >
< template v-else # state > < p class = "read-only-boundary" > 出库相关字段仅展示既有 库存流水事实 ; 本页面 不提供出库单跳转、 库存扣减或其他离仓操作 。 当前流水 DTO 未提供产品名称与海外仓名称 , 对应列显示空值 。 < / p > < / template >
< template v-else # state > < p class = "read-only-boundary" > 本页面仅用于查询 库存流水, 不提供出库单跳转或库存变更操作 。 < / p > < / template >
< section v-if = "!loadError" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="overseas-inventory-transactions" row-key="id" empty-text="暂无进销存记录" @update:page="load()" @update:page-size="page = 1; load()"><template #toolbar-actions><el-button v-if="access.can(permission.inventoryExport)" plain :loading="exportLoading" @click="exportLedger"><Download :size="16" />导出记录</el-button><el-button @click="back"><ArrowLeft :size="16" />返回库存管理</el-button></template><template #actions="{ row }"><el-button link type="primary" :disabled="(row as InventoryTransactionListItem).inboundCode === '—'" @click="openRelationship(row as InventoryTransactionListItem)" > 关联数据 < / el -button > < / template > < template # pagination -left > 共 { { total } } 条进销存记录 < / template > < / DataTable > < / section >
< InboundCodeRelationshipDialog v-if = "relationshipDialogOpen" v-model="relationshipDialogOpen" :inbound-code="relationshipDialogInboundCode" :can-read-base="access.can('platform:order:list')" :can-read-loading="access.can('platform:firstleg:smart-loading:list')" :can-read-overseas="access.can('platform:overseas-warehouse:inventory:list')" / >
< / ListPageLayout > < / AdminPage > < / template >