优化
This commit is contained in:
@@ -186,6 +186,32 @@ function formatDateTime(value: string) {
|
||||
})
|
||||
}
|
||||
|
||||
const pickupMethodLabels: Record<FirstLegOrder['pickupMethod'], string> = {
|
||||
SELF_EXPRESS: '自行快递到仓',
|
||||
PICKUP: '平台上门提货',
|
||||
SELF_TRUCK: '自行送货到仓',
|
||||
}
|
||||
const destinationTypeLabels: Record<FirstLegOrder['destinationType'], string> = {
|
||||
FBA: 'FBA 仓',
|
||||
OVERSEAS: '海外仓',
|
||||
PRIVATE: '私人地址',
|
||||
}
|
||||
const taxModeLabels: Record<FirstLegOrder['taxMode'], string> = {
|
||||
BUY_DECLARATION: '买单报关',
|
||||
TAX_REFUND: '退税报关',
|
||||
}
|
||||
const packageTypeLabels: Readonly<Record<string, string>> = {
|
||||
CARTON: '纸箱',
|
||||
PALLET: '托盘',
|
||||
WOODEN_CASE: '木箱',
|
||||
BAG: '袋装',
|
||||
OTHER: '其他',
|
||||
}
|
||||
|
||||
function displayValue(value: string | null | undefined) {
|
||||
return value?.trim() || '-'
|
||||
}
|
||||
|
||||
function emptyDraft(): FirstLegOrderDraft {
|
||||
return {
|
||||
referenceNo: '',
|
||||
@@ -967,7 +993,7 @@ async function searchOrders(model: QueryModel) {
|
||||
<template #footer><div class="editor-actions"><el-button :disabled="saving" @click="closeEditor">返回</el-button><el-button v-if="editingId ? canEditOrder : canCreateOrder" :loading="saving" @click="saveDraft">保存草稿</el-button><el-button v-if="auth.hasPermission('order:first-leg:submit')" type="primary" :loading="saving" @click="submitOrder"><Send :size="16" />{{ companySubmissionBlocked ? '提交前需完成资料审核' : '提交订单' }}</el-button></div></template>
|
||||
</FirstLegOrderEditorShell>
|
||||
|
||||
<el-drawer v-model="detailOpen" class="first-leg-detail" title="订单详情" size="680px">
|
||||
<el-drawer v-model="detailOpen" class="first-leg-detail" title="订单详情" size="820px">
|
||||
<div v-loading="detailLoading" class="order-detail-content">
|
||||
<div v-if="detailError" class="order-load-state is-compact" role="alert">
|
||||
<AlertTriangle :size="24" />
|
||||
@@ -976,17 +1002,40 @@ async function searchOrders(model: QueryModel) {
|
||||
</div>
|
||||
<template v-else-if="detailOrder">
|
||||
<div class="order-detail-heading">
|
||||
<div><span>{{ getFirstLegTransportModeLabel(detailOrder.transportMode) }}</span><h2>{{ detailOrder.orderNo || '未提交草稿' }}</h2><p>{{ detailOrder.origin }} → {{ detailOrder.destination }}</p></div>
|
||||
<div><span>{{ getFirstLegTransportModeLabel(detailOrder.transportMode) }}</span><h2>{{ detailOrder.orderNo || '未提交草稿' }}</h2><p>{{ detailOrder.origin || '起运地待更新' }} → {{ detailOrder.destination || '目的地待更新' }}</p></div>
|
||||
<el-tag :type="getFirstLegStatusTone(detailOrder.status)" effect="light">{{ getFirstLegStatusLabel(detailOrder.status) }}</el-tag>
|
||||
</div>
|
||||
<OrderProgressPanel :order="detailOrder" />
|
||||
<dl class="order-detail-grid">
|
||||
<div><dt>客户参考号</dt><dd>{{ detailOrder.referenceNo || '-' }}</dd></div><div><dt>运输方式</dt><dd>{{ getFirstLegTransportModeLabel(detailOrder.transportMode) }}</dd></div>
|
||||
<div><dt>发货方</dt><dd>{{ detailOrder.shipperPartySnapshot?.name || detailOrder.shipper }}<span v-if="detailOrder.shipperPartySnapshot" class="detail-subline">{{ detailOrder.shipperPartySnapshot.code }} · {{ detailOrder.shipperPartySnapshot.contactName || '未填写联系人' }} · {{ detailOrder.shipperPartySnapshot.phone || '未填写电话' }}<br />{{ detailOrder.shipperPartySnapshot.countryRegion }} {{ detailOrder.shipperPartySnapshot.province }} {{ detailOrder.shipperPartySnapshot.city }} {{ detailOrder.shipperPartySnapshot.address }}</span></dd></div>
|
||||
<div><dt>收货方</dt><dd>{{ detailOrder.consigneePartySnapshot?.name || detailOrder.consignee }}<span v-if="detailOrder.consigneePartySnapshot" class="detail-subline">{{ detailOrder.consigneePartySnapshot.code }} · {{ detailOrder.consigneePartySnapshot.contactName || '未填写联系人' }} · {{ detailOrder.consigneePartySnapshot.phone || '未填写电话' }}<br />{{ detailOrder.consigneePartySnapshot.countryRegion }} {{ detailOrder.consigneePartySnapshot.province }} {{ detailOrder.consigneePartySnapshot.city }} {{ detailOrder.consigneePartySnapshot.address }}</span></dd></div>
|
||||
<div><dt>平台收货仓</dt><dd>{{ detailOrder.receivingWarehouseSnapshot?.name || detailOrder.receivingWarehouseName }}<span v-if="detailOrder.receivingWarehouseSnapshot" class="detail-subline">{{ detailOrder.receivingWarehouseSnapshot.code }} · {{ detailOrder.receivingWarehouseSnapshot.province }}{{ detailOrder.receivingWarehouseSnapshot.city }}{{ detailOrder.receivingWarehouseSnapshot.address }}</span></dd></div><div><dt>负责人</dt><dd>{{ detailOrder.operatorName || '未提供' }}</dd></div>
|
||||
<div><dt>期望出发</dt><dd>{{ detailOrder.expectedDepartureDate || '-' }}</dd></div><div><dt>ETD / ETA</dt><dd>{{ detailOrder.etd || '-' }} / {{ detailOrder.eta || '-' }}</dd></div>
|
||||
</dl>
|
||||
<section class="detail-section">
|
||||
<h3>订单与运输</h3>
|
||||
<dl class="order-detail-grid">
|
||||
<div><dt>客户参考号</dt><dd>{{ displayValue(detailOrder.referenceNo) }}</dd></div>
|
||||
<div><dt>创建时间</dt><dd>{{ formatDateTime(detailOrder.createdAt) }}</dd></div>
|
||||
<div><dt>运输方式</dt><dd>{{ getFirstLegTransportModeLabel(detailOrder.transportMode) }}</dd></div>
|
||||
<div><dt>贸易条款</dt><dd>{{ displayValue(detailOrder.tradeTerm) }}</dd></div>
|
||||
<div><dt>发货方式</dt><dd>{{ pickupMethodLabels[detailOrder.pickupMethod] }}</dd></div>
|
||||
<div><dt>报关方式</dt><dd>{{ taxModeLabels[detailOrder.taxMode] }}</dd></div>
|
||||
<div><dt>运输渠道</dt><dd>{{ detailOrder.channelName || detailOrder.channelCode || '-' }}<span v-if="detailOrder.channelName && detailOrder.channelCode" class="detail-subline">{{ detailOrder.channelCode }}</span></dd></div>
|
||||
<div><dt>承运商</dt><dd>{{ detailOrder.carrierName || '待平台指派' }}</dd></div>
|
||||
<div><dt>国内收货仓</dt><dd>{{ detailOrder.receivingWarehouseSnapshot?.name || detailOrder.receivingWarehouseName || '-' }}<span v-if="detailOrder.receivingWarehouseSnapshot" class="detail-subline">{{ detailOrder.receivingWarehouseSnapshot.code || '仓库编码待更新' }} · {{ detailOrder.receivingWarehouseSnapshot.province }}{{ detailOrder.receivingWarehouseSnapshot.city }}{{ detailOrder.receivingWarehouseSnapshot.address }}</span></dd></div>
|
||||
<div><dt>收货要求</dt><dd>{{ displayValue(detailOrder.receivingTime) }}</dd></div>
|
||||
<div><dt>目的仓类型</dt><dd>{{ destinationTypeLabels[detailOrder.destinationType] }}</dd></div>
|
||||
<div><dt>目的国家 / 仓库</dt><dd>{{ detailOrder.destinationCountryCode || '-' }} · {{ detailOrder.destinationWarehouseName || '-' }}</dd></div>
|
||||
<div v-if="detailOrder.destinationType === 'FBA'"><dt>Amazon 仓库代码</dt><dd>{{ displayValue(detailOrder.amazonWarehouseCode) }}</dd></div>
|
||||
<div v-if="detailOrder.destinationType === 'FBA'"><dt>Shipment ID</dt><dd>{{ displayValue(detailOrder.shipmentId) }}</dd></div>
|
||||
<div><dt>期望出运</dt><dd>{{ displayValue(detailOrder.expectedDepartureDate) }}</dd></div>
|
||||
<div><dt>海关查验</dt><dd>{{ detailOrder.customsInspection ? '需要' : '不需要' }}</dd></div>
|
||||
<div><dt>提交时间</dt><dd>{{ detailOrder.submittedAt ? formatDateTime(detailOrder.submittedAt) : '-' }}</dd></div>
|
||||
<div><dt>完成时间</dt><dd>{{ detailOrder.completedAt ? formatDateTime(detailOrder.completedAt) : '-' }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
<section class="detail-section">
|
||||
<h3>收发货信息</h3>
|
||||
<div class="party-detail-grid">
|
||||
<article><span>发货方</span><strong>{{ detailOrder.shipperPartySnapshot?.name || detailOrder.shipper || '未提供' }}</strong><p v-if="detailOrder.shipperPartySnapshot">{{ detailOrder.shipperPartySnapshot.contactName || '未填写联系人' }} · {{ detailOrder.shipperPartySnapshot.phone || '未填写电话' }}<br />{{ detailOrder.shipperPartySnapshot.email || '' }}<br />{{ detailOrder.shipperPartySnapshot.countryRegion }} {{ detailOrder.shipperPartySnapshot.province }} {{ detailOrder.shipperPartySnapshot.city }} {{ detailOrder.shipperPartySnapshot.address }} {{ detailOrder.shipperPartySnapshot.postalCode }}</p></article>
|
||||
<article><span>收货方</span><strong>{{ detailOrder.consigneePartySnapshot?.name || detailOrder.consignee || detailOrder.destinationWarehouseName || '未提供' }}</strong><p v-if="detailOrder.consigneePartySnapshot">{{ detailOrder.consigneePartySnapshot.contactName || '未填写联系人' }} · {{ detailOrder.consigneePartySnapshot.phone || '未填写电话' }}<br />{{ detailOrder.consigneePartySnapshot.email || '' }}<br />{{ detailOrder.consigneePartySnapshot.countryRegion }} {{ detailOrder.consigneePartySnapshot.province }} {{ detailOrder.consigneePartySnapshot.city }} {{ detailOrder.consigneePartySnapshot.address }} {{ detailOrder.consigneePartySnapshot.postalCode }}</p><p v-else>平台目的仓地址以最终履约安排为准</p></article>
|
||||
</div>
|
||||
<div v-if="detailOrder.customerRemark" class="customer-remark"><strong>客户备注</strong><p>{{ detailOrder.customerRemark }}</p></div>
|
||||
</section>
|
||||
<section v-if="detailOrder.status !== 'DRAFT'" class="detail-section">
|
||||
<h3>关键业务摘要</h3>
|
||||
<dl class="fulfillment-summary">
|
||||
@@ -998,7 +1047,8 @@ async function searchOrders(model: QueryModel) {
|
||||
<div v-if="detailOrder.expressCompanyName || detailOrder.trackingNumber" class="express-information"><PackageCheck :size="20" /><div><span>快递公司</span><strong>{{ detailOrder.expressCompanyName || '待平台补充' }}</strong></div><div><span>快递单号</span><strong>{{ detailOrder.trackingNumber || '待平台补充' }}</strong></div></div>
|
||||
<div v-else class="express-empty"><PackageCheck :size="20" /><div><strong>平台尚未维护快递信息</strong><span>快递公司和快递单号由平台后台维护,客户端仅只读展示。</span></div></div>
|
||||
</section>
|
||||
<section class="detail-section"><h3>货物汇总</h3><div v-if="detailSummary" class="cargo-summary"><div><Box :size="18" /><span>明细</span><strong>{{ detailOrder.items.length }} 项</strong></div><div><span>件数</span><strong>{{ detailSummary.pieces }}</strong></div><div><span>毛重</span><strong>{{ detailSummary.grossWeightKg.toLocaleString() }} kg</strong></div><div><span>体积</span><strong>{{ detailSummary.volumeCbm }} m³</strong></div></div><ul class="cargo-detail-list"><li v-for="item in detailOrder.items" :key="item.id"><strong>{{ item.cargoNameCn }}</strong><span>{{ item.sku || '无 SKU' }} · {{ item.pieces }} 件 · {{ item.grossWeightKg }} kg</span></li></ul></section>
|
||||
<section class="detail-section"><h3>货物明细</h3><div v-if="detailSummary" class="cargo-summary"><div><Box :size="18" /><span>明细</span><strong>{{ detailOrder.items.length }} 项</strong></div><div><span>件数</span><strong>{{ detailSummary.pieces }}</strong></div><div><span>毛重</span><strong>{{ detailSummary.grossWeightKg.toLocaleString() }} kg</strong></div><div><span>体积</span><strong>{{ detailSummary.volumeCbm }} m³</strong></div></div><div v-if="detailOrder.items.length" class="cargo-detail-list"><article v-for="(item, index) in detailOrder.items" :key="item.id"><header><span>货物 {{ index + 1 }}</span><strong>{{ item.cargoNameCn || '未命名货物' }}</strong><small>{{ item.cargoNameEn || '' }}</small></header><dl><div><dt>SKU / UPC</dt><dd>{{ item.sku || '-' }} / {{ item.upc || '-' }}</dd></div><div><dt>HS 编码</dt><dd>{{ item.hsCode || '-' }}</dd></div><div><dt>包装 / 箱数</dt><dd>{{ packageTypeLabels[item.packageType] || item.packageType || '-' }} · {{ item.packageCount }} 箱</dd></div><div><dt>数量</dt><dd>{{ item.pieces }} 件</dd></div><div><dt>毛重</dt><dd>{{ item.grossWeightKg }} kg</dd></div><div><dt>体积</dt><dd>{{ item.volumeCbm }} m³</dd></div><div><dt>申报价值</dt><dd>{{ item.declaredValue ? `${item.currency} ${item.declaredValue.toLocaleString()}` : '-' }}</dd></div><div><dt>危险品</dt><dd>{{ item.dangerousGoods ? '是' : '否' }}</dd></div></dl><p v-if="item.remark">{{ item.remark }}</p></article></div><el-empty v-else description="暂无货物明细" :image-size="56" /></section>
|
||||
<OrderProgressPanel :order="detailOrder" />
|
||||
<InboundBillingPanel
|
||||
:data="inboundBilling"
|
||||
:loading="inboundBillingLoading"
|
||||
@@ -1115,6 +1165,13 @@ async function searchOrders(model: QueryModel) {
|
||||
.detail-subline { display: block; margin-top: 4px; color: var(--muted); font-size: 11px; font-weight: 400; line-height: 1.5; }
|
||||
.detail-section { margin-top: 22px; }
|
||||
.detail-section h3 { margin: 0 0 12px; font-size: 15px; }
|
||||
.party-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
||||
.party-detail-grid article { min-width: 0; padding: 15px; border: 1px solid var(--border); background: #fbfcfc; }
|
||||
.party-detail-grid span { display: block; color: var(--muted); font-size: 10px; }
|
||||
.party-detail-grid strong { display: block; margin-top: 5px; overflow-wrap: anywhere; color: var(--navy); font-size: 13px; }
|
||||
.party-detail-grid p, .customer-remark p { margin: 7px 0 0; overflow-wrap: anywhere; color: var(--muted); font-size: 11px; line-height: 1.65; }
|
||||
.customer-remark { margin-top: 10px; padding: 13px 15px; background: #f2f7f6; }
|
||||
.customer-remark strong { color: var(--navy); font-size: 11px; }
|
||||
.fulfillment-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0; border: 1px solid var(--border); }
|
||||
.fulfillment-summary > div { min-width: 0; padding: 13px; border-bottom: 1px solid var(--border); }
|
||||
.fulfillment-summary > div:not(:nth-child(3n)) { border-right: 1px solid var(--border); }
|
||||
@@ -1134,9 +1191,17 @@ async function searchOrders(model: QueryModel) {
|
||||
.cargo-summary div { display: flex; min-height: 74px; align-items: center; justify-content: center; flex-direction: column; gap: 4px; border-right: 1px solid var(--border); }
|
||||
.cargo-summary div:last-child { border-right: 0; }
|
||||
.cargo-summary span { color: var(--muted); font-size: 11px; }
|
||||
.cargo-detail-list { margin: 10px 0 0; padding: 0; list-style: none; }
|
||||
.cargo-detail-list li { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--border); font-size: 12px; }
|
||||
.cargo-detail-list span, :deep(.el-timeline-item__content p), :deep(.el-timeline-item__content > span) { color: var(--muted); }
|
||||
.cargo-detail-list { display: grid; gap: 10px; margin-top: 10px; }
|
||||
.cargo-detail-list article { padding: 14px 15px; border: 1px solid var(--border); }
|
||||
.cargo-detail-list header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: baseline; gap: 10px; }
|
||||
.cargo-detail-list header span, .cargo-detail-list header small { color: var(--muted); font-size: 10px; }
|
||||
.cargo-detail-list header strong { overflow-wrap: anywhere; color: var(--navy); font-size: 13px; }
|
||||
.cargo-detail-list header small { overflow-wrap: anywhere; text-align: right; }
|
||||
.cargo-detail-list dl { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 11px 16px; margin: 13px 0 0; }
|
||||
.cargo-detail-list dt { color: var(--muted); font-size: 10px; }
|
||||
.cargo-detail-list dd { margin: 4px 0 0; overflow-wrap: anywhere; color: var(--navy); font-size: 11px; font-weight: 600; }
|
||||
.cargo-detail-list article > p { margin: 11px 0 0; padding-top: 10px; border-top: 1px solid var(--border); color: var(--muted); font-size: 11px; }
|
||||
:deep(.el-timeline-item__content p), :deep(.el-timeline-item__content > span) { color: var(--muted); }
|
||||
:deep(.el-timeline-item__content p) { margin: 5px 0; font-size: 12px; }
|
||||
:deep(.el-timeline-item__content > span) { font-size: 11px; }
|
||||
@media (max-width: 1280px) {
|
||||
@@ -1167,7 +1232,8 @@ async function searchOrders(model: QueryModel) {
|
||||
.order-table-toolbar .el-button { width: 100%; min-height: 40px; }
|
||||
.order-eligibility { flex-wrap: wrap; }
|
||||
.order-eligibility .el-button { width: 100%; min-height: 44px; }
|
||||
.order-form-grid, .order-detail-grid, .cargo-primary-grid, .cargo-secondary-grid, .fulfillment-summary { grid-template-columns: 1fr; }
|
||||
.order-form-grid, .order-detail-grid, .cargo-primary-grid, .cargo-secondary-grid, .party-detail-grid { grid-template-columns: 1fr; }
|
||||
.fulfillment-summary { grid-template-columns: 1fr; }
|
||||
.fulfillment-summary > div, .fulfillment-summary > div:not(:nth-child(3n)) { border-right: 0; border-bottom: 1px solid var(--border); }
|
||||
.fulfillment-summary > div:last-child { border-bottom: 0; }
|
||||
.prototype-form-row, .prototype-form-row--wide, .prototype-form-row--compact, .prototype-form-row--short, .prototype-form-row--tabs { display: block; margin-bottom: 18px; }
|
||||
@@ -1207,9 +1273,14 @@ async function searchOrders(model: QueryModel) {
|
||||
.cargo-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.cargo-summary div:nth-child(2) { border-right: 0; }
|
||||
.cargo-summary div:nth-child(-n+2) { border-bottom: 1px solid var(--border); }
|
||||
.cargo-detail-list li { flex-direction: column; }
|
||||
.cargo-detail-list header { grid-template-columns: 1fr; gap: 4px; }
|
||||
.cargo-detail-list header small { text-align: left; }
|
||||
.cargo-detail-list dl { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
:global(.first-leg-editor), :global(.first-leg-detail) { width: 100% !important; }
|
||||
}
|
||||
@media (max-width: 360px) {
|
||||
.cargo-detail-list dl { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.editor-actions { display: grid; width: 100%; grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.editor-actions :deep(.el-button) { width: 100%; min-width: 0; margin-left: 0; }
|
||||
|
||||
Reference in New Issue
Block a user