Files
admin-web/src/modules/inbounds/core/DropshipPalletPrintPanel.vue
T

130 lines
6.0 KiB
Vue
Raw Normal View History

2026-09-14 17:52:00 +08:00
<script setup lang="ts">
2026-10-20 15:48:23 +08:00
import InlineHelp from '@/shared/components/InlineHelp.vue'
2026-09-14 17:52:00 +08:00
import { onBeforeUnmount, ref, watch } from 'vue'
import { Printer, RefreshCw } from '@lucide/vue'
import { ElMessage } from 'element-plus'
import '@/styles/adminDensity.css'
2026-09-14 17:52:00 +08:00
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import { downloadAllDropshipPalletLabels, downloadDropshipPalletLabel, listPlatformInboundDropshipPallets } from './api'
import type { InboundDropshipPallet } from './types'
const props = defineProps<{
customerId: string
inboundId: string
inboundCode?: string | null
}>()
const pallets = ref<InboundDropshipPallet[]>([])
const loading = ref(false)
const loadError = ref<ApiErrorDetails | null>(null)
const printing = ref('')
let requestSerial = 0
let disposed = false
function statusLabel(status: InboundDropshipPallet['status']) {
return status === 'SEALED' ? '已封托' : '待封托'
}
function download(blob: Blob, fileName: string) {
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = fileName
link.click()
window.setTimeout(() => URL.revokeObjectURL(url), 0)
}
async function loadPallets() {
const serial = ++requestSerial
loading.value = true
loadError.value = null
try {
const response = await listPlatformInboundDropshipPallets(props.customerId, props.inboundId)
if (!disposed && serial === requestSerial) pallets.value = response
} catch (error) {
if (!disposed && serial === requestSerial) {
pallets.value = []
loadError.value = getApiErrorDetails(error, '一件代发托盘加载失败')
}
} finally {
if (!disposed && serial === requestSerial) loading.value = false
}
}
async function printAll() {
if (!pallets.value.length || printing.value) return
printing.value = 'all'
try {
const blob = await downloadAllDropshipPalletLabels(props.customerId, props.inboundId)
download(blob, `${props.inboundCode || '一件代发'}-全部托盘.pdf`)
ElMessage.success(`已生成 ${pallets.value.length} 个托盘的打印文件`)
} catch (error) {
ElMessage.error(getApiErrorDetails(error, '全部托盘打印文件生成失败').message)
} finally {
printing.value = ''
}
}
async function printOne(pallet: InboundDropshipPallet) {
if (!pallet.palletNo || printing.value) return
printing.value = pallet.palletId
try {
const blob = await downloadDropshipPalletLabel(props.customerId, props.inboundId, pallet.palletId)
download(blob, `${pallet.palletNo}-托盘.pdf`)
ElMessage.success(`${pallet.palletNo} 打印文件已生成`)
} catch (error) {
ElMessage.error(getApiErrorDetails(error, '单托打印文件生成失败').message)
} finally {
printing.value = ''
}
}
watch(() => [props.customerId, props.inboundId], loadPallets, { immediate: true })
onBeforeUnmount(() => { disposed = true; requestSerial += 1 })
</script>
<template>
<section class="dropship-print-panel admin-density" aria-labelledby="dropship-print-title">
2026-09-14 17:52:00 +08:00
<header class="print-heading">
<div>
2026-10-20 15:48:23 +08:00
<h3 id="dropship-print-title">一件代发托盘打印<InlineHelp title="托盘打印说明"><p>测量审核生成托盘码后,在电脑端打印并贴到空托盘,再交由 PDA 扫码组托。</p><p>箱数与 SKU 种类数按整托统计,包含合托的其他入仓码;相同 SKU 编码只计一种,不代表商品件数。</p></InlineHelp></h3>
2026-09-14 17:52:00 +08:00
</div>
<div class="print-actions">
<el-button :disabled="loading || Boolean(printing)" @click="loadPallets"><RefreshCw :size="15" />刷新</el-button>
<el-button type="primary" :disabled="!pallets.length" :loading="printing === 'all'" @click="printAll"><Printer :size="15" />打印全部托盘</el-button>
</div>
</header>
<LoadErrorState v-if="loadError" title="托盘数据暂时无法加载" :message="loadError.message" @retry="loadPallets">
<template #details><ApiErrorMeta :details="loadError" /></template>
</LoadErrorState>
<el-table v-else-if="pallets.length" v-loading="loading" :data="pallets" border stripe empty-text="暂无托盘码" aria-label="一件代发托盘打印列表">
<el-table-column label="操作" width="120" fixed="left">
<template #default="{ row }">
<el-button class="density-action" :loading="printing === row.palletId" :disabled="Boolean(printing) && printing !== row.palletId" @click="printOne(row as InboundDropshipPallet)">单托补打</el-button>
2026-09-14 17:52:00 +08:00
</template>
</el-table-column>
<el-table-column prop="palletNo" label="托盘码" min-width="210" />
<el-table-column label="状态" width="110"><template #default="{ row }"><el-tag size="small" :type="row.status === 'SEALED' ? 'success' : 'warning'" effect="plain">{{ statusLabel(row.status) }}</el-tag></template></el-table-column>
<el-table-column prop="cartonCount" label="整托箱数" width="120" align="right" />
<el-table-column prop="skuCount" label="SKU 种类数" width="120" align="right" />
2026-09-14 17:52:00 +08:00
</el-table>
<el-empty v-else-if="!loading" description="测量审核通过并生成托盘码后,可在这里打印" :image-size="64" />
<div v-else class="loading-state"><el-skeleton :rows="4" animated /></div>
</section>
</template>
<style scoped>
.dropship-print-panel { min-width: 0; }
.print-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--admin-space-5); margin-bottom: var(--admin-space-5); }
.print-heading h3 { margin: 0; color: var(--cc-text); font-size: var(--admin-font-section); }
.print-heading p { margin: var(--admin-space-1) 0 0; color: var(--cc-muted); font-size: var(--admin-font-caption); line-height: 1.5; }
.print-actions { display: flex; flex-shrink: 0; gap: var(--admin-space-2); }
2026-09-14 17:52:00 +08:00
.print-actions .el-button { margin-left: 0; }
.loading-state { padding: var(--admin-space-5) 0; }
2026-09-14 17:52:00 +08:00
</style>