111 lines
4.9 KiB
Vue
111 lines
4.9 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import { AlertTriangle, Link2, RefreshCw } from '@lucide/vue'
|
|
import {
|
|
getMerchantInboundCodeAssociation,
|
|
type MerchantInboundCodeAssociation,
|
|
} from '@/modules/first-leg/api/inboundCodeAssociations'
|
|
import { getFirstLegStatusLabel } from '@/modules/first-leg/utils/firstLegOrder'
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean
|
|
inboundCode: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: boolean]
|
|
}>()
|
|
|
|
const loading = ref(false)
|
|
const error = ref('')
|
|
const association = ref<MerchantInboundCodeAssociation | null>(null)
|
|
let requestSequence = 0
|
|
|
|
const visible = computed({
|
|
get: () => props.modelValue,
|
|
set: (value) => emit('update:modelValue', value),
|
|
})
|
|
|
|
function displayValue(value: string | null | undefined) {
|
|
return value?.trim() || '—'
|
|
}
|
|
|
|
function destinationLabel(value: MerchantInboundCodeAssociation) {
|
|
return [value.destinationWarehouseCode, value.destinationWarehouseName].filter(Boolean).join(' · ') || '—'
|
|
}
|
|
|
|
async function loadAssociation() {
|
|
const inboundCode = props.inboundCode.trim()
|
|
const sequence = ++requestSequence
|
|
association.value = null
|
|
error.value = ''
|
|
loading.value = false
|
|
if (!inboundCode) {
|
|
error.value = '当前订单暂无可查询的入仓码'
|
|
return
|
|
}
|
|
loading.value = true
|
|
try {
|
|
const result = await getMerchantInboundCodeAssociation(inboundCode)
|
|
if (sequence === requestSequence && props.modelValue) association.value = result
|
|
} catch (reason) {
|
|
if (sequence === requestSequence && props.modelValue) {
|
|
error.value = reason instanceof Error ? reason.message : '入仓码关联查询失败'
|
|
}
|
|
} finally {
|
|
if (sequence === requestSequence) loading.value = false
|
|
}
|
|
}
|
|
|
|
watch(() => [props.modelValue, props.inboundCode] as const, ([open]) => {
|
|
if (open) void loadAssociation()
|
|
else {
|
|
requestSequence += 1
|
|
loading.value = false
|
|
error.value = ''
|
|
association.value = null
|
|
}
|
|
}, { immediate: true })
|
|
</script>
|
|
|
|
<template>
|
|
<el-dialog v-model="visible" class="inbound-association-dialog" title="入仓码关联摘要" width="620px" append-to-body destroy-on-close>
|
|
<div v-loading="loading" class="inbound-association-content">
|
|
<div v-if="error" class="inbound-association-error" role="alert">
|
|
<AlertTriangle :size="22" />
|
|
<div><strong>关联摘要加载失败</strong><span>{{ error }}</span></div>
|
|
<el-button :loading="loading" @click="loadAssociation"><RefreshCw :size="15" />重新加载</el-button>
|
|
</div>
|
|
<template v-else-if="association">
|
|
<header class="inbound-association-heading">
|
|
<Link2 :size="21" />
|
|
<div><span>入仓码</span><strong>{{ displayValue(association.inboundCode) }}</strong></div>
|
|
</header>
|
|
<dl class="inbound-association-grid">
|
|
<div><dt>订单状态</dt><dd>{{ getFirstLegStatusLabel(association.orderStatus) }}</dd></div>
|
|
<div><dt>客户名称</dt><dd>{{ displayValue(association.customerName) }}</dd></div>
|
|
<div><dt>目的国家</dt><dd>{{ displayValue(association.destinationCountryCode) }}</dd></div>
|
|
<div class="is-wide"><dt>目的仓</dt><dd>{{ destinationLabel(association) }}</dd></div>
|
|
</dl>
|
|
</template>
|
|
</div>
|
|
<template #footer><el-button @click="visible = false">关闭</el-button></template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.inbound-association-content { min-height: 190px; }
|
|
.inbound-association-error { display: grid; min-height: 170px; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 13px; padding: 20px; border: 1px solid #efc1bc; background: #fff8f7; color: #a33b32; }
|
|
.inbound-association-error > div { display: flex; min-width: 0; flex-direction: column; gap: 4px; }
|
|
.inbound-association-error span { overflow-wrap: anywhere; font-size: 12px; }
|
|
.inbound-association-heading { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 12px; padding: 15px 16px; background: #f2f7f6; color: var(--primary); }
|
|
.inbound-association-heading > div { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
|
|
.inbound-association-heading span { color: var(--muted); font-size: 11px; }
|
|
.inbound-association-heading strong { overflow-wrap: anywhere; color: var(--navy); font-size: 16px; }
|
|
.inbound-association-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 14px 0 0; border-top: 1px solid var(--border); border-left: 1px solid var(--border); }
|
|
.inbound-association-grid > div { min-width: 0; padding: 13px 14px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
|
|
.inbound-association-grid .is-wide { grid-column: 1 / -1; }
|
|
.inbound-association-grid dt { color: var(--muted); font-size: 11px; }
|
|
.inbound-association-grid dd { margin: 5px 0 0; overflow-wrap: anywhere; color: var(--navy); font-size: 13px; font-weight: 650; }
|
|
</style>
|