Files
client-web/src/modules/first-leg/components/InboundCodeAssociationDialog.vue
T
2026-09-19 17:02:11 +08:00

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>