130 lines
6.0 KiB
Vue
130 lines
6.0 KiB
Vue
<script setup lang="ts">
|
||
import InlineHelp from '@/shared/components/InlineHelp.vue'
|
||
import { onBeforeUnmount, ref, watch } from 'vue'
|
||
import { Printer, RefreshCw } from '@lucide/vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import '@/styles/adminDensity.css'
|
||
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">
|
||
<header class="print-heading">
|
||
<div>
|
||
<h3 id="dropship-print-title">一件代发托盘打印<InlineHelp title="托盘打印说明"><p>测量审核生成托盘码后,在电脑端打印并贴到空托盘,再交由 PDA 扫码组托。</p><p>箱数与 SKU 种类数按整托统计,包含合托的其他入仓码;相同 SKU 编码只计一种,不代表商品件数。</p></InlineHelp></h3>
|
||
|
||
|
||
</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>
|
||
</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" />
|
||
</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); }
|
||
.print-actions .el-button { margin-left: 0; }
|
||
.loading-state { padding: var(--admin-space-5) 0; }
|
||
</style>
|