|
|
|
@@ -102,6 +102,8 @@ const availableActions = computed(() => (actionsByStatus[detail.value?.status ||
|
|
|
|
|
|
|
|
|
|
const changeDialogOpen = ref(false)
|
|
|
|
|
const changeForm = reactive({ vesselName: '', voyageNo: '', estimatedSailingAt: null as string | null, estimatedArrivalAt: null as string | null, reason: '' })
|
|
|
|
|
const releaseDialogOpen = ref(false)
|
|
|
|
|
const releaseForm = reactive({ billOfLadingNo: '', vesselName: '', voyageNo: '', estimatedSailingAt: null as string | null, estimatedArrivalAt: null as string | null, reason: '' })
|
|
|
|
|
|
|
|
|
|
function statusLabel(status: OceanBookingStatus | string | null | undefined) {
|
|
|
|
|
return oceanBookingStatusLabel(status)
|
|
|
|
@@ -127,7 +129,7 @@ async function loadOptions() {
|
|
|
|
|
function selectCustomer(customerId: string) {
|
|
|
|
|
form.data.customerId = customerId
|
|
|
|
|
const customer = customers.value.find((item) => String(item.id) === customerId)
|
|
|
|
|
form.data.customerName = customer ? customer.legalName?.trim() || customer.tenantName : ''
|
|
|
|
|
form.data.customerName = customer?.tenantName || ''
|
|
|
|
|
}
|
|
|
|
|
function selectWarehouse(value: unknown) {
|
|
|
|
|
const warehouseId = String(value || '')
|
|
|
|
@@ -208,6 +210,37 @@ function openChangeDialog() {
|
|
|
|
|
changeForm.reason = ''
|
|
|
|
|
changeDialogOpen.value = true
|
|
|
|
|
}
|
|
|
|
|
function openReleaseDialog() {
|
|
|
|
|
releaseForm.billOfLadingNo = detail.value?.billOfLadingNo || ''
|
|
|
|
|
releaseForm.vesselName = detail.value?.vesselName || ''
|
|
|
|
|
releaseForm.voyageNo = detail.value?.voyageNo || ''
|
|
|
|
|
releaseForm.estimatedSailingAt = detail.value?.estimatedSailingAt || null
|
|
|
|
|
releaseForm.estimatedArrivalAt = detail.value?.estimatedArrivalAt || null
|
|
|
|
|
releaseForm.reason = ''
|
|
|
|
|
releaseDialogOpen.value = true
|
|
|
|
|
}
|
|
|
|
|
async function submitRelease() {
|
|
|
|
|
if (!detail.value) return
|
|
|
|
|
const billOfLadingNo = releaseForm.billOfLadingNo.trim()
|
|
|
|
|
const vesselName = releaseForm.vesselName.trim()
|
|
|
|
|
const voyageNo = releaseForm.voyageNo.trim()
|
|
|
|
|
if (!billOfLadingNo || !vesselName || !voyageNo || !releaseForm.estimatedSailingAt || !releaseForm.estimatedArrivalAt) {
|
|
|
|
|
actionError.value = getApiErrorDetails(new Error('请完整填写提单号、船名、航次、预计开船和预计到港时间'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (releaseForm.estimatedArrivalAt < releaseForm.estimatedSailingAt) {
|
|
|
|
|
actionError.value = getApiErrorDetails(new Error('预计到港时间不能早于预计开船时间'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
actionSaving.value = true
|
|
|
|
|
actionError.value = null
|
|
|
|
|
try {
|
|
|
|
|
applyDetail(await releaseOceanBooking(id.value, { expectedVersion: detail.value.version, billOfLadingNo, vesselName, voyageNo, estimatedSailingAt: releaseForm.estimatedSailingAt, estimatedArrivalAt: releaseForm.estimatedArrivalAt, reason: releaseForm.reason.trim() || null }))
|
|
|
|
|
releaseDialogOpen.value = false
|
|
|
|
|
ElMessage.success('确认放舱成功')
|
|
|
|
|
} catch (cause) { actionError.value = getApiErrorDetails(cause) }
|
|
|
|
|
finally { actionSaving.value = false }
|
|
|
|
|
}
|
|
|
|
|
async function submitChange() {
|
|
|
|
|
if (!detail.value || !changeForm.vesselName.trim() || !changeForm.voyageNo.trim() || !changeForm.reason.trim()) return
|
|
|
|
|
actionSaving.value = true
|
|
|
|
@@ -221,16 +254,14 @@ async function submitChange() {
|
|
|
|
|
}
|
|
|
|
|
async function runAction(action: OceanBookingAction) {
|
|
|
|
|
if (!detail.value || action === 'change-request') { if (action === 'change-request') openChangeDialog(); return }
|
|
|
|
|
if (action === 'release') { openReleaseDialog(); return }
|
|
|
|
|
actionError.value = null
|
|
|
|
|
if (!validateContainerRequirements()) return
|
|
|
|
|
try {
|
|
|
|
|
const reason = await requiredReason(action)
|
|
|
|
|
await ElMessageBox.confirm(`确认执行“${actionLabels[action]}”吗?`, actionLabels[action], { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' })
|
|
|
|
|
actionSaving.value = true
|
|
|
|
|
if (action === 'release') {
|
|
|
|
|
const releaseRequest = { expectedVersion: detail.value.version, billOfLadingNo: form.data.billOfLadingNo || '', vesselName: form.data.vesselName || '', voyageNo: form.data.voyageNo || '', estimatedSailingAt: form.data.estimatedSailingAt || '', estimatedArrivalAt: form.data.estimatedArrivalAt || '', reason }
|
|
|
|
|
applyDetail(await releaseOceanBooking(id.value, releaseRequest))
|
|
|
|
|
} else applyDetail(await performOceanBookingAction(id.value, action, { expectedVersion: detail.value.version, reason }))
|
|
|
|
|
applyDetail(await performOceanBookingAction(id.value, action, { expectedVersion: detail.value.version, reason }))
|
|
|
|
|
ElMessage.success(`${actionLabels[action]}成功`)
|
|
|
|
|
} catch (cause) {
|
|
|
|
|
if (cause !== 'cancel' && !(cause instanceof Error && cause.message === 'cancel')) actionError.value = getApiErrorDetails(cause)
|
|
|
|
@@ -278,14 +309,14 @@ onMounted(() => { void load(); void loadOptions() })
|
|
|
|
|
<section class="booking-group" aria-label="基本信息">
|
|
|
|
|
<h3>基本信息</h3><div class="booking-group__body">
|
|
|
|
|
<el-form-item label="订舱号"><el-input :model-value="detail?.bookingNo || '保存后生成'" disabled /></el-form-item>
|
|
|
|
|
<el-form-item label="客户" prop="customerId"><el-select :model-value="form.data.customerId" filterable placeholder="请选择客户" :loading="optionsLoading" :disabled="!isNew" style="width:100%" @update:model-value="selectCustomer(String($event || ''))"><el-option v-if="form.data.customerId && !selectableCustomers.some((customer) => String(customer.id) === String(form.data.customerId))" :label="form.data.customerName || '当前客户'" :value="String(form.data.customerId)" /><el-option v-for="customer in selectableCustomers" :key="String(customer.id)" :label="customer.legalName?.trim() || customer.tenantName" :value="String(customer.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="客户" prop="customerId"><el-select :model-value="form.data.customerId" filterable placeholder="请选择客户" :loading="optionsLoading" :disabled="!isNew" style="width:100%" @update:model-value="selectCustomer(String($event || ''))"><el-option v-if="form.data.customerId && !selectableCustomers.some((customer) => String(customer.id) === String(form.data.customerId))" :label="form.data.customerName || '客户名称缺失'" :value="String(form.data.customerId)" /><el-option v-for="customer in selectableCustomers" :key="String(customer.id)" :label="customer.tenantName" :value="String(customer.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="状态"><el-input :model-value="detail ? statusLabel(detail.status) : '待创建'" disabled /></el-form-item>
|
|
|
|
|
<el-form-item label="渠道编码" prop="channelCode"><el-input v-model="form.data.channelCode" placeholder="请输入渠道编码" /></el-form-item>
|
|
|
|
|
<el-form-item label="渠道名称" prop="channelName"><el-input v-model="form.data.channelName" placeholder="请输入渠道名称" /></el-form-item>
|
|
|
|
|
<el-form-item label="装柜仓库" prop="warehouseId"><el-select :model-value="form.data.warehouseId" filterable placeholder="请选择装柜仓库" style="width:100%" @update:model-value="selectWarehouse"><el-option v-for="warehouse in warehouses" :key="String(warehouse.id)" :label="warehouse.warehouseName" :value="String(warehouse.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="提单号"><el-input v-model="form.data.billOfLadingNo" placeholder="请输入提单号(可选)" /></el-form-item>
|
|
|
|
|
<el-form-item label="船公司"><el-select :model-value="form.data.shippingCompanyId" filterable clearable placeholder="请选择船公司" :loading="optionsLoading" style="width:100%" @update:model-value="selectPartner('shippingCompany', String($event || ''))"><el-option v-if="form.data.shippingCompanyId && !shippingCompanies.some((partner) => String(partner.id) === String(form.data.shippingCompanyId))" :label="form.data.shippingCompanyName || '当前船公司'" :value="String(form.data.shippingCompanyId)" /><el-option v-for="partner in shippingCompanies" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="订舱代理"><el-select :model-value="form.data.bookingAgentId" filterable clearable placeholder="请选择订舱代理" :loading="optionsLoading" style="width:100%" @update:model-value="selectPartner('bookingAgent', String($event || ''))"><el-option v-if="form.data.bookingAgentId && !bookingAgents.some((partner) => String(partner.id) === String(form.data.bookingAgentId))" :label="form.data.bookingAgentName || '当前订舱代理'" :value="String(form.data.bookingAgentId)" /><el-option v-for="partner in bookingAgents" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="船公司"><el-select :model-value="form.data.shippingCompanyId" filterable clearable placeholder="请选择船公司" :loading="optionsLoading" style="width:100%" @update:model-value="selectPartner('shippingCompany', String($event || ''))"><el-option v-if="form.data.shippingCompanyId && !shippingCompanies.some((partner) => String(partner.id) === String(form.data.shippingCompanyId))" :label="form.data.shippingCompanyName || '船公司名称缺失'" :value="String(form.data.shippingCompanyId)" /><el-option v-for="partner in shippingCompanies" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" /></el-select></el-form-item>
|
|
|
|
|
<el-form-item label="订舱代理"><el-select :model-value="form.data.bookingAgentId" filterable clearable placeholder="请选择订舱代理" :loading="optionsLoading" style="width:100%" @update:model-value="selectPartner('bookingAgent', String($event || ''))"><el-option v-if="form.data.bookingAgentId && !bookingAgents.some((partner) => String(partner.id) === String(form.data.bookingAgentId))" :label="form.data.bookingAgentName || '订舱代理名称缺失'" :value="String(form.data.bookingAgentId)" /><el-option v-for="partner in bookingAgents" :key="String(partner.id)" :label="partner.partnerName" :value="String(partner.id)" /></el-select></el-form-item>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
<section class="booking-group" aria-label="航线船期">
|
|
|
|
@@ -348,6 +379,18 @@ onMounted(() => { void load(); void loadOptions() })
|
|
|
|
|
<el-form-item label="预计到港"><el-date-picker v-model="changeForm.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到港时间" style="width:100%" /></el-form-item>
|
|
|
|
|
<el-form-item label="变更原因" required><el-input v-model="changeForm.reason" type="textarea" placeholder="请说明改船原因" /></el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<LoadErrorState v-if="actionError" compact title="提交失败" :message="actionError.message" :show-retry="false"><template #details><ApiErrorMeta :details="actionError" /></template></LoadErrorState>
|
|
|
|
|
</AppDialog>
|
|
|
|
|
<AppDialog v-model="releaseDialogOpen" title="确认放舱" width="min(560px, 94vw)" :confirm-loading="actionSaving" :confirm-disabled="!releaseForm.billOfLadingNo.trim() || !releaseForm.vesselName.trim() || !releaseForm.voyageNo.trim() || !releaseForm.estimatedSailingAt || !releaseForm.estimatedArrivalAt" confirm-text="确认放舱" cancel-text="取消" @confirm="submitRelease">
|
|
|
|
|
<el-form label-position="top">
|
|
|
|
|
<el-form-item label="提单号" required><el-input v-model="releaseForm.billOfLadingNo" maxlength="128" placeholder="请输入提单号" /></el-form-item>
|
|
|
|
|
<el-form-item label="船名" required><el-input v-model="releaseForm.vesselName" maxlength="128" placeholder="请输入船名" /></el-form-item>
|
|
|
|
|
<el-form-item label="航次" required><el-input v-model="releaseForm.voyageNo" maxlength="64" placeholder="请输入航次" /></el-form-item>
|
|
|
|
|
<el-form-item label="预计开船" required><el-date-picker v-model="releaseForm.estimatedSailingAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计开船时间" style="width:100%" /></el-form-item>
|
|
|
|
|
<el-form-item label="预计到港" required><el-date-picker v-model="releaseForm.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到港时间" style="width:100%" /></el-form-item>
|
|
|
|
|
<el-form-item label="备注"><el-input v-model="releaseForm.reason" type="textarea" maxlength="1000" placeholder="请输入放舱备注(可选)" /></el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<LoadErrorState v-if="actionError" compact title="放舱失败" :message="actionError.message" :show-retry="false"><template #details><ApiErrorMeta :details="actionError" /></template></LoadErrorState>
|
|
|
|
|
</AppDialog>
|
|
|
|
|
</AdminPage>
|
|
|
|
|
</template>
|
|
|
|
|