Files
admin-web/src/modules/appointment-calendar/WorkCreateAppointmentForm.vue
T

61 lines
4.6 KiB
Vue
Raw Normal View History

<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'
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">{{ 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>