76 lines
6.4 KiB
Vue
76 lines
6.4 KiB
Vue
<script setup lang="ts">
|
||
import '@/styles/adminDensity.css'
|
||
import '@/styles/adminWorkbench.css'
|
||
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
||
import { ElMessageBox } from 'element-plus'
|
||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||
import { overseasWarehousePermissions as permission } from '../permissions'
|
||
import { getDispatch } from '../api/dispatches'
|
||
import type { DispatchDetail } from '../dispatchContracts'
|
||
import DeliveryDocumentsDialog from './DeliveryDocumentsDialog.vue'
|
||
import DispatchVehicleDocuments from './DispatchVehicleDocuments.vue'
|
||
import DispatchLoadingDocuments from './DispatchLoadingDocuments.vue'
|
||
|
||
const open = defineModel<boolean>({ default: false })
|
||
const props = defineProps<{ dispatchNo: string }>()
|
||
const emit = defineEmits<{ saved: [] }>()
|
||
const access = useAccessPreviewStore()
|
||
const detail = ref<DispatchDetail | null>(null), loading = ref(false), error = ref<ApiErrorDetails | null>(null)
|
||
const tab = ref('RECEIPT'), selected = ref(''), fuelVisited = ref(false)
|
||
const editor = ref<InstanceType<typeof DeliveryDocumentsDialog>>(), vehicleEditor = ref<InstanceType<typeof DispatchVehicleDocuments>>()
|
||
const loadingEditor = ref<InstanceType<typeof DispatchLoadingDocuments>>()
|
||
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 || vehicleEditor.value?.busy || loadingEditor.value?.busy))
|
||
let sequence = 0
|
||
onBeforeUnmount(() => { sequence++ })
|
||
async function load() {
|
||
const current = ++sequence; loading.value = true; error.value = null
|
||
try {
|
||
const value = await getDispatch(props.dispatchNo)
|
||
if (current !== sequence || !open.value) return
|
||
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'
|
||
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, '排车资料读取失败') }
|
||
finally { if (current === sequence) loading.value = false }
|
||
}
|
||
async function canLeave() {
|
||
if (busy.value) return false
|
||
if (!editor.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 }
|
||
async function beforeClose(done: () => void) { if (await canLeave()) done() }
|
||
function selectTab(value: string) { if (!busy.value) { tab.value = value; if (value === 'FUEL') fuelVisited.value = true } }
|
||
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'; fuelVisited.value = false; error.value = null
|
||
if (visible && props.dispatchNo) void load()
|
||
}, { immediate: true })
|
||
</script>
|
||
<template>
|
||
<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: 'FUEL', 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>
|
||
<DispatchLoadingDocuments v-if="tab === 'LOADING'" ref="loadingEditor" :key="dispatchNo" :dispatch-no="dispatchNo" @saved="loadingSaved" />
|
||
<div v-show="tab === 'RECEIPT'" v-if="!beforeDeparture">
|
||
<el-form 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'">完成装车并补齐装车资料后,进入派送中管理{{ tab === 'FUEL' ? '油耗图片' : '签收文件' }}。</p>
|
||
<DispatchVehicleDocuments v-if="fuelVisited && !beforeDeparture" v-show="tab === 'FUEL'" ref="vehicleEditor" :key="dispatchNo" :dispatch-no="dispatchNo" @saved="saved" />
|
||
</template>
|
||
</AppDialog>
|
||
</template>
|