fix(admin): align coload groups with inbound fragments
This commit is contained in:
@@ -31,6 +31,7 @@ import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||||
import CopyButton from '@/shared/components/CopyButton.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import DetailPageLayout from '@/shared/components/DetailPageLayout.vue'
|
||||
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
|
||||
@@ -130,6 +131,7 @@ const selectedApprovalOrders = ref<UnifiedOrderResource[]>([])
|
||||
const selectedOrders = ref<UnifiedOrderResource[]>([])
|
||||
const coLoadGroupsByOrderId = ref(new Map<string, CoLoadGroup>())
|
||||
const coLoadCreateOpen = ref(false)
|
||||
const coLoadDetailGroup = ref<CoLoadGroup | null>(null)
|
||||
const coLoadSaving = ref(false)
|
||||
const coLoadError = ref<ApiErrorDetails | null>(null)
|
||||
const approvalOrder = ref<UnifiedOrderResource | null>(null)
|
||||
@@ -256,9 +258,8 @@ const columns = computed<DataTableColumn[]>(() => [
|
||||
key: 'selection', type: 'selection', width: 48, fixed: 'left',
|
||||
selectable: () => !batchApprovalSaving.value && !coLoadSaving.value,
|
||||
} satisfies DataTableColumn] : []),
|
||||
{ key: 'orderNo', prop: 'orderNo', label: '订单号', minWidth: 180, slotName: 'field' },
|
||||
{ key: 'orderNo', prop: 'orderNo', label: '订单号', minWidth: 230, slotName: 'orderNo' },
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 190, slotName: 'inboundCode' },
|
||||
{ key: 'coLoadGroup', label: '同柜组', minWidth: 190, slotName: 'coLoadGroup' },
|
||||
{ key: 'mainStatus', prop: 'mainStatus', label: '订单主状态', minWidth: 130, slotName: 'mainStatus' },
|
||||
{ key: 'customerName', prop: 'customerName', label: '客户', minWidth: 150, slotName: 'field' },
|
||||
{ key: 'receivingWarehouseName', prop: 'receivingWarehouseName', label: '平台收货仓', minWidth: 150, slotName: 'field' },
|
||||
@@ -791,6 +792,33 @@ function coLoadGroup(row: UnifiedOrderResource) {
|
||||
return coLoadGroupsByOrderId.value.get(String(row.orderId)) || null
|
||||
}
|
||||
|
||||
function coLoadGroupOrderCount(group: CoLoadGroup) {
|
||||
return new Set(group.members.map((member) => member.orderId)).size
|
||||
}
|
||||
|
||||
function coLoadGroupMemberLabel(group: CoLoadGroup) {
|
||||
const orderCount = coLoadGroupOrderCount(group)
|
||||
return group.members.length === orderCount
|
||||
? `${orderCount} 票`
|
||||
: `${orderCount} 票 · ${group.members.length} 个入仓片段`
|
||||
}
|
||||
|
||||
function coLoadGroupStatusLabel(group: CoLoadGroup) {
|
||||
return group.status === 'ACTIVE' ? '未锁定' : group.status === 'LOCKED' ? '已锁定' : group.status === 'CONSUMED' ? '已消费' : '已取消'
|
||||
}
|
||||
|
||||
function openCoLoadDetail(group: CoLoadGroup) {
|
||||
coLoadDetailGroup.value = group
|
||||
}
|
||||
|
||||
function visibleOrderNo(orderId: string) {
|
||||
return records.value.find((row) => String(row.orderId) === orderId)?.orderNo || ''
|
||||
}
|
||||
|
||||
function visibleInboundCode(inboundId: string) {
|
||||
return records.value.find((row) => String(row.inboundId || '') === inboundId)?.inboundCode || ''
|
||||
}
|
||||
|
||||
function openCoLoadCreate() {
|
||||
if (selectedOrders.value.length < 2) return
|
||||
coLoadError.value = null
|
||||
@@ -804,7 +832,7 @@ async function createCoLoadGroup() {
|
||||
try {
|
||||
const group = await createPlatformCoLoadGroup(selectedOrders.value.map((row) => String(row.orderId)))
|
||||
coLoadCreateOpen.value = false
|
||||
ElMessage.success(`已创建同柜不可拆组 ${group.groupNo},共 ${group.members.length} 票`)
|
||||
ElMessage.success(`已创建同柜不可拆组 ${group.groupNo},共 ${coLoadGroupMemberLabel(group)}`)
|
||||
await loadOrders()
|
||||
} catch (error) {
|
||||
coLoadError.value = getApiErrorDetails(error, '创建同柜不可拆组失败')
|
||||
@@ -819,7 +847,7 @@ async function cancelCoLoadGroup(row: UnifiedOrderResource) {
|
||||
let reason = ''
|
||||
try {
|
||||
const result = await ElMessageBox.prompt(
|
||||
`将整组取消 ${group.groupNo}(${group.members.length} 票)。不支持单独移除成员;如需调整,请取消后重新建组。`,
|
||||
`将整组取消 ${group.groupNo}(${coLoadGroupMemberLabel(group)})。不支持单独移除成员;如需调整,请取消后重新建组。`,
|
||||
'取消同柜不可拆组',
|
||||
{ confirmButtonText: '确认整组取消', cancelButtonText: '返回', inputPlaceholder: '取消原因(选填)', inputType: 'textarea' },
|
||||
)
|
||||
@@ -1336,7 +1364,7 @@ onBeforeUnmount(() => {
|
||||
><GitBranch :size="14" />{{ orderRelationLabel(row as UnifiedOrderResource) }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #coLoadGroup="{ row }"><div v-if="coLoadGroup(row as UnifiedOrderResource)" class="coload-cell"><el-tag size="small" :type="coLoadGroup(row as UnifiedOrderResource)?.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroup(row as UnifiedOrderResource)?.groupNo }}</el-tag><span>{{ coLoadGroup(row as UnifiedOrderResource)?.members.length }} 票 · {{ coLoadGroup(row as UnifiedOrderResource)?.status === 'ACTIVE' ? '未锁定' : coLoadGroup(row as UnifiedOrderResource)?.status === 'LOCKED' ? '已锁定' : '已消费' }}</span></div><span v-else>—</span></template>
|
||||
<template #orderNo="{ row }"><div class="order-code-cell"><strong>{{ (row as UnifiedOrderResource).orderNo || (row as UnifiedOrderResource).orderId }}</strong><button v-if="coLoadGroup(row as UnifiedOrderResource)" type="button" class="coload-tag-trigger" :aria-label="`查看同柜组 ${coLoadGroup(row as UnifiedOrderResource)?.groupNo} 详情`" @click.stop="openCoLoadDetail(coLoadGroup(row as UnifiedOrderResource)!)"><el-tag size="small" :type="coLoadGroup(row as UnifiedOrderResource)?.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroup(row as UnifiedOrderResource)?.groupNo }} · {{ coLoadGroupOrderCount(coLoadGroup(row as UnifiedOrderResource)!) }} 票 · {{ coLoadGroupStatusLabel(coLoadGroup(row as UnifiedOrderResource)!) }}</el-tag></button></div></template>
|
||||
<template #orderStatus="{ row }"><StatusBadge :status="orderStatusLabel((row as UnifiedOrderResource).internalOrderStatus || (row as UnifiedOrderResource).mainStatus)" /></template>
|
||||
<template #measurementReviewStatus="{ row }"><StatusBadge :status="measurementStatusLabel((row as UnifiedOrderResource).measurement?.reviewStatus)" /></template>
|
||||
<template #field="{ row, column }"><LineClampText v-if="['customerRemark', 'latestInternalNote'].includes(String(column.property))" :content="displayField(row as UnifiedOrderResource, column.property)" :lines="2" /><template v-else>{{ displayField(row as UnifiedOrderResource, column.property) }}</template></template>
|
||||
@@ -1355,10 +1383,21 @@ onBeforeUnmount(() => {
|
||||
</ListPageLayout>
|
||||
<SmartLoadingDialog v-model="smartLoadingOpen" />
|
||||
<AppDialog v-model="coLoadCreateOpen" title="设为同柜不可拆组" confirm-text="确认建组" :confirm-loading="coLoadSaving" :confirm-disabled="selectedOrders.length < 2" @confirm="createCoLoadGroup">
|
||||
<el-alert type="warning" :closable="false" show-icon :title="`所选 ${selectedOrders.length} 票必须全量进入同一柜,不能部分装柜、拆子单或单独移除成员。`" />
|
||||
<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.orderNo || row.inboundCode || row.orderId }}</span></div>
|
||||
<div v-if="coLoadError" class="coload-error" role="alert"><span>{{ coLoadError.message }}</span><ApiErrorMeta :details="coLoadError" /></div>
|
||||
</AppDialog>
|
||||
<AppDialog :model-value="Boolean(coLoadDetailGroup)" :title="coLoadDetailGroup ? `同柜组 ${coLoadDetailGroup.groupNo}` : '同柜组详情'" :show-confirm="false" cancel-text="关闭" width="min(760px, calc(100vw - 32px))" @update:model-value="coLoadDetailGroup = null">
|
||||
<template v-if="coLoadDetailGroup">
|
||||
<div class="coload-detail-summary"><el-tag size="small" :type="coLoadDetailGroup.status === 'ACTIVE' ? 'warning' : 'info'">{{ coLoadGroupOrderCount(coLoadDetailGroup) }} 票 · {{ coLoadDetailGroup.members.length }} 个入仓片段 · {{ coLoadGroupStatusLabel(coLoadDetailGroup) }}</el-tag><span>共 {{ coLoadDetailGroup.fullPackageCount }} 箱</span></div>
|
||||
<div class="coload-detail-scroll">
|
||||
<table class="coload-detail-table">
|
||||
<thead><tr><th>入仓码 / 入仓 ID</th><th>订单编码 / ID</th><th>箱数</th></tr></thead>
|
||||
<tbody><tr v-for="member in coLoadDetailGroup.members" :key="`${member.orderId}:${member.inboundId}`"><td><strong>{{ visibleInboundCode(member.inboundId) || member.inboundId }}</strong><span v-if="visibleInboundCode(member.inboundId)">ID {{ member.inboundId }}</span><CopyButton :value="member.inboundId" label="入仓 ID" button-text="复制 ID" size="small" text /></td><td><strong>{{ visibleOrderNo(member.orderId) || member.orderId }}</strong><span v-if="visibleOrderNo(member.orderId)">ID {{ member.orderId }}</span><CopyButton :value="member.orderId" label="订单 ID" button-text="复制 ID" size="small" text /></td><td>{{ member.fullPackageCount }} 箱</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
</AppDialog>
|
||||
</template>
|
||||
|
||||
<AppDrawer v-model="relationOpen" title="父子订单关联" size="min(860px, 94vw)" :show-footer="false" destroy-on-close>
|
||||
@@ -1426,10 +1465,22 @@ onBeforeUnmount(() => {
|
||||
.table-card { overflow: hidden; }
|
||||
.coload-error { margin: 8px 12px; padding: 8px 10px; border: 1px solid var(--el-color-danger-light-5); border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); font-size: 12px; }
|
||||
.coload-error :deep(.api-error-meta) { margin-top: 5px; }
|
||||
.coload-cell { display: grid; width: fit-content; gap: 3px; }
|
||||
.coload-cell span { color: var(--cc-muted); font-size: 11px; }
|
||||
.order-code-cell { display: grid; justify-items: start; gap: 4px; }
|
||||
.coload-tag-trigger { margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; }
|
||||
.coload-tag-trigger:focus-visible { outline: 2px solid var(--el-color-primary); outline-offset: 2px; border-radius: 3px; }
|
||||
.coload-selection { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
|
||||
.coload-selection > span { padding: 4px 7px; border: 1px solid var(--cc-border); border-radius: 4px; background: var(--cc-surface-muted); font-size: 12px; }
|
||||
.coload-detail-summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
|
||||
.coload-detail-scroll { overflow-x: auto; border: 1px solid var(--cc-border); border-radius: 4px; }
|
||||
.coload-detail-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
||||
.coload-detail-table th,.coload-detail-table td { padding: 8px 10px; border-bottom: 1px solid var(--cc-border); text-align: left; vertical-align: top; }
|
||||
.coload-detail-table th { background: var(--cc-surface-muted); color: var(--cc-muted); font-weight: 600; }
|
||||
.coload-detail-table tbody tr:last-child td { border-bottom: 0; }
|
||||
.coload-detail-table td { min-width: 210px; }
|
||||
.coload-detail-table td:last-child { min-width: 80px; white-space: nowrap; }
|
||||
.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; }
|
||||
.table-card :deep(.el-table) { --el-table-border-color: #e5e7eb; --el-table-header-bg-color: #f9fafb; --el-table-row-hover-bg-color: #f3f7fb; --el-table-tr-bg-color: #fff; color: #374151; font-size: 12px; }
|
||||
.table-card :deep(.el-table th.el-table__cell) { height: 42px; padding: 0; background: #f9fafb; color: #374151; font-size: 12px; font-weight: 600; }
|
||||
.table-card :deep(.el-table td.el-table__cell) { min-height: 42px; padding: 9px 0; }
|
||||
|
||||
Reference in New Issue
Block a user