Files
admin-web/src/modules/appointment-calendar/workPresentation.ts
T

71 lines
4.9 KiB
TypeScript

import type { WorkAction, WorkItem, WorkType, WorkView, BusinessReference } from './workApi'
import { actionCodes, workCommands, type WorkCommandName } from './workApi'
export const typeLabels: Record<WorkType, string> = { PICKUP: '提柜', WAREHOUSE_RECEIPT: '接仓卸柜', DELIVERY: '送仓 / FBA', TRANSPORT: '用车计划', EMPTY_RETURN: '还空柜', ROUTE: '确认路线' }
export const flowLabels = { TRANSFER: '海外仓中转', DIRECT: '整柜直送', UNKNOWN: '路线待确认' }
export const viewLabels: Record<WorkView, string> = { ALL: '全部待办', FOLLOW_TODAY: '今日需跟进', OVERDUE: '已超期', UNDATED: '未定日期', REVIEW: '需复核' }
export function workReason(reason: string) {
switch (reason) {
case 'SOURCE_TIMEZONE': return '原业务作业地点时区待核实'
case 'SOURCE_CONFIGURATION': return '原货源或仓库配置待核实'
case 'LASTLEG_SOURCE': return '尾程货源待建立'
case 'READY_WINDOW_UNVERIFIED': return '货物备妥窗口待核实'
case 'CARGO_QUANTITY_REVIEW': return '货量或重复关联需要核对。'
default: return reason
}
}
export function workStatus(item: WorkItem) {
if (item.executionStatus === 'COMPLETED') return '已完成'
if (item.reviewRequired) return '需复核'
if (item.executionStatus === 'IN_PROGRESS') return '执行中'
return { UNPLANNED: '待安排', TARGET_SET: '已有目标日期', AWAITING_REPLY: '等待回复', CONFIRMED: '已落实', NOT_REQUIRED: '无需预约' }[item.planStatus]
}
export function localInstant(value: string, timezone: string | null) {
if (!timezone) return '作业地点时区待核实'
try { return new Intl.DateTimeFormat('zh-CN', { timeZone: timezone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }).format(new Date(value)) }
catch { return '实际时间待核实' }
}
export function workDate(item: WorkItem) {
if (item.dateBasis === 'ACTUAL') {
const actual = item.actual
if (actual?.localOccurredAt) return `实际 ${actual.localOccurredAt.replace('T', ' ')}`
return actual?.occurredAt ? `实际 ${localInstant(actual.occurredAt, actual.timezone)}` : '实际时间待核实'
}
if (item.dateBasis === 'NONE' || !item.calendarDate) return '作业日期未定'
const slot = item.dateBasis === 'CONFIRMED' ? item.confirmed : item.target
const end = slot?.endDate && slot.endTime ? ` → ${slot.endDate} ${slot.endTime}` : ''
return `${item.dateBasis === 'CONFIRMED' ? '确认' : '目标'} ${slot?.date || item.calendarDate}${slot?.startTime ? ` ${slot.startTime}${end}` : ' · 时段未定'}`
}
export function commandName(action: WorkAction): WorkCommandName | null {
return action.target.kind === 'WORK_COMMAND' && workCommands.includes(action.target.command as WorkCommandName) ? action.target.command as WorkCommandName : null
}
export function sameAction(a: WorkAction, b: WorkAction) {
return a.code === b.code && a.targetWorkItemKey === b.targetWorkItemKey && JSON.stringify(a.target) === JSON.stringify(b.target)
}
export function actionAllowed(item: WorkItem, action: WorkAction) { return actionCodes.includes(action.code) && item.allowedActions.some(candidate => sameAction(candidate, action)) }
export function referenceRoute(reference: BusinessReference) {
if (!reference.businessNo) return null
if (reference.kind === 'DISPATCH') return `/overseas-warehouse/dispatches?dispatchNo=${encodeURIComponent(reference.businessNo)}`
if (reference.kind === 'DELIVERY') return `/overseas-warehouse/delivery-orders/${encodeURIComponent(reference.businessNo)}`
return null
}
export function actionRoute(action: WorkAction, references: BusinessReference[], item?: WorkItem) {
// Only known application routes. No server-provided URL or method is executed.
switch (action.target.kind) {
case 'FIRSTLEG_CONTAINER': {
const path = `/container-loading/overseas/${encodeURIComponent(action.target.containerBookingId)}`
if (!item || action.code === 'VIEW_DETAIL' || action.code === 'VIEW_HISTORY') return path
if (item.type === 'PICKUP') return `${path}?action=pickup`
if (item.type === 'EMPTY_RETURN' && action.code === 'RECORD_EXECUTION') return `${path}?action=empty-return`
if (item.type === 'DELIVERY' && item.flow === 'DIRECT' && action.code === 'RECORD_EXECUTION') return `${path}?action=direct-unload${item.requirementKey ? `&requirementKey=${encodeURIComponent(item.requirementKey)}` : ''}`
return path
}
case 'FLEET_TRIP': {
const id = action.target.tripId
const trip = references.find(ref => ref.kind === 'FLEET_TRIP' && ref.key === id)
return trip?.businessNo ? `/overseas-master-data/trips/${encodeURIComponent(trip.businessNo)}` : null
}
case 'WAREHOUSE_RECEIPT': return action.target.inboundPlanNo && action.target.customerId ? `/overseas-warehouse/inbound-orders/${encodeURIComponent(action.target.inboundPlanNo)}?customerId=${encodeURIComponent(action.target.customerId)}` : null
default: return null
}
}