修复bug
This commit is contained in:
@@ -40,6 +40,8 @@ const detailId = ref<string>()
|
||||
const saving = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detailError = ref('')
|
||||
const supportingDataLoading = ref(false)
|
||||
const supportingDataErrors = ref<string[]>([])
|
||||
const editorBaseline = ref('')
|
||||
let cargoSequence = 0
|
||||
|
||||
@@ -110,15 +112,37 @@ const hasActiveFilters = computed(() => Boolean(filters.keyword.trim() || filter
|
||||
const emptyDescription = computed(() => hasActiveFilters.value ? '没有符合筛选条件的头程订单' : '还没有头程订单')
|
||||
|
||||
async function loadPageData() {
|
||||
try {
|
||||
const requests: Promise<unknown>[] = [store.load(), firstLegPartySource.load()]
|
||||
if (auth.hasPermission('company:profile:view')) requests.push(tenantStore.loadCompanyContext())
|
||||
await Promise.all(requests)
|
||||
supportingDataLoading.value = true
|
||||
supportingDataErrors.value = []
|
||||
availableShippers.value = []
|
||||
availableConsignees.value = []
|
||||
|
||||
const companyRequest = auth.hasPermission('company:profile:view')
|
||||
? tenantStore.loadCompanyContext()
|
||||
: Promise.resolve()
|
||||
const [ordersResult, partiesResult, companyResult] = await Promise.allSettled([
|
||||
store.load(),
|
||||
firstLegPartySource.load(),
|
||||
companyRequest,
|
||||
])
|
||||
|
||||
if (partiesResult.status === 'fulfilled') {
|
||||
availableShippers.value = firstLegPartySource.listAvailable('SHIPPER')
|
||||
availableConsignees.value = firstLegPartySource.listAvailable('CONSIGNEE')
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '订单与往来单位数据加载失败')
|
||||
} else {
|
||||
supportingDataErrors.value.push(`往来单位加载失败:${errorMessage(partiesResult.reason)}`)
|
||||
}
|
||||
if (companyResult.status === 'rejected') {
|
||||
supportingDataErrors.value.push(`企业资料加载失败:${errorMessage(companyResult.reason)}`)
|
||||
}
|
||||
if (ordersResult.status === 'rejected' && !store.loadError) {
|
||||
supportingDataErrors.value.push(`订单与仓库加载失败:${errorMessage(ordersResult.reason)}`)
|
||||
}
|
||||
supportingDataLoading.value = false
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown) {
|
||||
return error instanceof Error ? error.message : '服务暂时不可用'
|
||||
}
|
||||
|
||||
function handleBeforeUnload(event: BeforeUnloadEvent) {
|
||||
@@ -401,11 +425,16 @@ function resetFilters() {
|
||||
|
||||
<template>
|
||||
<section class="first-leg-page">
|
||||
<div class="order-eligibility" :class="companyApproved ? 'is-approved' : 'is-pending'" role="status">
|
||||
<div v-if="!supportingDataErrors.some((message) => message.startsWith('企业资料'))" class="order-eligibility" :class="companyApproved ? 'is-approved' : 'is-pending'" role="status">
|
||||
<component :is="companyApproved ? CheckCircle2 : AlertTriangle" :size="20" />
|
||||
<div><strong>{{ companyApproved ? '企业资料已审核,可正常提交订单' : canCreateOrder ? '企业资料待审核,当前仅可保存订单草稿' : '企业资料待审核,当前订单为只读' }}</strong><span>{{ companyApproved ? '提交后订单将进入平台审核。' : canCreateOrder ? '平台审核通过后即可提交;您仍可创建、编辑和保存草稿。' : '您当前拥有只读权限,可以继续查看订单进展。' }}</span></div>
|
||||
<el-button v-if="!companyApproved && auth.hasPermission('company:profile:view')" link type="primary" @click="router.push('/company/profile')">查看企业资料</el-button>
|
||||
</div>
|
||||
<div v-if="supportingDataErrors.length" class="supporting-data-error" role="alert">
|
||||
<AlertTriangle :size="20" />
|
||||
<div><strong>部分订单配套数据加载失败</strong><span v-for="message in supportingDataErrors" :key="message">{{ message }}</span></div>
|
||||
<el-button :loading="supportingDataLoading || store.loading" @click="loadPageData">重新加载</el-button>
|
||||
</div>
|
||||
<div class="order-metrics" aria-label="订单概览">
|
||||
<article><span>全部订单</span><strong>{{ store.orders.length }}</strong></article>
|
||||
<article><span>进行中</span><strong>{{ store.activeCount }}</strong></article>
|
||||
@@ -463,14 +492,14 @@ function resetFilters() {
|
||||
<el-table-column label="状态" width="120">
|
||||
<template #default="{ row }"><el-tag :type="getFirstLegStatusTone((row as FirstLegOrder).status)" effect="light">{{ getFirstLegStatusLabel((row as FirstLegOrder).status) }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="负责人" prop="operatorName" width="110" />
|
||||
<el-table-column label="负责人" width="110"><template #default="{ row }">{{ (row as FirstLegOrder).operatorName || '-' }}</template></el-table-column>
|
||||
<el-table-column label="更新时间" width="155"><template #default="{ row }">{{ formatDateTime((row as FirstLegOrder).updatedAt) }}</template></el-table-column>
|
||||
</el-table>
|
||||
<div v-if="filteredOrders.length" class="mobile-order-list">
|
||||
<article v-for="order in filteredOrders" :key="order.id" class="mobile-order-card">
|
||||
<header><button type="button" @click="openDetail(order)">{{ order.orderNo || '未提交草稿' }}</button><el-tag :type="getFirstLegStatusTone(order.status)" effect="light">{{ getFirstLegStatusLabel(order.status) }}</el-tag></header>
|
||||
<span class="order-reference">{{ order.referenceNo || '无客户参考号' }}</span>
|
||||
<dl><div><dt>运输路线</dt><dd>{{ order.origin }} → {{ order.destination }}</dd></div><div><dt>收货仓</dt><dd>{{ order.receivingWarehouseName }}</dd></div><div><dt>货物</dt><dd>{{ order.items[0]?.cargoNameCn || '-' }}<span v-if="order.items.length > 1">等 {{ order.items.length }} 项</span></dd></div><div><dt>负责人</dt><dd>{{ order.operatorName }}</dd></div></dl>
|
||||
<dl><div><dt>运输路线</dt><dd>{{ order.origin }} → {{ order.destination }}</dd></div><div><dt>收货仓</dt><dd>{{ order.receivingWarehouseName }}</dd></div><div><dt>货物</dt><dd>{{ order.items[0]?.cargoNameCn || '-' }}<span v-if="order.items.length > 1">等 {{ order.items.length }} 项</span></dd></div><div><dt>负责人</dt><dd>{{ order.operatorName || '-' }}</dd></div></dl>
|
||||
<footer><span>更新于 {{ formatDateTime(order.updatedAt) }}</span><div><el-button link type="primary" @click="openDetail(order)"><Eye :size="15" />详情</el-button><el-button v-if="canEditOrder && canClientEditFirstLegOrder(order.status)" link type="primary" :loading="openingEditor" @click="openEdit(order)"><Pencil :size="15" />编辑</el-button></div></footer>
|
||||
</article>
|
||||
</div>
|
||||
@@ -489,7 +518,7 @@ function resetFilters() {
|
||||
<el-form-item label="业务类型" required><el-radio-group v-model="form.businessType"><el-radio-button value="EXPORT">出口</el-radio-button><el-radio-button value="IMPORT">进口</el-radio-button></el-radio-group></el-form-item>
|
||||
<el-form-item label="起运地" required><el-input v-model="form.origin" placeholder="例如:宁波" /></el-form-item>
|
||||
<el-form-item label="目的地" required><el-input v-model="form.destination" placeholder="例如:洛杉矶" /></el-form-item>
|
||||
<el-form-item label="平台收货仓" required><el-select v-model="form.receivingWarehouseId" filterable placeholder="请选择送货仓库" @change="loadSelectedWarehouse"><el-option v-for="warehouse in availableWarehouses" :key="warehouse.id" :label="`${warehouse.name} · ${warehouse.city}`" :value="warehouse.id" /></el-select></el-form-item>
|
||||
<el-form-item label="平台收货仓" required><el-select v-model="form.receivingWarehouseId" filterable placeholder="请选择送货仓库" @change="loadSelectedWarehouse"><el-option v-for="warehouse in availableWarehouses" :key="warehouse.id" :label="warehouse.city ? `${warehouse.name} · ${warehouse.city}` : warehouse.name" :value="warehouse.id" /></el-select></el-form-item>
|
||||
<el-form-item label="期望出发日期"><el-date-picker v-model="form.expectedDepartureDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item>
|
||||
<el-form-item label="客户参考号"><el-input v-model="form.referenceNo" maxlength="100" placeholder="贵司内部单号(选填)" /></el-form-item>
|
||||
<el-form-item label="优先级"><el-select v-model="form.priority"><el-option label="普通" value="NORMAL" /><el-option label="紧急" value="URGENT" /><el-option label="加急" value="EXPEDITED" /></el-select></el-form-item>
|
||||
@@ -504,7 +533,7 @@ function resetFilters() {
|
||||
<el-form-item v-if="form.destinationType === 'FBA'" label="Amazon 仓库编码"><el-input v-model="form.amazonWarehouseCode" maxlength="32" placeholder="例如:ONT8" /></el-form-item>
|
||||
<el-form-item v-if="form.destinationType === 'FBA'" label="Shipment ID"><el-input v-model="form.shipmentId" maxlength="64" /></el-form-item>
|
||||
</div>
|
||||
<div v-if="selectedWarehouse" class="selected-warehouse"><MapPin :size="17" /><div><strong>{{ selectedWarehouse.name }}</strong><span>{{ selectedWarehouse.province }}{{ selectedWarehouse.city }} · {{ selectedWarehouse.address }}</span><small>收货时间:{{ selectedWarehouse.businessHours }}</small></div></div>
|
||||
<div v-if="selectedWarehouse" class="selected-warehouse"><MapPin :size="17" /><div><strong>{{ selectedWarehouse.name }}</strong><span>{{ selectedWarehouse.address ? `${selectedWarehouse.province}${selectedWarehouse.city} · ${selectedWarehouse.address}` : '仓库地址详情暂不可用' }}</span><small>{{ selectedWarehouse.businessHours ? `收货时间:${selectedWarehouse.businessHours}` : '收货时间暂不可用' }}</small></div></div>
|
||||
</section>
|
||||
|
||||
<section class="order-form-section">
|
||||
@@ -598,7 +627,7 @@ function resetFilters() {
|
||||
<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.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">
|
||||
@@ -607,7 +636,7 @@ function resetFilters() {
|
||||
<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><el-timeline><el-timeline-item v-for="item in [...detailOrder.timeline].reverse()" :key="item.id" :timestamp="item.occurredAt" placement="top"><strong>{{ item.title }}</strong><p>{{ item.description }}</p><span>{{ item.operatorName }}</span></el-timeline-item></el-timeline></section>
|
||||
<section class="detail-section"><h3>处理时间线</h3><el-timeline><el-timeline-item v-for="item in [...detailOrder.timeline].reverse()" :key="item.id" :timestamp="item.occurredAt" placement="top"><strong>{{ item.title }}</strong><p>{{ item.description || '-' }}</p><span>{{ item.operatorName || '未提供' }}</span></el-timeline-item></el-timeline></section>
|
||||
</template>
|
||||
</div>
|
||||
<template #footer><div v-if="detailOrder" class="editor-actions"><el-button @click="detailOpen = false">关闭</el-button><el-button v-if="canEditOrder && canClientEditFirstLegOrder(detailOrder.status)" :loading="openingEditor" @click="openEdit(detailOrder)"><Pencil :size="16" />编辑草稿</el-button></div></template>
|
||||
@@ -624,6 +653,10 @@ function resetFilters() {
|
||||
.order-eligibility span { color: var(--muted); font-size: 11px; line-height: 1.55; }
|
||||
.order-eligibility.is-approved { border-color: #bcd9d2; background: #eff8f5; color: #20745f; }
|
||||
.order-eligibility.is-pending { border-color: #e5d2a5; background: #fff9eb; color: #9a6a12; }
|
||||
.supporting-data-error { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 14px 16px; border: 1px solid #e5d2a5; background: #fff9eb; color: #9a6a12; }
|
||||
.supporting-data-error > div { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
|
||||
.supporting-data-error strong { color: var(--navy); font-size: 13px; }
|
||||
.supporting-data-error span { overflow-wrap: anywhere; color: var(--muted); font-size: 11px; line-height: 1.55; }
|
||||
.order-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
|
||||
.order-metrics article { display: flex; align-items: center; justify-content: space-between; min-height: 76px; padding: 16px 20px; border: 1px solid var(--border); background: #fff; }
|
||||
.order-metrics span { color: var(--muted); font-size: 12px; }
|
||||
@@ -729,6 +762,8 @@ function resetFilters() {
|
||||
.editor-actions :deep(.el-button) { min-height: 44px; }
|
||||
.desktop-order-table { display: none; }
|
||||
.order-load-state { min-height: 240px; grid-template-columns: 1fr; justify-items: start; }
|
||||
.supporting-data-error { grid-template-columns: auto minmax(0, 1fr); }
|
||||
.supporting-data-error .el-button { grid-column: 1 / -1; width: 100%; min-height: 44px; }
|
||||
.order-load-state .el-button { width: 100%; min-height: 44px; }
|
||||
.mobile-order-list { grid-template-columns: 1fr; }
|
||||
.mobile-order-card { padding: 14px; border: 1px solid var(--border); background: #fff; }
|
||||
|
||||
Reference in New Issue
Block a user