feat: simplify same-carton SKU editing

This commit is contained in:
2026-09-11 16:50:23 +08:00
parent ec7f76fede
commit 23cef8e106
4 changed files with 106 additions and 18 deletions
@@ -185,10 +185,15 @@ test('counts one physical carton series for SKU lines in the same mixed-carton g
const first = { ...validDraft().items[0], packingGroupNo: 'A', packageCount: 10, pieces: 20, unitsPerPackage: 2 }
const second = { ...first, id: 'cargo-2', sku: 'SKU-002', pieces: 30, unitsPerPackage: 3 }
assert.equal(summarizeFirstLegCargo([first, second]).packageCount, 10)
assert.deepEqual(summarizeFirstLegCargo([first, second]), {
pieces: 50,
packageCount: 10,
grossWeightKg: first.grossWeightKg,
volumeCbm: first.volumeCbm,
})
const draft = { ...validDraft(), fulfillmentType: 'ONE_CLICK_DROPSHIP', items: [first, second] }
assert.ok(!validateFirstLegOrderDraft(draft).some((message) => message.includes('混箱组合')))
assert.ok(!validateFirstLegOrderDraft(draft).some((message) => message.includes('同箱 SKU')))
second.packageCount = 9
assert.ok(validateFirstLegOrderDraft(draft).includes('混箱组合 A 的箱数、包装类型和箱规必须一致'))
assert.ok(validateFirstLegOrderDraft(draft).includes('同箱 SKU 的箱数、包装类型、箱规、重量和体积必须一致'))
})
+8 -5
View File
@@ -150,8 +150,10 @@ export function validateFirstLegOrderDraft(draft: FirstLegOrderDraft) {
const first = groups.get(group)
if (!first) groups.set(group, item)
else if (first.packageCount !== item.packageCount || first.packageType !== item.packageType
|| first.lengthCm !== item.lengthCm || first.widthCm !== item.widthCm || first.heightCm !== item.heightCm) {
errors.push(`混箱组合 ${group} 的箱数、包装类型和箱规必须一致`)
|| first.lengthCm !== item.lengthCm || first.widthCm !== item.widthCm || first.heightCm !== item.heightCm
|| first.grossWeightKg !== item.grossWeightKg || first.netWeightKg !== item.netWeightKg
|| first.volumeCbm !== item.volumeCbm) {
errors.push(`同箱 SKU 的箱数、包装类型、箱规、重量和体积必须一致`)
}
})
}
@@ -164,13 +166,14 @@ export function summarizeFirstLegCargo(items: FirstLegCargoItem[]) {
return items.reduce(
(summary, item) => {
const group = item.packingGroupNo.trim()
const packageCount = group && countedGroups.has(group) ? 0 : item.packageCount
const alreadyCounted = Boolean(group && countedGroups.has(group))
const packageCount = alreadyCounted ? 0 : item.packageCount
if (group) countedGroups.add(group)
return ({
pieces: summary.pieces + item.pieces,
packageCount: summary.packageCount + packageCount,
grossWeightKg: summary.grossWeightKg + item.grossWeightKg,
volumeCbm: summary.volumeCbm + item.volumeCbm,
grossWeightKg: summary.grossWeightKg + (alreadyCounted ? 0 : item.grossWeightKg),
volumeCbm: summary.volumeCbm + (alreadyCounted ? 0 : item.volumeCbm),
})
},
{ pieces: 0, packageCount: 0, grossWeightKg: 0, volumeCbm: 0 },
@@ -357,6 +357,13 @@ test('distills order creation into the confirmed prototype sections with server
assert.doesNotMatch(source, /未填写行标识/)
assert.match(source, /batchImportError[\s\S]*requestId[\s\S]*traceId/)
assert.match(source, /添加行[\s\S]*删除所选行/)
assert.match(source, /添加 SKU/)
assert.match(source, /addSkuToPackingGroup/)
assert.match(source, /shared-carton-value">同箱/)
assert.match(source, /syncSkuQuantity/)
assert.match(source, /每个箱组默认装一个 SKU/)
assert.doesNotMatch(source, /v-model="item\.packingGroupNo"/)
assert.doesNotMatch(cargoTableSource, /<th[^>]*>混箱组合/)
assert.doesNotMatch(source, /货物明细导入模板\.xls/)
for (const heading of ['SKU NO.', '中文品名', '箱规长 cm', '总毛重', '报关 HS CODE', '货件编号', '跟踪编号', 'UPC NO.']) {
assert.ok(cargoTableSource.includes(heading))
@@ -215,6 +215,7 @@ const destinationWarehousesLoading = ref(false)
const pageDataLoading = computed(() => supportingDataLoading.value || (!isCreatePage.value && store.loading))
const editorBaseline = ref('')
let cargoSequence = 0
let packingGroupSequence = 0
let inboundBillingRequestSequence = 0
let channelRequestSequence = 0
let estimateRequestSequence = 0
@@ -1332,6 +1333,71 @@ function addCargoItem() {
resetEditorValidation()
}
const sharedPackingFields = [
'packageType', 'packageCount', 'lengthCm', 'widthCm', 'heightCm',
'grossWeightKg', 'netWeightKg', 'volumeCbm',
] as const
function packingGroupFor(item: FirstLegCargoItem) {
return item.packingGroupNo.trim()
}
function mixedSkuCount(item: FirstLegCargoItem) {
const group = packingGroupFor(item)
return group ? form.items.filter((candidate) => packingGroupFor(candidate) === group).length : 1
}
function isAdditionalMixedSku(item: FirstLegCargoItem, index: number) {
const group = packingGroupFor(item)
return Boolean(group && form.items.slice(0, index).some((candidate) => packingGroupFor(candidate) === group))
}
function syncSkuQuantity(item: FirstLegCargoItem) {
if (Number.isInteger(item.unitsPerPackage) && item.unitsPerPackage > 0
&& Number.isInteger(item.packageCount) && item.packageCount > 0) {
item.pieces = item.unitsPerPackage * item.packageCount
}
}
function syncPackingGroupFields(source: FirstLegCargoItem) {
if (form.fulfillmentType !== 'ONE_CLICK_DROPSHIP') return
syncSkuQuantity(source)
const group = packingGroupFor(source)
if (!group) return
form.items.forEach((item) => {
if (item === source || packingGroupFor(item) !== group) return
sharedPackingFields.forEach((field) => { item[field] = source[field] as never })
syncSkuQuantity(item)
})
}
function normalizeSingleSkuGroups() {
const counts = new Map<string, number>()
form.items.forEach((item) => {
const group = packingGroupFor(item)
if (group) counts.set(group, (counts.get(group) ?? 0) + 1)
})
form.items.forEach((item) => {
const group = packingGroupFor(item)
if (group && counts.get(group) === 1) item.packingGroupNo = ''
})
}
function addSkuToPackingGroup(index: number) {
const source = form.items[index]
if (!source) return
const group = packingGroupFor(source) || `PG-${Date.now().toString(36)}-${++packingGroupSequence}`
source.packingGroupNo = group
const added = createEmptyCargoItem(`new-cargo-${++cargoSequence}`)
added.packingGroupNo = group
sharedPackingFields.forEach((field) => { added[field] = source[field] as never })
syncSkuQuantity(added)
let insertAt = index + 1
while (insertAt < form.items.length && packingGroupFor(form.items[insertAt]!) === group) insertAt++
form.items.splice(insertAt, 0, added)
resetEditorValidation()
}
function removeCargoItem(index: number) {
if (form.items.length === 1) {
ElMessage.warning('订单至少需要保留一条货物明细')
@@ -1339,6 +1405,7 @@ function removeCargoItem(index: number) {
}
const [removed] = form.items.splice(index, 1)
if (removed) releaseProductImagePreviews([removed])
normalizeSingleSkuGroups()
resetEditorValidation()
}
@@ -1503,6 +1570,7 @@ function removeSelectedCargoItems() {
const selectedIds = new Set(selectedCargoIds.value)
const removedItems = form.items.filter((item) => selectedIds.has(item.id))
form.items.splice(0, form.items.length, ...form.items.filter((item) => !selectedIds.has(item.id)))
normalizeSingleSkuGroups()
releaseProductImagePreviews(removedItems)
selectedCargoIds.value = []
resetEditorValidation()
@@ -1969,32 +2037,31 @@ async function searchOrders(model: QueryModel) {
<span v-else class="cargo-table-hint">可逐行维护货物明细。</span>
<div class="cargo-table-actions"><el-button @click="addCargoItem"><Plus :size="16" />添加行</el-button><el-button type="danger" plain :disabled="!selectedCargoIds.length" @click="removeSelectedCargoItems"><Trash2 :size="15" />删除所选行</el-button></div>
</div>
<div class="cargo-table-wrap">
<div class="cargo-table-wrap" :class="{ 'is-dropship': form.fulfillmentType === 'ONE_CLICK_DROPSHIP' }">
<table class="cargo-edit-table">
<thead><tr>
<th class="cargo-check-cell"><el-checkbox :model-value="allCargoSelected" :indeterminate="someCargoSelected" aria-label="全选货物" @change="toggleAllCargoItems" /></th><th class="cargo-index-cell">#</th>
<th>SKU NO.<small>SKU必填</small></th><th v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP'">混箱组合<small>同号为同一批箱</small></th><th>中文品名</th><th class="cargo-image-column">图片</th><th>英文品名</th><th>报关 HS CODE</th><th>清关 HS CODE</th><th>包装类型</th><th>材质</th><th class="cargo-switch-column">危险品</th><th class="cargo-switch-column">木制品<small>勾选后须商检</small></th>
<th>SKU NO.<small>SKU必填</small></th><th>中文品名</th><th class="cargo-image-column">图片</th><th>英文品名</th><th>报关 HS CODE</th><th>清关 HS CODE</th><th>包装类型</th><th>材质</th><th class="cargo-switch-column">危险品</th><th class="cargo-switch-column">木制品<small>勾选后须商检</small></th>
<th class="cargo-number-column">箱规长 cm</th><th class="cargo-number-column">箱规宽 cm</th><th class="cargo-number-column">箱规高 cm</th><th class="cargo-number-column">每箱数量</th><th class="cargo-number-column">箱数</th><th class="cargo-number-column"><span class="required-field-mark" aria-hidden="true">*</span>总毛重</th><th class="cargo-number-column"><span class="required-field-mark" aria-hidden="true">*</span>总体积</th><th class="cargo-number-column">总数量</th><th class="cargo-number-column">总净重</th>
<th>清关申报单价 USD</th><th>清关申报金额 USD</th><th>用途</th><th>型号</th><th>品牌</th><th class="cargo-dimensions-column">产品规格 长×宽×高</th><th>货件编号<small>货件编号必填</small></th><th>跟踪编号<small>跟踪编号必填</small></th><th>UPC NO.</th><th>备注</th><th class="cargo-operation-column">操作</th>
</tr></thead>
<tbody><tr v-for="(item, index) in form.items" :key="item.id">
<tbody><tr v-for="(item, index) in form.items" :key="item.id" :class="{ 'mixed-sku-detail-row': isAdditionalMixedSku(item, index) }">
<td class="cargo-check-cell"><el-checkbox :model-value="selectedCargoIds.includes(item.id)" :aria-label="`选择货物 ${index + 1}`" @change="toggleCargoItem(item.id, $event)" /></td><td class="cargo-index-cell">{{ index + 1 }}</td>
<td><el-form-item :prop="`items.${index}.sku`" :required="form.destinationType === 'FBA' || form.fulfillmentType === 'ONE_CLICK_DROPSHIP'" :rules="form.destinationType === 'FBA' || form.fulfillmentType === 'ONE_CLICK_DROPSHIP' ? requiredTextRules('请输入 SKU NO.') : undefined"><el-input v-model="item.sku" maxlength="64" placeholder="SKU编号" /></el-form-item></td>
<td v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP'"><el-input v-model="item.packingGroupNo" maxlength="32" placeholder="如 A(选填)" /></td>
<td><el-form-item :prop="`items.${index}.sku`" :required="form.destinationType === 'FBA' || form.fulfillmentType === 'ONE_CLICK_DROPSHIP'" :rules="form.destinationType === 'FBA' || form.fulfillmentType === 'ONE_CLICK_DROPSHIP' ? requiredTextRules('请输入 SKU NO.') : undefined"><div class="cargo-sku-input"><el-input v-model="item.sku" maxlength="64" placeholder="SKU编号" /><small v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP' && mixedSkuCount(item) > 1" class="mixed-sku-badge">同箱</small></div></el-form-item></td>
<td><el-form-item :prop="`items.${index}.cargoNameCn`" required :rules="requiredTextRules('请输入中文品名')"><el-input v-model="item.cargoNameCn" maxlength="128" placeholder="中文品名" /></el-form-item></td>
<td class="cargo-image-column"><div class="cargo-table-image"><img v-if="item.productImagePreviewUrl" :src="item.productImagePreviewUrl" :alt="`${item.cargoNameCn || `货物 ${index + 1}`}产品图片`" /><el-upload v-else :show-file-list="false" accept=".jpg,.jpeg,.png,image/jpeg,image/png" :disabled="item.productImageUploading" :http-request="(options: UploadRequestOptions) => uploadCargoProductImage(item, options)"><el-button text :loading="item.productImageUploading" aria-label="上传图片"><ImagePlus :size="17" /></el-button></el-upload><el-button v-if="item.productImagePreviewUrl" class="cargo-image-remove" text type="danger" aria-label="移除图片" @click="removeCargoProductImage(item)"><Trash2 :size="12" /></el-button></div></td>
<td><el-input v-model="item.cargoNameEn" maxlength="128" placeholder="English Name" /></td><td><el-input v-model="item.hsCode" maxlength="20" placeholder="HS Code" /></td><td><el-input v-model="item.customsHsCode" maxlength="20" placeholder="HS Code" /></td>
<td><el-form-item :prop="`items.${index}.packageType`" required :rules="requiredSelectRules('请选择包装类型')"><el-select v-model="item.packageType"><el-option label="纸箱" value="CARTON" /><el-option label="托盘" value="PALLET" /><el-option label="木箱" value="WOODEN_CASE" /></el-select></el-form-item></td><td><el-input v-model="item.material" maxlength="128" placeholder="材质" /></td>
<td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同箱</span><el-form-item v-else :prop="`items.${index}.packageType`" required :rules="requiredSelectRules('请选择包装类型')"><el-select v-model="item.packageType" @change="syncPackingGroupFields(item)"><el-option label="纸箱" value="CARTON" /><el-option label="托盘" value="PALLET" /><el-option label="木箱" value="WOODEN_CASE" /></el-select></el-form-item></td><td><el-input v-model="item.material" maxlength="128" placeholder="材质" /></td>
<td class="cargo-switch-column"><el-switch v-model="item.dangerousGoods" :aria-label="`第 ${index + 1} 条货物是否危险品`" /></td><td class="cargo-switch-column"><div class="cargo-switch-cell"><el-switch v-model="item.woodenProduct" :aria-label="`第 ${index + 1} 条货物是否木制品`" /><small v-if="item.woodenProduct && form.customsInspection !== true">第 {{ index + 1 }} 条须商检</small></div></td>
<td><el-input-number v-model="item.lengthCm" :min="0" :precision="2" :controls="false" placeholder="长" /></td><td><el-input-number v-model="item.widthCm" :min="0" :precision="2" :controls="false" placeholder="宽" /></td><td><el-input-number v-model="item.heightCm" :min="0" :precision="2" :controls="false" placeholder="高" /></td>
<td><el-form-item :prop="`items.${index}.unitsPerPackage`" required :rules="positiveNumberRules('每箱数量必须大于 0', true)"><el-input-number v-model="item.unitsPerPackage" :min="1" :controls="false" placeholder="每箱数量" /></el-form-item></td><td><el-form-item :prop="`items.${index}.packageCount`" required :rules="positiveNumberRules('箱数必须大于 0', true)"><el-input-number v-model="item.packageCount" :min="1" :controls="false" placeholder="箱数" /></el-form-item></td><td><el-form-item :prop="`items.${index}.grossWeightKg`" required :rules="positiveNumberRules('总毛重必须大于 0')"><el-input-number v-model="item.grossWeightKg" :min="0" :precision="3" :controls="false" placeholder="总毛重" /></el-form-item></td><td><el-form-item :prop="`items.${index}.volumeCbm`" required :rules="positiveNumberRules('总体积必须大于 0')"><el-input-number v-model="item.volumeCbm" :min="0" :precision="4" :controls="false" placeholder="总体积" /></el-form-item></td><td><el-form-item :prop="`items.${index}.pieces`" required :rules="positiveNumberRules('总数量必须大于 0', true)"><el-input-number v-model="item.pieces" :min="1" :controls="false" placeholder="总数量" /></el-form-item></td><td><el-input-number v-model="item.netWeightKg" :min="0" :precision="3" :controls="false" placeholder="净重" /></td>
<td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同上</span><el-input-number v-else v-model="item.lengthCm" :min="0" :precision="2" :controls="false" placeholder="长" @change="syncPackingGroupFields(item)" /></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同上</span><el-input-number v-else v-model="item.widthCm" :min="0" :precision="2" :controls="false" placeholder="宽" @change="syncPackingGroupFields(item)" /></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同上</span><el-input-number v-else v-model="item.heightCm" :min="0" :precision="2" :controls="false" placeholder="高" @change="syncPackingGroupFields(item)" /></td>
<td><el-form-item :prop="`items.${index}.unitsPerPackage`" required :rules="positiveNumberRules('每箱数量必须大于 0', true)"><el-input-number v-model="item.unitsPerPackage" :min="1" :controls="false" placeholder="每箱数量" @change="syncSkuQuantity(item)" /></el-form-item></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同箱</span><el-form-item v-else :prop="`items.${index}.packageCount`" required :rules="positiveNumberRules('箱数必须大于 0', true)"><el-input-number v-model="item.packageCount" :min="1" :controls="false" placeholder="箱数" @change="syncPackingGroupFields(item)" /></el-form-item></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同箱</span><el-form-item v-else :prop="`items.${index}.grossWeightKg`" required :rules="positiveNumberRules('总毛重必须大于 0')"><el-input-number v-model="item.grossWeightKg" :min="0" :precision="3" :controls="false" placeholder="总毛重" @change="syncPackingGroupFields(item)" /></el-form-item></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同箱</span><el-form-item v-else :prop="`items.${index}.volumeCbm`" required :rules="positiveNumberRules('总体积必须大于 0')"><el-input-number v-model="item.volumeCbm" :min="0" :precision="4" :controls="false" placeholder="总体积" @change="syncPackingGroupFields(item)" /></el-form-item></td><td><span v-if="isAdditionalMixedSku(item, index)" class="derived-sku-value">{{ item.pieces }}</span><el-form-item v-else :prop="`items.${index}.pieces`" required :rules="positiveNumberRules('总数量必须大于 0', true)"><el-input-number v-model="item.pieces" :min="1" :controls="false" placeholder="总数量" /></el-form-item></td><td><span v-if="isAdditionalMixedSku(item, index)" class="shared-carton-value">同箱</span><el-input-number v-else v-model="item.netWeightKg" :min="0" :precision="3" :controls="false" placeholder="净重" @change="syncPackingGroupFields(item)" /></td>
<td><el-input-number v-model="item.unitDeclaredValue" :min="0" :precision="2" :controls="false" placeholder="申报单价" /></td><td><el-input-number v-model="item.declaredValue" :min="0" :precision="2" :controls="false" placeholder="申报总价" /></td><td><el-input v-model="item.usage" maxlength="128" placeholder="用途" /></td><td><el-input v-model="item.model" maxlength="64" placeholder="型号" /></td><td><el-input v-model="item.brand" maxlength="64" placeholder="品牌" /></td>
<td class="cargo-dimensions-column"><div class="cargo-table-dimensions"><el-input-number v-model="item.productLengthCm" :min="0" :precision="2" :controls="false" placeholder="长" /><span>×</span><el-input-number v-model="item.productWidthCm" :min="0" :precision="2" :controls="false" placeholder="宽" /><span>×</span><el-input-number v-model="item.productHeightCm" :min="0" :precision="2" :controls="false" placeholder="高" /></div></td>
<td><el-form-item :prop="`items.${index}.shipmentId`" :required="form.destinationType === 'FBA'" :rules="form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入货件编号') : undefined"><el-input v-model="item.shipmentId" maxlength="64" placeholder="货件编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.referenceId`" :required="form.destinationType === 'FBA'" :rules="form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入跟踪编号') : undefined"><el-input v-model="item.referenceId" maxlength="64" placeholder="跟踪编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.upc`"><el-input v-model="item.upc" maxlength="64" placeholder="UPC NO.(选填)" /></el-form-item></td><td><el-input v-model="item.remark" maxlength="500" placeholder="备注" /></td><td class="cargo-operation-column"><el-button link type="danger" :aria-label="`删除货物 ${index + 1}`" @click="removeCargoItem(index)"><Trash2 :size="15" />删除</el-button></td>
<td><el-form-item :prop="`items.${index}.shipmentId`" :required="form.destinationType === 'FBA'" :rules="form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入货件编号') : undefined"><el-input v-model="item.shipmentId" maxlength="64" placeholder="货件编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.referenceId`" :required="form.destinationType === 'FBA'" :rules="form.destinationType === 'FBA' ? requiredTextRules('FBA 货物请输入跟踪编号') : undefined"><el-input v-model="item.referenceId" maxlength="64" placeholder="跟踪编号" /></el-form-item></td><td><el-form-item :prop="`items.${index}.upc`"><el-input v-model="item.upc" maxlength="64" placeholder="UPC NO.(选填)" /></el-form-item></td><td><el-input v-model="item.remark" maxlength="500" placeholder="备注" /></td><td class="cargo-operation-column"><el-button v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP'" link type="primary" :aria-label="`为货物 ${index + 1} 添加同箱 SKU`" @click="addSkuToPackingGroup(index)"><Plus :size="15" />添加 SKU</el-button><el-button link type="danger" :aria-label="`删除货物 ${index + 1}`" @click="removeCargoItem(index)"><Trash2 :size="15" />删除</el-button></td>
</tr></tbody>
</table>
</div>
<div class="cargo-table-hint"><span v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP'">相同混箱组合号表示相同一批物理箱;组内箱数、包装类型和箱规必须一致,物理箱数只计算一次。</span><span v-else>SKU NO.、货件编号、跟踪编号为 FBA 货物必填项,UPC NO. 选填</span></div>
<div class="cargo-table-hint"><span v-if="form.fulfillmentType === 'ONE_CLICK_DROPSHIP'">每个箱组默认装一个 SKU;同一箱内有多个 SKU 时点击“添加 SKU”,箱数、箱规、重量和体积只填写一次。</span><span v-else>SKU NO.、货件编号、跟踪编号为 FBA 货物必填项,UPC NO. 选填</span></div>
</section>
<section class="order-form-section prototype-section cost-estimate-section" aria-label="费用预估">
@@ -2430,6 +2497,12 @@ async function searchOrders(model: QueryModel) {
.required-field-mark { margin-right: 2px; color: var(--el-color-danger); }
.cargo-dimensions-column { width: 240px !important; }
.cargo-operation-column { width: 76px !important; text-align: center !important; }
.cargo-table-wrap.is-dropship .cargo-operation-column { width: 148px !important; }
.cargo-sku-input { position: relative; width: 100%; }
.mixed-sku-badge { position: absolute; top: -10px; right: 2px; padding: 1px 5px; border-radius: 999px; background: #eaf6f2; color: var(--primary); font-size: 9px; font-weight: 650; line-height: 16px; white-space: nowrap; }
.mixed-sku-detail-row { background: #f8fbff; }
.shared-carton-value { display: inline-flex; min-height: 30px; align-items: center; color: #8a97a8; font-size: 12px; white-space: nowrap; }
.derived-sku-value { display: inline-flex; min-height: 30px; align-items: center; color: #172b4d; font-weight: 650; }
.cargo-switch-column { width: 112px !important; text-align: center !important; vertical-align: middle !important; }
.cargo-switch-cell { display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 4px; }
.cargo-switch-cell small { color: #b54708; font-size: 9px; line-height: 1.25; white-space: normal; }