优化
This commit is contained in:
@@ -44,7 +44,7 @@ onMounted(() => { void load() })
|
||||
<el-table-column label="已拣" width="90"><template #default="{ row }">{{ row.pickedPackageCount ?? '—' }}</template></el-table-column>
|
||||
<el-table-column label="未拣" width="90"><template #default="{ row }">{{ row.unpickedPackageCount ?? '—' }}</template></el-table-column>
|
||||
<el-table-column label="状态" width="130"><template #default="{ row }"><StatusBadge :status="unifiedOrderFulfillmentStatusLabel(row.status, row.splitRole)" /></template></el-table-column>
|
||||
<el-table-column label="柜次" min-width="150"><template #default="{ row }">{{ row.containerBookingId || '—' }}</template></el-table-column>
|
||||
<el-table-column label="柜次" min-width="150"><template #default="{ row }">{{ row.containerBookingId ? '已关联柜次' : '—' }}</template></el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<el-empty v-else description="暂无拆分履约数据" :image-size="64" />
|
||||
|
||||
@@ -92,9 +92,9 @@ describe('UnifiedOrdersView co-load group behavior', () => {
|
||||
await wrapper.get('[aria-label="查看同柜组 CLG-9001 详情"]').trigger('click')
|
||||
expect(wrapper.text()).toContain('2 票 · 3 个入仓片段 · 未锁定')
|
||||
expect(wrapper.text()).toContain('13 箱')
|
||||
expect(wrapper.text()).toContain('7003')
|
||||
expect(wrapper.findAll('[data-copy="7003"]')).toHaveLength(1)
|
||||
expect(wrapper.findAll('[data-copy="5001"]')).toHaveLength(2)
|
||||
expect(wrapper.text()).toContain('入仓码待补充')
|
||||
expect(wrapper.text()).not.toContain('7003')
|
||||
expect(wrapper.text()).not.toContain('5001')
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('取消同柜组'))!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(cancelPlatformCoLoadGroup).toHaveBeenCalledWith('9001', 0, '计划调整')
|
||||
|
||||
@@ -183,10 +183,12 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(source).toContain('coLoadGroupOrderCount(coLoadGroup(row as UnifiedOrderResource)!)')
|
||||
expect(source).not.toContain('<span v-else>—</span></template>\n <template #orderStatus')
|
||||
expect(source).toContain('v-for="member in coLoadDetailGroup.members"')
|
||||
expect(source).toContain('入仓码 / 入仓 ID')
|
||||
expect(source).toContain('订单编码 / ID')
|
||||
expect(source).toContain('<CopyButton :value="member.inboundId"')
|
||||
expect(source).toContain('<CopyButton :value="member.orderId"')
|
||||
expect(source).toContain('<th>入仓码</th><th>订单编码</th>')
|
||||
expect(source).toContain("visibleInboundCode(member.inboundId) || '入仓码待补充'")
|
||||
expect(source).toContain("visibleOrderNo(member.orderId) || '订单编码待补充'")
|
||||
expect(source).not.toContain('入仓码 / 入仓 ID')
|
||||
expect(source).not.toContain('订单编码 / ID')
|
||||
expect(source).not.toContain('客户 ID')
|
||||
})
|
||||
|
||||
it('keeps editable query values separate from the applied list request', () => {
|
||||
@@ -276,7 +278,8 @@ describe('UnifiedOrdersView contract', () => {
|
||||
expect(source).toContain('<el-table :data="measurementDetail?.items || []" row-key="id"')
|
||||
expect(source).toContain('<el-table-column label="箱号" prop="boxCode"')
|
||||
expect(source).toContain("row.boxCode || '—'")
|
||||
expect(source).toContain('`箱号 ${row.boxCode || row.itemNo || row.id} 的尺寸图片`')
|
||||
expect(source).toContain("`箱号 ${row.boxCode || row.itemNo || '箱规编码待补充'} 的尺寸图片`")
|
||||
expect(source).not.toContain('row.boxCode || row.itemNo || row.id')
|
||||
expect(source).toContain('label="尺寸图片"')
|
||||
expect(source).toContain(':file-id="row.intelligentImageFileId"')
|
||||
expect(source).toContain('openMeasurementEvidence(row.intelligentImageFileId')
|
||||
|
||||
@@ -31,12 +31,12 @@ 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'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import LineClampText from '@/shared/components/LineClampText.vue'
|
||||
import InboundCodeRelationshipDrawer from '@/shared/components/InboundCodeRelationshipDrawer.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
import StatusBadge from '@/shared/components/StatusBadge.vue'
|
||||
import {
|
||||
@@ -141,6 +141,8 @@ const noteContent = ref('')
|
||||
const noteOrder = ref<UnifiedOrderResource | null>(null)
|
||||
const relationOpen = ref(false)
|
||||
const relationOrder = ref<UnifiedOrderResource | null>(null)
|
||||
const inboundRelationshipOpen = ref(false)
|
||||
const inboundRelationshipCode = ref('')
|
||||
const measurementDetail = ref<InboundMeasurementDetail | null>(null)
|
||||
const measurementLoading = ref(false)
|
||||
const measurementError = ref<ApiErrorDetails | null>(null)
|
||||
@@ -344,7 +346,7 @@ function can(permission: string) {
|
||||
}
|
||||
|
||||
function customerName(row: UnifiedOrderResource) {
|
||||
return row.customerName?.trim() || customers.value.find((item) => String(item.id) === String(row.tenantId))?.tenantName || `客户 ${row.tenantId}`
|
||||
return row.customerName?.trim() || customers.value.find((item) => String(item.id) === String(row.tenantId))?.tenantName || '客户名称待补充'
|
||||
}
|
||||
|
||||
function orderRelationLabel(row: UnifiedOrderResource) {
|
||||
@@ -788,6 +790,13 @@ function handleOrderSelectionChange(rows: UnifiedOrderResource[]) {
|
||||
selectedApprovalOrders.value = rows.filter(canApprove)
|
||||
}
|
||||
|
||||
function openInboundRelationship(row: UnifiedOrderResource) {
|
||||
const code = row.inboundCode?.trim()
|
||||
if (!code) return
|
||||
inboundRelationshipCode.value = code
|
||||
inboundRelationshipOpen.value = true
|
||||
}
|
||||
|
||||
function coLoadGroup(row: UnifiedOrderResource) {
|
||||
return coLoadGroupsByOrderId.value.get(String(row.orderId)) || null
|
||||
}
|
||||
@@ -1197,6 +1206,7 @@ onBeforeUnmount(() => {
|
||||
<UnifiedOrderWorkflowPanel
|
||||
:tenant-id="String(detail.summary.tenantId)"
|
||||
:inbound-id="String(detail.summary.inboundId)"
|
||||
:inbound-code="detail.summary.inboundCode"
|
||||
:order-id="String(detail.summary.orderId)"
|
||||
:main-status="detail.summary.mainStatus"
|
||||
:inbound-version="detail.summary.inboundVersion"
|
||||
@@ -1242,9 +1252,9 @@ onBeforeUnmount(() => {
|
||||
:customer-id="String(detail.summary.tenantId)"
|
||||
:inbound-id="String(detail.summary.inboundId)"
|
||||
:file-id="row.intelligentImageFileId"
|
||||
:alt="`箱号 ${row.boxCode || row.itemNo || row.id} 的尺寸图片`"
|
||||
:alt="`箱号 ${row.boxCode || row.itemNo || '箱规编码待补充'} 的尺寸图片`"
|
||||
empty-text="未绑定尺寸图片"
|
||||
@open="openMeasurementEvidence(row.intelligentImageFileId, `尺寸图片 · ${row.boxCode || row.itemNo || row.id}`)"
|
||||
@open="openMeasurementEvidence(row.intelligentImageFileId, `尺寸图片 · ${row.boxCode || row.itemNo || '箱规编码待补充'}`)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -1354,6 +1364,7 @@ onBeforeUnmount(() => {
|
||||
<template #inboundCode="{ row }">
|
||||
<div class="inbound-relation-cell">
|
||||
<span>{{ (row as UnifiedOrderResource).inboundCode || '—' }}</span>
|
||||
<el-button v-if="(row as UnifiedOrderResource).inboundCode" class="order-relation-link" link type="primary" aria-label="查看入仓码关联数据" @click="openInboundRelationship(row as UnifiedOrderResource)"><Link2 :size="14" />关联数据</el-button>
|
||||
<el-button
|
||||
v-if="(row as UnifiedOrderResource).relation"
|
||||
class="order-relation-link"
|
||||
@@ -1364,12 +1375,12 @@ onBeforeUnmount(() => {
|
||||
><GitBranch :size="14" />{{ orderRelationLabel(row as UnifiedOrderResource) }}</el-button>
|
||||
</div>
|
||||
</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 #orderNo="{ row }"><div class="order-code-cell"><strong>{{ (row as UnifiedOrderResource).orderNo || '订单编码待补充' }}</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>
|
||||
<template #mainStatus="{ row }"><StatusBadge :status="statusLabel((row as UnifiedOrderResource).mainStatus)" /></template>
|
||||
<template #customer="{ row }"><div class="primary-cell"><strong>{{ customerName(row as UnifiedOrderResource) }}</strong><span>客户 ID {{ (row as UnifiedOrderResource).tenantId }}</span></div></template>
|
||||
<template #customer="{ row }"><div class="primary-cell"><strong>{{ customerName(row as UnifiedOrderResource) }}</strong></div></template>
|
||||
<template #receivingWarehouseName="{ row }">{{ (row as UnifiedOrderResource).receivingWarehouseName || '—' }}</template>
|
||||
<template #destinationWarehouseName="{ row }">{{ (row as UnifiedOrderResource).destinationWarehouseName || '—' }}</template>
|
||||
<template #transport="{ row }"><div class="primary-cell"><strong>{{ (row as UnifiedOrderResource).transport?.containerNo || unifiedOrderLoadingStatusLabel((row as UnifiedOrderResource).transport?.loadingStatus) }}</strong><span v-if="(row as UnifiedOrderResource).transport?.vesselName">{{ (row as UnifiedOrderResource).transport?.vesselName }} · {{ (row as UnifiedOrderResource).transport?.voyageNo || '—' }}</span></div></template>
|
||||
@@ -1384,7 +1395,7 @@ onBeforeUnmount(() => {
|
||||
<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} 票须属于同一国内仓、同一渠道,并全量进入同一柜;目的仓或目的港可以不同。资格由服务端按完整入仓片段校验。`" />
|
||||
<div class="coload-selection"><span v-for="row in selectedOrders" :key="String(row.orderId)">{{ row.orderNo || row.inboundCode || row.orderId }}</span></div>
|
||||
<div class="coload-selection"><span v-for="row in selectedOrders" :key="String(row.orderId)">{{ row.orderNo || row.inboundCode || '业务编码待补充' }}</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">
|
||||
@@ -1392,8 +1403,8 @@ onBeforeUnmount(() => {
|
||||
<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>
|
||||
<thead><tr><th>入仓码</th><th>订单编码</th><th>箱数</th></tr></thead>
|
||||
<tbody><tr v-for="member in coLoadDetailGroup.members" :key="`${member.orderId}:${member.inboundId}`"><td><strong>{{ visibleInboundCode(member.inboundId) || '入仓码待补充' }}</strong></td><td><strong>{{ visibleOrderNo(member.orderId) || '订单编码待补充' }}</strong></td><td>{{ member.fullPackageCount }} 箱</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1408,6 +1419,7 @@ onBeforeUnmount(() => {
|
||||
:inbound-code="relationOrder.inboundCode"
|
||||
/>
|
||||
</AppDrawer>
|
||||
<InboundCodeRelationshipDrawer v-if="inboundRelationshipOpen" v-model="inboundRelationshipOpen" :inbound-code="inboundRelationshipCode" :can-read-base="access.can('platform:order:list')" :can-read-loading="access.can('platform:firstleg:smart-loading:list')" :can-read-overseas="access.can('platform:overseas-warehouse:inventory:list')" />
|
||||
|
||||
<AppDialog v-model="noteOpen" title="添加内部备注" confirm-text="保存备注" :confirm-loading="noteSaving" @confirm="saveNote">
|
||||
<el-input v-model="noteContent" type="textarea" :rows="5" maxlength="2000" show-word-limit placeholder="请输入仅平台内部可见的备注" aria-label="内部备注内容" />
|
||||
|
||||
@@ -275,7 +275,7 @@ onMounted(() => { void load() })
|
||||
<template>
|
||||
<section class="smart-loading" aria-label="智能排柜工作台">
|
||||
<header class="smart-loading__heading">
|
||||
<div class="smart-loading__context"><span>当前柜次</span><strong>{{ container?.cabinetNo || container?.containerNo || `柜次 ${bookingId}` }}</strong><small>{{ container?.warehouseName || '仓库待加载' }}</small></div>
|
||||
<div class="smart-loading__context"><span>当前柜次</span><strong>{{ container?.cabinetNo || container?.containerNo || '柜次信息待补充' }}</strong><small>{{ container?.warehouseName || '仓库待加载' }}</small></div>
|
||||
<div class="smart-loading__actions">
|
||||
<el-button circle :loading="loading" aria-label="刷新智能排柜" @click="load"><RefreshCw :size="16" /></el-button>
|
||||
<el-button v-if="!plan" type="primary" :disabled="!canCreate || loading" :loading="action === 'create'" @click="createPlan"><Plus :size="15" />创建装柜草稿</el-button>
|
||||
|
||||
@@ -45,6 +45,7 @@ function mountPanel(permissions: string[], mainStatus: 'WAIT_SPLIT' | 'LOADING'
|
||||
return mount(UnifiedOrderWorkflowPanel, {
|
||||
props: {
|
||||
tenantId: '2086085512165634052', inboundId: '2086085512165634051', orderId: '2086085512165634053',
|
||||
inboundCode: 'NYEXX2608300001',
|
||||
mainStatus, inboundVersion: 4,
|
||||
},
|
||||
global: { plugins: [pinia, ElementPlus] },
|
||||
@@ -61,6 +62,14 @@ describe('UnifiedOrderWorkflowPanel preload editor', () => {
|
||||
vi.mocked(voidEvidence).mockReset()
|
||||
})
|
||||
|
||||
it('uses the inbound code as the visible workflow identifier', async () => {
|
||||
const wrapper = mountPanel([])
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('入库作业')
|
||||
expect(wrapper.text()).toContain('NYEXX2608300001')
|
||||
expect(wrapper.text()).not.toContain('#2086085512165634051')
|
||||
})
|
||||
|
||||
it('keeps workflow business actions as outlined semantic buttons with icons', () => {
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/modules/unified-orders/workflows/components/UnifiedOrderWorkflowPanel.vue'), 'utf8')
|
||||
expect(source).toContain('size="small" plain type="primary"')
|
||||
|
||||
@@ -38,6 +38,7 @@ import type { UnifiedOrderMainStatus } from '@/modules/unified-orders/api/unifie
|
||||
const props = withDefaults(defineProps<{
|
||||
tenantId: string
|
||||
inboundId: string | number
|
||||
inboundCode?: string | null
|
||||
orderId?: string | number | null
|
||||
mainStatus: UnifiedOrderMainStatus
|
||||
inboundVersion?: number | null
|
||||
@@ -411,7 +412,7 @@ onMounted(() => { void refresh() })
|
||||
<template>
|
||||
<section class="workflow-panel" aria-label="订单后半程作业">
|
||||
<div class="workflow-heading" aria-label="订单作业上下文">
|
||||
<div class="workflow-context"><span>入库作业</span><strong>#{{ props.inboundId }}</strong></div>
|
||||
<div class="workflow-context"><span>入库作业</span><strong>{{ props.inboundCode || '入仓码待补充' }}</strong></div>
|
||||
<el-tooltip content="刷新作业数据" placement="top"><el-button text :loading="loading" aria-label="刷新作业数据" @click="refresh"><RefreshCw :size="15" /></el-button></el-tooltip>
|
||||
</div>
|
||||
|
||||
@@ -507,7 +508,7 @@ onMounted(() => { void refresh() })
|
||||
</div>
|
||||
<div v-if="canEvidenceView" class="evidence-files">
|
||||
<div v-for="item in activeEvidences(entry.type)" :key="String(item.id)" class="evidence-file">
|
||||
<span>{{ item.fileName || `证据 #${item.id}` }}<template v-if="item.sealNo"> · 锁码 {{ item.sealNo }}</template></span>
|
||||
<span>{{ item.fileName || '证据文件名待补充' }}<template v-if="item.sealNo"> · 锁码 {{ item.sealNo }}</template></span>
|
||||
<div class="evidence-file__actions">
|
||||
<el-button size="small" plain type="primary" :disabled="Boolean(busy)" @click="previewEvidence(item)"><Eye :size="14" />查看</el-button>
|
||||
<el-button v-if="canEvidence" size="small" plain type="danger" :disabled="Boolean(busy)" @click="markEvidenceVoided(item)"><Trash2 :size="14" />作废</el-button>
|
||||
|
||||
@@ -47,6 +47,7 @@ onMounted(() => { void load() })
|
||||
v-else-if="summary && summary.inboundId"
|
||||
:tenant-id="tenantId"
|
||||
:inbound-id="summary.inboundId"
|
||||
:inbound-code="summary.inboundCode"
|
||||
:order-id="summary.orderId"
|
||||
:main-status="summary.mainStatus"
|
||||
:inbound-version="summary.inboundVersion"
|
||||
|
||||
Reference in New Issue
Block a user