优化
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
describe('InboundTaskList measurement scope', () => {
|
||||
it('uses the inbound tenant isolation key for measurement and review requests', () => {
|
||||
const source = readFileSync(resolve('src/modules/inbounds/components/InboundTaskList.vue'), 'utf8')
|
||||
|
||||
expect(source.match(/:tenant-id="selectedInbound\.tenantId"/g)).toHaveLength(2)
|
||||
expect(source).not.toContain('selectedInbound.customerId || selectedInbound.tenantId')
|
||||
})
|
||||
})
|
||||
@@ -260,7 +260,7 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1 })
|
||||
<el-dialog
|
||||
v-model="taskDialogOpen"
|
||||
:title="selectedInbound ? `${taskLabel} · ${selectedInbound.inboundCode || selectedInbound.id}` : taskLabel"
|
||||
width="min(1500px, calc(100vw - 32px))"
|
||||
width="min(1180px, calc(100vw - 32px))"
|
||||
top="4vh"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
@@ -268,13 +268,13 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1 })
|
||||
>
|
||||
<MeasurementReviewPanel
|
||||
v-if="isReview && selectedInbound"
|
||||
:customer-id="selectedInbound.customerId || selectedInbound.tenantId"
|
||||
:tenant-id="selectedInbound.tenantId"
|
||||
:inbound-id="selectedInbound.id"
|
||||
@changed="handleTaskChanged"
|
||||
/>
|
||||
<InboundMeasurementPanel
|
||||
v-else-if="selectedInbound"
|
||||
:customer-id="selectedInbound.customerId || selectedInbound.tenantId"
|
||||
:tenant-id="selectedInbound.tenantId"
|
||||
:inbound-id="selectedInbound.id"
|
||||
@changed="handleTaskChanged"
|
||||
/>
|
||||
|
||||
@@ -422,6 +422,10 @@ function processStatusLabel(status: string | undefined) {
|
||||
return inboundStatusLabel(status, inboundProcessStatusLabels)
|
||||
}
|
||||
|
||||
function measurementRowClassName({ row }: { row: EditableMeasurementItem }) {
|
||||
return validationErrors.value[row.clientKey]?.length ? 'measurement-table__row--error' : ''
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [customerId.value, props.inboundId, canView.value] as const,
|
||||
() => {
|
||||
@@ -548,12 +552,12 @@ defineExpose({ saveDraft, submitForReview, mayMaintain, maySubmit, saving, submi
|
||||
<div class="measurement-section__heading">
|
||||
<div><h3>原单数据</h3><p>原单值仅用于审核对比,不参与前端计算。</p></div>
|
||||
</div>
|
||||
<div class="measurement-form-grid">
|
||||
<label><span>原单据号码(CKW)</span><el-input :model-value="form.originalDocumentNo || '—'" readonly /></label>
|
||||
<label><span>原单据毛重(T.K.GW)</span><el-input :model-value="formatNumber(form.originalGrossWeightKg)" readonly /></label>
|
||||
<label><span>原单据总体积(T.CBM)</span><el-input :model-value="formatNumber(form.originalVolumeCbm, 4)" readonly /></label>
|
||||
<label><span>实际品名</span><el-input :model-value="form.actualGoodsName || '—'" readonly /></label>
|
||||
</div>
|
||||
<dl class="measurement-order-summary">
|
||||
<div><dt>原单据号码(CKW)</dt><dd>{{ form.originalDocumentNo || '—' }}</dd></div>
|
||||
<div><dt>原单据毛重(T.K.GW)</dt><dd>{{ formatNumber(form.originalGrossWeightKg) }}</dd></div>
|
||||
<div><dt>原单据总体积(T.CBM)</dt><dd>{{ formatNumber(form.originalVolumeCbm, 4) }}</dd></div>
|
||||
<div><dt>实际品名</dt><dd>{{ form.actualGoodsName || '—' }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="measurement-section measurement-items-section">
|
||||
@@ -562,31 +566,72 @@ defineExpose({ saveDraft, submitForReview, mayMaintain, maySubmit, saving, submi
|
||||
<el-button v-if="canManualEntry" type="primary" plain :disabled="!mayCreateManualEntry" @click="openManualEntry"><Plus :size="15" />手动录入箱规</el-button>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!form.items.length" description="暂无扫描箱测量数据" />
|
||||
|
||||
<article v-for="(item, index) in form.items" :key="item.clientKey" class="measurement-item" :class="{ 'has-error': validationErrors[item.clientKey]?.length }">
|
||||
<header>
|
||||
<div><strong>扫描箱 {{ index + 1 }}</strong><span>{{ item.boxCode || '未关联箱号' }} · {{ item.machineNo || '人工录入' }}</span></div>
|
||||
</header>
|
||||
<div class="measurement-item__grid">
|
||||
<label><span>箱号</span><el-input :model-value="item.boxCode || '—'" readonly /></label>
|
||||
<label><span>测量长(cm)</span><el-input :model-value="formatNumber(item.lengthCm)" readonly /></label>
|
||||
<label><span>测量宽(cm)</span><el-input :model-value="formatNumber(item.widthCm)" readonly /></label>
|
||||
<label><span>测量高(cm)</span><el-input :model-value="formatNumber(item.heightCm)" readonly /></label>
|
||||
<label><span>确认箱数 *</span><el-input-number v-model="item.packageCount" :min="1" :max="1000000" :step="1" step-strictly controls-position="right" /></label>
|
||||
<label><span>每箱毛重(kg)</span><el-input :model-value="formatNumber(item.unitWeightKg)" readonly /></label>
|
||||
<label><span>机器编号</span><el-input :model-value="item.machineNo || '—'" readonly /></label>
|
||||
<label><span>测量时间</span><el-input :model-value="item.measuredAt ? item.measuredAt.replace('T', ' ') : '—'" readonly /></label>
|
||||
<label class="measurement-image-field"><span>当前有效图片</span><el-button :disabled="!item.intelligentImageFileId" @click="previewImage(item.intelligentImageFileId)"><Image :size="15" />{{ item.intelligentImageFileId ? '预览图片' : '暂无图片' }}</el-button></label>
|
||||
</div>
|
||||
<dl class="measurement-item__results" aria-label="服务端计算结果">
|
||||
<div><dt>箱规周长</dt><dd>{{ formatNumber(item.serverValues.girthCm) }} cm</dd></div>
|
||||
<div><dt>单箱体积</dt><dd>{{ formatNumber(item.serverValues.unitVolumeCbm, 4) }} CBM</dd></div>
|
||||
<div><dt>总毛重</dt><dd>{{ formatNumber(item.serverValues.totalWeightKg) }} KG</dd></div>
|
||||
<div><dt>总体积</dt><dd>{{ formatNumber(item.serverValues.totalVolumeCbm, 4) }} CBM</dd></div>
|
||||
<div><dt>总材积重</dt><dd>{{ formatNumber(item.serverValues.volumeWeightKg) }} KG</dd></div>
|
||||
</dl>
|
||||
</article>
|
||||
<el-table
|
||||
v-if="form.items.length"
|
||||
:data="form.items"
|
||||
border
|
||||
stripe
|
||||
table-layout="fixed"
|
||||
class="measurement-table"
|
||||
:row-class-name="measurementRowClassName"
|
||||
>
|
||||
<el-table-column type="expand" width="42">
|
||||
<template #default="{ row }">
|
||||
<dl class="measurement-row-details">
|
||||
<div><dt>机器编号</dt><dd>{{ row.machineNo || '人工录入' }}</dd></div>
|
||||
<div><dt>测量时间</dt><dd>{{ row.measuredAt ? row.measuredAt.replace('T', ' ') : '—' }}</dd></div>
|
||||
<div><dt>箱规周长</dt><dd>{{ formatNumber(row.serverValues.girthCm) }} cm</dd></div>
|
||||
<div><dt>备注</dt><dd>{{ row.remark || '—' }}</dd></div>
|
||||
</dl>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="index" label="#" width="48" align="center" />
|
||||
<el-table-column label="箱号" min-width="156" show-overflow-tooltip>
|
||||
<template #default="{ row }"><span class="measurement-box-code">{{ row.boxCode || '—' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="尺寸 长×宽×高(cm)" width="166" align="center">
|
||||
<template #default="{ row }">{{ formatNumber(row.lengthCm) }} × {{ formatNumber(row.widthCm) }} × {{ formatNumber(row.heightCm) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="确认箱数" width="132" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.packageCount"
|
||||
class="measurement-count-editor"
|
||||
aria-label="确认箱数"
|
||||
:min="1"
|
||||
:max="1000000"
|
||||
:step="1"
|
||||
step-strictly
|
||||
controls-position="right"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="每箱毛重(kg)" width="106" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.unitWeightKg) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单箱体积(m³)" width="112" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.unitVolumeCbm, 4) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总毛重(kg)" width="104" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.totalWeightKg) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总体积(m³)" width="108" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.totalVolumeCbm, 4) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总材积重(kg)" width="116" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.volumeWeightKg) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="图片" width="82" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="row.intelligentImageFileId ? '预览测量图片' : '暂无测量图片'">
|
||||
<el-button text type="primary" :disabled="!row.intelligentImageFileId" aria-label="预览测量图片" @click="previewImage(row.intelligentImageFileId)">
|
||||
<Image :size="16" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-else description="暂无扫描箱测量数据" />
|
||||
</section>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -675,10 +720,13 @@ defineExpose({ saveDraft, submitForReview, mayMaintain, maySubmit, saving, submi
|
||||
.measurement-section__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 16px; }
|
||||
.measurement-section__heading > div { min-width: 0; }
|
||||
.measurement-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; }
|
||||
.measurement-item__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 15px; }
|
||||
.measurement-form label, .measurement-item label { display: flex; min-width: 0; flex-direction: column; gap: 7px; color: #344054; font-size: 12px; font-weight: 650; }
|
||||
.measurement-form label { display: flex; min-width: 0; flex-direction: column; gap: 7px; color: #344054; font-size: 12px; font-weight: 650; }
|
||||
.measurement-form :deep(.el-input-number), .measurement-form :deep(.el-input) { width: 100%; }
|
||||
.measurement-image-field :deep(.el-button) { width: 100%; margin: 0; }
|
||||
.measurement-order-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; border: 1px solid var(--cc-border); border-radius: 6px; background: #fff; }
|
||||
.measurement-order-summary > div { min-width: 0; padding: 11px 13px; border-right: 1px solid var(--cc-border); }
|
||||
.measurement-order-summary > div:last-child { border-right: 0; }
|
||||
.measurement-order-summary dt { color: var(--cc-muted); font-size: 11px; }
|
||||
.measurement-order-summary dd { margin: 4px 0 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 13px; font-weight: 650; }
|
||||
.measurement-image-preview { display: grid; min-height: 240px; place-items: center; }
|
||||
.measurement-image-preview img { display: block; width: auto; max-width: 100%; max-height: 68vh; object-fit: contain; }
|
||||
.manual-entry-alert { margin-bottom: 16px; }
|
||||
@@ -686,35 +734,29 @@ defineExpose({ saveDraft, submitForReview, mayMaintain, maySubmit, saving, submi
|
||||
.manual-entry-form :deep(.el-input-number), .manual-entry-form :deep(.el-date-editor) { width: 100%; }
|
||||
.manual-entry-upload { width: 100%; }
|
||||
.measurement-items-section { background: #fafbfc; }
|
||||
.measurement-item { overflow: hidden; margin-top: 14px; border: 1px solid var(--cc-border); border-radius: 9px; background: #fff; }
|
||||
.measurement-item.has-error { border-color: var(--el-color-danger-light-5); }
|
||||
.measurement-item > header { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 15px; border-bottom: 1px solid var(--cc-border); background: #f8fafc; }
|
||||
.measurement-item > header > div { display: flex; min-width: 0; flex-direction: column; gap: 3px; }
|
||||
.measurement-item > header strong { color: var(--cc-text); font-size: 13px; }
|
||||
.measurement-item > header span { overflow: hidden; color: var(--cc-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.measurement-item__grid { padding: 16px; }
|
||||
.measurement-item__advanced { margin: 0 16px 16px; border-top: 1px solid var(--cc-border); }
|
||||
.measurement-item__advanced summary { padding: 12px 0 0; color: var(--cc-primary); font-size: 12px; font-weight: 700; cursor: pointer; }
|
||||
.measurement-item__advanced .measurement-item__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); padding: 14px 0 0; }
|
||||
.measurement-item__advanced .span-two { grid-column: span 3; }
|
||||
.measurement-item__results { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--cc-border); background: #fbfcfe; }
|
||||
.measurement-item__results > div { min-width: 0; padding: 11px 14px; border-right: 1px solid var(--cc-border); }
|
||||
.measurement-item__results > div:last-child { border-right: 0; }
|
||||
.measurement-item__results dt { color: var(--cc-muted); font-size: 10px; }
|
||||
.measurement-item__results dd { margin: 4px 0 0; overflow-wrap: anywhere; color: #344054; font-size: 12px; font-weight: 700; }
|
||||
.measurement-table { width: 100%; font-size: 12px; }
|
||||
.measurement-table :deep(.el-table__header th) { padding: 8px 0; background: #f6f8fa; color: #475467; font-size: 11px; font-weight: 700; }
|
||||
.measurement-table :deep(.el-table__cell) { padding: 7px 0; }
|
||||
.measurement-table :deep(.cell) { line-height: 1.35; }
|
||||
.measurement-table :deep(.measurement-table__row--error > td.el-table__cell) { background: var(--el-color-danger-light-9); }
|
||||
.measurement-box-code { font-weight: 650; }
|
||||
.measurement-count-editor { width: 104px !important; }
|
||||
.measurement-row-details { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px 20px; margin: 0; padding: 4px 18px 8px 62px; }
|
||||
.measurement-row-details dt { color: var(--cc-muted); font-size: 11px; }
|
||||
.measurement-row-details dd { margin: 3px 0 0; overflow-wrap: anywhere; color: var(--cc-text); font-size: 12px; font-weight: 600; }
|
||||
.measurement-panel__footer { position: sticky; bottom: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 13px 20px; border-top: 1px solid var(--cc-border); background: rgba(255, 255, 255, .97); box-shadow: 0 -8px 22px rgba(15, 23, 42, .05); }
|
||||
.measurement-panel__footer > span { display: inline-flex; align-items: center; gap: 6px; color: var(--cc-muted); font-size: 12px; }
|
||||
.measurement-panel__footer > span:has(svg) { color: #b54708; font-weight: 650; }
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.measurement-form-grid, .measurement-item__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.measurement-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.measurement-order-summary, .measurement-row-details { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.measurement-order-summary > div:nth-child(2) { border-right: 0; }
|
||||
.measurement-order-summary > div:nth-child(-n+2) { border-bottom: 1px solid var(--cc-border); }
|
||||
.measurement-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.measurement-metrics > div:nth-child(2) { border-right: 0; }
|
||||
.measurement-metrics > div:nth-child(-n+2) { border-bottom: 1px solid var(--cc-border); }
|
||||
.measurement-item__results { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
.measurement-item__results > div:nth-child(3) { border-right: 0; }
|
||||
.measurement-item__results > div:nth-child(-n+3) { border-bottom: 1px solid var(--cc-border); }
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
@@ -724,18 +766,13 @@ defineExpose({ saveDraft, submitForReview, mayMaintain, maySubmit, saving, submi
|
||||
.measurement-tags { justify-content: flex-start; }
|
||||
.measurement-context, .measurement-section { padding-right: 14px; padding-left: 14px; }
|
||||
.measurement-panel > :deep(.el-alert), .measurement-alert { margin-right: 14px; margin-left: 14px; }
|
||||
.measurement-form-grid, .measurement-item__grid, .measurement-item__advanced .measurement-item__grid { grid-template-columns: 1fr; }
|
||||
.measurement-item__advanced .span-two { grid-column: auto; }
|
||||
.measurement-item__results { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.measurement-item__results > div { border-right: 1px solid var(--cc-border); border-bottom: 1px solid var(--cc-border); }
|
||||
.measurement-item__results > div:nth-child(even) { border-right: 0; }
|
||||
.measurement-item__results > div:last-child { border-bottom: 0; }
|
||||
.measurement-form-grid { grid-template-columns: 1fr; }
|
||||
.measurement-row-details { grid-template-columns: 1fr; padding-left: 54px; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.measurement-metrics, .measurement-item__results { grid-template-columns: 1fr; }
|
||||
.measurement-metrics > div, .measurement-item__results > div { border-right: 0; border-bottom: 1px solid var(--cc-border); }
|
||||
.measurement-metrics > div:last-child, .measurement-item__results > div:last-child { border-bottom: 0; }
|
||||
.measurement-item > header { align-items: flex-start; }
|
||||
.measurement-metrics, .measurement-order-summary { grid-template-columns: 1fr; }
|
||||
.measurement-metrics > div, .measurement-order-summary > div { border-right: 0; border-bottom: 1px solid var(--cc-border); }
|
||||
.measurement-metrics > div:last-child, .measurement-order-summary > div:last-child { border-bottom: 0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user