Files
client-web/src/utils/firstLegOrder.ts
T

102 lines
3.9 KiB
TypeScript
Raw Normal View History

2026-07-30 17:59:42 +08:00
import {
FIRST_LEG_ORDER_STATUSES,
FIRST_LEG_TRANSPORT_MODES,
type FirstLegOrderStatus,
type FirstLegTransportMode,
} from '@chongchong/shared'
import type { FirstLegCargoItem, FirstLegOrderDraft } from '../types/firstLegOrder.ts'
const statusLabels = new Map<FirstLegOrderStatus, string>(
FIRST_LEG_ORDER_STATUSES.map((item) => [item.code, item.label]),
)
const transportModeLabels = new Map<FirstLegTransportMode, string>(
FIRST_LEG_TRANSPORT_MODES.map((item) => [item.code, item.label]),
)
export function getFirstLegStatusLabel(status: FirstLegOrderStatus) {
return statusLabels.get(status) ?? status
}
export function getFirstLegTransportModeLabel(mode: FirstLegTransportMode) {
return transportModeLabels.get(mode) ?? mode
}
export function getFirstLegStatusTone(status: FirstLegOrderStatus): 'success' | 'warning' | 'info' | 'danger' {
if (status === 'COMPLETED') return 'success'
if (status === 'DRAFT' || status === 'PENDING_REVIEW') return 'warning'
if (status === 'CANCELLED') return 'danger'
return 'info'
}
export function canClientEditFirstLegOrder(status: FirstLegOrderStatus) {
return status === 'DRAFT'
}
export function canClientCancelFirstLegOrder(status: FirstLegOrderStatus) {
return status === 'DRAFT' || status === 'PENDING_REVIEW'
}
export function createEmptyCargoItem(id = `cargo-${Date.now()}`): FirstLegCargoItem {
return {
id,
sku: '',
cargoNameCn: '',
cargoNameEn: '',
pieces: 1,
packageCount: 1,
grossWeightKg: 0,
volumeCbm: 0,
containerType: '',
containerCount: 0,
remark: '',
packageType: 'CARTON',
hsCode: '',
declaredValue: 0,
currency: 'USD',
dangerousGoods: false,
}
}
export function validateFirstLegOrderDraft(draft: FirstLegOrderDraft) {
const errors: string[] = []
if (!draft.origin.trim()) errors.push('请填写起运地')
if (!draft.destination.trim()) errors.push('请填写目的地')
if (!draft.destinationCountryCode.trim()) errors.push('请填写目的国两位代码')
if (!draft.destinationWarehouseName.trim()) errors.push('请填写目的仓名称')
if (!draft.channelCode.trim() || !draft.channelName.trim()) errors.push('请填写渠道编码和渠道名称')
if (!draft.receivingWarehouseId) errors.push('请选择平台收货仓')
if (!draft.shipperPartyId || !draft.shipperPartySnapshot || !draft.shipper.trim()) errors.push('请选择发货单位')
if (draft.shipperPartySnapshot && !draft.shipperPartySnapshot.address.trim()) errors.push('请填写本单发货地址')
if (!draft.consigneePartyId || !draft.consigneePartySnapshot || !draft.consignee.trim()) errors.push('请选择收货单位')
if (draft.consigneePartySnapshot && !draft.consigneePartySnapshot.address.trim()) errors.push('请填写本单收货地址')
if (draft.items.length === 0) errors.push('请至少添加一条货物明细')
draft.items.forEach((item, index) => {
const line = `第 ${index + 1} 条货物`
if (!item.cargoNameCn.trim()) errors.push(`${line}缺少中文品名`)
if (!item.packageType.trim()) errors.push(`${line}缺少包装类型`)
if (!Number.isInteger(item.pieces) || item.pieces <= 0) errors.push(`${line}件数必须为正整数`)
if (item.grossWeightKg <= 0) errors.push(`${line}毛重必须大于 0`)
if (item.volumeCbm <= 0) errors.push(`${line}体积必须大于 0`)
if (draft.transportMode === 'FCL' && (!item.containerType || item.containerCount <= 0)) {
errors.push(`${line}需要填写箱型和柜量`)
}
})
return errors
}
export function summarizeFirstLegCargo(items: FirstLegCargoItem[]) {
return items.reduce(
(summary, item) => ({
pieces: summary.pieces + item.pieces,
packageCount: summary.packageCount + item.packageCount,
grossWeightKg: summary.grossWeightKg + item.grossWeightKg,
volumeCbm: summary.volumeCbm + item.volumeCbm,
}),
{ pieces: 0, packageCount: 0, grossWeightKg: 0, volumeCbm: 0 },
)
}