This commit is contained in:
2026-09-14 20:03:27 +08:00
parent 66f49b97e5
commit 349e8f60df
44 changed files with 658 additions and 459 deletions
@@ -1,4 +1,5 @@
<script setup lang="ts">
import '@/styles/adminDensity.css'
import { computed, ref, watch } from 'vue'
import { ArrowLeft } from '@lucide/vue'
import { useRoute, useRouter } from 'vue-router'
@@ -44,7 +45,7 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</script>
<template>
<AdminPage>
<AdminPage class="admin-density">
<DetailPageLayout aria-label="派送单详情">
<template #header>
<el-breadcrumb separator=">" class="detail-breadcrumb">
@@ -52,7 +53,7 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
<el-breadcrumb-item :to="{ name: 'overseas-delivery-orders' }">派送单列表</el-breadcrumb-item><el-breadcrumb-item>详情</el-breadcrumb-item>
</el-breadcrumb>
<div class="detail-heading">
<el-button aria-label="返回派送单列表" @click="back"><ArrowLeft :size="16" />返回</el-button>
<el-button class="density-back" link aria-label="返回派送单列表" @click="back"><ArrowLeft :size="16" />返回</el-button>
<div class="detail-title"><div class="title-line"><h1>派送单详情</h1><StatusBadge v-if="detail" :status="statusLabels[detail.order.status] || detail.order.status" /></div><p>派送单号 {{ deliveryNo }}</p></div>
</div>
</template>
@@ -62,11 +63,10 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</LoadErrorState>
<section v-else v-loading="loading" class="surface-panel delivery-detail">
<el-tabs v-model="activeTab" class="detail-tabs">
<el-tab-pane label="订单信息" name="order">
<section class="tab-content" aria-labelledby="delivery-order-heading">
<h2 id="delivery-order-heading">订单信息</h2>
<dl v-if="detail" class="info-grid">
<el-tabs v-model="activeTab" class="detail-tabs density-element-tabs density-contained-tabs">
<el-tab-pane name="order"><template #label><span class="density-tab-icon" aria-hidden="true">📋</span><span>订单信息</span></template>
<section class="tab-content" aria-label="订单信息">
<dl v-if="detail" class="info-grid density-fields">
<div class="info-item"><dt>派送单号</dt><dd>{{ show(detail.order.deliveryNo) }}</dd></div><div class="info-item"><dt>状态</dt><dd>{{ statusLabels[detail.order.status] || detail.order.status }}</dd></div>
<div class="info-item"><dt>仓库</dt><dd>{{ show(detail.order.overseasWarehouseName) }}</dd></div><div class="info-item"><dt>运单号</dt><dd>{{ show(detail.order.waybillNo) }}</dd></div>
<div class="info-item"><dt>派送人</dt><dd>{{ show(detail.order.carrierName) }}</dd></div><div class="info-item"><dt>派送时间</dt><dd>{{ formatOverseasDateTime(detail.order.dispatchedAt || detail.order.plannedDeliveryAt) }}</dd></div>
@@ -80,8 +80,8 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</section>
</el-tab-pane>
<el-tab-pane :label="`SKU明细 (${detail?.skus.length || 0})`" name="sku">
<section class="tab-content" aria-labelledby="delivery-sku-heading"><h2 id="delivery-sku-heading">SKU明细</h2>
<el-tab-pane name="sku"><template #label><span class="density-tab-icon" aria-hidden="true">📦</span><span>{{ `SKU明细 (${detail?.skus.length || 0})` }}</span></template>
<section class="tab-content" aria-label="SKU明细">
<el-table :data="detail?.skus || []" border stripe empty-text="暂无SKU明细">
<el-table-column prop="inboundCode" label="入库码" min-width="160" /><el-table-column prop="customerName" label="客户" min-width="180" />
<el-table-column prop="destinationWarehouseCode" label="目的仓编码" min-width="140" /><el-table-column prop="receivingAddress" label="收货地址" min-width="240" />
@@ -92,8 +92,8 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</section>
</el-tab-pane>
<el-tab-pane :label="`费用明细 (${detail?.fees.length || 0})`" name="fees">
<section class="tab-content" aria-labelledby="delivery-fee-heading"><h2 id="delivery-fee-heading">派送单费用明细</h2>
<el-tab-pane name="fees"><template #label><span class="density-tab-icon" aria-hidden="true">💰</span><span>{{ `费用明细 (${detail?.fees.length || 0})` }}</span></template>
<section class="tab-content" aria-label="派送单费用明细">
<el-table :data="detail?.fees || []" border stripe empty-text="暂无费用明细">
<el-table-column type="index" label="序号" width="80" /><el-table-column label="应收/应付" width="120"><template #default="scope">{{ scope.row.feeDirection === 'PAYABLE' ? '应付' : scope.row.feeDirection === 'RECEIVABLE' ? '应收' : show(scope.row.feeDirection) }}</template></el-table-column>
<el-table-column label="费用类型" min-width="180"><template #default="scope">{{ feeLabels[scope.row.feeType] || scope.row.feeType }}</template></el-table-column><el-table-column prop="currency" label="币种" width="100" />
@@ -102,8 +102,8 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</section>
</el-tab-pane>
<el-tab-pane :label="`操作记录 (${detail?.operations.length || 0})`" name="operations">
<section class="tab-content" aria-labelledby="delivery-operation-heading"><h2 id="delivery-operation-heading">操作记录</h2>
<el-tab-pane name="operations"><template #label><span class="density-tab-icon" aria-hidden="true">📝</span><span>{{ `操作记录 (${detail?.operations.length || 0})` }}</span></template>
<section class="tab-content" aria-label="操作记录">
<el-table :data="detail?.operations || []" border stripe empty-text="暂无操作记录">
<el-table-column label="操作时间" min-width="180"><template #default="scope">{{ formatOverseasDateTime(scope.row.operatedAt) }}</template></el-table-column>
<el-table-column label="操作节点" min-width="180"><template #default="scope">{{ operationLabels[scope.row.operationNode] || scope.row.operationNode }}</template></el-table-column>
@@ -118,8 +118,16 @@ watch([deliveryNo, customerId], () => { void load() }, { immediate: true })
</template>
<style scoped>
.detail-breadcrumb { margin-bottom: 12px; }.detail-heading { display: flex; align-items: center; gap: 14px; }.detail-title { min-width: 0; }.title-line { display: flex; align-items: center; gap: 10px; }.title-line h1 { margin: 0; font-size: 20px; line-height: 28px; }.detail-title p { margin: 2px 0 0; color: var(--cc-muted); font-size: 13px; }
.delivery-detail { min-height: 360px; overflow: hidden; }.detail-tabs :deep(.el-tabs__header) { margin: 0; padding: 0 18px; }.detail-tabs :deep(.el-tabs__nav-wrap::after) { height: 1px; }.detail-tabs :deep(.el-tabs__item) { height: 48px; padding: 0 22px; }.tab-content { width: 100%; padding: 18px 20px 24px; box-sizing: border-box; }.tab-content h2 { margin: 0 0 14px; font-size: 15px; line-height: 22px; }
.info-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--el-border-color-lighter); }.info-item { display: grid; grid-template-columns: 116px minmax(0, 1fr); min-height: 44px; align-items: center; border-bottom: 1px solid var(--el-border-color-lighter); }.info-item:nth-child(odd) { padding-right: 24px; }.info-item:nth-child(even) { padding-left: 24px; }.info-item--wide { grid-column: 1 / -1; padding-left: 0 !important; }.info-item dt { color: var(--cc-muted); font-size: 13px; }.info-item dd { min-width: 0; margin: 0; overflow-wrap: anywhere; color: var(--cc-text); font-weight: 500; }
.detail-breadcrumb { margin-bottom: var(--admin-space-5); }.detail-heading { display: flex; align-items: center; gap: var(--admin-space-5); }.title-line { display: flex; align-items: center; gap: var(--admin-space-4); }.tab-content h2 { margin: 0 0 var(--admin-space-5); font-size: var(--admin-font-section); line-height: 22px; }
@media (max-width: 980px) { .info-grid { grid-template-columns: 1fr; }.info-item, .info-item:nth-child(odd), .info-item:nth-child(even) { padding: 0; }.info-item--wide { grid-column: auto; } }
:deep(.detail-page-layout) { gap: 0; background: var(--admin-surface); border: 1px solid var(--cc-border); }
:deep(.detail-page-layout__header) { padding: var(--admin-space-5); border-bottom: 1px solid var(--cc-border); }
.detail-title { min-width: 0; display: flex; align-items: center; flex-wrap: wrap; gap: var(--admin-space-5); }
.detail-title p { margin: 0; color: var(--cc-muted); font-size: var(--admin-font-title); font-weight: 600; overflow-wrap: anywhere; }
.title-line h1 { margin: 0; font-size: var(--admin-font-section); line-height: 28px; font-weight: 500; }
.delivery-detail { min-height: 0; overflow: hidden; border: 0; border-radius: 0; }
.tab-content { width: 100%; padding: 0; box-sizing: border-box; }
.info-item--wide { grid-column: 1 / -1; }
</style>