fix: align audit and dispatch workflows and release failed requests

This commit is contained in:
2026-10-22 23:10:09 +08:00
parent 9cdcb9c077
commit 4a3456756f
124 changed files with 1296 additions and 2120 deletions
@@ -30,6 +30,7 @@ import {
listWarehouseLogisticsChannels,
updateOceanBookingDraft,
updateOceanBookingActualCargo,
updateOceanBookingEstimatedArrival,
type OceanLogisticsChannel,
} from '../api/platformOceanBookings'
import type {
@@ -115,9 +116,14 @@ const canPrint = computed(() => !!detail.value && access.can('platform:firstleg:
const canManageDocuments = computed(() => detail.value?.status === 'DRAFT' && access.can('platform:firstleg:ocean-booking:edit'))
const documentManageReason = computed(() => !access.can('platform:firstleg:ocean-booking:edit') ? '暂无订舱资料维护权限' : '')
const actualSaving = ref(false)
const arrivalDialogOpen = ref(false)
const arrivalSaving = ref(false)
const arrivalValue = ref<string | null>(null)
const arrivalError = ref<ApiErrorDetails | null>(null)
const canMaintainArrival = computed(() => detail.value?.status === 'RELEASED' && access.can('platform:firstleg:ocean-booking:edit'))
const actualReason = ref('')
const cargoWritable = computed(() => formWritable.value || (detail.value?.status === 'RELEASED' && access.can('platform:firstleg:ocean-booking:edit')))
const workflowBusy = computed(() => saving.value || actionSaving.value || actualSaving.value)
const workflowBusy = computed(() => saving.value || actionSaving.value || actualSaving.value || arrivalSaving.value)
const releaseDialogOpen = ref(false)
const operationSections = ref<string[]>([])
const printOpen = ref(false)
@@ -128,7 +134,7 @@ function statusLabel(status: OceanBookingStatus | string | null | undefined) {
return oceanBookingStatusLabel(status)
}
function historyActionLabel(action: string) {
const labels: Record<string, string> = { CREATE: '创建草稿', UPDATE_DRAFT: '编辑草稿', RELEASE: '确认放舱', UPDATE_ACTUAL_CARGO: '实货量补录/修正', UPLOAD_DOCUMENT: '上传文件', ATTACH_DOCUMENT: '关联文件', INVALIDATE_DOCUMENT: '作废文件' }
const labels: Record<string, string> = { CREATE: '创建草稿', UPDATE_DRAFT: '编辑草稿', RELEASE: '确认放舱', UPDATE_ESTIMATED_ARRIVAL: '维护预计到港', UPDATE_ACTUAL_CARGO: '实货量补录/修正', UPLOAD_DOCUMENT: '上传文件', ATTACH_DOCUMENT: '关联文件', INVALIDATE_DOCUMENT: '作废文件' }
return labels[action] || '资料维护'
}
async function loadOptions() {
@@ -185,6 +191,35 @@ function openPrint(type: OceanBookingPrintType) {
printType.value = type
printOpen.value = true
}
function openArrivalDialog() {
if (!canMaintainArrival.value || workflowBusy.value) return
arrivalValue.value = detail.value?.estimatedArrivalAt || null
arrivalError.value = null
arrivalDialogOpen.value = true
}
async function saveEstimatedArrival() {
const current = detail.value
if (!current || !canMaintainArrival.value || workflowBusy.value) return
if (!arrivalValue.value || (current.estimatedSailingAt && arrivalValue.value < current.estimatedSailingAt)) {
arrivalError.value = { message: !arrivalValue.value ? '请选择预计到港时间' : '预计到港时间不能早于预计开船时间' }
return
}
arrivalSaving.value = true
arrivalError.value = null
try {
const saved = await updateOceanBookingEstimatedArrival(current.id, { expectedVersion: current.version, estimatedArrivalAt: arrivalValue.value })
// 单独保存预计到港不丢弃客服尚未提交的实际货量。
const pendingCargo = { actualPackageCount: form.data.actualPackageCount, actualGrossWeightKg: form.data.actualGrossWeightKg, actualVolumeCbm: form.data.actualVolumeCbm }
const pendingReason = actualReason.value
applyDetail(saved)
Object.assign(form.data, pendingCargo)
actualReason.value = pendingReason
workspace.bumpCacheEpoch('ocean-bookings')
arrivalDialogOpen.value = false
ElMessage.success('预计到港已保存')
} catch (cause) { arrivalError.value = getApiErrorDetails(cause, '预计到港保存失败') }
finally { arrivalSaving.value = false }
}
async function saveActualCargo() {
if (detail.value?.status !== 'RELEASED' || !cargoWritable.value || workflowBusy.value) return
const validationError = cargoValidationError()
@@ -451,6 +486,7 @@ onMounted(() => { void load(); void loadOptions() })
<StatusBadge v-if="detail" :status="statusLabel(detail.status)" />
<el-button v-if="canDelete" type="danger" plain :loading="actionSaving" :disabled="workflowBusy" @click="removeDraft"><Trash2 :size="16" />删除草稿</el-button>
<el-button v-if="canSave" plain :loading="saving" :disabled="workflowBusy" @click="save"><Save :size="16" />保存草稿</el-button>
<el-button v-if="canMaintainArrival" plain :disabled="workflowBusy" @click="openArrivalDialog">维护预计到港</el-button>
</div>
</template>
</PageToolbar>
@@ -614,6 +650,10 @@ onMounted(() => { void load(); void loadOptions() })
</section>
</template>
</DetailPageLayout>
<AppDialog v-if="detail?.status === 'RELEASED'" v-model="arrivalDialogOpen" class="admin-density-dialog admin-workbench is-dialog" :title="bookingDialogTitle('维护预计到港')" width="560px" :confirm-loading="arrivalSaving" :confirm-disabled="!canMaintainArrival" confirm-text="保存" @confirm="saveEstimatedArrival">
<el-form class="density-form" label-position="top"><el-form-item label="预计到港" required><el-date-picker v-model="arrivalValue" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计到港时间" style="width:100%" /></el-form-item></el-form>
<LoadErrorState v-if="arrivalError" compact title="预计到港保存失败" :message="arrivalError.message" :show-retry="false"><template #details><ApiErrorMeta :details="arrivalError" /></template></LoadErrorState>
</AppDialog>
<OceanBookingPrintDialog v-if="detail" v-model="printOpen" :booking-id="detail.id" :booking-no="detail.bookingNo" :allowed="canPrint" :initial-type="printType" @previewed="onPrintPreviewed" />
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="releaseDialogOpen" :title="bookingDialogTitle('确认放舱')" width="min(560px, 94vw)" :confirm-loading="actionSaving" :confirm-disabled="workflowBusy" confirm-text="确认放舱" cancel-text="取消" @confirm="submitRelease">