fix: align appointment viewing and fleet follow-up navigation

This commit is contained in:
2026-10-11 15:33:09 +08:00
parent ee174f8265
commit 315cb8e663
21 changed files with 232 additions and 41 deletions
@@ -15,6 +15,7 @@ import CapacityInquiryFulfillment from '../components/CapacityInquiryFulfillment
import CapacityInquiryDialog from '../components/CapacityInquiryDialog.vue'
import { listCapacityOffers, listCapacityInquiries, getCapacityOffer, getCapacityInquiry } from '../api/capacityOffers'
import { offerStatusLabels, inquiryStatusLabels, offerTime, loadText, type CapacityOffer, type CapacityInquiry, type InquiryResult, type OfferQuery, type InquiryQuery } from '../capacityOfferTypes'
import { inquirySourceQuery, inquiryOrderTarget } from '../inquiryNavigation'
import { useCapacityInquiryCommands } from '../useCapacityInquiryCommands'
const router = useRouter(), access = useAccessPreviewStore()
const manage = computed(() => access.can('platform:fleet:capacity-offer:manage'))
@@ -25,10 +26,23 @@ const offers = ref<CapacityOffer[]>([]), inquiries = ref<CapacityInquiry[]>([])
const editorOpen = ref(false), editing = ref<CapacityInquiry | null>(null), selectedOffer = ref<CapacityOffer | null>(null), readonly = ref(true)
const confirmMode = ref(false)
const fulfillmentOpen = ref(false), fulfillmentRow = ref<CapacityInquiry | null>(null), fulfillmentMode = ref<'incoming' | 'receipt' | 'bind' | 'view'>('view')
async function fulfillmentSaved() {
const row = fulfillmentRow.value
fulfillmentOpen.value = false
if (!row) return
const current = ++followupSequence
await load()
try {
const updated = await getCapacityInquiry(row.bookingNo)
if (current === followupSequence) await inquiryAction('view', updated)
} catch (cause) { if (current === followupSequence) error.value = getApiErrorDetails(cause, '操作已完成,下一步进度读取失败,请刷新原登记') }
}
let followupSequence = 0
function openFulfillment(key: string, row: CapacityInquiry) {
if (key === 'business-order') { const target = inquiryOrderTarget(row, access.can); if (target) void router.push(target); return true }
if (key === 'appointment' && access.can('platform:appointment:list') && access.can('platform:overseas-warehouse:last-mile-order:list')) { void router.push({ name: 'appointment-calendar', query: { sourceKey: `BOOKING:${row.bookingNo}`, customerId: row.customerId, destinationKey: row.cargo.destinationKey } }); return true }
if (key === 'dispatch' && row.dispatchNo && access.can('platform:overseas-warehouse:dispatch:list')) { void router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: row.dispatchNo } }); return true }
if (key === 'trip' && row.dispatchNo && access.can('platform:fleet:trip:list')) { void router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: row.tripNo } }); return true }
if (key === 'dispatch' && row.dispatchNo && access.can('platform:overseas-warehouse:dispatch:list')) { void router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: row.dispatchNo }, query: inquirySourceQuery(row) }); return true }
if (key === 'trip' && row.dispatchNo && access.can('platform:fleet:trip:list')) { void router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: row.tripNo }, query: inquirySourceQuery(row) }); return true }
if (!['incoming', 'receipt', 'bind', 'fulfillment'].includes(key)) return false
editorOpen.value = false
fulfillmentRow.value = row; fulfillmentMode.value = key === 'fulfillment' ? 'view' : key as 'incoming' | 'receipt' | 'bind'; fulfillmentOpen.value = true
@@ -83,6 +97,7 @@ function accept(value: InquiryResult) {
}
const commands = useCapacityInquiryCommands('capacity-pool-command', accept)
async function inquiryAction(key: string, row: CapacityInquiry) {
followupSequence++
if (openFulfillment(key, row)) return
if (key === 'cancel') { await commands.run(row, key); return }
if (key === 'confirm' && !manage.value) return
@@ -103,7 +118,7 @@ async function recheckEditor() {
if (current === editorSequence) { selectedOffer.value = detail.offer; editing.value = checked }
} catch (cause) { if (current === editorSequence) error.value = getApiErrorDetails(cause, '最新意向核对失败') }
}
watch([tab, page, size], load); onMounted(load); onBeforeUnmount(() => { sequence++; editorSequence++ })
watch([tab, page, size], load); onMounted(load); onBeforeUnmount(() => { followupSequence++; sequence++; editorSequence++ })
</script>
<template><AdminPage class="admin-density"><ListPageLayout aria-label="拼车接货">
<DataTable v-if="tab === 'offers'" appearance="integrated" :stripe="false" preference-key="fleet-capacity-offers" column-settings-mode="show" :results-visible="!error" v-model:page="page" v-model:page-size="size" :data="offers" :columns="columns" :loading="loading" :total="total" row-key="offerNo" :empty-text="Object.values(applied).some(Boolean) ? '当前筛选条件下暂无运力发布' : '暂无获准查看的运力发布'">
@@ -133,6 +148,6 @@ watch([tab, page, size], load); onMounted(load); onBeforeUnmount(() => { sequenc
</template>
<template #notices><LoadErrorState v-if="error || commands.error.value" title="意向操作未完成" :message="(error || commands.error.value)?.message" @retry="commands.state.pending ? commands.retry() : load()"><template #details><ApiErrorMeta :details="(error || commands.error.value)!" /></template></LoadErrorState><el-button v-if="commands.state.pending" :loading="commands.busy.value" @click="commands.retry">重试原操作</el-button></template>
</CapacityInquiryTable>
<CapacityInquiryFulfillment v-model="fulfillmentOpen" :inquiry="fulfillmentRow" :mode="fulfillmentMode" @saved="load" />
<CapacityInquiryFulfillment v-model="fulfillmentOpen" :inquiry="fulfillmentRow" :mode="fulfillmentMode" @saved="fulfillmentSaved" />
<CapacityInquiryDialog v-model="editorOpen" :offer="selectedOffer" :inquiry="editing" :readonly="readonly" :confirm-mode="confirmMode" @action="inquiryAction" @saved="accept" @refresh="recheckEditor" />
</ListPageLayout></AdminPage></template>