62 lines
4.7 KiB
Vue
62 lines
4.7 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
|
import { FbaAppointmentFields, createAppointmentForm, appointmentFormError, appointmentPayload, overseasWarehousePermissions } from '@/modules/overseas-warehouse'
|
|
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
|
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
|
import { previewPlanningBatch, createSourceAppointment, type PlanningBatchPreview, type SourceAppointmentPayload } from './planningApi'
|
|
import type { SourceReference } from './workApi'
|
|
import { rejectedCommand, useWorkDraft } from './useWorkDraft'
|
|
import { workReason } from './workPresentation'
|
|
const props = defineProps<{ sources: SourceReference[]; canWrite: boolean }>()
|
|
const emit = defineEmits<{ saved: [] }>()
|
|
const access = useAccessPreviewStore(), preview = ref<PlanningBatchPreview | null>(null)
|
|
const form = reactive(createAppointmentForm()), baseline = ref(JSON.stringify(form))
|
|
const loading = ref(false), saving = ref(false), error = ref<ApiErrorDetails | null>(null), saveError = ref<ApiErrorDetails | null>(null)
|
|
const pending = ref<SourceAppointmentPayload | null>(null)
|
|
const writable = computed(() => props.canWrite && access.can(overseasWarehousePermissions.inboundOrderAppointment))
|
|
useWorkDraft(computed(() => baseline.value !== JSON.stringify(form)), computed(() => saving.value || !!pending.value))
|
|
async function load() {
|
|
if (!props.sources.length || props.sources.some(source => !source.customerId || !source.sourceKey || !source.destinationKey)) { error.value = { message: '缺少完整货源、客户或目的仓范围,无法新建预约。' }; return }
|
|
loading.value = true; error.value = null
|
|
try { preview.value = await previewPlanningBatch(props.sources.map(source => ({ ...source, destinationKey: source.destinationKey! }))) }
|
|
catch (cause) { error.value = getApiErrorDetails(cause, '预约货物预览失败') }
|
|
finally { loading.value = false }
|
|
}
|
|
async function save() {
|
|
if (!writable.value || loading.value || saving.value || error.value || !preview.value) return
|
|
if (!pending.value) {
|
|
const invalid = appointmentFormError(form); if (invalid) { ElMessage.warning(invalid); return }
|
|
pending.value = { originWarehouseId: preview.value.group.originWarehouseId, fbaWarehouseCode: preview.value.destinationWarehouseCode, sources: preview.value.sources.map(source => ({ sourceKey: source.sourceKey, customerId: source.customerId, destinationKey: source.destinationKey })), details: appointmentPayload(form), idempotencyKey: crypto.randomUUID() }
|
|
}
|
|
saving.value = true; saveError.value = null
|
|
try {
|
|
const result = await createSourceAppointment(pending.value)
|
|
pending.value = null; baseline.value = JSON.stringify(form)
|
|
ElMessage.success(`预约安排 ${result.appointmentNo} 已保存`); emit('saved')
|
|
} catch (cause) {
|
|
saveError.value = getApiErrorDetails(cause, '创建结果待确认,请重试本次请求')
|
|
if (rejectedCommand(cause)) { pending.value = null; await load() }
|
|
} finally { saving.value = false }
|
|
}
|
|
onMounted(load)
|
|
</script>
|
|
<template>
|
|
<section class="work-form" aria-label="新建预约安排">
|
|
<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-if="preview && !error">
|
|
<p>{{ preview.originWarehouseName }} → {{ preview.destinationWarehouseCode }} · {{ preview.group.sourceCount }} 票 · {{ preview.group.packages ?? '箱数待核实' }}</p>
|
|
<p v-for="source in preview.sources" :key="`${source.customerId}:${source.sourceKey}`">{{ source.inboundCode || source.bookingNo || '原票业务号未提供' }}</p>
|
|
<p v-for="field in preview.missingFields" :key="field">{{ workReason(field) }}</p>
|
|
<p v-if="preview.group.readinessIncomplete">货物备妥窗口待核实。</p>
|
|
<FbaAppointmentFields :model="form" :timezone="preview.destinationTimezone" :disabled="!writable || loading || saving || !!pending" />
|
|
<LoadErrorState v-if="saveError" title="预约尚未创建" :message="saveError.message" :show-retry="false"><template #details><ApiErrorMeta :details="saveError" /></template></LoadErrorState>
|
|
<el-button type="primary" :disabled="!writable || loading" :loading="saving" @click="save">{{ pending ? '重试本次创建' : '保存预约安排' }}</el-button>
|
|
</template>
|
|
</section>
|
|
</template>
|