diff --git a/web-admin/src/api/pl/index.ts b/web-admin/src/api/pl/index.ts index 60f50551b..f0be669c6 100644 --- a/web-admin/src/api/pl/index.ts +++ b/web-admin/src/api/pl/index.ts @@ -691,6 +691,7 @@ export interface PlanCreateFullPayload { followUserId?: string followUserName?: string remark?: string + saveMode?: 'draft' | 'formal' supplierContracts?: PlanContractLink[] demanderContracts?: PlanContractLink[] load?: PlanLoad diff --git a/web-admin/src/components/business/pl-plan-form-dialog/index.vue b/web-admin/src/components/business/pl-plan-form-dialog/index.vue index 92c7e6b73..f8cb01ebc 100644 --- a/web-admin/src/components/business/pl-plan-form-dialog/index.vue +++ b/web-admin/src/components/business/pl-plan-form-dialog/index.vue @@ -254,6 +254,13 @@ 取消 + + 暂存 + (createEmptyLoadForm()) const deliveryForms = ref([createEmptyDeliveryForm(1)]) const loadedPlanSnapshot = ref(null) @@ -489,6 +498,12 @@ /** 已安排:禁止仅「保存」,须走保存并安排/调整运输,避免计划与地磅等联动字段脱节 */ const showSaveButton = computed(() => loadedTransportStatus.value !== 'arranged') + const showDraftButton = computed(() => { + if (!isEdit.value) return true + return ( + loadedFollowStatus.value === 'drafting' || loadedFollowStatus.value === 'pending_arrange' + ) + }) const showSaveAndArrangeButton = computed(() => { if (!isEdit.value) return true if (props.openForArrange && loadedTransportStatus.value === 'arranged') return false @@ -1577,6 +1592,7 @@ function resetForm() { loadedTransportStatus.value = 'pending' + loadedFollowStatus.value = '' loadedPlanSnapshot.value = null vehicleBundle.value = null selectedDriverId.value = '' @@ -1669,6 +1685,7 @@ }) applyStopsFromPlan(data) loadedTransportStatus.value = data.transportStatus || 'pending' + loadedFollowStatus.value = data.followStatus || '' loadedPlanSnapshot.value = data syncCommittedPartyIds() @@ -1731,6 +1748,7 @@ products: row.products.map((product) => ({ ...product, planProductId: undefined })) })) loadedTransportStatus.value = 'pending' + loadedFollowStatus.value = '' syncCommittedPartyIds() formData.products = await enrichPlanProductLinesAlias( @@ -1976,11 +1994,51 @@ return missing } - async function handleSubmit(mode: 'save' | 'saveAndArrange' | 'rearrange' = 'save') { + async function handleSubmit( + mode: 'draft' | 'save' | 'saveAndArrange' | 'rearrange' = 'save' + ) { // 必须在 await validate 之前置位,避免连点并发多次 createFull if (submitLoading.value) return submitLoading.value = true try { + if (mode === 'draft') { + const companyOk = !!(formData.belongsCompany || '').trim() + const dateOk = !!(formData.createDate || '').trim() + if (!companyOk || !dateOk) { + ElMessage.warning('暂存请至少填写归属公司和创建日期') + await basicFormRef.value + ?.validateField?.(['belongsCompany', 'createDate']) + .catch(() => undefined) + return + } + mirrorPlanProductsFromLoad() + const draftPayload = { + ...buildPlanCommonPayload({ allowClearTransport: isEdit.value }), + saveMode: 'draft' as const, + hasFreight: formData.hasFreight + } + let planId = props.planId || '' + if (isEdit.value) { + if (!props.planId) return + await planApi.update(props.planId, draftPayload) + planId = props.planId + } else { + const res = await planApi.createFull( + draftPayload as Parameters[0] + ) + const created = pickBxData(res) + planId = String(created?.id || '') + if (!planId) { + ElMessage.error('暂存成功但未返回计划 ID') + return + } + } + ElMessage.success('暂存成功') + innerVisible.value = false + emit('success', { planId, arranged: false }) + return + } + const valid = await basicFormRef.value?.validate().catch(() => false) if (!valid || !validateBusinessRules()) return if (!validateSingleContractPerStop()) return @@ -2000,7 +2058,10 @@ } mirrorPlanProductsFromLoad() - const commonPayload = buildPlanCommonPayload({ allowClearTransport: isEdit.value }) + const commonPayload = { + ...buildPlanCommonPayload({ allowClearTransport: isEdit.value }), + saveMode: 'formal' as const + } let planId = props.planId || '' const wasCreate = !isEdit.value diff --git a/web-admin/src/utils/pl-display.ts b/web-admin/src/utils/pl-display.ts index c8f8e80d2..ed57668f9 100644 --- a/web-admin/src/utils/pl-display.ts +++ b/web-admin/src/utils/pl-display.ts @@ -366,7 +366,7 @@ export const PL_PLAN_TYPE_TABS: { label: string; name: PlPlanTypeTab }[] = [ ] export const PL_PLAN_LIST_TABS: { label: string; name: PlPlanListTab }[] = [ - { label: '待安排', name: 'pending_arrange' }, + { label: '待安排/草稿', name: 'pending_arrange' }, // name 保留;语义含 drafting ...PL_PLAN_TYPE_TABS ] @@ -384,7 +384,8 @@ export function plPlanListTabToFilters(tab: PlPlanListTab): { followStatus?: string } { if (tab === 'pending_arrange') { - return { planType: '', followStatus: 'pending_arrange' } + // followStatus 由列表 searchForm 控制(默认 pending_arrange + drafting) + return { planType: '' } } return { planType: plPlanTypeTabToFilter(tab) } } @@ -419,6 +420,7 @@ export function plPlanTypeFilterToTab(planType?: string): PlPlanTypeTab { /** 运输计划跟进状态选项(筛选) */ export const PL_PLAN_FOLLOW_STATUS_OPTIONS = [ + { label: '草稿中', value: 'drafting' }, { label: '等通知', value: 'wait_notice' }, { label: '待安排', value: 'pending_arrange' }, { label: '已安排', value: 'arranged' }, @@ -433,6 +435,7 @@ export const PL_PLAN_DEFAULT_FOLLOW_STATUSES = PL_PLAN_FOLLOW_STATUS_OPTIONS.fil export function getPlPlanFollowStatusTagType(status?: string) { const map: Record = { + drafting: 'info', wait_notice: 'warning', pending_arrange: 'danger', arranged: 'success', @@ -447,6 +450,7 @@ export function getPlPlanFollowStatusTagType(status?: string) { export function getPlPlanFollowStatusLabel(status?: string) { const map: Record = { + drafting: '草稿中', wait_notice: '等通知', pending_arrange: '待安排', arranged: '已安排', diff --git a/web-admin/src/views/pl/plan/index.vue b/web-admin/src/views/pl/plan/index.vue index c61dfc3a8..ad5570d24 100644 --- a/web-admin/src/views/pl/plan/index.vue +++ b/web-admin/src/views/pl/plan/index.vue @@ -26,7 +26,6 @@ placeholder="全部" clearable multiple - :disabled="activePlanTypeTab === 'pending_arrange'" >