feat: support dropship pallet loading

This commit is contained in:
2026-09-08 17:24:49 +08:00
parent 2dc357e55b
commit 7ba81007a8
21 changed files with 300 additions and 52 deletions
@@ -9,6 +9,7 @@ const props = withDefaults(defineProps<{
dropshipPallets?: InboundDropshipPallet[]
customerId?: string
inboundId?: string
canViewDropship?: boolean
files: InboundFileSummary[]
internalNotes: InboundInternalNote[]
statusHistory: InboundStatusHistory[]
@@ -16,6 +17,7 @@ const props = withDefaults(defineProps<{
sections?: Array<'dropship' | 'allocations' | 'files' | 'notes' | 'history' | 'remark'>
}>(), {
dropshipPallets: () => [],
canViewDropship: true,
sections: () => ['dropship', 'allocations', 'files', 'notes', 'history', 'remark'],
})
@@ -30,6 +32,9 @@ function date(value: string | null) {
const parsed = new Date(value)
return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString('zh-CN', { hour12: false })
}
function dropshipPalletStatus(value: string) {
return ({ OPEN: '清点组托中', SEALED: '已封托' } as Record<string, string>)[value] || value || '状态未知'
}
async function printPallet(pallet: InboundDropshipPallet) {
if (!props.customerId || !props.inboundId || !pallet.palletNo) return
try {
@@ -44,9 +49,35 @@ async function printPallet(pallet: InboundDropshipPallet) {
<template>
<div class="history-panel">
<section v-if="shows('dropship')" class="history-section">
<div class="section-heading"><h3>一件代发绑定托盘</h3><span>{{ dropshipPallets.length }} 个托盘</span></div>
<div v-if="dropshipPallets.length" class="simple-list"><div v-for="pallet in dropshipPallets" :key="pallet.palletId"><strong>{{ value(pallet.palletNo) }} · {{ pallet.cartonCount }} 箱 <el-button v-if="pallet.palletNo" link type="primary" @click="printPallet(pallet)">打印托盘唛</el-button></strong><span v-for="item in pallet.items" :key="`${pallet.palletId}-${item.inboundDetailId}`">{{ item.inboundCode }} · SKU {{ item.skuCode }} · {{ item.cartonCount }} 箱</span></div></div>
<p v-else class="muted-empty">暂无一件代发托盘绑定</p>
<div class="section-heading"><h3>一件代发清点与组托结果</h3><span>{{ dropshipPallets.length }} 个托盘</span></div>
<el-alert v-if="!canViewDropship" title="当前账号缺少一件代发 PDA 查看权限,无法读取清点组托结果。" type="warning" :closable="false" show-icon />
<div v-else-if="dropshipPallets.length" class="dropship-pallet-list">
<article v-for="pallet in dropshipPallets" :key="pallet.palletId" class="dropship-pallet-card">
<header>
<div><strong>{{ value(pallet.palletNo) }}</strong><el-tag size="small" :type="pallet.status === 'SEALED' ? 'success' : 'warning'" effect="plain">{{ dropshipPalletStatus(pallet.status) }}</el-tag></div>
<el-button v-if="pallet.palletNo" link type="primary" @click="printPallet(pallet)">打印托盘唛</el-button>
</header>
<dl class="dropship-pallet-summary">
<div><dt>箱数</dt><dd>{{ pallet.cartonCount }}</dd></div>
<div><dt>SKU 数</dt><dd>{{ pallet.skuCount }}</dd></div>
<div><dt>毛重</dt><dd>{{ number(pallet.weightKg) }} kg</dd></div>
<div><dt>体积</dt><dd>{{ number(pallet.volumeCbm) }} m³</dd></div>
<div><dt>差异箱数</dt><dd><el-tag size="small" :type="pallet.discrepancyCartons ? 'warning' : 'success'">{{ pallet.discrepancyCartons }}</el-tag></dd></div>
<div><dt>封托时间</dt><dd>{{ date(pallet.sealedAt) }}</dd></div>
</dl>
<div class="dropship-item-table">
<div class="dropship-item-row dropship-item-row--header"><span>入仓码</span><span>SKU / 货物</span><span>商品数量</span><span>箱数</span><span>箱码</span></div>
<div v-for="item in pallet.items" :key="`${pallet.palletId}-${item.inboundDetailId}`" class="dropship-item-row">
<span>{{ value(item.inboundCode) }}</span>
<span><strong>{{ value(item.skuCode) }}</strong><small>{{ value(item.cargoName) }}</small></span>
<span>{{ item.skuQuantity }}</span>
<span>{{ item.cartonCount }}</span>
<span>{{ item.cartonMarks.length ? item.cartonMarks.join('、') : '—' }}</span>
</div>
</div>
</article>
</div>
<p v-else class="muted-empty">暂无一件代发清点与组托结果</p>
</section>
<section v-if="shows('allocations')" class="history-section">
<div class="section-heading"><h3>托盘与库位</h3><span>{{ palletLocations.length }} 条分配</span></div>
@@ -93,6 +124,25 @@ async function printPallet(pallet: InboundDropshipPallet) {
.allocation-location { overflow-wrap: anywhere; color: var(--cc-primary); text-align: right; }
.simple-list > div { padding: 10px 12px; border-bottom: 1px solid #edf0f4; font-size: 12px; }
.simple-list strong, .simple-list span { overflow-wrap: anywhere; }
.dropship-pallet-list { display: grid; gap: 12px; }
.dropship-pallet-card { min-width: 0; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 6px; background: #fff; }
.dropship-pallet-card > header { display: flex; min-height: 42px; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 12px; border-bottom: 1px solid #edf0f4; background: #f8fafc; }
.dropship-pallet-card > header > div { display: flex; min-width: 0; align-items: center; gap: 8px; }
.dropship-pallet-card > header strong { overflow-wrap: anywhere; color: #172033; font-size: 13px; }
.dropship-pallet-summary { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); margin: 0; border-bottom: 1px solid #edf0f4; }
.dropship-pallet-summary > div { min-width: 0; padding: 8px 12px; border-right: 1px solid #edf0f4; }
.dropship-pallet-summary > div:last-child { border-right: 0; }
.dropship-pallet-summary dt { color: var(--cc-muted); font-size: 11px; }
.dropship-pallet-summary dd { margin: 3px 0 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 12px; font-weight: 650; }
.dropship-item-table { min-width: 680px; }
.dropship-pallet-card { overflow-x: auto; }
.dropship-item-row { display: grid; grid-template-columns: minmax(140px, 1fr) minmax(180px, 1.2fr) 100px 70px minmax(220px, 1.5fr); min-width: 800px; border-bottom: 1px solid #edf0f4; }
.dropship-item-row:last-child { border-bottom: 0; }
.dropship-item-row > span { min-width: 0; padding: 8px 12px; overflow-wrap: anywhere; color: var(--cc-text); font-size: 12px; }
.dropship-item-row > span + span { border-left: 1px solid #edf0f4; }
.dropship-item-row small { display: block; margin-top: 3px; color: var(--cc-muted); font-size: 11px; }
.dropship-item-row--header { background: #f8fafc; }
.dropship-item-row--header > span { color: var(--cc-muted); font-size: 11px; font-weight: 650; }
.muted-empty { margin: 0; padding: 18px 12px; border: 1px dashed var(--cc-border); border-radius: 6px; background: #f8fafc; color: var(--cc-muted); font-size: 12px; text-align: center; }
.timeline { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.timeline li { display: grid; grid-template-columns: 16px 1fr; gap: 10px; min-width: 0; padding-bottom: 16px; }
@@ -101,5 +151,6 @@ async function printPallet(pallet: InboundDropshipPallet) {
.timeline strong, .timeline span, .timeline small { display: block; overflow-wrap: anywhere; }
.timeline span, .timeline small { margin-top: 3px; color: var(--cc-muted); font-size: 12px; line-height: 1.5; }
.remark-section p { margin: 0; padding: 12px; border-radius: 6px; background: #f8fafc; color: var(--cc-text); font-size: 13px; line-height: 1.6; white-space: pre-wrap; }
@media (max-width: 720px) { .dropship-pallet-summary { grid-template-columns: 1fr; } .dropship-pallet-summary > div { border-right: 0; border-bottom: 1px solid #edf0f4; } .dropship-pallet-summary > div:last-child { border-bottom: 0; } }
@media (max-width: 480px) { .allocation-row { grid-template-columns: 1fr auto; } .allocation-location { grid-column: 1 / -1; text-align: left; } }
</style>