Merge remote-tracking branch 'origin/release/test' into dev
This commit is contained in:
@@ -322,6 +322,8 @@ describe('UnifiedOrdersView contract', () => {
|
|||||||
expect(source).toContain('仓库修改外箱(宽)')
|
expect(source).toContain('仓库修改外箱(宽)')
|
||||||
expect(source).toContain('仓库修改外箱(高)')
|
expect(source).toContain('仓库修改外箱(高)')
|
||||||
expect(source).toContain('saveWarehouseOuterDimensions')
|
expect(source).toContain('saveWarehouseOuterDimensions')
|
||||||
|
expect(source).not.toContain('请至少输入一项需要修改的外箱尺寸')
|
||||||
|
expect(source).toContain(".map((value) => value == null || value === '' ? null : Number(value))")
|
||||||
expect(source).toContain('相关数值已重新计算')
|
expect(source).toContain('相关数值已重新计算')
|
||||||
expect(source).toContain('runMoreAction($event, row as UnifiedOrderResource)')
|
expect(source).toContain('runMoreAction($event, row as UnifiedOrderResource)')
|
||||||
expect(source).toContain(':aria-label="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"')
|
expect(source).toContain(':aria-label="moreUnifiedOrderActionsLabel(row as UnifiedOrderResource)"')
|
||||||
@@ -334,6 +336,11 @@ describe('UnifiedOrdersView contract', () => {
|
|||||||
expect(source).not.toContain("value.replaceAll('_', ' ')")
|
expect(source).not.toContain("value.replaceAll('_', ' ')")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('keeps the customer order package count independent from measured inbound totals', () => {
|
||||||
|
expect(source).toContain("{ source: '客户下单数据', count: customerTotals.count")
|
||||||
|
expect(source).not.toContain("{ source: '客户下单数据', count: summary?.totalPackageCount")
|
||||||
|
})
|
||||||
|
|
||||||
it('uses the inbound measurement girth formula for cargo dimensions', () => {
|
it('uses the inbound measurement girth formula for cargo dimensions', () => {
|
||||||
expect(source).toContain('formatNumber(length + 2 * (width + height))')
|
expect(source).toContain('formatNumber(length + 2 * (width + height))')
|
||||||
expect(source).not.toContain('formatNumber(2 * (width + height))')
|
expect(source).not.toContain('formatNumber(2 * (width + height))')
|
||||||
@@ -358,14 +365,19 @@ describe('UnifiedOrdersView contract', () => {
|
|||||||
expect(source).toContain("updateMeasurementSectionMode('actual', $event)")
|
expect(source).toContain("updateMeasurementSectionMode('actual', $event)")
|
||||||
expect(source).not.toContain("updateMeasurementSectionMode('outer', $event)")
|
expect(source).not.toContain("updateMeasurementSectionMode('outer', $event)")
|
||||||
expect(source.match(/@change="selectAndSaveOuterMeasurementMode\(\$event\)"/g)).toHaveLength(2)
|
expect(source.match(/@change="selectAndSaveOuterMeasurementMode\(\$event\)"/g)).toHaveLength(2)
|
||||||
|
expect(source).toContain('@visible-change="handleOuterModeDropdownVisibleChange($event)"')
|
||||||
expect(source.match(/:disabled="Boolean\(measurementStrategySavingId\) \|\| !can\('platform:firstleg:inbound:measure:edit'\)"/g)).toHaveLength(2)
|
expect(source.match(/:disabled="Boolean\(measurementStrategySavingId\) \|\| !can\('platform:firstleg:inbound:measure:edit'\)"/g)).toHaveLength(2)
|
||||||
expect(source).toContain('const previousMode = measurementSectionModes.outer')
|
expect(source).toContain('const previousMode = measurementSectionModes.outer')
|
||||||
expect(source).toContain('if (!saved) measurementSectionModes.outer = previousMode')
|
expect(source).toContain('if (!saved) measurementSectionModes.outer = previousMode')
|
||||||
expect(source).toContain('const saved = await updateMeasurementCalculationMode(null, mode)')
|
expect(source).toContain('const selectedSameMode = outerModeAtDropdownOpen.value === measurementSectionModes.outer')
|
||||||
|
expect(source).toContain('await nextTick()')
|
||||||
|
expect(source).toContain('const saved = await updateMeasurementCalculationMode(null, mode, ruleId)')
|
||||||
expect(source).toContain('calculatedMeasurementRow(row, measurementSectionModes.customer, true)')
|
expect(source).toContain('calculatedMeasurementRow(row, measurementSectionModes.customer, true)')
|
||||||
expect(source).toContain('calculatedMeasurementRow(source, measurementSectionModes.outer)')
|
expect(source).toContain('calculatedMeasurementRow(source, measurementSectionModes.outer)')
|
||||||
expect(source).toContain('calculatedMeasurementRow(row as unknown as Record<string, unknown>, measurementSectionModes.actual)')
|
expect(source).toContain('calculatedMeasurementRow(row as unknown as Record<string, unknown>, measurementSectionModes.actual, false, false)')
|
||||||
expect(source).toContain("return Number(hasOuterOverride && override != null && override !== '' ? override : value)")
|
expect(source).toContain("return Number(hasOuterOverride && override != null && override !== '' ? override : value)")
|
||||||
|
expect(source).toContain("const hasOuterOverride = useWarehouseOuterOverrides && !customerSource")
|
||||||
|
expect(source).not.toContain("!customerSource && mode !== 'ACTUAL'")
|
||||||
expect(source).toContain('rawDimensions.map((value) => value + rule.edgeAdditionCm * 2)')
|
expect(source).toContain('rawDimensions.map((value) => value + rule.edgeAdditionCm * 2)')
|
||||||
expect(source).toContain('matchedSpecificationCoefficient(chargeRule, calculationSource)')
|
expect(source).toContain('matchedSpecificationCoefficient(chargeRule, calculationSource)')
|
||||||
expect(source).toContain('listCustomerChargeRules(customerId, tenantId)')
|
expect(source).toContain('listCustomerChargeRules(customerId, tenantId)')
|
||||||
@@ -381,20 +393,38 @@ describe('UnifiedOrdersView contract', () => {
|
|||||||
expect(source).toContain("ElMessage.warning('外箱规数据不完整,请先保存外箱规尺寸和计算规则')")
|
expect(source).toContain("ElMessage.warning('外箱规数据不完整,请先保存外箱规尺寸和计算规则')")
|
||||||
expect(source).toContain("weightSource: 'OUTER'")
|
expect(source).toContain("weightSource: 'OUTER'")
|
||||||
expect(source).toContain("volumeSource: 'OUTER'")
|
expect(source).toContain("volumeSource: 'OUTER'")
|
||||||
|
expect(source).not.toContain('测量审核通过后才能确认并发送费用')
|
||||||
|
expect(source).not.toContain("reviewStatus !== 'APPROVED'")
|
||||||
|
expect(source).toContain('const hasActualMeasurementData = computed(() => (measurementDetail.value?.items?.length ?? 0) > 0)')
|
||||||
|
expect(source).toContain("暂无实际测量数据,不能确认并发送费用")
|
||||||
|
expect(source).toContain(':disabled="confirmFeeSending || measurementLoading || !hasActualMeasurementData || !canSendFeeForCurrentStatus"')
|
||||||
expect(source).toContain('selectAndSaveOuterMeasurementMode($event)')
|
expect(source).toContain('selectAndSaveOuterMeasurementMode($event)')
|
||||||
expect(source).toContain('const saved = await updateMeasurementCalculationMode(null, mode)')
|
expect(source).toContain('const saved = await updateMeasurementCalculationMode(null, mode, ruleId)')
|
||||||
expect(source).toContain('coefficientMode: targetIds.has(String(item.id)) ? mode')
|
expect(source).toContain('coefficientMode: targetIds.has(String(item.id)) ? mode')
|
||||||
|
expect(source).toContain('outerLengthCm: item.outerLengthCm')
|
||||||
|
expect(source).toContain('outerWidthCm: item.outerWidthCm')
|
||||||
|
expect(source).toContain('outerHeightCm: item.outerHeightCm')
|
||||||
|
expect(source).toContain("String(item.chargeRuleId) === String(activeRule!.id)")
|
||||||
|
expect(source).toContain('item.calculatedOuterLengthCm, item.calculatedOuterWidthCm, item.calculatedOuterHeightCm')
|
||||||
|
expect(source).toContain('if (dimensions.every((dimension) => dimension == null)) return true')
|
||||||
|
expect(source).toContain('已保存并绑定计算规则,体积已重新计算')
|
||||||
expect(source).not.toContain('feeConfirmVolume.value = Number(summary.measurement?.actualVolumeCbm)')
|
expect(source).not.toContain('feeConfirmVolume.value = Number(summary.measurement?.actualVolumeCbm)')
|
||||||
const sendFeeSource = source.slice(source.indexOf('async function executeConfirmAndSendFee()'), source.indexOf('function handleOrderSelectionChange'))
|
const sendFeeSource = source.slice(source.indexOf('async function executeConfirmAndSendFee()'), source.indexOf('function handleOrderSelectionChange'))
|
||||||
expect(sendFeeSource).not.toContain('saveInboundMeasurementDraft')
|
expect(sendFeeSource).not.toContain('saveInboundMeasurementDraft')
|
||||||
expect(sendFeeSource).not.toContain('updateMeasurementCalculationMode')
|
expect(sendFeeSource).not.toContain('updateMeasurementCalculationMode')
|
||||||
|
expect(source.slice(source.indexOf('async function openFeeConfirmDialog()'), source.indexOf('async function executeConfirmAndSendFee()'))).not.toContain('getLatestInboundFee')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('restores the saved outer-carton rule and server-calculated dimensions on re-entry', () => {
|
it('restores the saved outer-carton rule and server-calculated dimensions on re-entry', () => {
|
||||||
expect(source).toContain("mode is MeasurementCalculationMode => mode === 'ACTUAL' || mode === 'ADD' || mode === 'MULTIPLY'")
|
expect(source).toContain('const savedModes = [...new Set(response.items.map((item) => item.coefficientMode)')
|
||||||
expect(source).toContain('measurementSectionModes.outer = savedModes.length === 1 ? savedModes[0]!')
|
expect(source).toContain('measurementSectionModes.outer = savedModes.length === 1')
|
||||||
|
expect(source).toContain('? savedModes[0]!')
|
||||||
|
expect(source).toContain("configuredMode === 'ADD' ? 'ADD' : 'ACTUAL'")
|
||||||
expect(source).toContain('row.calculatedOuterLengthCm')
|
expect(source).toContain('row.calculatedOuterLengthCm')
|
||||||
expect(source).toContain('measurementCalculationModeForRow(source) === measurementSectionModes.outer')
|
expect(source).toContain('measurementCalculationModeForRow(source) === measurementSectionModes.outer')
|
||||||
|
expect(source).toContain("String(row.calculationRuleVersion || '').startsWith('WAREHOUSE_OUTER_OVERRIDE_')")
|
||||||
|
expect(source).toContain('warehouseOuterOverride')
|
||||||
|
expect(source).toContain('Math.abs(Number(value) - Number(recalculatedDimensions[index])) < 0.001')
|
||||||
expect(source).toContain('lengthCm: Number(savedDimensions[0])')
|
expect(source).toContain('lengthCm: Number(savedDimensions[0])')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ import {
|
|||||||
createFeeActionIdempotencyKey,
|
createFeeActionIdempotencyKey,
|
||||||
getInboundMeasurement,
|
getInboundMeasurement,
|
||||||
saveInboundMeasurementDraft,
|
saveInboundMeasurementDraft,
|
||||||
getLatestInboundFee,
|
|
||||||
InboundFeePanel,
|
InboundFeePanel,
|
||||||
InboundMeasurementDialog,
|
InboundMeasurementDialog,
|
||||||
MeasurementEvidenceThumbnail,
|
MeasurementEvidenceThumbnail,
|
||||||
@@ -188,6 +187,7 @@ type MeasurementCalculationMode = 'ACTUAL' | 'ADD' | 'MULTIPLY'
|
|||||||
const measurementSectionModes = reactive<Record<MeasurementSection, MeasurementCalculationMode>>({
|
const measurementSectionModes = reactive<Record<MeasurementSection, MeasurementCalculationMode>>({
|
||||||
customer: 'ACTUAL', outer: 'ACTUAL', actual: 'ACTUAL',
|
customer: 'ACTUAL', outer: 'ACTUAL', actual: 'ACTUAL',
|
||||||
})
|
})
|
||||||
|
const outerModeAtDropdownOpen = ref<MeasurementCalculationMode | null>(null)
|
||||||
const cargoImagePreviewUrls = new Set<string>()
|
const cargoImagePreviewUrls = new Set<string>()
|
||||||
const measurementDialogOpen = ref(false)
|
const measurementDialogOpen = ref(false)
|
||||||
const openWarehouseSuggestionOnMeasurementLoad = ref(false)
|
const openWarehouseSuggestionOnMeasurementLoad = ref(false)
|
||||||
@@ -202,7 +202,7 @@ const feeConfirmRows = computed(() => {
|
|||||||
const actualVolume = Number(measurement?.actualVolumeCbm)
|
const actualVolume = Number(measurement?.actualVolumeCbm)
|
||||||
const customerTotals = measurementTotals(customerMeasurementRows.value)
|
const customerTotals = measurementTotals(customerMeasurementRows.value)
|
||||||
return [
|
return [
|
||||||
{ source: '客户下单数据', count: summary?.totalPackageCount, weight: customerTotals.gross, volume: customerTotals.volume ?? summary?.expectedVolumeCbm },
|
{ source: '客户下单数据', count: customerTotals.count, weight: customerTotals.gross, volume: customerTotals.volume ?? summary?.expectedVolumeCbm },
|
||||||
{ source: '外箱规数据', count: measurementTotals(outerMeasurementRows.value).count, weight: measurementTotals(outerMeasurementRows.value).gross, volume: measurementTotals(outerMeasurementRows.value).volume },
|
{ source: '外箱规数据', count: measurementTotals(outerMeasurementRows.value).count, weight: measurementTotals(outerMeasurementRows.value).gross, volume: measurementTotals(outerMeasurementRows.value).volume },
|
||||||
{ source: '实际测量数据', count: measurementDetail.value?.actualPackageCount, weight: actualWeight, volume: actualVolume },
|
{ source: '实际测量数据', count: measurementDetail.value?.actualPackageCount, weight: actualWeight, volume: actualVolume },
|
||||||
]
|
]
|
||||||
@@ -324,8 +324,20 @@ function matchedSpecificationCoefficient(rule: CustomerChargeRule, row: Record<s
|
|||||||
return (matched || tiers[tiers.length - 1]).coefficient
|
return (matched || tiers[tiers.length - 1]).coefficient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 系数模式可使用客户名下任一启用规则;默认规则仅用于优先选择,不作为唯一条件。
|
||||||
const activeMeasurementChargeRule = computed(() => measurementChargeRules.value
|
const activeMeasurementChargeRule = computed(() => measurementChargeRules.value
|
||||||
.find((rule) => rule.defaultRule && rule.status === 'ACTIVE') || null)
|
.find((rule) => rule.defaultRule && rule.status === 'ACTIVE')
|
||||||
|
|| measurementChargeRules.value.find((rule) => rule.status === 'ACTIVE')
|
||||||
|
|| null)
|
||||||
|
const boundOuterMeasurementChargeRule = computed(() => {
|
||||||
|
const boundId = outerMeasurementRows.value
|
||||||
|
.map((row) => String(row.chargeRuleId || ''))
|
||||||
|
.find((id) => id)
|
||||||
|
return boundId
|
||||||
|
? measurementChargeRules.value.find((rule) => rule.id === boundId && rule.status === 'ACTIVE') || null
|
||||||
|
: null
|
||||||
|
})
|
||||||
|
const activeMeasurementChargeRules = computed(() => measurementChargeRules.value.filter((rule) => rule.status === 'ACTIVE'))
|
||||||
const multiplyModeDisabled = computed(() => !measurementChargeRulesLoading.value && !activeMeasurementChargeRule.value)
|
const multiplyModeDisabled = computed(() => !measurementChargeRulesLoading.value && !activeMeasurementChargeRule.value)
|
||||||
|
|
||||||
function roundedUp(value: number, digits: number) {
|
function roundedUp(value: number, digits: number) {
|
||||||
@@ -337,9 +349,10 @@ function calculatedMeasurementRow(
|
|||||||
source: Record<string, unknown>,
|
source: Record<string, unknown>,
|
||||||
mode: MeasurementCalculationMode,
|
mode: MeasurementCalculationMode,
|
||||||
customerSource = false,
|
customerSource = false,
|
||||||
) {
|
useWarehouseOuterOverrides = true,
|
||||||
|
): Record<string, unknown> {
|
||||||
const outerOverrides = [source.outerLengthCm, source.outerWidthCm, source.outerHeightCm]
|
const outerOverrides = [source.outerLengthCm, source.outerWidthCm, source.outerHeightCm]
|
||||||
const hasOuterOverride = !customerSource && mode !== 'ACTUAL'
|
const hasOuterOverride = useWarehouseOuterOverrides && !customerSource
|
||||||
&& outerOverrides.some((value) => value != null && value !== '')
|
&& outerOverrides.some((value) => value != null && value !== '')
|
||||||
const rawDimensions = [source.lengthCm, source.widthCm, source.heightCm].map((value, index) => {
|
const rawDimensions = [source.lengthCm, source.widthCm, source.heightCm].map((value, index) => {
|
||||||
const override = outerOverrides[index]
|
const override = outerOverrides[index]
|
||||||
@@ -374,7 +387,7 @@ function calculatedMeasurementRow(
|
|||||||
: null
|
: null
|
||||||
const sourceUnitWeight = customerSource
|
const sourceUnitWeight = customerSource
|
||||||
? Number(source.grossWeightKg) / packageCount
|
? Number(source.grossWeightKg) / packageCount
|
||||||
: Number.isFinite(Number(source.unitWeightKg)) ? Number(source.unitWeightKg) : fallbackMeasuredUnitWeight
|
: Number(source.unitWeightKg) > 0 ? Number(source.unitWeightKg) : fallbackMeasuredUnitWeight
|
||||||
const unitWeightKg = Number.isFinite(sourceUnitWeight) ? sourceUnitWeight : null
|
const unitWeightKg = Number.isFinite(sourceUnitWeight) ? sourceUnitWeight : null
|
||||||
const totalWeightKg = unitWeightKg != null && Number.isFinite(packageCount) ? unitWeightKg * packageCount : null
|
const totalWeightKg = unitWeightKg != null && Number.isFinite(packageCount) ? unitWeightKg * packageCount : null
|
||||||
return {
|
return {
|
||||||
@@ -391,11 +404,21 @@ const customerMeasurementRows = computed(() => (detail.value?.order.cargoes || [
|
|||||||
)))
|
)))
|
||||||
const outerMeasurementRows = computed(() => (measurementDetail.value?.items || []).map((row) => {
|
const outerMeasurementRows = computed(() => (measurementDetail.value?.items || []).map((row) => {
|
||||||
const source = row as unknown as Record<string, unknown>
|
const source = row as unknown as Record<string, unknown>
|
||||||
|
const recalculated = calculatedMeasurementRow(source, measurementSectionModes.outer)
|
||||||
const savedDimensions = [row.calculatedOuterLengthCm, row.calculatedOuterWidthCm, row.calculatedOuterHeightCm]
|
const savedDimensions = [row.calculatedOuterLengthCm, row.calculatedOuterWidthCm, row.calculatedOuterHeightCm]
|
||||||
|
const recalculatedDimensions = [recalculated.lengthCm, recalculated.widthCm, recalculated.heightCm]
|
||||||
|
const warehouseOuterOverride = String(row.calculationRuleVersion || '').startsWith('WAREHOUSE_OUTER_OVERRIDE_')
|
||||||
if (savedDimensions.every((value) => Number.isFinite(Number(value)) && Number(value) > 0)
|
if (savedDimensions.every((value) => Number.isFinite(Number(value)) && Number(value) > 0)
|
||||||
|
&& (warehouseOuterOverride
|
||||||
|
|| savedDimensions.every((value, index) => Math.abs(Number(value) - Number(recalculatedDimensions[index])) < 0.001))
|
||||||
&& measurementCalculationModeForRow(source) === measurementSectionModes.outer) {
|
&& measurementCalculationModeForRow(source) === measurementSectionModes.outer) {
|
||||||
const packageCount = Number(row.packageCount)
|
const packageCount = Number(row.packageCount)
|
||||||
const unitWeightKg = Number.isFinite(Number(row.unitWeightKg)) ? Number(row.unitWeightKg) : null
|
const unitWeightKg = Number(row.unitWeightKg) > 0
|
||||||
|
? Number(row.unitWeightKg)
|
||||||
|
: Number.isFinite(Number(measurementDetail.value?.actualGrossWeightKg))
|
||||||
|
&& Number(measurementDetail.value?.actualPackageCount) > 0
|
||||||
|
? Number(measurementDetail.value?.actualGrossWeightKg) / Number(measurementDetail.value?.actualPackageCount)
|
||||||
|
: null
|
||||||
return {
|
return {
|
||||||
...source,
|
...source,
|
||||||
lengthCm: Number(savedDimensions[0]),
|
lengthCm: Number(savedDimensions[0]),
|
||||||
@@ -405,11 +428,17 @@ const outerMeasurementRows = computed(() => (measurementDetail.value?.items || [
|
|||||||
totalWeightKg: unitWeightKg != null && Number.isFinite(packageCount) ? unitWeightKg * packageCount : null,
|
totalWeightKg: unitWeightKg != null && Number.isFinite(packageCount) ? unitWeightKg * packageCount : null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return calculatedMeasurementRow(source, measurementSectionModes.outer)
|
return recalculated
|
||||||
}))
|
}))
|
||||||
const actualMeasurementRows = computed(() => (measurementDetail.value?.items || []).map((row) => (
|
const actualMeasurementRows = computed(() => (measurementDetail.value?.items || []).map((row) => (
|
||||||
calculatedMeasurementRow(row as unknown as Record<string, unknown>, measurementSectionModes.actual)
|
calculatedMeasurementRow(row as unknown as Record<string, unknown>, measurementSectionModes.actual, false, false)
|
||||||
)))
|
)))
|
||||||
|
const hasActualMeasurementData = computed(() => (measurementDetail.value?.items?.length ?? 0) > 0)
|
||||||
|
const feeSendEditableStatuses = new Set([
|
||||||
|
'WAIT_MEASURE', 'MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED',
|
||||||
|
'WAIT_PRICING', 'FEE_CONFIRMING', 'WAIT_SPLIT',
|
||||||
|
])
|
||||||
|
const canSendFeeForCurrentStatus = computed(() => feeSendEditableStatuses.has(String(detail.value?.summary?.mainStatus || '')))
|
||||||
|
|
||||||
function updateMeasurementSectionMode(section: MeasurementSection, mode: MeasurementCalculationMode) {
|
function updateMeasurementSectionMode(section: MeasurementSection, mode: MeasurementCalculationMode) {
|
||||||
if (mode === 'MULTIPLY' && !activeMeasurementChargeRule.value) {
|
if (mode === 'MULTIPLY' && !activeMeasurementChargeRule.value) {
|
||||||
@@ -419,7 +448,7 @@ function updateMeasurementSectionMode(section: MeasurementSection, mode: Measure
|
|||||||
measurementSectionModes[section] = mode
|
measurementSectionModes[section] = mode
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectAndSaveOuterMeasurementMode(mode: MeasurementCalculationMode) {
|
async function selectAndSaveOuterMeasurementMode(mode: MeasurementCalculationMode, ruleId?: string) {
|
||||||
if (measurementStrategySavingId.value) return
|
if (measurementStrategySavingId.value) return
|
||||||
const previousMode = measurementSectionModes.outer
|
const previousMode = measurementSectionModes.outer
|
||||||
if (mode === 'MULTIPLY' && !activeMeasurementChargeRule.value) {
|
if (mode === 'MULTIPLY' && !activeMeasurementChargeRule.value) {
|
||||||
@@ -427,10 +456,25 @@ async function selectAndSaveOuterMeasurementMode(mode: MeasurementCalculationMod
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
measurementSectionModes.outer = mode
|
measurementSectionModes.outer = mode
|
||||||
const saved = await updateMeasurementCalculationMode(null, mode)
|
const saved = await updateMeasurementCalculationMode(null, mode, ruleId)
|
||||||
if (!saved) measurementSectionModes.outer = previousMode
|
if (!saved) measurementSectionModes.outer = previousMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleOuterModeDropdownVisibleChange(visible: boolean) {
|
||||||
|
if (visible) {
|
||||||
|
outerModeAtDropdownOpen.value = measurementSectionModes.outer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Element Plus may emit dropdown-close before change. Wait until the change handler has
|
||||||
|
// updated the mode/saving state, otherwise one click can submit both the old and new mode.
|
||||||
|
await nextTick()
|
||||||
|
const selectedSameMode = outerModeAtDropdownOpen.value === measurementSectionModes.outer
|
||||||
|
outerModeAtDropdownOpen.value = null
|
||||||
|
if (selectedSameMode && !measurementStrategySavingId.value) {
|
||||||
|
void selectAndSaveOuterMeasurementMode(measurementSectionModes.outer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const statusTabs = computed(() => [
|
const statusTabs = computed(() => [
|
||||||
{ value: '' as const, label: '全部' },
|
{ value: '' as const, label: '全部' },
|
||||||
...unifiedOrderStatusOptions,
|
...unifiedOrderStatusOptions,
|
||||||
@@ -842,7 +886,7 @@ function measurementCalculationModeForRow(row: Record<string, unknown>): Measure
|
|||||||
return measuredItem?.coefficientMode === 'MULTIPLY' ? 'MULTIPLY' : measuredItem?.coefficientMode === 'ADD' ? 'ADD' : 'ACTUAL'
|
return measuredItem?.coefficientMode === 'MULTIPLY' ? 'MULTIPLY' : measuredItem?.coefficientMode === 'ADD' ? 'ADD' : 'ACTUAL'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateMeasurementCalculationMode(row: Record<string, unknown> | null, mode: MeasurementCalculationMode): Promise<boolean> {
|
async function updateMeasurementCalculationMode(row: Record<string, unknown> | null, mode: MeasurementCalculationMode, selectedRuleId?: string): Promise<boolean> {
|
||||||
const summary = detail.value?.summary
|
const summary = detail.value?.summary
|
||||||
const measurement = measurementDetail.value
|
const measurement = measurementDetail.value
|
||||||
if (!summary?.inboundId || !measurement?.measurementId || measurement.version == null || !can('platform:firstleg:inbound:measure:edit')) return false
|
if (!summary?.inboundId || !measurement?.measurementId || measurement.version == null || !can('platform:firstleg:inbound:measure:edit')) return false
|
||||||
@@ -851,6 +895,9 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
|
|||||||
const targetIds = new Set(measurement.items
|
const targetIds = new Set(measurement.items
|
||||||
.filter((item) => !row || (itemId ? String(item.id) === itemId : normalizedIdentifier(item.itemNo) === itemNo))
|
.filter((item) => !row || (itemId ? String(item.id) === itemId : normalizedIdentifier(item.itemNo) === itemNo))
|
||||||
.map((item) => String(item.id)))
|
.map((item) => String(item.id)))
|
||||||
|
const targetItemNos = new Set(measurement.items
|
||||||
|
.filter((item) => targetIds.has(String(item.id)))
|
||||||
|
.map((item) => normalizedIdentifier(item.itemNo)))
|
||||||
if (!targetIds.size) {
|
if (!targetIds.size) {
|
||||||
ElMessage.warning('当前数据尚未关联实测明细,暂不能设置计算方式')
|
ElMessage.warning('当前数据尚未关联实测明细,暂不能设置计算方式')
|
||||||
return false
|
return false
|
||||||
@@ -858,7 +905,15 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
|
|||||||
measurementStrategySavingId.value = row ? (itemId || `item-${itemNo}`) : 'all'
|
measurementStrategySavingId.value = row ? (itemId || `item-${itemNo}`) : 'all'
|
||||||
try {
|
try {
|
||||||
await Promise.all([loadMeasurementChargeRules(), loadMeasurementDimensionConfig()])
|
await Promise.all([loadMeasurementChargeRules(), loadMeasurementDimensionConfig()])
|
||||||
const activeRule = measurementChargeRules.value.find((rule) => rule.defaultRule && rule.status === 'ACTIVE')
|
const existingRuleId = selectedRuleId || measurement.items
|
||||||
|
.map((item) => String(item.chargeRuleId || ''))
|
||||||
|
.find((id) => id)
|
||||||
|
const activeRule = measurementChargeRules.value.find((rule) => rule.id === existingRuleId && rule.status === 'ACTIVE')
|
||||||
|
|| activeMeasurementChargeRule.value
|
||||||
|
if (mode === 'MULTIPLY' && selectedRuleId && !activeRule) {
|
||||||
|
ElMessage.warning('请选择当前客户已启用的计费规则')
|
||||||
|
return false
|
||||||
|
}
|
||||||
const requiresMultiply = measurement.items.some((item) => (
|
const requiresMultiply = measurement.items.some((item) => (
|
||||||
targetIds.has(String(item.id)) ? mode : item.coefficientMode
|
targetIds.has(String(item.id)) ? mode : item.coefficientMode
|
||||||
) === 'MULTIPLY')
|
) === 'MULTIPLY')
|
||||||
@@ -870,7 +925,7 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
|
|||||||
measurementId: String(measurement.measurementId),
|
measurementId: String(measurement.measurementId),
|
||||||
version: measurement.version,
|
version: measurement.version,
|
||||||
actualGoodsName: measurement.actualGoodsName,
|
actualGoodsName: measurement.actualGoodsName,
|
||||||
items: measurement.items.map((item) => ({
|
items: measurement.items.filter((item) => Number(item.packageCount) > 0).map((item) => ({
|
||||||
inboundDetailId: item.inboundDetailId ?? undefined,
|
inboundDetailId: item.inboundDetailId ?? undefined,
|
||||||
boxCode: item.boxCode,
|
boxCode: item.boxCode,
|
||||||
itemNo: item.itemNo,
|
itemNo: item.itemNo,
|
||||||
@@ -884,11 +939,13 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
|
|||||||
machineNo: item.machineNo,
|
machineNo: item.machineNo,
|
||||||
measuredAt: item.deviceMeasuredAt,
|
measuredAt: item.deviceMeasuredAt,
|
||||||
overLimitWarning: item.overLimitWarning,
|
overLimitWarning: item.overLimitWarning,
|
||||||
|
// 人工外箱值是本次策略的计算基数,切换策略时必须一并保留。
|
||||||
outerLengthCm: item.outerLengthCm,
|
outerLengthCm: item.outerLengthCm,
|
||||||
outerWidthCm: item.outerWidthCm,
|
outerWidthCm: item.outerWidthCm,
|
||||||
outerHeightCm: item.outerHeightCm,
|
outerHeightCm: item.outerHeightCm,
|
||||||
chargeRuleId: (targetIds.has(String(item.id)) ? mode : item.coefficientMode) === 'MULTIPLY'
|
chargeRuleId: (targetIds.has(String(item.id)) ? mode : item.coefficientMode) === 'MULTIPLY'
|
||||||
? activeRule!.id
|
? (targetIds.has(String(item.id)) || !measurementChargeRules.value.some((rule) => rule.id === String(item.chargeRuleId) && rule.status === 'ACTIVE')
|
||||||
|
? activeRule!.id : item.chargeRuleId)
|
||||||
: null,
|
: null,
|
||||||
coefficientMode: targetIds.has(String(item.id)) ? mode : item.coefficientMode || 'ACTUAL',
|
coefficientMode: targetIds.has(String(item.id)) ? mode : item.coefficientMode || 'ACTUAL',
|
||||||
remark: item.remark,
|
remark: item.remark,
|
||||||
@@ -896,7 +953,24 @@ async function updateMeasurementCalculationMode(row: Record<string, unknown> | n
|
|||||||
})
|
})
|
||||||
measurementLoadedKey.value = ''
|
measurementLoadedKey.value = ''
|
||||||
await loadMeasurementDetails()
|
await loadMeasurementDetails()
|
||||||
ElMessage.success(`${row ? '当前数据' : '全部测量数据'}的计算方式已更新,体积已重新计算`)
|
const persistedItems = measurementDetail.value?.items
|
||||||
|
.filter((item) => targetItemNos.has(normalizedIdentifier(item.itemNo))) || []
|
||||||
|
const persisted = persistedItems.length === targetItemNos.size && persistedItems.every((item) => (
|
||||||
|
item.coefficientMode === mode
|
||||||
|
&& (mode !== 'MULTIPLY' || String(item.chargeRuleId) === String(activeRule!.id))
|
||||||
|
&& [item.calculatedOuterLengthCm, item.calculatedOuterWidthCm, item.calculatedOuterHeightCm]
|
||||||
|
.every((value, index, dimensions) => {
|
||||||
|
// 旧服务实例可能尚未返回新版计算结果字段;计算方式已回读成功时不能误判并回滚下拉。
|
||||||
|
if (dimensions.every((dimension) => dimension == null)) return true
|
||||||
|
if (value == null) return false
|
||||||
|
const expected = calculatedMeasurementRow(item as unknown as Record<string, unknown>, mode)
|
||||||
|
return Math.abs(Number(value) - Number([expected.lengthCm, expected.widthCm, expected.heightCm][index])) < 0.001
|
||||||
|
})
|
||||||
|
))
|
||||||
|
if (!persisted) {
|
||||||
|
throw new Error('计算策略保存后回读不一致,请刷新后重试')
|
||||||
|
}
|
||||||
|
ElMessage.success(`${row ? '当前数据' : '全部测量数据'}已保存并绑定计算规则,体积已重新计算`)
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
measurementLoadedKey.value = ''
|
measurementLoadedKey.value = ''
|
||||||
@@ -924,10 +998,6 @@ async function saveWarehouseOuterDimensions(row: Record<string, unknown>) {
|
|||||||
|| !can('platform:firstleg:inbound:measure:edit')) return
|
|| !can('platform:firstleg:inbound:measure:edit')) return
|
||||||
const dimensions = [row.outerLengthCm, row.outerWidthCm, row.outerHeightCm]
|
const dimensions = [row.outerLengthCm, row.outerWidthCm, row.outerHeightCm]
|
||||||
.map((value) => value == null || value === '' ? null : Number(value))
|
.map((value) => value == null || value === '' ? null : Number(value))
|
||||||
if (dimensions.every((value) => value == null)) {
|
|
||||||
ElMessage.warning('请至少输入一项需要修改的外箱尺寸')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (dimensions.some((value) => value != null && (!Number.isFinite(value) || value <= 0))) {
|
if (dimensions.some((value) => value != null && (!Number.isFinite(value) || value <= 0))) {
|
||||||
ElMessage.warning('仓库修改外箱尺寸必须大于 0')
|
ElMessage.warning('仓库修改外箱尺寸必须大于 0')
|
||||||
return
|
return
|
||||||
@@ -942,7 +1012,7 @@ async function saveWarehouseOuterDimensions(row: Record<string, unknown>) {
|
|||||||
measurementId: String(measurement.measurementId),
|
measurementId: String(measurement.measurementId),
|
||||||
version: measurement.version,
|
version: measurement.version,
|
||||||
actualGoodsName: measurement.actualGoodsName,
|
actualGoodsName: measurement.actualGoodsName,
|
||||||
items: measurement.items.map((item) => ({
|
items: measurement.items.filter((item) => Number(item.packageCount) > 0).map((item) => ({
|
||||||
inboundDetailId: item.inboundDetailId ?? undefined,
|
inboundDetailId: item.inboundDetailId ?? undefined,
|
||||||
boxCode: item.boxCode,
|
boxCode: item.boxCode,
|
||||||
itemNo: item.itemNo,
|
itemNo: item.itemNo,
|
||||||
@@ -1010,9 +1080,9 @@ async function loadMeasurementChargeRules() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMeasurementDimensionConfig() {
|
async function loadMeasurementDimensionConfig(force = false) {
|
||||||
const tenantId = normalizedIdentifier(detail.value?.summary.tenantId)
|
const tenantId = normalizedIdentifier(detail.value?.summary.tenantId)
|
||||||
if (!tenantId || measurementDimensionConfigLoadedKey.value === tenantId || measurementDimensionConfigLoading.value) return
|
if (!tenantId || (!force && measurementDimensionConfigLoadedKey.value === tenantId) || measurementDimensionConfigLoading.value) return
|
||||||
measurementDimensionConfigLoading.value = true
|
measurementDimensionConfigLoading.value = true
|
||||||
try {
|
try {
|
||||||
measurementDimensionConfig.value = await getCustomerDimensionConfig(tenantId)
|
measurementDimensionConfig.value = await getCustomerDimensionConfig(tenantId)
|
||||||
@@ -1089,18 +1159,23 @@ async function loadMeasurementDetails() {
|
|||||||
const [response] = await Promise.all([
|
const [response] = await Promise.all([
|
||||||
getInboundMeasurement(String(summary.tenantId), String(summary.inboundId)),
|
getInboundMeasurement(String(summary.tenantId), String(summary.inboundId)),
|
||||||
loadMeasurementChargeRules(),
|
loadMeasurementChargeRules(),
|
||||||
loadMeasurementDimensionConfig(),
|
loadMeasurementDimensionConfig(true),
|
||||||
])
|
])
|
||||||
if (serial !== measurementRequestSerial) return
|
if (serial !== measurementRequestSerial) return
|
||||||
measurementDetail.value = response
|
measurementDetail.value = response
|
||||||
const savedModes = [...new Set(response.items.map((item) => item.coefficientMode)
|
|
||||||
.filter((mode): mode is MeasurementCalculationMode => mode === 'ACTUAL' || mode === 'ADD' || mode === 'MULTIPLY'))]
|
|
||||||
const configuredMode = measurementDimensionConfig.value?.defaultCalculationMode
|
const configuredMode = measurementDimensionConfig.value?.defaultCalculationMode
|
||||||
// 已保存的明细规则优先于客户默认配置,确保再次进入时回显上次选择及其计算结果。
|
const savedModes = [...new Set(response.items.map((item) => item.coefficientMode)
|
||||||
measurementSectionModes.outer = savedModes.length === 1 ? savedModes[0]!
|
.filter((mode): mode is MeasurementCalculationMode => (
|
||||||
: configuredMode === 'MULTIPLY' && activeMeasurementChargeRule.value ? 'MULTIPLY'
|
mode === 'ACTUAL' || mode === 'ADD' || mode === 'MULTIPLY'
|
||||||
: configuredMode === 'ADD' ? 'ADD' : 'ACTUAL'
|
)))]
|
||||||
measurementSectionModes.customer = measurementSectionModes.outer
|
// 订单测量明细中已落库的规则是本次计费快照,必须优先于客户当前默认规则。
|
||||||
|
// 客户默认值只用于尚未绑定规则的新测量数据,避免保存后又被覆盖回 ACTUAL。
|
||||||
|
measurementSectionModes.outer = savedModes.length === 1
|
||||||
|
? savedModes[0]!
|
||||||
|
: configuredMode === 'MULTIPLY' && activeMeasurementChargeRule.value
|
||||||
|
? 'MULTIPLY' : configuredMode === 'ADD' ? 'ADD' : 'ACTUAL'
|
||||||
|
measurementSectionModes.customer = configuredMode === 'MULTIPLY' && activeMeasurementChargeRule.value
|
||||||
|
? 'MULTIPLY' : configuredMode === 'ADD' ? 'ADD' : 'ACTUAL'
|
||||||
measurementSectionModes.actual = 'ACTUAL'
|
measurementSectionModes.actual = 'ACTUAL'
|
||||||
measurementLoadedKey.value = key
|
measurementLoadedKey.value = key
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1331,26 +1406,19 @@ function openMeasurement(row: UnifiedOrderResource) {
|
|||||||
|
|
||||||
async function openFeeConfirmDialog() {
|
async function openFeeConfirmDialog() {
|
||||||
const summary = detail.value?.summary
|
const summary = detail.value?.summary
|
||||||
const reviewStatus = String(summary?.measurement?.reviewStatus ?? '').trim().toUpperCase()
|
|
||||||
if (reviewStatus !== 'APPROVED') {
|
|
||||||
ElMessage.warning('测量审核通过后才能确认并发送费用')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!summary?.inboundId) {
|
if (!summary?.inboundId) {
|
||||||
ElMessage.warning('当前单据缺少入仓信息,暂不能发送费用')
|
ElMessage.warning('当前单据缺少入仓信息,暂不能发送费用')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (confirmFeeSending.value) {
|
if (!hasActualMeasurementData.value) {
|
||||||
|
ElMessage.warning('暂无实际测量数据,不能确认并发送费用')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
if (!canSendFeeForCurrentStatus.value) {
|
||||||
const snapshot = await getLatestInboundFee(String(summary.tenantId), String(summary.inboundId))
|
ElMessage.warning('订单已进入装柜流程,不能再修改或发送费用')
|
||||||
if (snapshot.customerConfirmationStatus === 'CONFIRMED' || snapshot.customerConfirmedAt) {
|
return
|
||||||
ElMessage.warning('客户已确认该费用,费用已锁定,不允许再修改或重新发送')
|
}
|
||||||
return
|
if (confirmFeeSending.value) {
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error(getApiErrorDetails(error, '费用状态查询失败,请刷新后重试').message)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (measurementStrategySavingId.value) {
|
if (measurementStrategySavingId.value) {
|
||||||
@@ -1358,12 +1426,16 @@ async function openFeeConfirmDialog() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const outerTotals = measurementTotals(outerMeasurementRows.value)
|
const outerTotals = measurementTotals(outerMeasurementRows.value)
|
||||||
if (!outerMeasurementRows.value.length || !(Number(outerTotals.gross) > 0)
|
if (!outerMeasurementRows.value.length || !(Number(outerTotals.volume) > 0)) {
|
||||||
|| !(Number(outerTotals.volume) > 0)) {
|
|
||||||
ElMessage.warning('外箱规数据不完整,请先保存外箱规尺寸和计算规则')
|
ElMessage.warning('外箱规数据不完整,请先保存外箱规尺寸和计算规则')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
feeConfirmWeight.value = Number(outerTotals.gross)
|
const actualTotals = measurementTotals(actualMeasurementRows.value)
|
||||||
|
const customerTotals = measurementTotals(customerMeasurementRows.value)
|
||||||
|
const defaultWeight = Number(outerTotals.gross) > 0 ? Number(outerTotals.gross)
|
||||||
|
: Number(actualTotals.gross) > 0 ? Number(actualTotals.gross)
|
||||||
|
: Number(customerTotals.gross) > 0 ? Number(customerTotals.gross) : null
|
||||||
|
feeConfirmWeight.value = defaultWeight
|
||||||
feeConfirmVolume.value = Number(outerTotals.volume)
|
feeConfirmVolume.value = Number(outerTotals.volume)
|
||||||
feeConfirmOpen.value = true
|
feeConfirmOpen.value = true
|
||||||
}
|
}
|
||||||
@@ -1381,21 +1453,13 @@ async function executeConfirmAndSendFee() {
|
|||||||
try {
|
try {
|
||||||
const tenantId = String(summary.tenantId)
|
const tenantId = String(summary.tenantId)
|
||||||
const inboundId = String(summary.inboundId)
|
const inboundId = String(summary.inboundId)
|
||||||
let snapshot = await getLatestInboundFee(tenantId, inboundId)
|
let snapshot = await calculateInboundFee(tenantId, inboundId, {
|
||||||
if (snapshot.customerConfirmationStatus === 'CONFIRMED' || snapshot.customerConfirmedAt) {
|
selectedWeightKg,
|
||||||
ElMessage.warning('客户已确认该费用,费用已锁定,不允许再修改或重新发送')
|
selectedVolumeCbm,
|
||||||
feeConfirmOpen.value = false
|
weightSource: 'OUTER',
|
||||||
return
|
volumeSource: 'OUTER',
|
||||||
}
|
}, createFeeActionIdempotencyKey('calculate'))
|
||||||
if (snapshot.feeStatus === 'CALCULATED') {
|
snapshot = await confirmInboundFee(tenantId, inboundId, createFeeActionIdempotencyKey('confirm'))
|
||||||
snapshot = await calculateInboundFee(tenantId, inboundId, {
|
|
||||||
selectedWeightKg,
|
|
||||||
selectedVolumeCbm,
|
|
||||||
weightSource: 'OUTER',
|
|
||||||
volumeSource: 'OUTER',
|
|
||||||
}, createFeeActionIdempotencyKey('calculate'))
|
|
||||||
snapshot = await confirmInboundFee(tenantId, inboundId, createFeeActionIdempotencyKey('confirm'))
|
|
||||||
}
|
|
||||||
if (snapshot.feeStatus === 'CONFIRMED') {
|
if (snapshot.feeStatus === 'CONFIRMED') {
|
||||||
await sendInboundFee(tenantId, inboundId, createFeeActionIdempotencyKey('send'))
|
await sendInboundFee(tenantId, inboundId, createFeeActionIdempotencyKey('send'))
|
||||||
ElMessage.success('费用已确认并发送给客户')
|
ElMessage.success('费用已确认并发送给客户')
|
||||||
@@ -1403,8 +1467,7 @@ async function executeConfirmAndSendFee() {
|
|||||||
await loadDetail(true)
|
await loadDetail(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (snapshot.feeStatus === 'SENT') ElMessage.info('该费用已经发送给客户,无需重复发送')
|
ElMessage.warning('当前费用状态不允许发送,请刷新后重试')
|
||||||
else ElMessage.warning('当前没有可发送的试算费用,请先在费用信息中完成费用试算')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error(getApiErrorDetails(error, '确认费用发送失败').message)
|
ElMessage.error(getApiErrorDetails(error, '确认费用发送失败').message)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1746,10 +1809,8 @@ async function loadDetail(force = false) {
|
|||||||
measurementChargeRules.value = []
|
measurementChargeRules.value = []
|
||||||
measurementChargeRulesLoadedKey.value = ''
|
measurementChargeRulesLoadedKey.value = ''
|
||||||
}
|
}
|
||||||
if (measurementDimensionConfigLoadedKey.value !== tenantId) {
|
measurementDimensionConfig.value = null
|
||||||
measurementDimensionConfig.value = null
|
measurementDimensionConfigLoadedKey.value = ''
|
||||||
measurementDimensionConfigLoadedKey.value = ''
|
|
||||||
}
|
|
||||||
measurementPreviewOpen.value = false
|
measurementPreviewOpen.value = false
|
||||||
releaseMeasurementPreview()
|
releaseMeasurementPreview()
|
||||||
releaseCargoImagePreviews()
|
releaseCargoImagePreviews()
|
||||||
@@ -2030,7 +2091,8 @@ onBeforeUnmount(() => {
|
|||||||
v-if="can('firstleg:inbound:confirm-send-fee')"
|
v-if="can('firstleg:inbound:confirm-send-fee')"
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="confirmFeeSending"
|
:loading="confirmFeeSending"
|
||||||
:title="detail.summary.measurement?.reviewStatus === 'APPROVED' ? '确认并发送费用' : '测量审核通过后才能发送费用'"
|
:disabled="confirmFeeSending || measurementLoading || !hasActualMeasurementData || !canSendFeeForCurrentStatus"
|
||||||
|
title="确认并发送费用"
|
||||||
data-testid="confirm-send-fee-button"
|
data-testid="confirm-send-fee-button"
|
||||||
@click="openFeeConfirmDialog"
|
@click="openFeeConfirmDialog"
|
||||||
><Send :size="15" />确认费用发送</el-button>
|
><Send :size="15" />确认费用发送</el-button>
|
||||||
@@ -2058,12 +2120,23 @@ onBeforeUnmount(() => {
|
|||||||
</section>
|
</section>
|
||||||
<div class="measurement-rule-selector">
|
<div class="measurement-rule-selector">
|
||||||
<span class="measurement-rule-selector__label">箱规计算规则选择</span>
|
<span class="measurement-rule-selector__label">箱规计算规则选择</span>
|
||||||
<el-select class="measurement-rule-selector__control" :model-value="measurementSectionModes.outer" aria-label="外箱规数据计算方式" :loading="Boolean(measurementStrategySavingId)" :disabled="Boolean(measurementStrategySavingId) || !can('platform:firstleg:inbound:measure:edit')" @change="selectAndSaveOuterMeasurementMode($event)">
|
<el-select class="measurement-rule-selector__control" :model-value="measurementSectionModes.outer" aria-label="外箱规数据计算方式" :loading="Boolean(measurementStrategySavingId)" :disabled="Boolean(measurementStrategySavingId) || !can('platform:firstleg:inbound:measure:edit')" @change="selectAndSaveOuterMeasurementMode($event)" @visible-change="handleOuterModeDropdownVisibleChange($event)">
|
||||||
<el-option label="实测原尺寸(不调整)" value="ACTUAL" />
|
<el-option label="实测原尺寸(不调整)" value="ACTUAL" />
|
||||||
<el-option label="箱规尺寸" value="ADD" />
|
<el-option label="箱规尺寸" value="ADD" />
|
||||||
<el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" />
|
<el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<span class="measurement-rule-selector__hint">规则参数来源:客户管理</span>
|
<el-select
|
||||||
|
v-if="measurementSectionModes.outer === 'MULTIPLY'"
|
||||||
|
class="measurement-rule-selector__rule"
|
||||||
|
:model-value="boundOuterMeasurementChargeRule?.id"
|
||||||
|
:disabled="Boolean(measurementStrategySavingId) || !activeMeasurementChargeRules.length"
|
||||||
|
placeholder="选择系数规则"
|
||||||
|
aria-label="箱规系数规则"
|
||||||
|
@change="selectAndSaveOuterMeasurementMode('MULTIPLY', $event)"
|
||||||
|
>
|
||||||
|
<el-option v-for="rule in activeMeasurementChargeRules" :key="rule.id" :label="`${rule.ruleCode} v${rule.ruleVersion}`" :value="rule.id" />
|
||||||
|
</el-select>
|
||||||
|
<span class="measurement-rule-selector__hint">规则参数来源:客户管理<span v-if="boundOuterMeasurementChargeRule"> · 当前绑定:{{ boundOuterMeasurementChargeRule.ruleCode }} v{{ boundOuterMeasurementChargeRule.ruleVersion }}</span></span>
|
||||||
</div>
|
</div>
|
||||||
<section class="measurement-section">
|
<section class="measurement-section">
|
||||||
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.actual" @click="toggleMeasurementSection('actual')"><h3>三、实际测量数据</h3><span class="measurement-section-summary"><span>总件数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(actualMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.actual" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.actual" aria-label="实际测量数据计算方式" @change="updateMeasurementSectionMode('actual', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
<div class="measurement-section-title"><button type="button" class="measurement-section-toggle" :aria-expanded="!measurementCollapsed.actual" @click="toggleMeasurementSection('actual')"><h3>三、实际测量数据</h3><span class="measurement-section-summary"><span>总件数:{{ measurementTotals(actualMeasurementRows).count ?? '—' }}</span><span>总毛重:{{ formatNumber(measurementTotals(actualMeasurementRows).gross) }}</span><span>总体积:{{ formatNumber(measurementTotals(actualMeasurementRows).volume, 6) }}</span><span>总材积重:{{ formatNumber(measurementTotals(actualMeasurementRows).volumetric) }}</span><span>重量体积:{{ formatNumber(measurementTotals(actualMeasurementRows).weightVolume, 6) }}</span></span><ChevronUp v-if="!measurementCollapsed.actual" :size="18" /><ChevronDown v-else :size="18" /></button><el-select class="measurement-section-mode" :model-value="measurementSectionModes.actual" aria-label="实际测量数据计算方式" @change="updateMeasurementSectionMode('actual', $event)"><el-option label="实测原尺寸(不调整)" value="ACTUAL" /><el-option label="箱规尺寸" value="ADD" /><el-option label="箱规系数" value="MULTIPLY" :disabled="multiplyModeDisabled" /></el-select></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user