import type { FormRules } from 'element-plus' export type TransportDemandKind = 'truck' | 'looseboard' export interface TransportDemandForm { deliveryMode: 'DELIVERY_TO_WAREHOUSE' | 'WAREHOUSE_PICKUP' operatingWarehouseId: string deliveryContactName: string deliveryContactPhone: string estimatedArrivalAt: string trackingNo: string destinationType: 'FBA' | 'PRIVATE' destinationPostalCode: string vehicleType: '' | 'PALLET_15' | 'PALLET_30' origin: string destination: string originAddress: string destinationAddress: string originContactName: string originContactPhone: string destinationContactName: string destinationContactPhone: string productName: string estimatedPalletCount: number packageCount: number totalWeightKg: number totalVolumeCbm: number budgetAmount: number | undefined currency: 'USD' | 'CNY' pickupAt: string deliveryAt: string remark: string } export function createTransportDemandForm(): TransportDemandForm { return { deliveryMode: 'DELIVERY_TO_WAREHOUSE', operatingWarehouseId: '', deliveryContactName: '', deliveryContactPhone: '', estimatedArrivalAt: '', trackingNo: '', destinationType: 'PRIVATE', destinationPostalCode: '', vehicleType: '', origin: '', destination: '', originAddress: '', destinationAddress: '', originContactName: '', originContactPhone: '', destinationContactName: '', destinationContactPhone: '', productName: '', estimatedPalletCount: 0, packageCount: 0, totalWeightKg: 0, totalVolumeCbm: 0, budgetAmount: undefined, currency: 'USD', pickupAt: '', deliveryAt: '', remark: '', } } const requiredText = (kind: TransportDemandKind, form: TransportDemandForm) => ({ ...(kind === 'truck' || form.deliveryMode === 'WAREHOUSE_PICKUP' ? { origin: kind === 'truck' ? '取货地' : '起运地', originContactName: kind === 'truck' ? '取货联系人' : '起运地联系人', originContactPhone: kind === 'truck' ? '取货联系电话' : '起运地联系电话' } : {}), destination: kind === 'truck' ? '送达地' : '目的地', destinationContactName: kind === 'truck' ? '送达联系人' : '目的地联系人', destinationContactPhone: kind === 'truck' ? '送达联系电话' : '目的地联系电话', } as const) export function localMinuteInput(value: unknown) { return typeof value === 'string' && value ? `${value.slice(0, 16)}:00` : '' } export function transportDemandRules(kind: TransportDemandKind, form: TransportDemandForm): FormRules { const rules: FormRules = {} const labels = requiredText(kind, form) for (const [field, label] of Object.entries(labels)) { rules[field as keyof typeof labels] = [{ required: true, whitespace: true, message: `请输入${label}`, trigger: 'blur' }] } if (kind === 'looseboard') { if (form.deliveryMode === 'WAREHOUSE_PICKUP') rules.originAddress = [{ required: true, whitespace: true, message: '请输入起运详细地址', trigger: 'blur' }] rules.operatingWarehouseId = [{ required: true, message: '请选择收货仓库', trigger: 'change' }] if (form.destinationType === 'PRIVATE') { rules.destinationAddress = [{ required: true, whitespace: true, message: '请输入目的详细地址', trigger: 'blur' }] rules.destinationPostalCode = [{ required: true, whitespace: true, message: '请输入目的地邮编', trigger: 'blur' }] } if (form.deliveryMode === 'DELIVERY_TO_WAREHOUSE') { for (const [field, label] of [['deliveryContactName', '到仓联系人'], ['deliveryContactPhone', '到仓联系电话'], ['estimatedArrivalAt', '预计到货时间']] as const) rules[field] = [{ required: true, message: '请填写' + label, trigger: 'change' }] } } if (kind === 'truck') rules.vehicleType = [{ required: true, message: '请选择车辆类型', trigger: 'change' }] for (const [field, label] of [['packageCount', '件数'], ['estimatedPalletCount', '预计托数'], ['totalWeightKg', '总重量'], ['totalVolumeCbm', '总体积']] as const) { if (field === 'packageCount' && kind === 'truck') continue rules[field] = [{ required: true, type: 'number', min: 0, message: `${label}须为不小于 0 的数字`, trigger: 'change' }] } rules.budgetAmount = [{ required: kind === 'truck', type: 'number', min: kind === 'truck' ? 0.01 : 0, message: '请输入大于 0 的预算总价', trigger: 'change' }] const pickupLabel = kind === 'truck' ? '取货时间' : '取货时间', deliveryLabel = kind === 'truck' ? '送达时间' : '送货时间' rules.pickupAt = [{ required: true, message: `请选择${pickupLabel}`, trigger: 'change' }] rules.deliveryAt = [{ required: true, message: `请选择${deliveryLabel}`, trigger: 'change' }, { validator: (_rule, value, callback) => callback(value && form.pickupAt && localMinuteInput(value) < localMinuteInput(form.pickupAt) ? new Error(`${deliveryLabel}不能早于${pickupLabel}`) : undefined), trigger: 'change', }] return rules } export function transportDemandError(kind: TransportDemandKind, form: TransportDemandForm): string | undefined { if (kind === 'looseboard') { if (!form.operatingWarehouseId) return '请选择收货仓库' if (!['DELIVERY_TO_WAREHOUSE', 'WAREHOUSE_PICKUP'].includes(form.deliveryMode)) return '请选择送货方式' if (form.destinationType === 'PRIVATE' && (!form.destinationAddress.trim() || !form.destinationPostalCode.trim())) return '请填写目的详细地址和邮编' if (form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' && (!form.deliveryContactName.trim() || !form.deliveryContactPhone.trim() || !form.estimatedArrivalAt)) return '送货到仓须填写仓库联系人和预计到货时间' if (form.deliveryMode === 'WAREHOUSE_PICKUP' && !form.originAddress.trim()) return '请输入起运详细地址' if (form.packageCount < 1) return '散板收货件数须大于 0' } if (kind === 'truck' && !['PALLET_15', 'PALLET_30'].includes(form.vehicleType)) return '请选择车辆类型' const labels = requiredText(kind, form) for (const [field, label] of Object.entries(labels)) { if (!String(form[field as keyof typeof labels] || '').trim()) return `请输入${label}` } const pickupLabel = kind === 'truck' ? '取货时间' : '取货时间', deliveryLabel = kind === 'truck' ? '送达时间' : '送货时间' for (const [field, label] of [['pickupAt', pickupLabel], ['deliveryAt', deliveryLabel]] as const) { if (!form[field]) return `请选择${label}` if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/.test(form[field])) return `请选择有效的${label}` } if (localMinuteInput(form.deliveryAt) < localMinuteInput(form.pickupAt)) return `${deliveryLabel}不能早于${pickupLabel}` for (const [field, label] of [['packageCount', '件数'], ['totalWeightKg', '总重量'], ['totalVolumeCbm', '总体积'], ...(kind === 'looseboard' ? [['estimatedPalletCount', '预计托数'] as const] : [])] as const) { if (!Number.isFinite(form[field]) || form[field] < 0) return `${label}须为不小于 0 的数字` } if (!Number.isInteger(form.packageCount) || kind === 'looseboard' && !Number.isInteger(form.estimatedPalletCount)) return '件数和预计托数须为整数' if (kind === 'truck' && (!Number.isSafeInteger(form.estimatedPalletCount) || form.estimatedPalletCount <= 0)) return '请输入大于 0 的托数' if (kind === 'truck' && (form.budgetAmount == null || !Number.isFinite(form.budgetAmount) || form.budgetAmount <= 0)) return '请输入大于 0 的预算总价' if (form.budgetAmount != null && (!Number.isFinite(form.budgetAmount) || form.budgetAmount < 0)) return '预算总价不能小于 0' if (!['USD', 'CNY'].includes(form.currency)) return '请选择预算币种' return undefined } function commonPayload(form: TransportDemandForm) { return { origin: form.origin.trim(), destination: form.destination.trim(), originAddress: form.originAddress.trim() || undefined, destinationAddress: form.destinationAddress.trim() || undefined, originContactName: form.originContactName.trim(), originContactPhone: form.originContactPhone.trim(), destinationContactName: form.destinationContactName.trim(), destinationContactPhone: form.destinationContactPhone.trim(), packageCount: form.packageCount, totalWeightKg: form.totalWeightKg, totalVolumeCbm: form.totalVolumeCbm, budgetAmount: form.budgetAmount ?? undefined, currency: form.currency, pickupAt: localMinuteInput(form.pickupAt), deliveryAt: localMinuteInput(form.deliveryAt), remark: form.remark.trim() || undefined, } } type CommonPayload = ReturnType type TruckPayload = Omit & { palletCount: number; vehicleType: 'PALLET_15' | 'PALLET_30' } type LooseboardPayload = CommonPayload & { deliveryMode: 'DELIVERY_TO_WAREHOUSE' | 'WAREHOUSE_PICKUP'; operatingWarehouseId: string; destinationType: 'FBA' | 'PRIVATE'; destinationPostalCode?: string; deliveryContactName?: string; deliveryContactPhone?: string; estimatedArrivalAt?: string; trackingNo?: string; productName: string | undefined; estimatedPalletCount: number } export function buildTransportDemand(kind: 'truck', form: TransportDemandForm): TruckPayload export function buildTransportDemand(kind: 'looseboard', form: TransportDemandForm): LooseboardPayload export function buildTransportDemand(kind: TransportDemandKind, form: TransportDemandForm): TruckPayload | LooseboardPayload { const failure = transportDemandError(kind, form) if (failure) throw new Error(failure) const common = commonPayload(form) return kind === 'truck' ? { ...(({ packageCount: _packages, ...rest }) => rest)(common), palletCount: form.estimatedPalletCount, vehicleType: form.vehicleType as 'PALLET_15' | 'PALLET_30' } : { ...common, ...(form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' ? { origin: '', originAddress: undefined, originContactName: '', originContactPhone: '' } : {}), deliveryMode: form.deliveryMode, operatingWarehouseId: form.operatingWarehouseId, deliveryContactName: form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' ? form.deliveryContactName.trim() : undefined, deliveryContactPhone: form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' ? form.deliveryContactPhone.trim() : undefined, estimatedArrivalAt: form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' ? localMinuteInput(form.estimatedArrivalAt) : undefined, trackingNo: form.deliveryMode === 'DELIVERY_TO_WAREHOUSE' ? form.trackingNo.trim() || undefined : undefined, destinationType: form.destinationType, destinationPostalCode: form.destinationType === 'PRIVATE' ? form.destinationPostalCode.trim() : undefined, productName: form.productName.trim() || undefined, estimatedPalletCount: form.estimatedPalletCount } }