2026-08-19 16:45:51 +08:00
< script setup lang = "ts" >
2026-09-15 09:58:15 +08:00
import '@/styles/adminDensity.css'
2026-09-14 10:02:39 +08:00
import { computed , onActivated , onMounted , ref , watch } from 'vue'
2026-08-20 09:10:36 +08:00
import { useRouter } from 'vue-router'
2026-08-19 16:45:51 +08:00
import AdminPage from '@/shared/components/AdminPage.vue'
2026-08-20 09:10:36 +08:00
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
2026-08-19 16:45:51 +08:00
import DataTable , { type DataTableColumn } from '@/shared/components/DataTable.vue'
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
2026-08-20 09:10:36 +08:00
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
2026-08-19 16:45:51 +08:00
import QueryToolbar , { type QueryField , type QueryModel } from '@/shared/components/QueryToolbar.vue'
2026-08-20 09:10:36 +08:00
import { getApiErrorDetails , type ApiErrorDetails } from '@/shared/api/http'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
2026-08-20 18:02:52 +08:00
import { listOverseasPurchaseCustomerOptions , listOverseasPurchases } from '../api/platformOverseasBusiness'
2026-08-20 09:10:36 +08:00
import { overseasBusinessPermissions } from '../permissions'
2026-08-20 18:02:52 +08:00
import type { OverseasPurchaseCustomerOption , OverseasPurchaseQuery , OverseasPurchaseStatus , OverseasPurchaseSummary } from '../types'
2026-08-19 16:45:51 +08:00
2026-08-20 09:10:36 +08:00
const router = useRouter (); const access = useAccessPreviewStore ()
const rows = ref < OverseasPurchaseSummary [] >([]); const loading = ref ( false ); const loadError = ref < ApiErrorDetails | null >( null )
const page = ref ( 1 ); const pageSize = ref ( 20 ); const total = ref ( 0 )
2026-08-20 18:02:52 +08:00
const filters = ref < QueryModel >({ orderNo : '' , customerId : '' , purchaseCategory : '' , productName : '' }); const applied = ref ({ ... filters . value })
const customerOptions = ref < OverseasPurchaseCustomerOption [] >([]); const customersLoading = ref ( false )
const fields = computed < QueryField [] >(() => [
2026-08-19 16:45:51 +08:00
{ key : 'orderNo' , type : 'input' , label : '订单编号' , placeholder : '请输入订单编号' , width : 210 },
2026-08-20 18:02:52 +08:00
{ key : 'customerId' , type : 'select' , label : '客户' , placeholder : '全部客户' , width : 210 , options : customerOptions . value . map (( item ) => ({ label : item . customerName , value : item . customerId })), props : { filterable : true , loading : customersLoading . value } },
2026-08-20 09:10:36 +08:00
{ key : 'purchaseCategory' , type : 'input' , label : '采购品类' , placeholder : '请输入采购品类' , width : 190 },
2026-08-19 16:45:51 +08:00
{ key : 'productName' , type : 'input' , label : '商品名称' , placeholder : '请输入商品名称' , width : 210 },
2026-08-20 18:02:52 +08:00
])
2026-08-19 16:45:51 +08:00
const columns : DataTableColumn [] = [
{ key : 'actions' , label : '操作' , width : 176 , fixed : 'left' , slotName : 'actions' },
{ key : 'orderNo' , prop : 'orderNo' , label : '订单编号' , minWidth : 170 },
2026-08-20 09:10:36 +08:00
{ key : 'customerName' , prop : 'customerName' , label : '客户名称' , minWidth : 180 , formatter : ( row : OverseasPurchaseSummary ) => row . customerName || '—' },
{ key : 'purchaseWarehouseName' , prop : 'purchaseWarehouseName' , label : '采购仓库' , minWidth : 160 , formatter : ( row : OverseasPurchaseSummary ) => row . purchaseWarehouseName || '—' },
{ key : 'purchaseCategory' , prop : 'purchaseCategory' , label : '采购品类' , minWidth : 130 },
2026-08-19 16:45:51 +08:00
{ key : 'productName' , prop : 'productName' , label : '商品名称' , minWidth : 160 },
2026-08-20 09:10:36 +08:00
{ key : 'productSpecification' , prop : 'productSpecification' , label : '商品规格' , minWidth : 180 , formatter : ( row : OverseasPurchaseSummary ) => row . productSpecification || '—' },
{ key : 'budgetUnitPrice' , label : '采购单价' , width : 120 , align : 'right' , formatter : ( row : OverseasPurchaseSummary ) => formatAmount ( row . budgetUnitPrice , row . currency ) },
2026-08-19 16:45:51 +08:00
{ key : 'inboundQuantity' , prop : 'inboundQuantity' , label : '入库量' , width : 95 , align : 'right' },
2026-08-30 16:40:03 +08:00
{ key : 'outboundQuantity' , prop : 'outboundQuantity' , label : '出库量' , width : 95 , align : 'right' },
2026-08-20 09:10:36 +08:00
{ key : 'inventoryQuantity' , prop : 'inventoryQuantity' , label : '库存量' , width : 95 , align : 'right' },
{ key : 'status' , label : '状态' , width : 105 , formatter : ( row : OverseasPurchaseSummary ) => purchaseStatusLabel ( row . status ) },
2026-08-28 14:44:53 +08:00
{ key : 'createdAt' , prop : 'createdAt' , label : '创建时间' , minWidth : 165 , formatter : ( row : OverseasPurchaseSummary ) => formatDateTime ( row . createdAt ) },
2026-08-19 16:45:51 +08:00
]
2026-08-30 16:40:03 +08:00
const statusLabels : Record < OverseasPurchaseStatus , string > = { PENDING_REVIEW : '待审核' , APPROVED : '已通过' , REJECTED : '已拒绝' , PENDING_OUTBOUND : '待出库' , PARTIALLY_OUTBOUND : '部分出库' , OUTBOUNDED : '全部出库' }
2026-08-20 09:10:36 +08:00
function purchaseStatusLabel ( status : OverseasPurchaseStatus ) { return statusLabels [ status ] || status }
function formatAmount ( value : string | null , currency : string ) { if ( value == null ) return '—' ; const amount = Number ( value ); return ` ${ Number . isFinite ( amount ) ? amount . toLocaleString ( 'zh-CN' , { minimumFractionDigits : 2 , maximumFractionDigits : 2 } ) : value} ${ currency } ` }
2026-08-28 14:44:53 +08:00
function formatDateTime ( value ?: string | null ) { return value ? value . replace ( 'T' , ' ' ). slice ( 0 , 16 ) : '—' }
2026-08-20 09:10:36 +08:00
async function load () {
loading . value = true ; loadError . value = null
try {
2026-08-20 18:02:52 +08:00
const result = await listOverseasPurchases ({ page : page . value , size : pageSize . value , customerId : String ( applied . value . customerId || '' ), orderNo : String ( applied . value . orderNo || '' ), purchaseCategory : String ( applied . value . purchaseCategory || '' ), productName : String ( applied . value . productName || '' ) } satisfies OverseasPurchaseQuery )
2026-08-20 09:10:36 +08:00
rows . value = result . records ; total . value = result . total
} catch ( error ) { rows . value = []; total . value = 0 ; loadError . value = getApiErrorDetails ( error , '海外采购订单加载失败,请稍后重试' ) } finally { loading . value = false }
}
function search ( model : QueryModel ) { applied . value = { ... model }; if ( page . value === 1 ) void load (); else page . value = 1 }
function open ( row : OverseasPurchaseSummary , edit = false ) { void router . push ({ name : edit ? 'overseas-business-purchase-edit' : 'overseas-business-purchase-detail' , params : { purchaseId : row . orderNo }, query : { customerId : row . customerId } }) }
2026-08-20 18:02:52 +08:00
async function loadCustomerOptions () { customersLoading . value = true ; try { customerOptions . value = await listOverseasPurchaseCustomerOptions () } catch { customerOptions . value = [] } finally { customersLoading . value = false } }
2026-09-14 10:02:39 +08:00
let activatedOnce = false
2026-08-20 18:02:52 +08:00
watch ([ page , pageSize ], () => { void load () }); onMounted (() => { void load (); void loadCustomerOptions () })
2026-09-14 10:02:39 +08:00
onActivated (() => { if ( activatedOnce ) void load (); else activatedOnce = true })
2026-08-19 16:45:51 +08:00
</ script >
< template >
2026-09-15 09:58:15 +08:00
< AdminPage class = "admin-density" >< ListPageLayout aria -label =" 海外采购订单列表 " >
2026-08-20 09:10:36 +08:00
< template # toolbar >< QueryToolbar v-model = "filters" class="surface-panel" :fields="fields" ariaLabel="海外采购订单筛选" preference-key="overseas-business-purchases-query" @search="search" /></ template >
< template v-if = "loadError" #state><LoadErrorState title="海外采购订单暂时无法加载" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></ template ></ LoadErrorState ></ 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-business-purchases" row-key="id" empty-text="暂无符合筛选条件的海外采购订单" >
2026-09-15 09:58:15 +08:00
< template # actions = "{ row }" >< div class = "density-actions" >< el-button class = "density-action" data -tone =" neutral " size = "small" @click ="open(row as OverseasPurchaseSummary)" > 详情 </ el -button >< el-button class = "density-action" data -tone =" neutral " v-if = "access.can(overseasBusinessPermissions.purchaseEdit)" size="small" @click="open(row as OverseasPurchaseSummary, true)" > 编辑 </ el -button ></ div ></ template >
2026-08-20 09:10:36 +08:00
< template # pagination -left > 共 {{ total }} 条海外采购订单 </ template >
2026-08-19 16:45:51 +08:00
</ DataTable ></ section >
</ ListPageLayout ></ AdminPage >
</ template >
< style scoped >
2026-09-15 09:58:15 +08:00
. table - panel { overflow : hidden ; }
2026-08-19 16:45:51 +08:00
</ style >