fix: align audit and dispatch workflows and release failed requests
This commit is contained in:
@@ -20,7 +20,6 @@ import { dispatchActionPermissions } from '../dispatchPresentation'
|
||||
import { useDispatchCommandRecovery, validateCurrentDispatch } from '../dispatchCommandRecovery'
|
||||
import type { InboundOrderListItem } from '../types'
|
||||
import SmartDispatchCargoBoard from './SmartDispatchCargoBoard.vue'
|
||||
import SmartDispatchExistingCargo from './SmartDispatchExistingCargo.vue'
|
||||
import { matchesSmartDispatchWarehouseTab, type SmartDispatchWarehouseTab } from '../smartDispatchWarehouseTab'
|
||||
|
||||
const props = defineProps<{ modelValue: boolean; orders: InboundOrderListItem[] }>()
|
||||
@@ -30,8 +29,6 @@ const recovery = useDispatchCommandRecovery(() => auth.user?.id || '')
|
||||
const owner = auth.user?.id || '', storageKey = `dispatch-draft-command:${owner}`
|
||||
type DraftCommand = { action: 'CREATE' | 'CONTAINER' | 'LOCK' | 'UNLOCK' | 'DELETE' | 'RELEASE' | 'CONVERT'; body: Record<string, unknown>; containerId?: string; priorContainerIds?: string[] }
|
||||
const pending = ref<DraftCommand | null>(null)
|
||||
if (owner) sessionStorage.removeItem(storageKey)
|
||||
const workspace = ref<'create' | 'existing'>('create'), existingBusy = ref(false)
|
||||
const settingsExpanded = ref(false)
|
||||
const mode = ref<'cargo' | 'empty'>('cargo'), warehouseId = ref(''), warehouses = ref<PlatformWarehouseResource[]>([])
|
||||
const draft = ref<DispatchDraft | null>(null), drafts = ref<DispatchDraft[]>([]), activeContainerId = ref(''), capacity = ref(30)
|
||||
@@ -44,8 +41,6 @@ const warehouseTab = ref<SmartDispatchWarehouseTab>('ALL')
|
||||
const publishLooseboardDemand = ref(false)
|
||||
const frozen = computed(() => !canCreate.value || preparing.value || saving.value || recovery.busy.value)
|
||||
const canCreate = computed(() => access.can(dispatchActionPermissions.CREATE))
|
||||
const canEditCargo = computed(() => access.can(dispatchActionPermissions.ADD_CARGO))
|
||||
const workspaceFrozen = computed(() => loading.value || preparing.value || saving.value || existingBusy.value || recovery.busy.value)
|
||||
const planIds = computed(() => [...new Set(props.orders.map(row => row.planId).filter(Boolean))])
|
||||
const currentContainer = computed(() => draft.value?.containers.find(row => row.draftContainerId === activeContainerId.value))
|
||||
const populatedGroupCount = computed(() => draft.value?.containers.filter(row => row.sourceKeys.length).length || 0)
|
||||
@@ -107,8 +102,7 @@ async function init() {
|
||||
const sequence = ++generation
|
||||
working.value = true; error.value = null
|
||||
try {
|
||||
if (!canCreate.value && canEditCargo.value) workspace.value = 'existing'
|
||||
if (workspace.value === 'existing' || !canCreate.value) return
|
||||
if (!canCreate.value) return
|
||||
const [directory, saved] = await Promise.all([listAllPlatformWarehouses({ warehouseType: 'OVERSEAS', status: 'ACTIVE' }), api.listDispatchDrafts()])
|
||||
if (sequence !== generation) return
|
||||
warehouses.value = directory; drafts.value = saved
|
||||
@@ -124,13 +118,6 @@ async function init() {
|
||||
} catch (cause) { if (sequence === generation) error.value = getApiErrorDetails(cause, cause instanceof DispatchInputError ? cause.message : '排车候选或草稿恢复失败,请保留原选择重试') }
|
||||
finally { if (sequence === generation) working.value = false }
|
||||
}
|
||||
function switchWorkspace(value: 'create' | 'existing') {
|
||||
if (workspaceFrozen.value || value === workspace.value || (value === 'create' ? !canCreate.value : !canEditCargo.value)) return
|
||||
workspace.value = value
|
||||
existingBusy.value = false
|
||||
candidatePage.reset()
|
||||
void init()
|
||||
}
|
||||
async function loadCandidates(sequence = generation) {
|
||||
if (sequence !== generation) return
|
||||
const candidateWarehouse = draft.value?.warehouseId
|
||||
@@ -219,20 +206,9 @@ async function retryDraft() {
|
||||
if (draft.value) warehouseId.value = draft.value.warehouseId
|
||||
activeContainerId.value = addedContainerId || draft.value?.containers.find(row => row.draftContainerId === activeContainerId.value)?.draftContainerId || draft.value?.containers[0]?.draftContainerId || ''
|
||||
if (command.action !== 'CREATE' && command.action !== 'CONTAINER') await loadCandidates()
|
||||
} catch (cause) {
|
||||
// 请求失败后读取服务器最新草稿,保留本地时间和备注,后续点击按当前版本重新提交。
|
||||
if (command.body.draftToken) {
|
||||
try {
|
||||
const saved = await api.listDispatchDrafts()
|
||||
drafts.value = saved; draft.value = saved.find(row => row.draftToken === command.body.draftToken) || null
|
||||
activeContainerId.value = draft.value?.containers.find(row => row.draftContainerId === activeContainerId.value)?.draftContainerId || draft.value?.containers[0]?.draftContainerId || ''
|
||||
} catch { /* 刷新失败只保留原错误,不锁住其他操作。 */ }
|
||||
}
|
||||
throw cause
|
||||
} finally { pending.value = null; saving.value = false }
|
||||
}
|
||||
async function submit() {
|
||||
if (workspace.value === 'existing') return
|
||||
if (!canCreate.value || preparing.value || saving.value || loading.value) return
|
||||
error.value = null
|
||||
try {
|
||||
@@ -246,13 +222,7 @@ async function submit() {
|
||||
watch(() => props.modelValue, value => { if (value) { publishLooseboardDemand.value = false; void init() } else { generation++; candidatePage.reset() } }, { immediate: true })
|
||||
</script>
|
||||
<template>
|
||||
<AppDialog class="admin-density-dialog admin-density admin-workbench is-dialog smart-dispatch-dialog" :class="{ 'is-cargo-workspace': workspace === 'create' && mode === 'cargo' }" append-to-body :lock-scroll="true" :model-value="modelValue" title="智能排车" width="min(1800px, calc(100vw - 40px))" fullscreenable default-fullscreen :loading="working" :show-confirm="workspace === 'create'" :confirm-loading="saving || recovery.busy.value || existingBusy" :confirm-disabled="confirmDisabled" :confirm-text="mode === 'empty' ? '保存空排车单' : '生成排车单'" @update:model-value="emit('update:modelValue', $event)" @confirm="submit">
|
||||
<div class="density-status-tabs" role="tablist" aria-label="智能排车工作方式">
|
||||
<button v-if="canCreate" type="button" role="tab" :aria-selected="workspace === 'create'" :class="{ 'is-active': workspace === 'create' }" :disabled="workspaceFrozen" @click="switchWorkspace('create')">新建排车</button>
|
||||
<button v-if="canEditCargo" type="button" role="tab" :aria-selected="workspace === 'existing'" :class="{ 'is-active': workspace === 'existing' }" :disabled="workspaceFrozen" @click="switchWorkspace('existing')">调整已有排车货物</button>
|
||||
</div>
|
||||
<SmartDispatchExistingCargo v-if="workspace === 'existing' && modelValue" @saved="emit('created')" @busy="existingBusy = $event" />
|
||||
<template v-else-if="workspace === 'create'">
|
||||
<AppDialog class="admin-density-dialog admin-density admin-workbench is-dialog smart-dispatch-dialog" :class="{ 'is-cargo-workspace': mode === 'cargo' }" append-to-body :lock-scroll="true" :model-value="modelValue" title="智能排车" width="min(1800px, calc(100vw - 40px))" fullscreenable default-fullscreen :loading="working" :confirm-loading="saving || recovery.busy.value" :confirm-disabled="confirmDisabled" :confirm-text="mode === 'empty' ? '保存空排车单' : '生成排车单'" @update:model-value="emit('update:modelValue', $event)" @confirm="submit">
|
||||
<div class="density-dialog-sections dispatch-workspace">
|
||||
<LoadErrorState v-if="error" title="排车未完成" :message="error.message" :loading="loading || saving || recovery.busy.value" @retry="init()"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
|
||||
<section class="density-dialog-section dispatch-search" aria-label="候选货物查询">
|
||||
@@ -283,7 +253,6 @@ watch(() => props.modelValue, value => { if (value) { publishLooseboardDemand.va
|
||||
</SmartDispatchCargoBoard>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</AppDialog>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user