|
|
@@ -28,6 +28,7 @@ import {
|
|
|
|
} from '@/modules/first-leg/api/firstLeg'
|
|
|
|
} from '@/modules/first-leg/api/firstLeg'
|
|
|
|
import { loadAllWarehouseRecords } from '@/modules/first-leg/utils/warehousePagination'
|
|
|
|
import { loadAllWarehouseRecords } from '@/modules/first-leg/utils/warehousePagination'
|
|
|
|
import { createIdempotencyKey } from '@/utils/idempotencyKey'
|
|
|
|
import { createIdempotencyKey } from '@/utils/idempotencyKey'
|
|
|
|
|
|
|
|
import RelabelDocumentsPanel from '../components/RelabelDocumentsPanel.vue'
|
|
|
|
import { relabelSkuCodes, relabelSkuCount } from '../utils/relabelSkuDisplay'
|
|
|
|
import { relabelSkuCodes, relabelSkuCount } from '../utils/relabelSkuDisplay'
|
|
|
|
|
|
|
|
|
|
|
|
type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
|
|
|
|
type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
|
|
|
@@ -148,29 +149,34 @@ interface ServiceDetail extends Record<string, unknown> {
|
|
|
|
cartons?: TransferCarton[]; pallets?: TransferPallet[]; measurementPhotos?: TransferMeasurementPhoto[]; customerConfirmedAt?: string
|
|
|
|
cartons?: TransferCarton[]; pallets?: TransferPallet[]; measurementPhotos?: TransferMeasurementPhoto[]; customerConfirmedAt?: string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const selectedDetail = ref<ServiceDetail | null>(null)
|
|
|
|
const selectedDetail = ref<ServiceDetail | null>(null)
|
|
|
|
|
|
|
|
/** 仅汇总客户原始预报件数,旧单缺失时明确提示,不用实收件数冒充。 */
|
|
|
|
|
|
|
|
const transferForecastQuantity = computed(() => {
|
|
|
|
|
|
|
|
const items = selectedDetail.value?.items || []
|
|
|
|
|
|
|
|
if (!items.length || items.some(item => item.quantity == null)) return '未记录'
|
|
|
|
|
|
|
|
return items.reduce((sum, item) => sum + Number(item.quantity), 0) + ' 件'
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 预报箱唛在提交后即可打印,与PDA实收清点明细分别展示。
|
|
|
|
// 预报箱唛在提交后即可打印,与PDA实收清点明细分别展示。
|
|
|
|
const showForecastCartons = ref(false)
|
|
|
|
const showForecastCartons = ref(false)
|
|
|
|
const increaseForecastOpen = ref(false)
|
|
|
|
const increaseForecastOpen = ref(false)
|
|
|
|
const increasingForecast = ref(false)
|
|
|
|
const increasingForecast = ref(false)
|
|
|
|
const forecastSnapshot = ref<ServiceDetail | null>(null)
|
|
|
|
const forecastSnapshot = ref<ServiceDetail | null>(null)
|
|
|
|
const increaseForecastForm = reactive({ itemId: '', additionalCount: 1 })
|
|
|
|
const increaseForecastForm = reactive({ additionalCount: 1 })
|
|
|
|
const canIncreaseForecast = computed(() => canCreate.value && selectedDetail.value?.transferNo === selectedDetail.value?.inboundCode && ['PENDING_REVIEW', 'WAIT_PDA_RECEIVING'].includes(selectedDetail.value?.status || ''))
|
|
|
|
const canIncreaseForecast = computed(() => canCreate.value && selectedDetail.value?.transferNo === selectedDetail.value?.inboundCode && ['PENDING_REVIEW', 'WAIT_PDA_RECEIVING'].includes(selectedDetail.value?.status || ''))
|
|
|
|
/** 保存原预报快照,防止弹窗打开后其他设备的修改被覆盖。 */
|
|
|
|
/** 保存原预报快照,防止弹窗打开后其他设备的修改被覆盖。 */
|
|
|
|
function openIncreaseForecast() {
|
|
|
|
function openIncreaseForecast() {
|
|
|
|
if (!canIncreaseForecast.value || !selectedDetail.value) return
|
|
|
|
if (!canIncreaseForecast.value || !selectedDetail.value) return
|
|
|
|
forecastSnapshot.value = selectedDetail.value
|
|
|
|
forecastSnapshot.value = selectedDetail.value
|
|
|
|
increaseForecastForm.itemId = String(selectedDetail.value.items?.[0]?.id || '')
|
|
|
|
|
|
|
|
increaseForecastForm.additionalCount = 1; increaseForecastOpen.value = true
|
|
|
|
increaseForecastForm.additionalCount = 1; increaseForecastOpen.value = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/** 只补箱唛,SKU预报件数不随包装箱数自动改变。 */
|
|
|
|
/** 只补箱唛,SKU预报件数不随包装箱数自动改变。 */
|
|
|
|
async function saveIncreaseForecast() {
|
|
|
|
async function saveIncreaseForecast() {
|
|
|
|
if (increasingForecast.value || !forecastSnapshot.value?.transferNo) return
|
|
|
|
if (increasingForecast.value || !forecastSnapshot.value?.transferNo) return
|
|
|
|
const snapshot = forecastSnapshot.value
|
|
|
|
const snapshot = forecastSnapshot.value
|
|
|
|
const item = snapshot.items?.find(row => String(row.id) === increaseForecastForm.itemId)
|
|
|
|
if (!Number.isSafeInteger(increaseForecastForm.additionalCount) || increaseForecastForm.additionalCount < 1) { ElMessage.warning('请填写有效新增箱数'); return }
|
|
|
|
if (!item || !Number.isSafeInteger(increaseForecastForm.additionalCount) || increaseForecastForm.additionalCount < 1) { ElMessage.warning('请选择SKU并填写新增箱数'); return }
|
|
|
|
|
|
|
|
increasingForecast.value = true
|
|
|
|
increasingForecast.value = true
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const result = await increaseTransferForecastCartons(String(snapshot.transferNo), { expectedPackageCount: Number(snapshot.packageCount), transferItemId: String(item.id), expectedItemPackageCount: Number(item.packageCount), packageCount: Number(item.packageCount) + increaseForecastForm.additionalCount })
|
|
|
|
const result = await increaseTransferForecastCartons(String(snapshot.transferNo), { expectedPackageCount: Number(snapshot.packageCount), packageCount: Number(snapshot.packageCount) + increaseForecastForm.additionalCount })
|
|
|
|
selectedDetail.value = result as ServiceDetail; increaseForecastOpen.value = false
|
|
|
|
selectedDetail.value = result as ServiceDetail; increaseForecastOpen.value = false
|
|
|
|
ElMessage.success('预报箱数已增加,新增箱唛可打印;请通知PDA刷新后继续清点')
|
|
|
|
ElMessage.success('预报箱数已增加,新增箱唛可打印;请通知PDA刷新后继续清点')
|
|
|
|
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '增加预报箱数失败') }
|
|
|
|
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '增加预报箱数失败') }
|
|
|
@@ -1149,7 +1155,8 @@ watch([page, pageSize], ([nextPage, nextSize], [previousPage, previousSize]) =>
|
|
|
|
if (nextSize !== previousSize && nextPage !== 1) { page.value = 1; return }
|
|
|
|
if (nextSize !== previousSize && nextPage !== 1) { page.value = 1; return }
|
|
|
|
void load()
|
|
|
|
void load()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
async function submit(action: 'draft' | 'submit' = 'submit') {
|
|
|
|
async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
|
|
|
|
|
|
|
|
if (saving.value) return
|
|
|
|
const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse
|
|
|
|
const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse
|
|
|
|
const relabelRows = labelingMode.value === 'carton' ? cartonDetailRows.value : skuRelabelRows.value
|
|
|
|
const relabelRows = labelingMode.value === 'carton' ? cartonDetailRows.value : skuRelabelRows.value
|
|
|
|
const invalidCargo = props.service === 'disposal'
|
|
|
|
const invalidCargo = props.service === 'disposal'
|
|
|
@@ -1228,6 +1235,9 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
|
|
|
|
if (props.service !== 'transfer' || action === 'submit') {
|
|
|
|
if (props.service !== 'transfer' || action === 'submit') {
|
|
|
|
try { await ElMessageBox.confirm(`确认提交${config.value.title}吗?`, '提交确认', { confirmButtonText: '提交', cancelButtonText: '取消' }) } catch { return }
|
|
|
|
try { await ElMessageBox.confirm(`确认提交${config.value.title}吗?`, '提交确认', { confirmButtonText: '提交', cancelButtonText: '取消' }) } catch { return }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const draftPrintWindow = action === 'print' ? window.open('', '_blank') : null
|
|
|
|
|
|
|
|
if (action === 'print' && !draftPrintWindow) { ElMessage.warning('请允许浏览器打开打印窗口'); return }
|
|
|
|
|
|
|
|
if (draftPrintWindow) draftPrintWindow.opener = null
|
|
|
|
saving.value = true
|
|
|
|
saving.value = true
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const idempotencyKey = createIdempotencyKey()
|
|
|
|
const idempotencyKey = createIdempotencyKey()
|
|
|
@@ -1281,6 +1291,17 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
|
|
|
|
: await createTransfer(payload)
|
|
|
|
: await createTransfer(payload)
|
|
|
|
const transferNo = String(created.transferNo || '')
|
|
|
|
const transferNo = String(created.transferNo || '')
|
|
|
|
if (!transferNo) throw new Error('转运草稿已保存,但未返回转运单号')
|
|
|
|
if (!transferNo) throw new Error('转运草稿已保存,但未返回转运单号')
|
|
|
|
|
|
|
|
if (action === 'print') {
|
|
|
|
|
|
|
|
// 保存服务端草稿后打印同一套箱唛,后续编辑及正式提交保留原号。
|
|
|
|
|
|
|
|
editingTransferNo.value = transferNo
|
|
|
|
|
|
|
|
const label = await getTransferCartonLabel(transferNo)
|
|
|
|
|
|
|
|
if (draftPrintWindow) {
|
|
|
|
|
|
|
|
draftPrintWindow.document.open(); draftPrintWindow.document.write(label.printableHtml); draftPrintWindow.document.close()
|
|
|
|
|
|
|
|
draftPrintWindow.focus(); draftPrintWindow.print()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ElMessage.success('草稿已保存,正在打印箱唛;请继续填写并提交订单')
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
if (action === 'submit') {
|
|
|
|
if (action === 'submit') {
|
|
|
|
const submitted = await submitTransfer(transferNo)
|
|
|
|
const submitted = await submitTransfer(transferNo)
|
|
|
|
if (form.relabel) {
|
|
|
|
if (form.relabel) {
|
|
|
@@ -1307,7 +1328,7 @@ async function submit(action: 'draft' | 'submit' = 'submit') {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ElMessage.success(props.service === 'transfer' ? '转运单草稿保存成功' : `${config.value.title}提交成功`)
|
|
|
|
ElMessage.success(props.service === 'transfer' ? '转运单草稿保存成功' : `${config.value.title}提交成功`)
|
|
|
|
await goToServiceList()
|
|
|
|
await goToServiceList()
|
|
|
|
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '提交失败') }
|
|
|
|
} catch (error) { draftPrintWindow?.close(); ElMessage.error(error instanceof Error ? error.message : '提交失败') }
|
|
|
|
finally { saving.value = false }
|
|
|
|
finally { saving.value = false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async function openDetail(value: Record<string, string>) {
|
|
|
|
async function openDetail(value: Record<string, string>) {
|
|
|
@@ -1785,7 +1806,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
|
|
|
|
</tbody></table></div>
|
|
|
|
</tbody></table></div>
|
|
|
|
<div v-if="!transferSourceLoading && !transferSourceError && !transferSourceCartons.length" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>库存箱数</th><th>原目的仓</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td>{{ row.sku || '—' }}</td><td>{{ row.name || '—' }}</td><td>{{ row.expectedPackageCount }}</td><td>{{ transferInventoryCandidates.filter(item => item.inboundCode === selectedTransferInventory && item.skuCode === row.sku).map(item => item.destinationFbaWarehouseCode || item.destinationFbaWarehouseName).filter(Boolean).join(' / ') || '未指定' }}</td></tr></tbody></table></div>
|
|
|
|
<div v-if="!transferSourceLoading && !transferSourceError && !transferSourceCartons.length" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>库存箱数</th><th>原目的仓</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td>{{ row.sku || '—' }}</td><td>{{ row.name || '—' }}</td><td>{{ row.expectedPackageCount }}</td><td>{{ transferInventoryCandidates.filter(item => item.inboundCode === selectedTransferInventory && item.skuCode === row.sku).map(item => item.destinationFbaWarehouseCode || item.destinationFbaWarehouseName).filter(Boolean).join(' / ') || '未指定' }}</td></tr></tbody></table></div>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-section-heading" :class="{ 'warehouse-step-heading': props.service === 'transfer' }"><div><h3><span v-if="props.service === 'transfer'" class="warehouse-step-number">1</span>货物明细</h3></div><div v-if="props.service !== 'disposal'" class="warehouse-heading-actions"><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []">清空</el-button></div></div>
|
|
|
|
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-section-heading" :class="{ 'warehouse-step-heading': props.service === 'transfer' }"><div><h3><span v-if="props.service === 'transfer'" class="warehouse-step-number">1</span>货物明细</h3></div><div v-if="props.service !== 'disposal'" class="warehouse-heading-actions"><el-button v-if="props.service === 'transfer'" size="small" :loading="saving" @click="submit('print')"><Printer :size="15" />打印唛头</el-button><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []">清空</el-button></div></div>
|
|
|
|
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU{{ props.service === 'transfer' ? '' : ' *' }}</th><th>中文品名{{ props.service === 'transfer' ? '' : ' *' }}</th><th v-if="props.service === 'disposal'">可用箱数</th><th v-if="props.service === 'relabel'">FNSKU / 箱唛</th><th v-if="props.service === 'transfer'">SKU 数量(件)*</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : '预计箱数' }} *</th><th v-if="props.service !== 'disposal'">操作</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td><el-input v-model="row.sku" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入商品 SKU'" /></td><td><el-input v-model="row.name" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入中文品名'" /></td><td v-if="props.service === 'disposal'"><strong>{{ row.availableQuantity }} 箱</strong></td><td v-if="props.service === 'relabel'"><el-input v-model="row.shippingMark" placeholder="请输入FNSKU或箱唛" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.quantity" :min="1" :precision="0" controls-position="right" placeholder="请输入件数" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.expectedPackageCount" :min="1" :precision="0" controls-position="right" placeholder="请输入预计箱数" /></td><td v-else><el-input-number v-model="row.quantity" :disabled="props.service === 'disposal'" :min="props.service === 'disposal' ? 0 : 1" :max="row.availableQuantity" controls-position="right" placeholder="请输入箱数" /></td><td v-if="props.service !== 'disposal'"><button type="button" class="warehouse-delete" title="删除" @click="removeCargoRow(row.id)"><Trash2 :size="16" />删除</button></td></tr><tr v-if="cargoRows.length === 0"><td :colspan="props.service === 'disposal' ? 4 : props.service === 'relabel' || props.service === 'transfer' ? 5 : 4"><div class="warehouse-empty">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
|
|
|
|
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU{{ props.service === 'transfer' ? '' : ' *' }}</th><th>中文品名{{ props.service === 'transfer' ? '' : ' *' }}</th><th v-if="props.service === 'disposal'">可用箱数</th><th v-if="props.service === 'relabel'">FNSKU / 箱唛</th><th v-if="props.service === 'transfer'">SKU 数量(件)*</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : '预计箱数' }} *</th><th v-if="props.service !== 'disposal'">操作</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td><el-input v-model="row.sku" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入商品 SKU'" /></td><td><el-input v-model="row.name" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入中文品名'" /></td><td v-if="props.service === 'disposal'"><strong>{{ row.availableQuantity }} 箱</strong></td><td v-if="props.service === 'relabel'"><el-input v-model="row.shippingMark" placeholder="请输入FNSKU或箱唛" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.quantity" :min="1" :precision="0" controls-position="right" placeholder="请输入件数" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.expectedPackageCount" :min="1" :precision="0" controls-position="right" placeholder="请输入预计箱数" /></td><td v-else><el-input-number v-model="row.quantity" :disabled="props.service === 'disposal'" :min="props.service === 'disposal' ? 0 : 1" :max="row.availableQuantity" controls-position="right" placeholder="请输入箱数" /></td><td v-if="props.service !== 'disposal'"><button type="button" class="warehouse-delete" title="删除" @click="removeCargoRow(row.id)"><Trash2 :size="16" />删除</button></td></tr><tr v-if="cargoRows.length === 0"><td :colspan="props.service === 'disposal' ? 4 : props.service === 'relabel' || props.service === 'transfer' ? 5 : 4"><div class="warehouse-empty">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
@@ -1826,7 +1847,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
|
|
|
|
</header>
|
|
|
|
</header>
|
|
|
|
<div v-if="detailError" class="warehouse-detail-error" role="alert"><strong>{{ detailError }}</strong><el-button @click="loadDetailPage">重新加载</el-button></div>
|
|
|
|
<div v-if="detailError" class="warehouse-detail-error" role="alert"><strong>{{ detailError }}</strong><el-button @click="loadDetailPage">重新加载</el-button></div>
|
|
|
|
<el-alert v-else-if="props.service === 'labeling' && selectedRelabelDetail?.application.status === 'REJECTED'" class="warehouse-rejected-alert" type="error" :closable="false" show-icon title="贴标单审核已驳回" :description="selectedRelabelDetail.application.reviewRemark || '平台未填写驳回备注'" />
|
|
|
|
<el-alert v-else-if="props.service === 'labeling' && selectedRelabelDetail?.application.status === 'REJECTED'" class="warehouse-rejected-alert" type="error" :closable="false" show-icon title="贴标单审核已驳回" :description="selectedRelabelDetail.application.reviewRemark || '平台未填写驳回备注'" />
|
|
|
|
<div v-else class="warehouse-card warehouse-form-section"><h3>订单概览</h3><div v-if="props.service === 'labeling'" class="warehouse-detail-grid"><div><span>操作仓</span><strong>{{ warehouseDisplay(selectedRelabelDetail?.application.overseasWarehouseId) }}</strong></div><div><span>客户参考号</span><strong>{{ selectedRelabelDetail?.application.customerOrderNo || '—' }}</strong></div><div><span>SKU 数(种)</span><strong>{{ selectedRelabelDetail ? relabelSkuCount(selectedRelabelDetail.items) : '—' }}</strong></div><div><span>总箱数</span><strong>{{ selectedRelabelDetail?.application.totalPackageCount ?? '—' }}</strong></div><div><span>贴标截止时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.labelDeadline) }}</strong></div><div><span>仓库联系人</span><strong>{{ selectedRelabelDetail?.application.contactName || '—' }}</strong></div><div><span>联系电话</span><strong>{{ selectedRelabelDetail?.application.contactPhone || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.createdAt) }}</strong></div></div><div v-else-if="props.service === 'transfer'" class="warehouse-detail-grid warehouse-transfer-overview-grid"><div><span>状态</span><el-tag :type="transferStatusTagType(selectedDetail?.status || '')" effect="light">{{ statusLabel(selectedDetail?.status || '') }}</el-tag></div><div><span>目的去向</span><strong>{{ destinationTypeLabel(selectedDetail?.destinationType, selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if="selectedDetail?.destinationType !== 'PRIVATE'"><span>FBA 目的仓</span><strong>{{ selectedDetail?.destinationWarehouseName || warehouseDisplay(selectedDetail?.destinationWarehouseCode || selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if="selectedDetail?.destinationType !== 'PRIVATE'"><span>FBA 货件号</span><strong>{{ selectedDetail?.fbaShipmentNo || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'" class="warehouse-overview-wide"><span>私人仓地址</span><strong>{{ selectedDetail?.privateDestinationAddress || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>邮编</span><strong>{{ selectedDetail?.privateDestinationPostalCode || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>私人仓联系人</span><strong>{{ selectedDetail?.privateDestinationContactName || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>联系电话</span><strong>{{ selectedDetail?.privateDestinationContactPhone || '—' }}</strong></div><div><span>海外仓</span><strong>{{ warehouseDisplay(selectedDetail?.operatingWarehouseId) }}</strong></div><div><span>预报 SKU 种数</span><strong>{{ new Set((selectedDetail?.items || []).map(item => item.skuCode).filter(Boolean)).size }}</strong></div><div><span>预计箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>实际签收箱数</span><strong>{{ selectedDetail?.cartons?.filter((carton) => carton.status === 'COUNTED').length ?? '—' }}</strong></div><div><span>入仓方式</span><strong>{{ deliveryMethodLabel(selectedDetail?.deliveryMethod || '') }}</strong></div><div><span>期望发货时间</span><strong>{{ formatDateTime(selectedDetail?.estimatedShipAt) }}</strong></div><div><span>实际发货时间</span><strong>{{ formatDateTime(selectedDetail?.actualShipAt) }}</strong></div><div><span>是否换标</span><strong>{{ selectedDetail?.needRelabel ? '是' : '否' }}</strong></div><div><span>换标状态</span><el-tag :type="transferRelabelStatusTagType(transferRelabelStatus(selectedDetail || {}).code)" effect="light">{{ transferRelabelStatus(selectedDetail || {}).label }}</el-tag></div><div><span>换标单号</span><strong>{{ selectedDetail?.relabelNo || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedDetail?.createdAt) }}</strong></div></div><div v-else class="warehouse-detail-grid"><div v-for="(column, index) in config.columns.slice(1)" :key="column"><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
|
|
|
|
<div v-else class="warehouse-card warehouse-form-section"><h3>订单概览</h3><div v-if="props.service === 'labeling'" class="warehouse-detail-grid"><div><span>操作仓</span><strong>{{ warehouseDisplay(selectedRelabelDetail?.application.overseasWarehouseId) }}</strong></div><div><span>客户参考号</span><strong>{{ selectedRelabelDetail?.application.customerOrderNo || '—' }}</strong></div><div><span>SKU 数(种)</span><strong>{{ selectedRelabelDetail ? relabelSkuCount(selectedRelabelDetail.items) : '—' }}</strong></div><div><span>总箱数</span><strong>{{ selectedRelabelDetail?.application.totalPackageCount ?? '—' }}</strong></div><div><span>贴标截止时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.labelDeadline) }}</strong></div><div><span>仓库联系人</span><strong>{{ selectedRelabelDetail?.application.contactName || '—' }}</strong></div><div><span>联系电话</span><strong>{{ selectedRelabelDetail?.application.contactPhone || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedRelabelDetail?.application.createdAt) }}</strong></div></div><div v-else-if="props.service === 'transfer'" class="warehouse-detail-grid warehouse-transfer-overview-grid"><div><span>状态</span><el-tag :type="transferStatusTagType(selectedDetail?.status || '')" effect="light">{{ statusLabel(selectedDetail?.status || '') }}</el-tag></div><div><span>目的去向</span><strong>{{ destinationTypeLabel(selectedDetail?.destinationType, selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if="selectedDetail?.destinationType !== 'PRIVATE'"><span>FBA 目的仓</span><strong>{{ selectedDetail?.destinationWarehouseName || warehouseDisplay(selectedDetail?.destinationWarehouseCode || selectedDetail?.fbaWarehouseCode) }}</strong></div><div v-if="selectedDetail?.destinationType !== 'PRIVATE'"><span>FBA 货件号</span><strong>{{ selectedDetail?.fbaShipmentNo || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'" class="warehouse-overview-wide"><span>私人仓地址</span><strong>{{ selectedDetail?.privateDestinationAddress || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>邮编</span><strong>{{ selectedDetail?.privateDestinationPostalCode || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>私人仓联系人</span><strong>{{ selectedDetail?.privateDestinationContactName || '—' }}</strong></div><div v-if="selectedDetail?.destinationType === 'PRIVATE'"><span>联系电话</span><strong>{{ selectedDetail?.privateDestinationContactPhone || '—' }}</strong></div><div><span>海外仓</span><strong>{{ warehouseDisplay(selectedDetail?.operatingWarehouseId) }}</strong></div><div><span>预报 SKU 种数</span><strong>{{ new Set((selectedDetail?.items || []).map(item => item.skuCode).filter(Boolean)).size }}</strong></div><div><span>预报 SKU 总件数</span><strong>{{ transferForecastQuantity }}</strong></div><div><span>预计箱数</span><strong>{{ selectedDetail?.packageCount ?? '—' }}</strong></div><div><span>实际签收箱数</span><strong>{{ selectedDetail?.cartons?.filter((carton) => carton.status === 'COUNTED').length ?? '—' }}</strong></div><div><span>入仓方式</span><strong>{{ deliveryMethodLabel(selectedDetail?.deliveryMethod || '') }}</strong></div><div><span>期望发货时间</span><strong>{{ formatDateTime(selectedDetail?.estimatedShipAt) }}</strong></div><div><span>实际发货时间</span><strong>{{ formatDateTime(selectedDetail?.actualShipAt) }}</strong></div><div><span>是否换标</span><strong>{{ selectedDetail?.needRelabel ? '是' : '否' }}</strong></div><div><span>换标状态</span><el-tag :type="transferRelabelStatusTagType(transferRelabelStatus(selectedDetail || {}).code)" effect="light">{{ transferRelabelStatus(selectedDetail || {}).label }}</el-tag></div><div><span>换标单号</span><strong>{{ selectedDetail?.relabelNo || '—' }}</strong></div><div><span>创建时间</span><strong>{{ formatDateTime(selectedDetail?.createdAt) }}</strong></div></div><div v-else class="warehouse-detail-grid"><div v-for="(column, index) in config.columns.slice(1)" :key="column"><span>{{ column }}</span><strong>{{ selectedDetail?.[String(index)] || '—' }}</strong></div></div></div>
|
|
|
|
<div v-if="props.service === 'disposal'" class="warehouse-card warehouse-form-section">
|
|
|
|
<div v-if="props.service === 'disposal'" class="warehouse-card warehouse-form-section">
|
|
|
|
<h3>本次废弃箱子</h3><div class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>入仓码</th><th>SKU</th><th>品名</th><th>申请数量</th><th>所选箱唛</th></tr></thead><tbody><tr v-for="(item, index) in selectedDetail?.items || []" :key="index"><td>{{ item.inboundCode || '—' }}</td><td>{{ item.skuCode || '—' }}</td><td>{{ item.productName || '—' }}</td><td>{{ item.discardPackages ?? '—' }}</td><td>{{ item.cartonMarks?.join('、') || '全部库存(历史记录无箱唛快照)' }}</td></tr></tbody></table></div>
|
|
|
|
<h3>本次废弃箱子</h3><div class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>入仓码</th><th>SKU</th><th>品名</th><th>申请数量</th><th>所选箱唛</th></tr></thead><tbody><tr v-for="(item, index) in selectedDetail?.items || []" :key="index"><td>{{ item.inboundCode || '—' }}</td><td>{{ item.skuCode || '—' }}</td><td>{{ item.productName || '—' }}</td><td>{{ item.discardPackages ?? '—' }}</td><td>{{ item.cartonMarks?.join('、') || '全部库存(历史记录无箱唛快照)' }}</td></tr></tbody></table></div>
|
|
|
|
<h3>废弃现场照片</h3><div v-if="selectedDetail?.photos?.length" class="warehouse-photo-grid"><div v-for="photo in selectedDetail.photos" :key="photo.fileId" class="warehouse-photo-item"><el-image v-if="discardPhotoUrls[photo.fileId]" :src="discardPhotoUrls[photo.fileId]" :preview-src-list="discardPhotoPreviewUrls" :initial-index="discardPhotoPreviewUrls.indexOf(discardPhotoUrls[photo.fileId])" fit="cover" preview-teleported /><div v-else-if="discardPhotoLoading[photo.fileId]" class="warehouse-photo-placeholder">照片加载中…</div><button v-else class="warehouse-photo-placeholder warehouse-photo-retry" type="button" @click="loadDiscardPhoto(photo.fileId)">{{ discardPhotoErrors[photo.fileId] ? '加载失败,点击重试' : '加载照片' }}</button><small>{{ formatDateTime(photo.submittedAt) }}</small></div></div><div v-else class="warehouse-empty">暂无已提交的废弃现场照片</div>
|
|
|
|
<h3>废弃现场照片</h3><div v-if="selectedDetail?.photos?.length" class="warehouse-photo-grid"><div v-for="photo in selectedDetail.photos" :key="photo.fileId" class="warehouse-photo-item"><el-image v-if="discardPhotoUrls[photo.fileId]" :src="discardPhotoUrls[photo.fileId]" :preview-src-list="discardPhotoPreviewUrls" :initial-index="discardPhotoPreviewUrls.indexOf(discardPhotoUrls[photo.fileId])" fit="cover" preview-teleported /><div v-else-if="discardPhotoLoading[photo.fileId]" class="warehouse-photo-placeholder">照片加载中…</div><button v-else class="warehouse-photo-placeholder warehouse-photo-retry" type="button" @click="loadDiscardPhoto(photo.fileId)">{{ discardPhotoErrors[photo.fileId] ? '加载失败,点击重试' : '加载照片' }}</button><small>{{ formatDateTime(photo.submittedAt) }}</small></div></div><div v-else class="warehouse-empty">暂无已提交的废弃现场照片</div>
|
|
|
@@ -1835,13 +1856,13 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
|
|
|
|
<div class="warehouse-section-heading warehouse-transfer-receipt-heading"><h3>预报箱唛({{ selectedDetail?.packageCount || 0 }} 箱)</h3><div class="warehouse-transfer-receipt-actions"><el-button v-if="canIncreaseForecast" type="primary" size="small" @click="openIncreaseForecast"><Plus :size="14" />增加预报箱数</el-button><el-button type="primary" size="small" @click="printTransferCartons(String(selectedDetail?.transferNo || ''))"><Printer :size="14" />打印预报箱唛</el-button><el-button size="small" @click="showForecastCartons = true">查看预报箱唛</el-button></div></div>
|
|
|
|
<div class="warehouse-section-heading warehouse-transfer-receipt-heading"><h3>预报箱唛({{ selectedDetail?.packageCount || 0 }} 箱)</h3><div class="warehouse-transfer-receipt-actions"><el-button v-if="canIncreaseForecast" type="primary" size="small" @click="openIncreaseForecast"><Plus :size="14" />增加预报箱数</el-button><el-button type="primary" size="small" @click="printTransferCartons(String(selectedDetail?.transferNo || ''))"><Printer :size="14" />打印预报箱唛</el-button><el-button size="small" @click="showForecastCartons = true">查看预报箱唛</el-button></div></div>
|
|
|
|
<el-dialog v-model="showForecastCartons" title="预报箱唛明细" width="900px" align-center destroy-on-close append-to-body><el-table :data="selectedDetail?.cartons || []" size="small" border><el-table-column prop="cartonMark" label="仓库唛头" min-width="300" /><el-table-column label="预报SKU" min-width="240"><template #default="{ row }">{{ selectedDetail?.items?.find(item => String(item.id) === String(row.transferItemId))?.skuCode || '—' }}</template></el-table-column><el-table-column label="清点状态" width="130"><template #default="{ row }">{{ row.status === 'COUNTED' ? '已清点' : row.status === 'NOT_RECEIVED' ? '未收到' : '待清点' }}</template></el-table-column></el-table><template #footer><el-button @click="showForecastCartons = false">关闭</el-button></template></el-dialog>
|
|
|
|
<el-dialog v-model="showForecastCartons" title="预报箱唛明细" width="900px" align-center destroy-on-close append-to-body><el-table :data="selectedDetail?.cartons || []" size="small" border><el-table-column prop="cartonMark" label="仓库唛头" min-width="300" /><el-table-column label="预报SKU" min-width="240"><template #default="{ row }">{{ selectedDetail?.items?.find(item => String(item.id) === String(row.transferItemId))?.skuCode || '—' }}</template></el-table-column><el-table-column label="清点状态" width="130"><template #default="{ row }">{{ row.status === 'COUNTED' ? '已清点' : row.status === 'NOT_RECEIVED' ? '未收到' : '待清点' }}</template></el-table-column></el-table><template #footer><el-button @click="showForecastCartons = false">关闭</el-button></template></el-dialog>
|
|
|
|
<el-dialog v-model="increaseForecastOpen" title="增加预报箱数" width="560px" align-center destroy-on-close append-to-body :close-on-click-modal="!increasingForecast" :show-close="!increasingForecast">
|
|
|
|
<el-dialog v-model="increaseForecastOpen" title="增加预报箱数" width="560px" align-center destroy-on-close append-to-body :close-on-click-modal="!increasingForecast" :show-close="!increasingForecast">
|
|
|
|
<el-form label-width="110px"><el-form-item label="预报SKU"><el-select v-model="increaseForecastForm.itemId" :disabled="increasingForecast" style="width:100%"><el-option v-for="item in forecastSnapshot?.items || []" :key="String(item.id)" :value="String(item.id)" :label="item.skuCode || item.productNameZh || '未命名SKU'" /></el-select></el-form-item><el-form-item label="新增箱数"><el-input-number v-model="increaseForecastForm.additionalCount" :disabled="increasingForecast" :min="1" :max="Math.max(1, 5000 - Number(forecastSnapshot?.packageCount || 0))" :precision="0" /></el-form-item></el-form><p class="warehouse-form-hint">保留原箱唛,仅补生成新增箱唛;SKU预报件数保持不变。</p>
|
|
|
|
<el-form label-width="110px"><el-form-item label="新增箱数"><el-input-number v-model="increaseForecastForm.additionalCount" :disabled="increasingForecast" :min="1" :max="Math.max(1, 5000 - Number(forecastSnapshot?.packageCount || 0))" :precision="0" /></el-form-item></el-form><p class="warehouse-form-hint">保留原箱唛,仅补生成新增箱唛;箱内SKU和件数以PDA清点为准,SKU预报总件数保持不变。</p>
|
|
|
|
<template #footer><el-button :disabled="increasingForecast" @click="increaseForecastOpen = false">取消</el-button><el-button type="primary" :loading="increasingForecast" @click="saveIncreaseForecast">确认增加</el-button></template>
|
|
|
|
<template #footer><el-button :disabled="increasingForecast" @click="increaseForecastOpen = false">取消</el-button><el-button type="primary" :loading="increasingForecast" @click="saveIncreaseForecast">确认增加</el-button></template>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="props.service === 'transfer'" class="warehouse-card warehouse-form-section warehouse-transfer-cargo-card">
|
|
|
|
<div v-if="props.service === 'transfer'" class="warehouse-card warehouse-form-section warehouse-transfer-cargo-card">
|
|
|
|
<div class="warehouse-section-heading"><h3>预报与实际签收</h3></div>
|
|
|
|
<div class="warehouse-section-heading"><h3>预报与实际签收</h3></div>
|
|
|
|
<div class="warehouse-table-scroll"><table class="warehouse-edit-table warehouse-transfer-comparison-table"><colgroup><col style="width:500px" /><col style="width:140px" /><col style="width:130px" /><col style="width:100px" /><col style="width:130px" /><col style="width:130px" /></colgroup><thead><tr><th>商品 SKU</th><th>中文品名</th><th>预报数量(件)</th><th>预计箱数</th><th>实际签收件数</th><th>实际签收箱数</th></tr></thead><tbody><tr v-for="(item, index) in selectedDetail?.items || []" :key="String(item.id || index)"><td>{{ item.skuCode || '—' }}</td><td>{{ item.productNameZh || '—' }}</td><td>{{ item.quantity ?? '—' }}</td><td>{{ item.packageCount ?? '—' }}</td><td>{{ receivedItemQuantity(item.id) }}</td><td>{{ receivedItemCartons(item.id).length }}</td></tr><tr v-if="!selectedDetail?.items?.length"><td colspan="6"><div class="warehouse-empty">暂无货物明细</div></td></tr></tbody></table></div>
|
|
|
|
<div class="warehouse-table-scroll"><table class="warehouse-edit-table warehouse-transfer-comparison-table"><colgroup><col style="width:500px" /><col style="width:140px" /><col style="width:130px" /><col style="width:100px" /><col style="width:130px" /><col style="width:130px" /></colgroup><thead><tr><th>商品 SKU</th><th>中文品名</th><th>预报数量(件)</th><th>预计箱数</th><th>实际签收件数</th><th>实际签收箱数</th></tr></thead><tbody><tr v-for="(item, index) in selectedDetail?.items || []" :key="String(item.id || index)"><td>{{ item.skuCode || '—' }}</td><td>{{ item.productNameZh || '—' }}</td><td>{{ item.quantity ?? '未记录' }}</td><td>{{ item.packageCount ?? '—' }}</td><td>{{ receivedItemQuantity(item.id) }}</td><td>{{ receivedItemCartons(item.id).length }}</td></tr><tr v-if="!selectedDetail?.items?.length"><td colspan="6"><div class="warehouse-empty">暂无货物明细</div></td></tr></tbody></table></div>
|
|
|
|
<details class="warehouse-transfer-pallet-group" open>
|
|
|
|
<details class="warehouse-transfer-pallet-group" open>
|
|
|
|
<summary class="warehouse-section-heading warehouse-transfer-pallet-heading warehouse-transfer-pallet-summary"><h3>PDA 打托实测数据</h3><span class="warehouse-transfer-pallet-count">{{ selectedDetail?.pallets?.length || 0 }} 托</span><span class="warehouse-transfer-pallet-toggle"><span class="when-collapsed">展开托盘</span><span class="when-expanded">收起托盘</span><ChevronDown :size="14" /></span></summary>
|
|
|
|
<summary class="warehouse-section-heading warehouse-transfer-pallet-heading warehouse-transfer-pallet-summary"><h3>PDA 打托实测数据</h3><span class="warehouse-transfer-pallet-count">{{ selectedDetail?.pallets?.length || 0 }} 托</span><span class="warehouse-transfer-pallet-toggle"><span class="when-collapsed">展开托盘</span><span class="when-expanded">收起托盘</span><ChevronDown :size="14" /></span></summary>
|
|
|
|
<div v-if="selectedDetail?.pallets?.length" class="warehouse-transfer-pallet-list">
|
|
|
|
<div v-if="selectedDetail?.pallets?.length" class="warehouse-transfer-pallet-list">
|
|
|
@@ -1896,6 +1917,8 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
|
|
|
|
<div class="warehouse-evidence"><span>仓库完工照片</span><div v-if="selectedRelabelDetail?.completionEvidence?.photoFileIds?.length" class="warehouse-photo-grid"><div v-for="(fileId, index) in selectedRelabelDetail.completionEvidence.photoFileIds" :key="fileId" class="warehouse-photo-item"><el-image v-if="completionPhotoUrls[fileId]" :src="completionPhotoUrls[fileId]" :preview-src-list="completionPhotoPreviewUrls" :initial-index="completionPhotoPreviewUrls.indexOf(completionPhotoUrls[fileId])" fit="cover" preview-teleported /><div v-else-if="completionPhotoLoading[fileId]" class="warehouse-photo-placeholder">照片 {{ index + 1 }} 加载中…</div><button v-else class="warehouse-photo-placeholder warehouse-photo-retry" type="button" @click="loadCompletionPhoto(fileId)">{{ completionPhotoErrors[fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传完工照片</strong></div>
|
|
|
|
<div class="warehouse-evidence"><span>仓库完工照片</span><div v-if="selectedRelabelDetail?.completionEvidence?.photoFileIds?.length" class="warehouse-photo-grid"><div v-for="(fileId, index) in selectedRelabelDetail.completionEvidence.photoFileIds" :key="fileId" class="warehouse-photo-item"><el-image v-if="completionPhotoUrls[fileId]" :src="completionPhotoUrls[fileId]" :preview-src-list="completionPhotoPreviewUrls" :initial-index="completionPhotoPreviewUrls.indexOf(completionPhotoUrls[fileId])" fit="cover" preview-teleported /><div v-else-if="completionPhotoLoading[fileId]" class="warehouse-photo-placeholder">照片 {{ index + 1 }} 加载中…</div><button v-else class="warehouse-photo-placeholder warehouse-photo-retry" type="button" @click="loadCompletionPhoto(fileId)">{{ completionPhotoErrors[fileId] ? `照片 ${index + 1} 加载失败,点击重试` : `加载照片 ${index + 1}` }}</button></div></div><strong v-else>仓库尚未上传完工照片</strong></div>
|
|
|
|
<p v-if="selectedRelabelDetail?.completionEvidence?.completionRemark" class="warehouse-completion-remark">完工备注:{{ selectedRelabelDetail.completionEvidence.completionRemark }}</p>
|
|
|
|
<p v-if="selectedRelabelDetail?.completionEvidence?.completionRemark" class="warehouse-completion-remark">完工备注:{{ selectedRelabelDetail.completionEvidence.completionRemark }}</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-if="props.service === 'labeling' && selectedRelabelDetail" class="warehouse-card warehouse-form-section"><RelabelDocumentsPanel :relabel-no="selectedRelabelDetail.application.relabelNo" :detail="selectedRelabelDetail" /></div>
|
|
|
|
|
|
|
|
<div v-if="props.service === 'transfer' && selectedDetail?.relabelNo" class="warehouse-card warehouse-form-section"><RelabelDocumentsPanel :relabel-no="selectedDetail.relabelNo" /></div>
|
|
|
|
<div v-if="props.service === 'labeling'" class="warehouse-card warehouse-form-section">
|
|
|
|
<div v-if="props.service === 'labeling'" class="warehouse-card warehouse-form-section">
|
|
|
|
<div class="warehouse-section-heading"><h3>贴标明细资料</h3><input ref="relabelFileInput" class="warehouse-hidden-input" type="file" accept="application/pdf,.pdf" @change="uploadSelectedRelabelFile"></div>
|
|
|
|
<div class="warehouse-section-heading"><h3>贴标明细资料</h3><input ref="relabelFileInput" class="warehouse-hidden-input" type="file" accept="application/pdf,.pdf" @change="uploadSelectedRelabelFile"></div>
|
|
|
|
<div class="warehouse-hint">需要补充或替换资料时,可在对应明细行上传 SKU 或箱唛文件。</div>
|
|
|
|
<div class="warehouse-hint">需要补充或替换资料时,可在对应明细行上传 SKU 或箱唛文件。</div>
|
|
|
|