feat(admin): validate and cancel co-load groups
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
||||
type UnifiedOrderMainStatus,
|
||||
type UnifiedOrderResource,
|
||||
} from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { createPlatformCoLoadGroup, getPlatformCoLoadGroupByInboundCode, type CoLoadBusinessSummary, type CoLoadGroup } from '@/modules/unified-orders/api/coLoadGroups'
|
||||
import { cancelPlatformCoLoadGroup, createPlatformCoLoadGroup, getPlatformCoLoadGroupByInboundCode, type CoLoadBusinessSummary, type CoLoadGroup } from '@/modules/unified-orders/api/coLoadGroups'
|
||||
import OrderFulfillmentTreePanel from '@/modules/unified-orders/components/OrderFulfillmentTreePanel.vue'
|
||||
import InboundFeeDetailManager from '@/modules/unified-orders/components/InboundFeeDetailManager.vue'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
@@ -131,6 +131,8 @@ const coLoadDetailInboundCode = ref('')
|
||||
const coLoadDetailSummary = ref<CoLoadBusinessSummary | null>(null)
|
||||
const coLoadDetailLoading = ref(false)
|
||||
const coLoadDetailError = ref<ApiErrorDetails | null>(null)
|
||||
const coLoadCancelError = ref<ApiErrorDetails | null>(null)
|
||||
const coLoadCancelSaving = ref(false)
|
||||
const coLoadSaving = ref(false)
|
||||
const coLoadError = ref<ApiErrorDetails | null>(null)
|
||||
const approvalOrder = ref<UnifiedOrderResource | null>(null)
|
||||
@@ -813,6 +815,19 @@ function handleOrderSelectionChange(rows: UnifiedOrderResource[]) {
|
||||
selectedApprovalOrders.value = rows.filter(canApprove)
|
||||
}
|
||||
|
||||
const coLoadSelectionProblem = computed(() => {
|
||||
if (selectedOrders.value.length < 2) return null
|
||||
if (selectedOrders.value.some((row) => row.coLoadGroup)) return '所选订单中已有同柜组,请先解除原同柜组'
|
||||
const warehouseIds = selectedOrders.value.map((row) => String(row.receivingWarehouseId || '').trim())
|
||||
if (warehouseIds.some((value) => !value)) return '所选订单缺少平台收货仓,请刷新后重试'
|
||||
if (new Set(warehouseIds).size > 1) return '所选订单的平台收货仓不同,不能设为同柜组'
|
||||
const channels = selectedOrders.value.map((row) => String(row.channelName || '').trim())
|
||||
if (channels.some((value) => !value)) return '所选订单缺少渠道,请刷新后重试'
|
||||
if (new Set(channels).size > 1) return '所选订单的渠道不同,不能设为同柜组'
|
||||
return null
|
||||
})
|
||||
const canCreateCoLoadGroup = computed(() => selectedOrders.value.length >= 2 && !coLoadSelectionProblem.value)
|
||||
|
||||
function coLoadGroup(row: UnifiedOrderResource) {
|
||||
return row.coLoadGroup || null
|
||||
}
|
||||
@@ -860,18 +875,22 @@ function openCoLoadDetail(row: UnifiedOrderResource) {
|
||||
coLoadDetailInboundCode.value = row.inboundCode?.trim() || ''
|
||||
coLoadDetailOpen.value = true
|
||||
coLoadDetailSummary.value = null
|
||||
coLoadCancelError.value = null
|
||||
coLoadDetailError.value = coLoadDetailInboundCode.value ? null : getApiErrorDetails(new Error('同柜组成员入仓码尚未返回'), '同柜组成员入仓码尚未返回,请刷新后重试')
|
||||
if (coLoadDetailInboundCode.value) void loadCoLoadDetail()
|
||||
}
|
||||
|
||||
function openCoLoadCreate() {
|
||||
if (selectedOrders.value.length < 2) return
|
||||
if (!canCreateCoLoadGroup.value) {
|
||||
if (coLoadSelectionProblem.value) ElMessage.warning(coLoadSelectionProblem.value)
|
||||
return
|
||||
}
|
||||
coLoadError.value = null
|
||||
coLoadCreateOpen.value = true
|
||||
}
|
||||
|
||||
async function createCoLoadGroup() {
|
||||
if (selectedOrders.value.length < 2 || coLoadSaving.value) return
|
||||
if (!canCreateCoLoadGroup.value || coLoadSaving.value) return
|
||||
coLoadSaving.value = true
|
||||
coLoadError.value = null
|
||||
try {
|
||||
@@ -886,6 +905,33 @@ async function createCoLoadGroup() {
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelCoLoadGroup() {
|
||||
const inboundCode = coLoadDetailInboundCode.value
|
||||
if (!inboundCode || coLoadDetailSummary.value?.status !== 'ACTIVE' || coLoadCancelSaving.value) return
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确认解除整个同柜组吗?解除后这些订单可重新选择成员建组;不会拆分订单或子单。',
|
||||
'解除同柜组',
|
||||
{ confirmButtonText: '确认解除', cancelButtonText: '取消', type: 'warning' },
|
||||
)
|
||||
} catch (error) {
|
||||
if (error === 'cancel' || error === 'close') return
|
||||
throw error
|
||||
}
|
||||
coLoadCancelSaving.value = true
|
||||
coLoadCancelError.value = null
|
||||
try {
|
||||
await cancelPlatformCoLoadGroup(inboundCode, '重新调整同柜组成员')
|
||||
coLoadDetailOpen.value = false
|
||||
ElMessage.success('同柜组已整组解除')
|
||||
await loadOrders()
|
||||
} catch (error) {
|
||||
coLoadCancelError.value = getApiErrorDetails(error, '解除同柜组失败')
|
||||
} finally {
|
||||
coLoadCancelSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function batchApproveOrders() {
|
||||
const selected = [...selectedApprovalOrders.value]
|
||||
if (!selected.length || batchApprovalSaving.value) return
|
||||
@@ -1372,7 +1418,10 @@ onBeforeUnmount(() => {
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="records" :columns="columns" preference-key="unified-orders-table-v2" :loading="loading" :total="total" :page-sizes="[20, 50, 100]" row-key="orderId" border stripe empty-text="暂无符合条件的订单" aria-label="统一订单列表" @selection-change="handleOrderSelectionChange">
|
||||
<template #toolbar-actions>
|
||||
<el-button v-if="can('platform:order:approve')" type="success" :loading="batchApprovalSaving" :disabled="loading || !selectedApprovalOrders.length" @click="batchApproveOrders"><CircleCheck :size="16" />批量审核通过<span v-if="selectedApprovalOrders.length">({{ selectedApprovalOrders.length }})</span></el-button>
|
||||
<el-button v-if="can('platform:firstleg:smart-loading:assign')" type="primary" plain :loading="coLoadSaving" :disabled="loading || selectedOrders.length < 2" @click="openCoLoadCreate"><Link2 :size="16" />设为同柜不可拆组<span v-if="selectedOrders.length">({{ selectedOrders.length }})</span></el-button>
|
||||
<div v-if="can('platform:firstleg:smart-loading:assign')" class="coload-toolbar-action">
|
||||
<el-button type="primary" plain :loading="coLoadSaving" :disabled="loading || !canCreateCoLoadGroup" :title="coLoadSelectionProblem || undefined" @click="openCoLoadCreate"><Link2 :size="16" />设为同柜不可拆组<span v-if="selectedOrders.length">({{ selectedOrders.length }})</span></el-button>
|
||||
<small v-if="selectedOrders.length >= 2 && coLoadSelectionProblem" role="status">{{ coLoadSelectionProblem }}</small>
|
||||
</div>
|
||||
<el-button v-if="can('platform:firstleg:smart-loading:list')" type="primary" @click="smartLoadingOpen = true"><PackageOpen :size="16" />智能装柜</el-button>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
@@ -1420,12 +1469,12 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
</ListPageLayout>
|
||||
<SmartLoadingDialog v-model="smartLoadingOpen" />
|
||||
<AppDialog v-model="coLoadCreateOpen" title="设为同柜不可拆组" confirm-text="确认建组" :confirm-loading="coLoadSaving" :confirm-disabled="selectedOrders.length < 2" @confirm="createCoLoadGroup">
|
||||
<AppDialog v-model="coLoadCreateOpen" title="设为同柜不可拆组" confirm-text="确认建组" :confirm-loading="coLoadSaving" :confirm-disabled="!canCreateCoLoadGroup" @confirm="createCoLoadGroup">
|
||||
<el-alert type="warning" :closable="false" show-icon :title="`所选 ${selectedOrders.length} 票须属于同一国内仓、同一渠道,并全量进入同一柜;目的仓或目的港可以不同。资格由服务端按完整入仓片段校验。`" />
|
||||
<div class="coload-selection"><span v-for="row in selectedOrders" :key="String(row.orderId)">{{ row.inboundCode || '待生成入仓码' }}</span></div>
|
||||
<div v-if="coLoadError" class="coload-error" role="alert"><span>{{ coLoadError.message }}</span><ApiErrorMeta :details="coLoadError" /></div>
|
||||
</AppDialog>
|
||||
<AppDialog v-model="coLoadDetailOpen" title="同柜组成员" :show-confirm="false" cancel-text="关闭" width="min(720px, 94vw)" :loading="coLoadDetailLoading">
|
||||
<AppDialog v-model="coLoadDetailOpen" title="同柜组成员" :show-confirm="false" cancel-text="关闭" width="min(720px, 94vw)" :loading="coLoadDetailLoading" :confirm-loading="coLoadCancelSaving">
|
||||
<LoadErrorState v-if="coLoadDetailError" title="同柜组成员加载失败" :message="coLoadDetailError.message" :loading="coLoadDetailLoading" @retry="loadCoLoadDetail"><template #details><ApiErrorMeta :details="coLoadDetailError" /></template></LoadErrorState>
|
||||
<template v-else-if="coLoadDetailSummary">
|
||||
<div class="coload-detail-summary"><el-tag size="small" :type="coLoadDetailSummary.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroupSummary(coLoadDetailSummary) }}</el-tag><span>{{ coLoadDetailSummary.memberCount }} 个成员</span></div>
|
||||
@@ -1437,7 +1486,9 @@ onBeforeUnmount(() => {
|
||||
</table>
|
||||
</div>
|
||||
<el-empty v-else description="暂无同柜组成员" :image-size="56" />
|
||||
<div v-if="coLoadCancelError" class="coload-error" role="alert"><span>{{ coLoadCancelError.message }}</span><ApiErrorMeta :details="coLoadCancelError" /></div>
|
||||
</template>
|
||||
<template #footer="{ close }"><div class="app-dialog__footer"><el-button :disabled="coLoadCancelSaving" @click="close">关闭</el-button><el-button v-if="coLoadDetailSummary?.status === 'ACTIVE' && can('platform:firstleg:smart-loading:assign')" type="danger" :loading="coLoadCancelSaving" @click="cancelCoLoadGroup">解除同柜组</el-button></div></template>
|
||||
</AppDialog>
|
||||
</template>
|
||||
|
||||
@@ -1522,6 +1573,8 @@ onBeforeUnmount(() => {
|
||||
.coload-detail-table td strong,.coload-detail-table td span { display: block; overflow-wrap: anywhere; }
|
||||
.coload-detail-table td span { color: var(--cc-muted); }
|
||||
.coload-detail-table :deep(.el-button) { min-height: 24px; padding: 2px 0; }
|
||||
.coload-toolbar-action { display: flex; min-width: 0; align-items: center; gap: 8px; }
|
||||
.coload-toolbar-action small { max-width: 360px; overflow: hidden; color: var(--el-color-danger); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.subsequent-fulfillment { min-height: 72px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--cc-border); }
|
||||
.subsequent-fulfillment h3 { margin: 0 0 10px; font-size: 15px; }
|
||||
.fulfillment-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0 0 10px; border: 1px solid var(--cc-border); border-radius: 6px; overflow: hidden; }
|
||||
|
||||
Reference in New Issue
Block a user