88 lines
6.0 KiB
Vue
88 lines
6.0 KiB
Vue
<script setup lang="ts">
|
|
import '@/styles/adminDensity.css'
|
|
import { computed, reactive, ref, watch } from 'vue'
|
|
import { CheckCircle2, XCircle } from '@lucide/vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
|
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
|
import AppDialog from '@/shared/components/AppDialog.vue'
|
|
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
|
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
|
import { getWarehouseTransfer, reviewWarehouseTransfer, type WarehouseTransferDetailDto } from '../api/warehouseTransfers'
|
|
|
|
const props = defineProps<{ modelValue: boolean; customerId: string; customerName: string; transferNo: string }>()
|
|
const emit = defineEmits<{ 'update:modelValue': [value: boolean]; reviewed: [] }>()
|
|
const open = computed({ get: () => props.modelValue, set: (value) => emit('update:modelValue', value) })
|
|
const detail = ref<WarehouseTransferDetailDto | null>(null)
|
|
const loading = ref(false)
|
|
const saving = ref(false)
|
|
const error = ref<ApiErrorDetails | null>(null)
|
|
const form = reactive<{ decision: 'APPROVE' | 'REJECT'; remark: string }>({ decision: 'APPROVE', remark: '' })
|
|
const itemColumns: DataTableColumn[] = [
|
|
{ key: 'skuCode', label: 'SKU', minWidth: 140 }, { key: 'productNameZh', label: '品名', minWidth: 160 },
|
|
{ key: 'packageCount', label: '箱数', width: 80, align: 'right' }, { key: 'shippingMark', label: '客户箱唛', minWidth: 160 },
|
|
]
|
|
const bindingColumns: DataTableColumn[] = [
|
|
{ key: 'operationType', label: '换标类型', minWidth: 120 }, { key: 'customerCartonMark', label: '客户箱唛', minWidth: 150 },
|
|
{ key: 'originalSku', label: '原 SKU', minWidth: 120 }, { key: 'newSku', label: '新 SKU', minWidth: 120 },
|
|
{ key: 'quantity', label: '数量', width: 80, align: 'right' }, { key: 'fbaCartonMark', label: '新箱唛', minWidth: 150 },
|
|
]
|
|
const palletColumns: DataTableColumn[] = [
|
|
{ key: 'sequenceNo', label: '托序', width: 70, align: 'center' }, { key: 'palletNo', label: '托盘号', minWidth: 150 },
|
|
{ key: 'cartonCount', label: '箱数', width: 80, align: 'right' }, { key: 'volumeCbm', label: '托盘总体积(m³)', width: 150, align: 'right' },
|
|
{ key: 'grossWeightKg', label: '托盘总重量(kg)', width: 150, align: 'right' },
|
|
]
|
|
|
|
async function load() {
|
|
if (!props.customerId || !props.transferNo) return
|
|
loading.value = true; error.value = null; detail.value = null
|
|
try { detail.value = await getWarehouseTransfer(props.customerId, props.transferNo) }
|
|
catch (cause) { error.value = getApiErrorDetails(cause, '转运单审核资料加载失败,请重试') }
|
|
finally { loading.value = false }
|
|
}
|
|
async function submit() {
|
|
if (saving.value) return
|
|
if (form.decision === 'REJECT' && !form.remark.trim()) { ElMessage.warning('驳回时请填写审核原因'); return }
|
|
saving.value = true
|
|
try {
|
|
await reviewWarehouseTransfer(props.customerId, props.transferNo, form.decision, form.remark.trim() || undefined)
|
|
ElMessage.success(form.decision === 'APPROVE' ? '转运单审核通过,已推送 PDA 收货打托' : '转运单已驳回')
|
|
open.value = false; emit('reviewed')
|
|
} catch (cause) { ElMessage.error(getApiErrorDetails(cause, '审核提交失败,请重试').message) }
|
|
finally { saving.value = false }
|
|
}
|
|
watch(
|
|
() => [props.modelValue, props.customerId, props.transferNo] as const,
|
|
([value]) => {
|
|
if (!value) return
|
|
form.decision = 'APPROVE'
|
|
form.remark = ''
|
|
void load()
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<AppDialog class="admin-density-dialog" v-model="open" title="审核仓储转运单" width="min(920px, calc(100vw - 28px))" confirm-text="提交审核" :confirm-loading="saving" :loading="loading" @confirm="submit">
|
|
<LoadErrorState v-if="error" compact title="审核资料加载失败" :message="error.message" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
|
|
<el-form v-else-if="detail" label-position="top" class="review-form density-form">
|
|
<div class="review-heading"><strong>{{ detail.transferNo }}</strong><span>{{ customerName }} · {{ detail.packageCount }} 箱 · {{ detail.needRelabel ? '需要换标' : '无需换标' }}</span></div>
|
|
<h3>货物明细</h3><DataTable :data="detail.items" :columns="itemColumns" :show-pagination="false" row-key="id" empty-text="暂无货物明细" />
|
|
<template v-if="detail.needRelabel"><h3>换标绑定明细</h3><DataTable :data="detail.relabelBindings" :columns="bindingColumns" :show-pagination="false" row-key="customerCartonMark" empty-text="暂无换标绑定明细" /></template>
|
|
<template v-if="detail.pallets?.length"><h3>PDA 打托实测</h3><DataTable :data="detail.pallets" :columns="palletColumns" :show-pagination="false" row-key="id" empty-text="暂无打托数据" /></template>
|
|
<el-form-item label="审核结论" required><el-radio-group v-model="form.decision"><el-radio-button value="APPROVE"><CheckCircle2 :size="15" />审核通过</el-radio-button><el-radio-button value="REJECT"><XCircle :size="15" />驳回</el-radio-button></el-radio-group></el-form-item>
|
|
<el-form-item :label="form.decision === 'REJECT' ? '驳回原因' : '审核备注'" :required="form.decision === 'REJECT'"><el-input v-model="form.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入审核备注(选填)" /></el-form-item>
|
|
</el-form>
|
|
</AppDialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.review-form { display: grid; gap: var(--admin-space-5); }
|
|
.review-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--admin-space-5); }
|
|
.review-heading span { color: var(--cc-muted); font-size: var(--admin-font-caption); }
|
|
h3 { margin: 2px 0 0; font-size: var(--admin-font-section); }
|
|
.review-form :deep(.el-form-item) { margin: var(--admin-space-1) 0 0; }
|
|
.review-form :deep(.el-radio-button__inner) { display: inline-flex; align-items: center; gap: var(--admin-space-2); }
|
|
</style>
|