2026-10-10 02:19:39 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed } from 'vue'
|
|
|
|
|
|
import type { ProcurementDetail, ProcurementFee } from '../api/procurement'
|
2026-10-26 13:43:20 +08:00
|
|
|
|
import { directionLabel, regionText, dateTime, feePurposes, feeStatus, feeTone, feeTotals, measurement, money, productHref, text } from '../procurementPresentation'
|
2026-10-10 02:19:39 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ detail: ProcurementDetail }>()
|
2026-10-26 13:43:20 +08:00
|
|
|
|
const t = (text: string) => regionText(text, props.detail.order.procurementDirection)
|
2026-10-10 02:19:39 +08:00
|
|
|
|
const totals = computed(() => feeTotals(props.detail.fees))
|
|
|
|
|
|
const progress = computed(() => props.detail.progress.filter(step => step.key !== 'closed_at' || step.completedAt))
|
|
|
|
|
|
const ended = computed(() => ['REJECTED', 'CANCELLED', 'EXCEPTION_CLOSED', 'SHIPPED_TO_CUSTOMER'].includes(props.detail.order.status))
|
|
|
|
|
|
const hasPending = computed(() => props.detail.fees.some(f => f.walletStatus?.endsWith('_PENDING') || f.walletStatus?.endsWith('_FAILED')))
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<section class="procurement-section" aria-label="采购履约进度">
|
|
|
|
|
|
<h3>履约进度</h3>
|
|
|
|
|
|
<ol class="procurement-progress">
|
|
|
|
|
|
<li v-for="step in progress" :key="step.key" :class="{ 'is-complete': step.completedAt }">
|
|
|
|
|
|
<strong>{{ step.title }}</strong><span>{{ step.completedAt ? dateTime(step.completedAt) : ended ? '未执行' : '待处理' }}</span>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ol>
|
|
|
|
|
|
<p v-if="detail.purchaseNeedsAttention" class="procurement-note">购买资料正在核对,客服将协助处理。已发生的收货与费用结果仍保留在本单。</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section class="procurement-section" aria-label="商品与收件信息">
|
|
|
|
|
|
<h3>商品与收件信息</h3>
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<dl class="procurement-grid"><div><dt>采购方向</dt><dd>{{ directionLabel(detail.order.procurementDirection) }}</dd></div>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<div><dt>采购品类</dt><dd>{{ detail.order.purchaseCategory }}</dd></div>
|
|
|
|
|
|
<div><dt>商品名称</dt><dd>{{ detail.order.productName }}</dd></div>
|
|
|
|
|
|
<div><dt>商品规格</dt><dd>{{ text(detail.order.productSpecification) }}</dd></div>
|
|
|
|
|
|
<div><dt>采购数量</dt><dd>{{ detail.order.purchaseQuantity }} 件</dd></div>
|
|
|
|
|
|
<div><dt>下单预算单价</dt><dd>{{ money(detail.order.budgetUnitPrice, detail.order.currency) }}</dd></div>
|
|
|
|
|
|
<div><dt>货款预算总上限</dt><dd>{{ money(detail.budget.totalAmount, detail.budget.currency) }}</dd></div>
|
|
|
|
|
|
<div><dt>预算核验时间</dt><dd>{{ dateTime(detail.budget.authorizedAt) }}</dd></div>
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<div><dt>{{ t('预计到海外仓') }}</dt><dd>{{ dateTime(detail.order.estimatedArrivalAt) }}</dd></div>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<div><dt>收件人</dt><dd>{{ detail.order.contactName }}</dd></div>
|
|
|
|
|
|
<div><dt>联系电话</dt><dd>{{ detail.order.contactPhone }}</dd></div>
|
|
|
|
|
|
<div class="wide"><dt>下单收货地址</dt><dd>{{ detail.order.receivingAddress }}</dd></div>
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<div class="wide"><dt>{{ t('实际海外仓') }}</dt><dd>{{ text(detail.order.actualWarehouseName) }}<span v-if="detail.order.actualWarehouseCode">({{ detail.order.actualWarehouseCode }})</span></dd></div>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<div class="wide"><dt>商品链接</dt><dd><a v-if="productHref(detail.order.productUrl)" :href="productHref(detail.order.productUrl)" target="_blank" rel="noopener noreferrer">{{ detail.order.productUrl }}</a><span v-else>{{ text(detail.order.productUrl) }}</span></dd></div>
|
|
|
|
|
|
</dl>
|
|
|
|
|
|
</section>
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<section class="procurement-section" :aria-label="t('已确认海外装箱清单')">
|
|
|
|
|
|
<h3>{{ t('已确认海外装箱清单') }}</h3>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<el-table :data="detail.boxes" border stripe empty-text="尚无已确认的装箱清单" row-key="cartonNo">
|
|
|
|
|
|
<el-table-column prop="cartonNo" label="箱号" min-width="170" />
|
|
|
|
|
|
<el-table-column label="箱内商品" min-width="200"><template #default>{{ detail.order.productName }}</template></el-table-column>
|
|
|
|
|
|
<el-table-column label="规格" min-width="180"><template #default>{{ text(detail.order.productSpecification) }}</template></el-table-column>
|
|
|
|
|
|
<el-table-column prop="quantity" label="数量(件)" width="120" />
|
|
|
|
|
|
<el-table-column label="外箱长 × 宽 × 高(cm)" min-width="240"><template #default="{ row }">{{ measurement(row.lengthCm, 2) }} × {{ measurement(row.widthCm, 2) }} × {{ measurement(row.heightCm, 2) }}</template></el-table-column>
|
|
|
|
|
|
<el-table-column label="封箱毛重(kg)" width="170"><template #default="{ row }">{{ measurement(row.grossWeightKg, 3) }}</template></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section class="procurement-section" aria-label="两段实际运输">
|
|
|
|
|
|
<h3>两段实际运输</h3>
|
|
|
|
|
|
<el-table :data="detail.shipments" border stripe empty-text="尚无实际交寄记录">
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<el-table-column label="运输段" width="150"><template #default="{ row }">{{ t(row.leg === 'OVERSEAS' ? '国际段 · 海外交寄' : '国内段 · 寄客户') }}</template></el-table-column>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<el-table-column prop="shipmentNo" label="交寄编号" min-width="270" />
|
|
|
|
|
|
<el-table-column prop="cartonNo" label="原箱号" min-width="150" />
|
|
|
|
|
|
<el-table-column prop="quantity" label="实际件数" width="110" />
|
|
|
|
|
|
<el-table-column prop="carrier" label="承运商" min-width="140" />
|
|
|
|
|
|
<el-table-column prop="trackingNo" label="运单号" min-width="230" />
|
|
|
|
|
|
<el-table-column label="实际交寄时间" width="185"><template #default="{ row }">{{ dateTime(row.shippedAt) }}</template></el-table-column>
|
|
|
|
|
|
</el-table>
|
2026-10-20 14:52:22 +08:00
|
|
|
|
|
2026-10-10 02:19:39 +08:00
|
|
|
|
</section>
|
|
|
|
|
|
<section class="procurement-section" aria-label="采购费用与资金结果">
|
|
|
|
|
|
<h3>费用与资金结果</h3>
|
|
|
|
|
|
<dl class="procurement-grid money-grid">
|
|
|
|
|
|
<div><dt>累计成功扣款</dt><dd>{{ money(totals?.paid) }}</dd></div>
|
|
|
|
|
|
<div><dt>累计成功退款</dt><dd>{{ money(totals?.refunded) }}</dd></div>
|
|
|
|
|
|
<div><dt>净实扣</dt><dd>{{ money(totals?.net) }}</dd></div>
|
|
|
|
|
|
<div><dt>预估国际运费占款</dt><dd>{{ money(totals?.estimatedHold) }}</dd></div>
|
|
|
|
|
|
</dl>
|
|
|
|
|
|
<p class="procurement-note">预算上限仅包含样品货款。预估国际运费只冻结、不直接扣款,最终以实际费用结算;表内保留原费用、追加及退款记录。</p>
|
|
|
|
|
|
<el-table :data="detail.fees" border stripe empty-text="尚无已核定费用" row-key="feeNo">
|
|
|
|
|
|
<el-table-column prop="feeNo" label="费用编号" min-width="270" />
|
2026-10-26 13:43:20 +08:00
|
|
|
|
<el-table-column label="费用用途" min-width="145"><template #default="{ row }">{{ t(feePurposes[row.purpose] || '待核定') }}</template></el-table-column>
|
2026-10-10 02:19:39 +08:00
|
|
|
|
<el-table-column prop="name" label="费用名称" min-width="145" />
|
|
|
|
|
|
<el-table-column label="原币金额" min-width="130"><template #default="{ row }">{{ money(row.originalAmount, row.originalCurrency) }}</template></el-table-column>
|
|
|
|
|
|
<el-table-column label="汇率 / 月份" min-width="150"><template #default="{ row }">{{ measurement(row.exchangeRate, 6) }}<br /><small>{{ text(row.exchangeRateMonth) }}</small></template></el-table-column>
|
|
|
|
|
|
<el-table-column label="人民币金额" min-width="130"><template #default="{ row }">{{ money(row.amount) }}</template></el-table-column>
|
|
|
|
|
|
<el-table-column label="处理结果" min-width="190"><template #default="{ row }"><el-tag :type="feeTone(row as ProcurementFee)" size="small">{{ feeStatus(row as ProcurementFee) }}</el-tag><small v-if="row.purpose !== 'INTERNATIONAL_ESTIMATE' && row.walletStatus === 'SETTLED' && !row.sourceLinked" class="fee-sync">费用结果同步中</small></template></el-table-column>
|
|
|
|
|
|
<el-table-column prop="parentFeeNo" label="关联原费用 / 预估" min-width="270"><template #default="{ row }">{{ text(row.parentFeeNo) }}</template></el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
<p v-if="hasPending" class="procurement-note">部分资金处理尚未完成,可点击页面“刷新”查看最新结果;如需协助请联系客户服务。</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
<section v-if="detail.exceptionSummaries.length" class="procurement-section" aria-label="客服处理说明">
|
|
|
|
|
|
<h3>客服处理说明</h3><ul class="procurement-summaries"><li v-for="(summary, index) in detail.exceptionSummaries" :key="index">{{ summary }}</li></ul>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.procurement-section { margin-top: 18px; min-width: 0; }
|
|
|
|
|
|
h3 { margin: 0 0 10px; padding-bottom: 7px; border-bottom: 1px solid var(--border); color: var(--navy); font-size: 14px; }
|
|
|
|
|
|
.procurement-progress { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; padding: 0; margin: 0; list-style: none; border-top: 1px solid var(--border); border-left: 1px solid var(--border); }
|
|
|
|
|
|
.procurement-progress li { display: grid; gap: 7px; padding: 12px; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); color: var(--muted); }
|
|
|
|
|
|
.procurement-progress strong { font-size: 13px; font-weight: 600; }.procurement-progress span { font-size: 12px; }.procurement-progress .is-complete strong { color: var(--navy); }
|
|
|
|
|
|
.procurement-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; border-left: 1px solid var(--border); border-top: 1px solid var(--border); }
|
|
|
|
|
|
.procurement-grid > div { display: grid; grid-template-columns: 110px minmax(0, 1fr); align-items: center; gap: 8px; padding: 9px 12px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); min-width: 0; }
|
|
|
|
|
|
.procurement-grid dt { font-size: 12px; color: var(--muted); }.procurement-grid dd { margin: 0; font-size: 13px; line-height: 1.6; color: var(--navy); overflow-wrap: anywhere; }
|
|
|
|
|
|
.procurement-grid .wide { grid-column: span 2; }.money-grid > div { grid-template-columns: minmax(0, 1fr); }.money-grid dd { font-size: 16px; font-weight: 600; }
|
|
|
|
|
|
a { color: var(--client-action); } .procurement-note { margin: 10px 0; color: var(--muted); font-size: 12px; line-height: 1.7; }
|
|
|
|
|
|
.fee-sync { display: block; padding-top: 5px; color: var(--muted); }.procurement-summaries { margin: 0; padding-left: 22px; line-height: 1.8; color: var(--navy); font-size: 13px; white-space: pre-wrap; }
|
|
|
|
|
|
@media (max-width: 767px) {
|
|
|
|
|
|
.procurement-progress, .procurement-grid { grid-template-columns: 1fr; }.procurement-grid .wide { grid-column: auto; }
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|