Files
admin-web/src/modules/loading-lists/components/PalletLabelPrintDialog.vue
T

77 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import '@/styles/adminDensity.css'
import '@/styles/adminWorkbench.css'
import { computed, ref } from 'vue'
import { Printer } from '@lucide/vue'
import { ElMessage } from 'element-plus'
import AppDialog from '@/shared/components/AppDialog.vue'
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
import { preparePalletLabels, printPalletLabels, type PalletLabelManifest } from '../api/platformLoadingLists'
const props = defineProps<{ planId: string; warehouseId: string; disabled?: boolean; dense?: boolean }>()
const open = ref(false)
const loading = ref(false)
const printing = ref(false)
const error = ref<ApiErrorDetails | null>(null)
const manifest = ref<PalletLabelManifest | null>(null)
const scope = ref<'ALL' | 'WAREHOUSE'>('ALL')
const warehouseCode = ref('')
const count = ref(1)
const dialogTitle = computed(() => {
const code = manifest.value?.containerNo?.trim() || manifest.value?.planNo?.trim()
return props.dense && code ? `散货托盘码 · ${code}` : '散货托盘码'
})
const selectedGroup = computed(() => manifest.value?.groups.find((group) => group.destinationWarehouseCode === warehouseCode.value) || null)
const disabledConfirm = computed(() => !manifest.value?.totalCount || (scope.value === 'WAREHOUSE' && (!selectedGroup.value || count.value < 1 || count.value > selectedGroup.value.totalCount)))
async function show() {
open.value = true; loading.value = true; error.value = null; manifest.value = null
try {
manifest.value = await preparePalletLabels(props.planId, props.warehouseId)
warehouseCode.value = manifest.value.groups[0]?.destinationWarehouseCode || ''
count.value = manifest.value.groups[0]?.totalCount || 1
} catch (cause) { error.value = getApiErrorDetails(cause, '托盘码清单加载失败') }
finally { loading.value = false }
}
function onWarehouseChange() { count.value = selectedGroup.value?.totalCount || 1 }
async function print() {
if (!manifest.value) return
printing.value = true; error.value = null
try {
const blob = await printPalletLabels(props.planId, props.warehouseId,
scope.value === 'WAREHOUSE' ? warehouseCode.value : undefined,
scope.value === 'WAREHOUSE' ? count.value : undefined)
const url = URL.createObjectURL(blob); const anchor = document.createElement('a')
anchor.href = url; anchor.download = `${manifest.value.containerNo}-托盘码.pdf`; anchor.click(); URL.revokeObjectURL(url)
ElMessage.success(`已生成 ${scope.value === 'ALL' ? manifest.value.totalCount : count.value} 张托盘码`)
} catch (cause) { error.value = getApiErrorDetails(cause, '托盘码打印失败') }
finally { printing.value = false }
}
</script>
<template>
<el-button :disabled="disabled" @click="show"><Printer :size="16" />打印托盘码</el-button>
<AppDialog class="admin-workbench is-dialog" :class="{ 'admin-density-dialog': dense }" v-model="open" :title="dialogTitle" :width="dense ? 'min(920px, calc(100vw - 28px))' : 'min(760px, calc(100vw - 28px))'" :fullscreenable="dense" confirm-text="生成 PDF" :loading="loading" :confirm-loading="printing" :confirm-disabled="disabledConfirm" @confirm="print">
<LoadErrorState v-if="error" compact title="托盘码操作失败" :message="error.message" :show-retry="false" />
<div class="admin-workbench" v-if="manifest" :class="{ 'density-dialog-sections': dense }">
<section :class="{ 'density-dialog-section': dense }">
<el-radio-group v-model="scope" class="print-scope" :class="{ 'print-scope--dense': dense }"><el-radio value="ALL">全部打印({{ manifest.totalCount }} 张)</el-radio><el-radio value="WAREHOUSE">按 FBA 仓补打</el-radio></el-radio-group>
<div v-if="scope === 'WAREHOUSE'" class="print-options" :class="{ 'density-form': dense }"><el-select v-model="warehouseCode" placeholder="选择 FBA 仓" @change="onWarehouseChange"><el-option v-for="group in manifest.groups" :key="group.destinationWarehouseCode" :label="`${group.destinationWarehouseCode} · ${group.totalCount} 张`" :value="group.destinationWarehouseCode" /></el-select><el-input-number v-model="count" placeholder="打印张数" :min="1" :max="selectedGroup?.totalCount || 1" /><span>张(从该仓第 1 张起)</span></div>
</section>
<section :class="{ 'density-dialog-section': dense }">
<el-table :data="manifest.groups" border :max-height="dense ? undefined : 300"><el-table-column label="FBA 仓编码" prop="destinationWarehouseCode" width="140" /><el-table-column label="内部编号" prop="destinationWarehouseInternalCode" width="120" /><el-table-column label="FBA 仓名称" prop="destinationWarehouseName" min-width="160" :show-overflow-tooltip="dense"><template #default="{ row }">{{ row.destinationWarehouseName || '—' }}</template></el-table-column><el-table-column label="托盘数" prop="totalCount" width="90" align="right" /><el-table-column label="编号范围" min-width="230"><template #default="{ row }">{{ row.labels[0]?.palletCode || '—' }}<template v-if="row.labels.length > 1"> ~ {{ row.labels[row.labels.length - 1]?.palletCode }}</template></template></el-table-column></el-table>
</section>
</div>
</AppDialog>
</template>
<style scoped>
.print-scope { display:flex; margin:18px 0 var(--admin-space-5, 12px); }
.print-options { display:flex; align-items:center; gap:var(--admin-space-5, 12px); margin-bottom:var(--admin-space-6, 16px); }
.print-options .el-select { width:240px; }
.print-scope--dense { margin: var(--admin-space-3) 0 0; flex-wrap: wrap; }
.print-options.density-form { display: grid; grid-template-columns: minmax(0, 1fr) 160px auto; margin: var(--admin-space-3) 0 0; }
</style>