Merge remote-tracking branch 'origin/release/test' into dev
# Conflicts: # AGENTS.md # src/modules/container-loading/pages/GlobalSmartLoadingView.vue # src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { Plus, RefreshCw, Sparkles, Trash2 } from '@lucide/vue'
|
||||
import { Pencil, Plus, RefreshCw, Sparkles, Trash2 } from '@lucide/vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
} from '../smartLoadingChannelGroups'
|
||||
|
||||
const props = withDefaults(defineProps<{ embedded?: boolean }>(), { embedded: false })
|
||||
const editingQuantityId = ref('')
|
||||
|
||||
const access = useAccessPreviewStore()
|
||||
const containers = ref<SmartLoadingWorkbenchContainer[]>([])
|
||||
@@ -234,7 +235,7 @@ const itemColumns: DataTableColumn[] = [
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 140, fixed: 'left', showOverflowTooltip: true },
|
||||
{ key: 'coLoadGroup', label: '同柜', minWidth: 80, slotName: 'coLoadGroup' },
|
||||
{ key: 'customerName', prop: 'customerName', label: '客户', minWidth: 70, slotName: 'customerName' },
|
||||
{ key: 'packageCount', label: '分配箱数', width: 100, slotName: 'packageCount' },
|
||||
{ key: 'packageCount', label: '分配箱数', width: 140, slotName: 'packageCount' },
|
||||
{ key: 'grossWeightKg', label: '毛重', width: 90, slotName: 'weight' },
|
||||
{ key: 'volumeCbm', label: '体积', width: 90, slotName: 'volume' },
|
||||
]
|
||||
@@ -250,8 +251,12 @@ function candidateRow(source: object) { return source as SmartLoadingCandidateRo
|
||||
function itemRow(source: object) { return source as SmartLoadingInboundItem }
|
||||
function adjustmentMax(source: object) {
|
||||
const item = itemRow(source)
|
||||
const remaining = candidates.value.find((candidate) => candidate.inboundId === item.inboundId)?.availablePackageCount || 0
|
||||
return Math.max(item.minPackageCount, item.packageCount + remaining)
|
||||
return Math.max(item.minPackageCount, item.totalPackageCount || item.packageCount)
|
||||
}
|
||||
function startQuantityEdit(source: object) {
|
||||
const item = itemRow(source)
|
||||
quantityDrafts[item.id] = item.packageCount
|
||||
editingQuantityId.value = item.id
|
||||
}
|
||||
function displayCabinetNo(container: SmartLoadingWorkbenchContainer | null | undefined) {
|
||||
if (!container) return '待生成'
|
||||
@@ -440,6 +445,12 @@ async function selectChannels(codes: string[]) {
|
||||
candidatePage.value = 1
|
||||
await loadWorkbenchData()
|
||||
}
|
||||
async function selectOnlyChannel(code: string) {
|
||||
if (selectedChannelCodes.value.length === 1 && selectedChannelCodes.value[0] === code) return
|
||||
selectedChannelCodes.value = [code]
|
||||
candidatePage.value = 1
|
||||
await loadWorkbenchData()
|
||||
}
|
||||
async function applyFilters(model: QueryModel) {
|
||||
appliedQuery.value = {
|
||||
...model,
|
||||
@@ -539,6 +550,7 @@ async function adjust(source: object) {
|
||||
await mutate(async () => {
|
||||
acceptPlan(await adjustSmartLoadingItem(plan.value!.id, item.id, count, plan.value!.version, item.version))
|
||||
await loadCandidateData()
|
||||
editingQuantityId.value = ''
|
||||
ElMessage.success('箱数已调整')
|
||||
}, '调整箱数失败', { preserveDrafts: { ...quantityDrafts } })
|
||||
}
|
||||
@@ -623,7 +635,14 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<div v-if="channelOptions.length" class="channel-summary-row">
|
||||
<el-checkbox-group :model-value="selectedChannelCodes" class="channel-tags" aria-label="渠道筛选,至少选择一项" @change="selectChannels($event as string[])">
|
||||
<span class="channel-tags__label">渠道</span>
|
||||
<el-checkbox-button v-for="channel in channelOptions" :key="channel.code" :label="channel.code" :value="channel.code">{{ channel.name }}</el-checkbox-button>
|
||||
<el-checkbox-button
|
||||
v-for="channel in channelOptions"
|
||||
:key="channel.code"
|
||||
:label="channel.code"
|
||||
:value="channel.code"
|
||||
title="双击仅选择此渠道"
|
||||
@dblclick.stop.prevent="selectOnlyChannel(channel.code)"
|
||||
>{{ channel.name }}</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
<dl class="compact-summary" aria-label="已选渠道装柜统计">
|
||||
<div><dt>候选明细</dt><dd><strong>{{ candidateTotal }}</strong> 条</dd></div>
|
||||
@@ -761,7 +780,7 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<template #actions="{ row }"><div class="row-actions"><el-button v-if="canDelete && editable && row.canRemove" size="small" type="danger" text :disabled="mutating" @click="remove(row)"><Trash2 :size="15" />删除</el-button></div></template>
|
||||
<template #customerName="{ row }"><LineClampText :content="row.customerName" /></template>
|
||||
<template #coLoadGroup="{ row }"><div v-if="row.coLoadGroup" class="candidate-group"><el-tag size="small" type="warning"><LineClampText :content="`${row.coLoadGroup.groupNo} · ${groupMemberLabel(row.coLoadGroup)}`" /></el-tag><LineClampText :content="row.memberOrderSummaries.join(';')" /><LineClampText :content="`固定 ${row.coLoadGroup.fullPackageCount} 箱 · ${row.coLoadGroup.lockedPlanId ? '已锁定' : '未锁定'}`" /></div><LineClampText v-else content="普通单票" /></template>
|
||||
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :max="adjustmentMax(row)" :step="1" :controls="false" :disabled="mutating || !canAssign || !editable || !row.canAdjust" placeholder="请输入调整箱数" @blur="adjust(row)" @keyup.enter="adjust(row)" /><LineClampText v-if="row.detailCount > 1 && editable" content="多规格,需删除后重加" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
|
||||
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-if="editingQuantityId === row.id" v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :max="adjustmentMax(row)" :step="1" :controls="false" :disabled="mutating" @blur="adjust(row)" @keyup.enter="adjust(row)" /><div v-else class="package-count-summary"><div class="package-count-used"><strong>使用:{{ row.packageCount }}</strong><el-button v-if="canAssign && editable && row.canAdjust" link circle :disabled="mutating" aria-label="调整箱数" @click="startQuantityEdit(row)"><Pencil :size="14" /></el-button></div><small v-if="(row.remainingPackageCount ?? 0) > 0">未用:{{ row.remainingPackageCount }}</small></div><LineClampText v-if="row.detailCount > 1 && editable" content="多规格,需删除后重加" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
|
||||
<template #weight="{ row }">{{ formatNumber(Number(row.grossWeightKg || 0), 1) }} kg</template>
|
||||
<template #volume="{ row }">{{ formatNumber(Number(row.volumeCbm || 0)) }} m³</template>
|
||||
</DataTable>
|
||||
@@ -775,7 +794,7 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<p>目标柜次:{{ displayCabinetNo(selectedContainer) }} · 集装箱号:{{ displayActualContainerNo(selectedContainer) }}</p>
|
||||
<p>发货仓:{{ selectedContainer?.warehouseName || '—' }} · 柜次渠道:{{ selectedContainer?.channelName || '—' }}</p>
|
||||
<p>订单渠道:{{ selectedCandidate.channelName || '—' }}</p>
|
||||
<el-alert v-if="crossChannelAssignment" title="订单渠道与柜次渠道不同;这是人工排柜,确认后仍会加入。智能推荐不会跨渠道。" type="warning" :closable="false" />
|
||||
<el-alert v-if="crossChannelAssignment" title="订单渠道与柜次渠道不同;这是人工装柜分配,确认后仍会加入。智能推荐不会跨渠道。" type="warning" :closable="false" />
|
||||
<template v-if="selectedCandidate.coLoadGroupId">
|
||||
<p>组内 {{ selectedCandidate.memberOrderCount }} 票、{{ selectedCandidate.inboundFragmentCount }} 个入仓片段,共 {{ selectedCandidate.groupTotalBoxes }} 箱</p>
|
||||
<p v-for="summary in selectedCandidate.memberOrderSummaries" :key="summary">{{ summary }}</p>
|
||||
@@ -845,6 +864,10 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
.readonly-status-card p { max-width: 460px; margin: 0; color: var(--cc-muted); line-height: 1.6; }
|
||||
.meter-title { justify-content: space-between; margin-bottom: 8px; font-size: 12px; }
|
||||
.package-count-editor { display: grid; min-width: 0; gap: 4px; }
|
||||
.package-count-summary { display: grid; gap: 3px; line-height: 1.35; }
|
||||
.package-count-used { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
|
||||
.package-count-summary strong { font-size: 13px; font-weight: 600; }
|
||||
.package-count-summary small { color: var(--cc-muted); font-size: 12px; }
|
||||
.plan-pane :deep(.package-count-editor .el-input-number) { width: 100%; min-width: 0; }
|
||||
.assign-dialog p { margin: 0 0 10px; }
|
||||
.assign-dialog :deep(.el-input-number) { width: 100%; }
|
||||
|
||||
Reference in New Issue
Block a user