43 lines
3.9 KiB
Vue
43 lines
3.9 KiB
Vue
<script setup lang="ts">
|
|
import '@/styles/adminWorkbench.css'
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
|
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
|
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
|
import { resolvePlanningSource, resolvePlanningOrder, type ResolvedPlanningSource, type PlanningSourceLookup } from './planningApi'
|
|
import type { LeaveGuard } from './useWorkDraft'
|
|
import CargoVerificationForm from './CargoVerificationForm.vue'
|
|
import WorkAppointmentForm from './WorkAppointmentForm.vue'
|
|
import AppointmentBatchForm from './AppointmentBatchForm.vue'
|
|
const props = defineProps<{ source: PlanningSourceLookup; canLeave: LeaveGuard }>()
|
|
const emit = defineEmits<{ updated: [] }>()
|
|
const context = ref<ResolvedPlanningSource | null>(null), error = ref<ApiErrorDetails | null>(null), loading = ref(false)
|
|
const appointmentNo = ref<string | null>(null), batchKey = ref<string | null>(null)
|
|
const canonicalSource = computed(() => context.value ? { sourceKey: context.value.source.sourceKey, customerId: context.value.source.customerId, destinationKey: context.value.source.destinationKey } : null)
|
|
async function load() {
|
|
loading.value = true; error.value = null
|
|
try { context.value = 'sourceKey' in props.source ? await resolvePlanningSource(props.source) : await resolvePlanningOrder(props.source) }
|
|
catch (cause) { error.value = getApiErrorDetails(cause, '货源预约关联读取失败') }
|
|
finally { loading.value = false }
|
|
}
|
|
async function openAppointment(number: string) { if (await props.canLeave()) { appointmentNo.value = number; batchKey.value = null } }
|
|
async function openBatch(key: string) { if (await props.canLeave()) { batchKey.value = key; appointmentNo.value = null } }
|
|
async function back() { if (await props.canLeave()) { appointmentNo.value = null; batchKey.value = null; await load() } }
|
|
async function updated() { emit('updated'); await load() }
|
|
onMounted(load)
|
|
</script>
|
|
<template>
|
|
<section aria-label="货源与全部预约批次">
|
|
<el-button v-if="appointmentNo || batchKey" @click="back">返回本票全部预约</el-button>
|
|
<AppointmentBatchForm v-if="batchKey" :key="batchKey" :batch-key="batchKey" @updated="updated" @appointment="openAppointment" />
|
|
<WorkAppointmentForm v-else-if="appointmentNo" :key="appointmentNo" :appointment-no="appointmentNo" :cancel="false" :can-write="true" @saved="updated" @batch="openBatch" />
|
|
<template v-else>
|
|
<h3>本票预约与货物核实</h3>
|
|
<LoadErrorState v-if="error" title="货源预约关联读取失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
|
|
<p v-if="loading">正在读取全部关联预约…</p>
|
|
<template v-else-if="context && !error"><p>{{ context.source.inboundCode || context.source.bookingNo || '原票业务号未提供' }} · {{ context.source.destinationWarehouseCode || '目的仓待核实' }}</p><p v-for="reason in context.source.blockReasons" :key="reason">{{ reason }}</p><p>共 {{ context.coverage.appointments.length }} 个关联预约,分别查看和处理。</p><div v-for="appointment in context.coverage.appointments" :key="appointment.appointmentNo" class="source-appointment"><el-button @click="openAppointment(appointment.appointmentNo)">{{ appointment.appointmentNo }}</el-button><span>{{ appointment.confirmedAt || '尚无确认时段' }} · {{ appointment.timezone || '地点时区待核实' }}</span></div><CargoVerificationForm v-if="canonicalSource" :source="canonicalSource" @saved="updated" /></template>
|
|
</template>
|
|
</section>
|
|
</template>
|
|
<style scoped>.source-appointment { display: flex; flex-wrap: wrap; align-items: center; gap: var(--admin-space-3); padding-block: var(--admin-space-3); border-bottom: 1px solid var(--workbench-border); }</style>
|