This commit is contained in:
2026-11-02 17:15:37 +08:00
parent 7704532e8f
commit f8408b8076
26 changed files with 595 additions and 52 deletions
@@ -13,6 +13,7 @@ import { getDispatch } from '../api/dispatches'
import type { DispatchDetail } from '../dispatchContracts'
import DeliveryDocumentsDialog from './DeliveryDocumentsDialog.vue'
import DispatchLoadingDocuments from './DispatchLoadingDocuments.vue'
import DispatchLooseboardExecution from './DispatchLooseboardExecution.vue'
const open = defineModel<boolean>({ default: false })
const props = defineProps<{ dispatchNo: string }>()
@@ -22,10 +23,14 @@ const detail = ref<DispatchDetail | null>(null), loading = ref(false), error = r
const tab = ref('RECEIPT'), selected = ref('')
const editor = ref<InstanceType<typeof DeliveryDocumentsDialog>>()
const loadingEditor = ref<InstanceType<typeof DispatchLoadingDocuments>>()
const looseboardEditor = ref<InstanceType<typeof DispatchLooseboardExecution>>()
const directLooseboard = ref(false)
const hasLooseboard = computed(() => detail.value?.orderDetails.some(row => row.sourceBusinessType === 'LOCAL_LOOSEBOARD') === true)
const tabs = computed(() => [...(hasLooseboard.value ? [{ key: 'LOOSEBOARD', label: '散板取货与送达' }] : []), ...(!directLooseboard.value ? [{ key: 'RECEIPT', label: '签收文件' }, { key: 'LOADING', label: '装车资料' }] : [])])
const deliveries = computed(() => detail.value?.documents.deliveries || [])
const beforeDeparture = computed(() => ['LOADING', 'AFTER_LOADING'].includes(detail.value?.stage || ''))
const delivery = computed(() => deliveries.value.find(item => item.deliveryNo === selected.value))
const busy = computed(() => Boolean(editor.value?.busy || loadingEditor.value?.busy))
const busy = computed(() => Boolean(editor.value?.busy || loadingEditor.value?.busy || looseboardEditor.value?.busy))
let sequence = 0
onBeforeUnmount(() => { sequence++ })
async function load() {
@@ -36,6 +41,7 @@ async function load() {
if (value.dispatchNo !== props.dispatchNo) throw new Error('资料与当前排车单不一致')
if (value.status !== 'ACTIVE' || !['LOADING', 'AFTER_LOADING', 'DELIVERING', 'DELIVERED'].includes(value.stage)) throw new Error('进入装车中后可管理资料')
if (!detail.value && ['LOADING', 'AFTER_LOADING'].includes(value.stage)) tab.value = 'LOADING'
if (!detail.value && value.orderDetails.some(row => row.sourceBusinessType === 'LOCAL_LOOSEBOARD')) tab.value = 'LOOSEBOARD'
detail.value = value
if (!value.documents.deliveries.some(item => item.deliveryNo === selected.value)) selected.value = value.documents.deliveries[0]?.deliveryNo || ''
} catch (cause) { if (current === sequence) error.value = getApiErrorDetails(cause, '排车资料读取失败') }
@@ -43,7 +49,7 @@ async function load() {
}
async function canLeave() {
if (busy.value) return false
if (!editor.value?.dirty) return true
if (!editor.value?.dirty && !looseboardEditor.value?.dirty) return true
try { await ElMessageBox.confirm('有尚未保存的派送资料,是否放弃修改?', '未保存资料', { customClass: 'admin-density-confirm admin-workbench is-dialog', confirmButtonText: '放弃修改', cancelButtonText: '继续编辑' }); return true } catch { return false }
}
async function selectDelivery(value: string) { if (value !== selected.value && await canLeave()) selected.value = value }
@@ -52,7 +58,7 @@ function selectTab(value: string) { if (!busy.value) { tab.value = value } }
function saved() { emit('saved') }
async function loadingSaved() { emit('saved'); await load() }
watch(() => [open.value, props.dispatchNo] as const, ([visible]) => {
sequence++; detail.value = null; selected.value = ''; tab.value = 'RECEIPT'; error.value = null
sequence++; detail.value = null; selected.value = ''; tab.value = 'RECEIPT'; error.value = null; directLooseboard.value = false
if (visible && props.dispatchNo) void load()
}, { immediate: true })
</script>
@@ -60,14 +66,15 @@ watch(() => [open.value, props.dispatchNo] as const, ([visible]) => {
<AppDialog v-model="open" class="admin-density admin-density-dialog admin-workbench is-dialog" :title="'管理资料 · ' + (dispatchNo || '—')" width="min(1100px, calc(100vw - 40px))" fullscreenable :loading="loading" :confirm-loading="busy" :before-close="beforeClose" :show-confirm="tab === 'RECEIPT' && Boolean(editor?.canEdit)" :confirm-disabled="loading || editor?.loading || editor?.conflict" confirm-text="保存资料" cancel-text="关闭" @confirm="editor?.save()">
<LoadErrorState v-if="error" compact title="排车资料读取失败" :message="error.message" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
<template v-if="detail">
<div class="density-status-tabs" role="tablist" aria-label="排车资料类别"><button v-for="item in [{ key: 'RECEIPT', label: '签收文件' }, { key: 'LOADING', label: '装车资料' }]" :key="item.key" role="tab" :aria-selected="tab === item.key" :disabled="busy" :class="{ 'is-active': tab === item.key }" @click="selectTab(item.key)">{{ item.label }}</button></div>
<div class="density-status-tabs" role="tablist" aria-label="排车资料类别"><button v-for="item in tabs" :key="item.key" role="tab" :aria-selected="tab === item.key" :disabled="busy" :class="{ 'is-active': tab === item.key }" @click="selectTab(item.key)">{{ item.label }}</button></div>
<DispatchLooseboardExecution v-if="hasLooseboard" v-show="tab === 'LOOSEBOARD'" ref="looseboardEditor" :key="dispatchNo" :dispatch-no="dispatchNo" @mode="directLooseboard = $event" @saved="loadingSaved" />
<DispatchLoadingDocuments v-if="tab === 'LOADING'" ref="loadingEditor" :key="dispatchNo" :dispatch-no="dispatchNo" @saved="loadingSaved" />
<div v-show="tab === 'RECEIPT'" v-if="!beforeDeparture">
<el-form v-if="deliveries.length > 1" class="density-form" label-position="left" label-width="80px"><el-form-item label="派送单"><el-select :model-value="selected" :disabled="busy || !access.can(permission.deliveryList)" aria-label="选择派送单" @update:model-value="selectDelivery"><el-option v-for="item in deliveries" :key="item.deliveryNo" :value="item.deliveryNo" :label="item.deliveryNo" /></el-select></el-form-item></el-form>
<DeliveryDocumentsDialog v-if="delivery && access.can(permission.deliveryList)" ref="editor" :key="delivery.customerId + ':' + delivery.deliveryNo" :model-value="open" embedded :delivery-no="delivery.deliveryNo" :customer-id="delivery.customerId" :categories="['RECEIPT']" @saved="saved" />
<p v-else>{{ access.can(permission.deliveryList) ? '暂无关联派送单' : '当前账号无派送资料查看权限' }}</p>
</div>
<p v-if="beforeDeparture && tab !== 'LOADING'">完成装车并补齐装车资料后,进入派送中管理签收文件。</p>
<p v-if="beforeDeparture && tab === 'RECEIPT'">完成装车并补齐装车资料后,进入派送中管理签收文件。</p>
</template>
</AppDialog>
</template>