diff --git a/src/modules/sailing-calendar/api/platformSailingCalendar.ts b/src/modules/sailing-calendar/api/platformSailingCalendar.ts index c120a55f..f0d3d1c5 100644 --- a/src/modules/sailing-calendar/api/platformSailingCalendar.ts +++ b/src/modules/sailing-calendar/api/platformSailingCalendar.ts @@ -145,8 +145,8 @@ const exceptionPath = '/platform/v1/sailing-assignments/exceptions' function channel(raw: SailingChannelResource): SailingChannelResource { return { ...raw, id: String(raw.id), defaultCutoffDays: number(raw.defaultCutoffDays), version: number(raw.version) } } function template(raw: SailingTemplateResource): SailingTemplateResource { return { ...raw, id: String(raw.id), channelId: String(raw.channelId), warehouseId: String(raw.warehouseId), weekdays: (raw.weekdays || []).map((value) => number(value)), cutoffDays: number(raw.cutoffDays), version: number(raw.version) } } -export async function listSailingChannels() { - const response = await http.get, ApiEnvelope>(channelPath) +export async function listSailingChannels(warehouseId?: string) { + const response = await http.get, ApiEnvelope>(channelPath, { params: warehouseId ? { warehouseId } : undefined }) const result = unwrapApiResponse(response) as SailingChannelResource[] | { records?: SailingChannelResource[] } | null return (Array.isArray(result) ? result : result?.records || []).map(channel) } diff --git a/src/modules/sailing-calendar/pages/SailingCalendarView.vue b/src/modules/sailing-calendar/pages/SailingCalendarView.vue index 0babdce1..a9bfb8db 100644 --- a/src/modules/sailing-calendar/pages/SailingCalendarView.vue +++ b/src/modules/sailing-calendar/pages/SailingCalendarView.vue @@ -234,23 +234,38 @@ function onTemplateChannelChanged(id: string) { templateForm.destinationPortCode = channel.defaultDestinationPortCode || '' templateForm.vesselName = '待订舱确认' } -function onTemplateWarehouseChanged(id: string) { applyWarehouse(id, templateForm) } -function onFormWarehouseChanged(id: string) { applyWarehouse(id, form) } +async function loadChannelsForWarehouse(warehouseId?: string) { + channels.value = warehouseId ? await listSailingChannels(warehouseId) : [] +} +async function onTemplateWarehouseChanged(id: string) { + applyWarehouse(id, templateForm) + templateForm.channelId = '' + templateForm.departurePortCode = '' + templateForm.destinationPortCode = '' + await loadChannelsForWarehouse(id) +} +async function onFormWarehouseChanged(id: string) { + applyWarehouse(id, form) + form.channelCode = '' + form.channelId = null + await loadChannelsForWarehouse(id) +} async function openTemplateCreate() { await ensureWarehouseOptions() editingTemplateId.value = '' - const activeChannel = channels.value.find((item) => item.status === 'ACTIVE') - Object.assign(templateForm, { templateName: '', channelId: activeChannel?.id || '', warehouseId: '', warehouseName: '', weekdays: [1], effectiveFrom: formatDate(today), effectiveTo: '', departureTime: '10:00:00', cutoffDays: activeChannel?.defaultCutoffDays ?? 3, cutoffLocalTime: '18:00:00', voyageRule: '', vesselName: '待订舱确认', departurePortCode: activeChannel?.defaultDeparturePortCode || '', destinationPortCode: activeChannel?.defaultDestinationPortCode || '', remark: '', expectedVersion: undefined }) + Object.assign(templateForm, { templateName: '', channelId: '', warehouseId: '', warehouseName: '', weekdays: [1], effectiveFrom: formatDate(today), effectiveTo: '', departureTime: '10:00:00', cutoffDays: 3, cutoffLocalTime: '18:00:00', voyageRule: '', vesselName: '待订舱确认', departurePortCode: '', destinationPortCode: '', remark: '', expectedVersion: undefined }) + channels.value = [] templatePreview.value = [] resetGenerationRange(templateForm.effectiveFrom) templateDialogOpen.value = true } -function openTemplateEdit(row: unknown, options: { preserveGenerationRange?: boolean } = {}) { +async function openTemplateEdit(row: unknown, options: { preserveGenerationRange?: boolean } = {}) { const templateRow = row as SailingTemplateResource editingTemplateId.value = templateRow.id Object.assign(templateForm, { templateName: templateRow.templateName || '', channelId: templateRow.channelId, warehouseId: templateRow.warehouseId, warehouseName: templateRow.warehouseName || '', weekdays: [...templateRow.weekdays], effectiveFrom: templateRow.effectiveFrom, effectiveTo: templateRow.effectiveTo || '', departureTime: templateRow.departureTime, cutoffDays: templateRow.cutoffDays, cutoffLocalTime: templateRow.cutoffLocalTime, voyageRule: templateRow.voyageRule || '', vesselName: templateRow.vesselName || '', departurePortCode: templateRow.departurePortCode || '', destinationPortCode: templateRow.destinationPortCode || '', remark: templateRow.remark || '', expectedVersion: templateRow.version }) templatePreview.value = [] if (!options.preserveGenerationRange) resetGenerationRange(templateForm.effectiveFrom) + try { await loadChannelsForWarehouse(templateForm.warehouseId) } catch (cause) { ElMessage.error(getApiErrorDetails(cause, '渠道加载失败').message); return } templateDialogOpen.value = true } function templatePayload() { syncTemplateDerivedFields(); return { ...templateForm, effectiveTo: templateForm.effectiveTo || null, weekdays: [...templateForm.weekdays].sort((a, b) => a - b), voyageRule: templateForm.voyageRule || null, vesselName: templateForm.vesselName || '待订舱确认', remark: templateForm.remark || null } } @@ -318,16 +333,16 @@ async function openCreate() { cutoffDays.value = 3 cutoffLocalTime.value = '18:00:00' Object.assign(form, blankForm(), { expectedVersion: undefined }) + channels.value = [] routeExpanded.value = [] dialogOpen.value = true formOptionsLoading.value = true try { - const [rows] = await Promise.all([listSailingChannels(), ensureWarehouseOptions()]) - channels.value = rows + await ensureWarehouseOptions() } catch (cause) { ElMessage.error(getApiErrorDetails(cause, '排船资料加载失败,请关闭后重试').message) } finally { formOptionsLoading.value = false } } -async function openEdit(record: SailingCalendarEntry) { try { const [rows] = await Promise.all([listSailingChannels(), ensureWarehouseOptions()]); channels.value = rows } catch (cause) { ElMessage.error(getApiErrorDetails(cause, '排船资料加载失败').message); return } routeExpanded.value = []; dialogMode.value = 'edit'; cutoffMode.value = 'custom'; cutoffDays.value = 3; cutoffLocalTime.value = record.cutoffAt.slice(11, 19) || '18:00:00'; Object.assign(form, { templateId: record.templateId ?? null, channelId: record.channelId ?? null, warehouseId: record.warehouseId, warehouseName: record.warehouseName, channelCode: record.channelCode, channelName: record.channelName, vesselName: record.vesselName, voyageNo: record.voyageNo, departurePortCode: record.departurePortCode, departurePortName: record.departurePortName || '', destinationPortCode: record.destinationPortCode, destinationPortName: record.destinationPortName || '', etdAt: record.etdAt.slice(0, 19), etaAt: record.etaAt ?? null, cutoffAt: record.cutoffAt.slice(0, 19), sourceType: record.sourceType ?? 'MANUAL', plannedCapacityCbm: record.plannedCapacityCbm ?? null, remark: record.remark || '', expectedVersion: record.version }); dialogOpen.value = true } +async function openEdit(record: SailingCalendarEntry) { try { await ensureWarehouseOptions(); await loadChannelsForWarehouse(record.warehouseId) } catch (cause) { ElMessage.error(getApiErrorDetails(cause, '排船资料加载失败').message); return } routeExpanded.value = []; dialogMode.value = 'edit'; cutoffMode.value = 'custom'; cutoffDays.value = 3; cutoffLocalTime.value = record.cutoffAt.slice(11, 19) || '18:00:00'; Object.assign(form, { templateId: record.templateId ?? null, channelId: record.channelId ?? null, warehouseId: record.warehouseId, warehouseName: record.warehouseName, channelCode: record.channelCode, channelName: record.channelName, vesselName: record.vesselName, voyageNo: record.voyageNo, departurePortCode: record.departurePortCode, departurePortName: record.departurePortName || '', destinationPortCode: record.destinationPortCode, destinationPortName: record.destinationPortName || '', etdAt: record.etdAt.slice(0, 19), etaAt: record.etaAt ?? null, cutoffAt: record.cutoffAt.slice(0, 19), sourceType: record.sourceType ?? 'MANUAL', plannedCapacityCbm: record.plannedCapacityCbm ?? null, remark: record.remark || '', expectedVersion: record.version }); dialogOpen.value = true } function onEtdChanged() { if (dialogMode.value === 'create' || cutoffMode.value === 'rule') syncCutoffFromRule() } function validateForm() { const required: Array<[keyof SailingSaveRequest, string]> = [['warehouseId', '收货仓'], ['channelCode', '渠道'], ['departurePortCode', '起运港'], ['destinationPortCode', '目的港'], ['etdAt', '开船时间'], ['cutoffAt', '截止时间']]; const missing = required.find(([key]) => !String(form[key] || '').trim()); if (missing) { ElMessage.error(`请填写${missing[1]}`); return false } if (new Date(form.cutoffAt).getTime() >= new Date(form.etdAt).getTime()) { ElMessage.error('收货截止时间必须早于预计开船时间'); return false } return true } async function save() { @@ -422,14 +437,14 @@ onMounted(() => void load()) - 选择已有渠道,自动带入排船默认资料。
{{ form.vesselName || '待订舱确认' }} / {{ form.voyageNo || '待订舱确认' }}
未确认船名、航次时可先排船,显示为待订舱确认。
按开船前 N 天自定义时间
提前天数2 天3 天4 天
截止时刻
建议截止{{ dateTime(form.cutoffAt) }}按开船本地日期倒推自然日
+ 选择已有渠道,自动带入排船默认资料。
{{ form.vesselName || '待订舱确认' }} / {{ form.voyageNo || '待订舱确认' }}
未确认船名、航次时可先排船,显示为待订舱确认。
按开船前 N 天自定义时间
提前天数2 天3 天4 天
截止时刻
建议截止{{ dateTime(form.cutoffAt) }}按开船本地日期倒推自然日
-
查看已有船期渠道及默认收货规则。
设置每周开船日和收货截止,预览后批量生成;重复日期自动跳过。新增周模板
- 周{{ ['一', '二', '三', '四', '五', '六', '日'][day - 1] }}
2 天3 天4 天
快捷生成范围:未来 4 周未来 8 周未来 12 周

生成预览({{ templatePreview.length }} 条)

+
渠道只读展示当前生效的通用船期价格表;周模板按收货仓筛选可用渠道。
设置每周开船日和收货截止,预览后批量生成;重复日期自动跳过。新增周模板
+ 渠道来自当前生效的通用船期价格表,并按收货仓的有效报价筛选。周{{ ['一', '二', '三', '四', '五', '六', '日'][day - 1] }}
2 天3 天4 天
快捷生成范围:未来 4 周未来 8 周未来 12 周

生成预览({{ templatePreview.length }} 条)