This commit is contained in:
2026-08-13 09:48:51 +08:00
parent 3366c58a5e
commit d765bd4340
10 changed files with 799 additions and 27 deletions
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox, type FormInstance, type FormItemRule, type FormRules, type UploadRequestOptions } from 'element-plus'
import { AlertTriangle, Box, Download, Eye, FilePlus2, ImagePlus, MapPin, PackageCheck, Pencil, Plus, Send, Trash2, Upload } from '@lucide/vue'
import { FIRST_LEG_ORDER_STATUSES, type FirstLegOrderStatus } from '@chongchong/shared'
@@ -42,9 +42,11 @@ import { validateProductImageFile } from '@/modules/first-leg/utils/productImage
const store = useFirstLegOrdersStore()
const tenantStore = useTenantAdministrationStore()
const auth = useAuthStore()
const route = useRoute()
const router = useRouter()
const props = withDefaults(defineProps<{ createPage?: boolean }>(), { createPage: false })
const isCreatePage = computed(() => props.createPage)
const routeDraftId = computed(() => typeof route.params.draftId === 'string' ? route.params.draftId : '')
const canViewCompanyProfile = computed(() => auth.hasPermission('company:profile:view'))
const companyAuditLoaded = ref(false)
const companySubmissionBlocked = computed(() => companyAuditLoaded.value && tenantStore.company.auditStatus !== 'ACTIVE')
@@ -277,7 +279,57 @@ function emptyDraft(): FirstLegOrderDraft {
}
}
const form = reactive<FirstLegOrderDraft>(emptyDraft())
function quickOrderDraft() {
const draft = emptyDraft()
if (!isCreatePage.value || route.query.quick !== '1') return draft
const destinationType = String(route.query.destinationType ?? '')
if (destinationType === 'FBA' || destinationType === 'OVERSEAS' || destinationType === 'PRIVATE') draft.destinationType = destinationType
draft.receivingWarehouseId = String(route.query.receivingWarehouseId ?? '')
draft.channelCode = String(route.query.channelCode ?? '')
draft.channelName = String(route.query.channelName ?? '')
draft.destinationWarehouseId = String(route.query.destinationWarehouseId ?? '')
draft.destinationWarehouseName = String(route.query.destinationWarehouseName ?? '')
draft.destinationCountryCode = String(route.query.destinationCountryCode ?? draft.destinationCountryCode).toUpperCase()
draft.amazonWarehouseCode = String(route.query.amazonWarehouseCode ?? '')
if (draft.destinationType === 'PRIVATE') {
try {
const consignee = JSON.parse(String(route.query.consignee ?? '{}')) as Record<string, unknown>
draft.consigneePartySnapshot = {
...createEmptyPartySnapshot('CONSIGNEE'),
name: draft.destinationWarehouseName,
countryRegion: draft.destinationCountryCode,
contactName: typeof consignee.contactName === 'string' ? consignee.contactName : '',
phone: typeof consignee.phone === 'string' ? consignee.phone : '',
city: typeof consignee.city === 'string' ? consignee.city : '',
address: typeof consignee.address === 'string' ? consignee.address : '',
postalCode: typeof consignee.postalCode === 'string' ? consignee.postalCode : '',
}
draft.consignee = draft.destinationWarehouseName
draft.destination = draft.destinationWarehouseName
} catch {
draft.consigneePartySnapshot = createEmptyPartySnapshot('CONSIGNEE')
}
} else {
draft.destination = draft.destinationWarehouseName
}
try {
const cargo = JSON.parse(String(route.query.cargo ?? '[]')) as Array<{ sku?: unknown; packageCount?: unknown }>
if (Array.isArray(cargo) && cargo.length) {
draft.items = cargo.map((item) => ({
...createEmptyCargoItem(`quick-cargo-${++cargoSequence}`),
sku: typeof item.sku === 'string' ? item.sku.slice(0, 64) : '',
packageCount: Number.isInteger(item.packageCount) && Number(item.packageCount) > 0 ? Number(item.packageCount) : 1,
}))
}
} catch {
// Keep the standard empty row when quick-order data is malformed.
}
return draft
}
const form = reactive<FirstLegOrderDraft>(quickOrderDraft())
const serializedDraft = computed(() => JSON.stringify(form))
const editorDirty = computed(() => (editorOpen.value || isCreatePage.value) && serializedDraft.value !== editorBaseline.value)
const unsavedRouteKey = props.createPage ? 'first-leg-order-create' : 'first-leg-orders'
@@ -381,9 +433,23 @@ async function loadPageData() {
if (!isCreatePage.value && store.draftLoadError) supportingDataErrors.value.push(`订单草稿加载失败:${store.draftLoadError}`)
if (destinationResult.status === 'rejected') supportingDataErrors.value.push(`目的仓加载失败:${errorMessage(destinationResult.reason)}`)
await autofillWarehouseSelections()
if (isCreatePage.value && routeDraftId.value) await loadRouteDraft(routeDraftId.value)
supportingDataLoading.value = false
}
async function loadRouteDraft(id: string) {
const detailedOrder = await store.loadDraftDetail(id)
if (!canClientEditFirstLegOrder(detailedOrder.status)) throw new Error('当前订单已不是草稿状态,无法继续编辑')
const draft = draftFromOrder(detailedOrder)
if (draft.destinationType !== 'PRIVATE') await loadDestinationWarehouses(draft.destinationType)
editingId.value = detailedOrder.id
replaceForm(draft)
await Promise.all(form.items.map(ensureProductImagePreview))
if (form.receivingWarehouseId) await loadSelectedWarehouse(form.receivingWarehouseId)
resetEditorValidation()
markEditorClean()
}
async function loadDestinationWarehouses(type: 'FBA' | 'OVERSEAS' | 'PRIVATE') {
if (type === 'PRIVATE') return
destinationWarehousesLoading.value = true
@@ -411,8 +477,9 @@ onMounted(() => {
watch(isCreatePage, (isCreate, wasCreate) => {
if (!isCreate || wasCreate) return
if (routeDraftId.value) return
editingId.value = undefined
replaceForm(emptyDraft())
replaceForm(quickOrderDraft())
resetEditorValidation()
}, { immediate: true })