Merge branch 'release/test' of http://8.163.30.128:10082/html/admin-web into release/test
This commit is contained in:
@@ -20,7 +20,7 @@ import LineClampText from '@/shared/components/LineClampText.vue'
|
||||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||||
import { firstLegBusinessTypeLabel } from '@/shared/utils/firstLegBusinessLabels'
|
||||
import { listPlatformInbounds } from '../core/api'
|
||||
import type { InboundOrderSummary } from '../core/types'
|
||||
import type { InboundOrderSummary, InboundPageResult } from '../core/types'
|
||||
import InboundMeasurementDialog from '../measurement/InboundMeasurementDialog.vue'
|
||||
import MeasurementReviewPanel from '../review/MeasurementReviewPanel.vue'
|
||||
import { approveMeasurement, getMeasurementReviewDetail } from '../review/api'
|
||||
@@ -34,7 +34,11 @@ type FilterModel = QueryModel & {
|
||||
expectedArrivalRange: string[]
|
||||
}
|
||||
|
||||
const props = defineProps<{ mode: InboundTaskMode; title?: string }>()
|
||||
const props = defineProps<{
|
||||
mode: InboundTaskMode
|
||||
title?: string
|
||||
taskLoader?: (pagination: { page: number; size: number }) => Promise<InboundPageResult>
|
||||
}>()
|
||||
const emit = defineEmits<{ changed: [] }>()
|
||||
const router = useRouter()
|
||||
const access = useAccessPreviewStore()
|
||||
@@ -144,6 +148,7 @@ function dateParam(value: string | undefined, end = false) {
|
||||
}
|
||||
|
||||
async function loadOptions() {
|
||||
if (props.taskLoader) return
|
||||
const serial = ++optionsRequestSerial
|
||||
customerLoadError.value = null
|
||||
try {
|
||||
@@ -170,7 +175,7 @@ async function loadTasks() {
|
||||
loadError.value = null
|
||||
const applied = appliedFilters.value
|
||||
try {
|
||||
const response = await listPlatformInbounds(applied.customerId || undefined, buildInboundTaskQuery(props.mode, {
|
||||
const response = props.taskLoader ? await props.taskLoader({ page: page.value, size: pageSize.value }) : await listPlatformInbounds(applied.customerId || undefined, buildInboundTaskQuery(props.mode, {
|
||||
page: page.value,
|
||||
size: pageSize.value,
|
||||
warehouseId: applied.warehouseId || undefined,
|
||||
@@ -329,6 +334,12 @@ function clearSelectedInbound() {
|
||||
reviewNavigationLoading.value = false
|
||||
}
|
||||
|
||||
function reload() {
|
||||
if (page.value === 1) return loadTasks()
|
||||
page.value = 1
|
||||
}
|
||||
defineExpose({ reload })
|
||||
|
||||
watch([page, pageSize], () => { void loadTasks() })
|
||||
onMounted(() => { void loadOptions(); void loadTasks() })
|
||||
onActivated(() => {
|
||||
@@ -364,6 +375,7 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1; optionsRequestSeria
|
||||
>
|
||||
<template #toolbar-start><slot name="status-tabs"><h1>{{ title || (isReview ? '测量数据审核' : '订单测量') }}</h1></slot></template>
|
||||
<template #filters>
|
||||
<slot name="filters" :loading="loading">
|
||||
<QueryToolbar
|
||||
v-model="filters"
|
||||
:ariaLabel="`${taskLabel}待办筛选`"
|
||||
@@ -373,6 +385,7 @@ onBeforeUnmount(() => { disposed = true; requestSerial += 1; optionsRequestSeria
|
||||
@search="applyFilters"
|
||||
@reset="resetFilters"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #notices><template v-if="customerLoadError || loadError">
|
||||
<div class="state-stack">
|
||||
|
||||
@@ -20,3 +20,5 @@ export type { AdjustPublishedInboundFeeRequest, InboundFeeSnapshot } from './fee
|
||||
export { downloadWeightImage } from './weight/api/weightOcr'
|
||||
export type { InboundMeasurementDetail } from './measurement/types'
|
||||
export type { InboundHoldCommand, InboundHoldDetail, InboundHoldEvidenceUpload, InboundHoldHistoryEntry } from './core/types'
|
||||
|
||||
export type { InboundOrderSummary, InboundPageResult } from './core/types'
|
||||
|
||||
@@ -273,7 +273,7 @@ describe('InboundMeasurementPanel', () => {
|
||||
expect(text).toContain('客户申报')
|
||||
expect(text).toContain('平台实测')
|
||||
expect(text).toContain('差异 +1.5 kg')
|
||||
expect(text).toContain('差异 -0.2 m³')
|
||||
expect(text).toContain('差异 -0.200 m³')
|
||||
expect(text).toContain('平台实测箱规')
|
||||
expect(text).not.toContain('租户')
|
||||
expect(text).not.toContain('数据版本')
|
||||
@@ -297,7 +297,7 @@ describe('InboundMeasurementPanel', () => {
|
||||
const wrapper = await mountPanel()
|
||||
const rows = wrapper.findAll('.measurement-comparison tbody tr')
|
||||
expect(rows.map(row => row.findAll('td').map(cell => cell.text()))).toEqual([
|
||||
['10', '10', '一致'], ['118.5', '120', '差异 +1.5 kg'], ['0.8', '0.6', '差异 -0.2 m³'],
|
||||
['10', '10', '一致'], ['118.5', '120', '差异 +1.5 kg'], ['0.800', '0.600', '差异 -0.200 m³'],
|
||||
])
|
||||
expect(wrapper.find('.measurement-summary-section .measurement-declaration').exists()).toBe(false)
|
||||
expect(wrapper.find('.measurement-declaration').element.parentElement).toBe(wrapper.find('.measurement-summary-section').element.parentElement)
|
||||
@@ -319,8 +319,8 @@ describe('InboundMeasurementPanel', () => {
|
||||
vi.mocked(getInboundMeasurement).mockResolvedValueOnce(measurement({ measurementId: null, items: [], weightResults: [] }))
|
||||
const wrapper = await mountPanel()
|
||||
expect(wrapper.find('.measurement-summary-header h4').text()).toBe('申报与实测对比')
|
||||
expect(wrapper.find('.measurement-declaration-header').text()).toContain('DOC-001')
|
||||
expect(wrapper.find('.measurement-section__heading .measurement-goods-name input').exists()).toBe(true)
|
||||
expect(wrapper.find('.measurement-declaration-header').text()).not.toContain('DOC-001')
|
||||
expect(wrapper.find('.measurement-section__heading .measurement-goods-name').exists()).toBe(false)
|
||||
expect(wrapper.find('.measurement-section__heading .measurement-section-actions').text()).toContain('手动录入箱规')
|
||||
expect(wrapper.findAll('.el-alert').some(alert => alert.text().includes('请先完成箱规测量'))).toBe(false)
|
||||
expect(wrapper.find('.measurement-panel__footer').text()).toContain('请先完成箱规测量')
|
||||
@@ -334,11 +334,11 @@ describe('InboundMeasurementPanel', () => {
|
||||
expect(comparison.text()).toContain('118.5')
|
||||
expect(comparison.text()).toContain('0.8')
|
||||
expect(comparison.findAll('tbody tr')[0].text()).toContain('10')
|
||||
expect(declaration.text()).toContain('原单据号码')
|
||||
expect(declaration.text()).toContain('DOC-001')
|
||||
expect(declaration.findAll('.measurement-readonly-field')).toHaveLength(1)
|
||||
expect(declaration.text()).not.toContain('原单据号码')
|
||||
expect(declaration.text()).not.toContain('DOC-001')
|
||||
expect(declaration.findAll('.measurement-readonly-field')).toHaveLength(0)
|
||||
expect(declaration.findAll('input')).toHaveLength(0)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name input').exists()).toBe(true)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name').exists()).toBe(false)
|
||||
expect(declaration.find('[aria-label="客户申报箱规明细"]').text()).toContain('蓝牙音箱')
|
||||
expect(declaration.find('[aria-label="客户申报箱规明细"]').text()).toContain('50 × 40 × 30')
|
||||
})
|
||||
@@ -356,13 +356,14 @@ describe('InboundMeasurementPanel', () => {
|
||||
|
||||
expect(declaration.find('input').exists()).toBe(false)
|
||||
expect(declaration.text()).toContain('蓝牙音箱')
|
||||
expect(declaration.findAll('.measurement-readonly-field')).toHaveLength(1)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name').text()).toContain('蓝牙音箱')
|
||||
expect(declaration.findAll('.measurement-readonly-field')).toHaveLength(0)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('saves the corrected actual goods name without overriding declaration facts', async () => {
|
||||
it('preserves the hidden actual goods name without overriding declaration facts', async () => {
|
||||
const wrapper = await mountPanel()
|
||||
await wrapper.find('.measurement-goods-name input').setValue('修正后的蓝牙音箱')
|
||||
expect(wrapper.text()).not.toContain('实际品名')
|
||||
await wrapper.find('input[aria-label="仓库修改外箱长"]').setValue('55')
|
||||
const save = wrapper.findAll('button').find((button) => button.text().includes('保存测量数据'))
|
||||
await save?.trigger('click')
|
||||
await flushPromises()
|
||||
@@ -370,7 +371,7 @@ describe('InboundMeasurementPanel', () => {
|
||||
expect(saveInboundMeasurementDraft).toHaveBeenCalledWith('10001', '7001', expect.objectContaining({
|
||||
measurementId: '9001',
|
||||
version: 2,
|
||||
actualGoodsName: '修正后的蓝牙音箱',
|
||||
actualGoodsName: '蓝牙音箱',
|
||||
items: expect.any(Array),
|
||||
}))
|
||||
const payload = vi.mocked(saveInboundMeasurementDraft).mock.calls[0]?.[2]
|
||||
@@ -573,7 +574,7 @@ describe('InboundMeasurementPanel', () => {
|
||||
const wrapper = await mountPanel()
|
||||
expect(wrapper.find('.measurement-declaration input').exists()).toBe(false)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-expected-pallets input').exists()).toBe(true)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name input').exists()).toBe(true)
|
||||
expect(wrapper.find('.measurement-items-section .measurement-goods-name').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('requires at least one confirmed weight image before submitting for review', async () => {
|
||||
|
||||
@@ -124,7 +124,7 @@ const confirmedPackageCount = computed(() => form.items.reduce((sum, item) => su
|
||||
const comparisonMetrics = computed(() => [
|
||||
{ label: '箱数', declared: detail.value?.originalPackageCount, actual: form.items.length ? confirmedPackageCount.value : null, digits: 0, unit: '' },
|
||||
{ label: '毛重(kg)', declared: detail.value?.originalGrossWeightKg, actual: detail.value?.actualGrossWeightKg, digits: 3, unit: ' kg' },
|
||||
{ label: '总体积(m³)', declared: detail.value?.originalVolumeCbm, actual: detail.value?.actualVolumeCbm, digits: 4, unit: ' m³' },
|
||||
{ label: '总体积(m³)', declared: detail.value?.originalVolumeCbm, actual: detail.value?.actualVolumeCbm, digits: 3, unit: ' m³' },
|
||||
])
|
||||
|
||||
const expectedPackageCount = computed(() => detail.value?.originalPackageCount ?? null)
|
||||
@@ -557,9 +557,9 @@ async function submitForReview() {
|
||||
}
|
||||
}
|
||||
|
||||
function formatNumber(value: number | null | undefined, maximumFractionDigits = 3) {
|
||||
function formatNumber(value: number | null | undefined, maximumFractionDigits = 3, minimumFractionDigits = 0) {
|
||||
if (value == null || !Number.isFinite(value)) return '—'
|
||||
return new Intl.NumberFormat('zh-CN', { maximumFractionDigits }).format(value)
|
||||
return new Intl.NumberFormat('zh-CN', { maximumFractionDigits, minimumFractionDigits }).format(value)
|
||||
}
|
||||
|
||||
function deviationRatio(actual: number | null | undefined, declared: number | null | undefined) {
|
||||
@@ -576,7 +576,7 @@ function formatDifference(actual: number | null | undefined, declared: number |
|
||||
const difference = actual - declared
|
||||
if (difference === 0) return null
|
||||
const sign = difference > 0 ? '+' : ''
|
||||
return `差异 ${sign}${formatNumber(difference, maximumFractionDigits)}${unit}`
|
||||
return `差异 ${sign}${formatNumber(difference, maximumFractionDigits, unit === ' m³' ? 3 : 0)}${unit}`
|
||||
}
|
||||
|
||||
function releaseImagePreview() {
|
||||
@@ -728,8 +728,8 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
|
||||
<tbody>
|
||||
<tr v-for="metric in comparisonMetrics" :key="metric.label">
|
||||
<th scope="row">{{ metric.label }}</th>
|
||||
<td>{{ formatNumber(metric.declared, metric.digits) }}</td>
|
||||
<td>{{ metric.actual == null ? '未测量' : formatNumber(metric.actual, metric.digits) }}</td>
|
||||
<td>{{ formatNumber(metric.declared, metric.digits, metric.unit === ' m³' ? 3 : 0) }}</td>
|
||||
<td>{{ metric.actual == null ? '未测量' : formatNumber(metric.actual, metric.digits, metric.unit === ' m³' ? 3 : 0) }}</td>
|
||||
<td :class="{ 'measurement-comparison__difference': metric.actual != null && metric.declared != null && metric.actual !== metric.declared }">
|
||||
{{ metric.actual == null ? '待测量' : metric.declared == null ? '—' : formatDifference(metric.actual, metric.declared, metric.digits, metric.unit) || '一致' }}
|
||||
</td>
|
||||
@@ -746,7 +746,6 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
|
||||
<section class="measurement-declaration" aria-labelledby="measurement-declaration-title">
|
||||
<header class="measurement-declaration-header">
|
||||
<h4 id="measurement-declaration-title">客户申报明细</h4>
|
||||
<div class="measurement-readonly-field measurement-document-number"><span>原单据号码</span><strong>{{ detail.originalDocumentNo || '—' }}</strong></div>
|
||||
</header>
|
||||
<el-table
|
||||
v-if="detail.latestDeclaration?.items.length"
|
||||
@@ -770,9 +769,7 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
|
||||
<div class="measurement-section__heading">
|
||||
<div><h3>平台实测箱规</h3></div>
|
||||
|
||||
<div class="measurement-data-grid measurement-platform-fields">
|
||||
<label v-if="mayMaintain" class="measurement-goods-name"><span>实际品名</span><el-input v-model="form.actualGoodsName" placeholder="请输入实际品名" maxlength="255" /></label>
|
||||
<div v-else class="measurement-readonly-field measurement-goods-name"><span>实际品名</span><strong>{{ detail.actualGoodsName || '—' }}</strong></div>
|
||||
<div v-if="isDropship" class="measurement-data-grid measurement-platform-fields">
|
||||
<label v-if="isDropship && mayMaintain" class="measurement-expected-pallets"><span>预计托数</span><el-input-number v-model="form.expectedPalletCount" placeholder="请输入预计托数" :min="1" :max="1000" :step="1" step-strictly controls-position="right" /></label>
|
||||
<div v-else-if="isDropship" class="measurement-readonly-field measurement-expected-pallets"><span>预计托数</span><strong>{{ detail.expectedPalletCount ?? '—' }}</strong></div>
|
||||
</div>
|
||||
@@ -800,7 +797,7 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
|
||||
<div><dt>测量时间</dt><dd>{{ row.measuredAt ? row.measuredAt.replace('T', ' ') : '—' }}</dd></div>
|
||||
<div><dt>计算方式</dt><dd>{{ calculationModeLabel(row.coefficientMode) }}</dd></div>
|
||||
<div><dt>计算后外箱</dt><dd>{{ formatNumber(row.calculatedOuterLengthCm) }} × {{ formatNumber(row.calculatedOuterWidthCm) }} × {{ formatNumber(row.calculatedOuterHeightCm) }} cm</dd></div>
|
||||
<div><dt>单箱体积</dt><dd>{{ formatNumber(row.serverValues.unitVolumeCbm, 4) }} m³</dd></div>
|
||||
<div><dt>单箱体积</dt><dd>{{ formatNumber(row.serverValues.unitVolumeCbm, 3, 3) }} m³</dd></div>
|
||||
<div><dt>箱规周长</dt><dd>{{ formatNumber(row.serverValues.girthCm) }} cm</dd></div>
|
||||
<div><dt>总材积重</dt><dd>{{ formatNumber(row.serverValues.volumeWeightKg) }} kg</dd></div>
|
||||
<div><dt>计算规则版本</dt><dd>{{ row.calculationRuleVersion || '—' }}</dd></div>
|
||||
@@ -848,7 +845,7 @@ defineExpose({ saveDraft, submitForReview, updateWeightEvidence, submissionBlock
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总体积(m³)" width="108" align="right">
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.totalVolumeCbm, 4) }}</template>
|
||||
<template #default="{ row }">{{ formatNumber(row.serverValues.totalVolumeCbm, 3, 3) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="图片" width="82" align="center">
|
||||
<template #default="{ row }">
|
||||
|
||||
Reference in New Issue
Block a user