Merge branch 'release/test' of http://8.163.30.128:10082/html/admin-web into release/test
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import '@/styles/adminDensity.css'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { Download, Pencil } from '@lucide/vue'
|
||||
import { Download, Pencil, PackagePlus } from '@lucide/vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
||||
@@ -18,7 +18,7 @@ import RowActions, { type RowActionItem } from '@/shared/components/RowActions.v
|
||||
import { changeContainerReceivingWarehouse, convertLastMileContainerToInboundPlans, createManualInboundPlan, downloadLastMileOrders, getContainerInboundPlanPreview, getContainerPlanProgress, getLastMileOrderManualNodes, listContainerPlanProgress, listLastMileOrders, reviewDirectRelabelOrder, reviewReturnOrder, transferLastMileOrders, updateLastMileOrderHold, updateLastMileOrderManualNodes, type ContainerInboundPlanPreviewDto, type ContainerInboundPlanPreviewGroupDto, type ContainerInboundPlanPreviewOrderDto, type ContainerPlanProgressDetailDto, type ContainerPlanProgressSummaryDto, type ManualNodeCode } from '../api/lastMileOrders'
|
||||
import OrderReferenceText from '../components/OrderReferenceText.vue'
|
||||
import WarehouseTransferReviewDialog from '../components/WarehouseTransferReviewDialog.vue'
|
||||
import { confirmRelabelCompletion, getRelabelReviewTask, returnRelabelForRework } from '../api/relabelOrders'
|
||||
import { confirmRelabelCompletion, createReturnPackingPlan, getRelabelReviewTask, returnRelabelForRework, type ReturnPackingPlanBox } from '../api/relabelOrders'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { overseasWarehousePermissions as permission } from '../permissions'
|
||||
import { lastMileStatusLabel, packageDifferenceText, toLastMileOrderListItem } from '../lastMilePresentation'
|
||||
@@ -42,6 +42,32 @@ const containerDetail = ref<ContainerPlanProgressDetailDto | null>(null)
|
||||
const transferReviewOpen = ref(false)
|
||||
const transferReviewTarget = ref<LastMileOrderListItem | null>(null)
|
||||
const selectedRows = ref<LastMileOrderListItem[]>([])
|
||||
const packingPlanOpen = ref(false)
|
||||
const packingPlanLoading = ref(false)
|
||||
const packingPlanMode = ref<'CUSTOMER_INSTRUCTION' | 'WAREHOUSE_RECOMMENDATION'>('CUSTOMER_INSTRUCTION')
|
||||
const packingPlanRemark = ref('')
|
||||
const packingPlanBoxes = ref<ReturnPackingPlanBox[]>([])
|
||||
const selectedReturnRows = computed(() => selectedRows.value.filter((row) => row.orderTypeCode === 'RETURN_RELABEL' && ['COMPLETED', 'RECEIVED', 'IN_WAREHOUSE', '已完成入库'].includes(String(row.currentStatusCode || row.status || ''))))
|
||||
function openReturnPackingPlan() {
|
||||
if (!selectedReturnRows.value.length) { ElMessage.warning('请勾选已入库的退货订单'); return }
|
||||
packingPlanMode.value = 'CUSTOMER_INSTRUCTION'; packingPlanRemark.value = ''
|
||||
packingPlanBoxes.value = [{ boxNo: 1, items: [{ skuCode: '', quantity: 0 }] }]
|
||||
packingPlanOpen.value = true
|
||||
}
|
||||
function addReturnPackingBox() { packingPlanBoxes.value.push({ boxNo: packingPlanBoxes.value.length + 1, items: [{ skuCode: '', quantity: 0 }] }) }
|
||||
function removeReturnPackingBox(index: number) { if (packingPlanBoxes.value.length > 1) packingPlanBoxes.value.splice(index, 1) }
|
||||
function addReturnPackingItem(box: ReturnPackingPlanBox) { box.items.push({ skuCode: '', quantity: 0 }) }
|
||||
function removeReturnPackingItem(box: ReturnPackingPlanBox, index: number) { if (box.items.length > 1) box.items.splice(index, 1) }
|
||||
async function submitReturnPackingPlan() {
|
||||
const boxes = packingPlanBoxes.value.map((box) => ({ ...box, items: box.items.filter((item) => item.skuCode.trim() && Number(item.quantity) > 0) })).filter((box) => box.items.length)
|
||||
if (!boxes.length) { ElMessage.warning('至少配置一个目标箱'); return }
|
||||
packingPlanLoading.value = true
|
||||
try {
|
||||
const result = await createReturnPackingPlan({ returnNos: selectedReturnRows.value.map((row) => String(row.sourceOrderNo || row.orderNo)).filter(Boolean), mode: packingPlanMode.value, boxes, remark: packingPlanRemark.value.trim() || undefined, idempotencyKey: globalThis.crypto?.randomUUID?.() || `return-packing-${Date.now()}` })
|
||||
ElMessage.success(`合箱计划 ${result.planNo} 已推送 PDA`); packingPlanOpen.value = false; selectedRows.value = []
|
||||
} catch (error) { ElMessage.error(getApiErrorDetails(error, '合箱计划推送失败,请重试').message) }
|
||||
finally { packingPlanLoading.value = false }
|
||||
}
|
||||
const actionLoading = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
async function exportOrders() { exportLoading.value = true; try { const { createdAt: range, ...rest } = filters.value; saveCsv(await downloadLastMileOrders({ ...rest, createdFrom: Array.isArray(range) && range[0] ? `${String(range[0]).slice(0, 10)}T00:00:00` : undefined, createdTo: Array.isArray(range) && range[1] ? `${String(range[1]).slice(0, 10)}T23:59:59` : undefined }) as Blob, '尾程订单.csv') } catch (cause) { ElMessage.error(getApiErrorDetails(cause, '尾程订单导出失败,请重试').message) } finally { exportLoading.value = false } }
|
||||
@@ -446,6 +472,10 @@ function isRejectedReturn(source: object) {
|
||||
}
|
||||
function hasSelectedRejectedReturn() { return selectedRows.value.some(isRejectedReturn) }
|
||||
function hasSelectedInboundPlan() { return selectedRows.value.some(hasInboundPlan) }
|
||||
const inboundPlanExcludedOrderTypes = new Set(['WAREHOUSE_TRANSFER', 'RELABEL_APPLICATION', 'RETURN_RELABEL'])
|
||||
function canGenerateInboundPlan(row: LastMileOrderListItem) {
|
||||
return !inboundPlanExcludedOrderTypes.has(String(row.orderTypeCode || row.orderType || ''))
|
||||
}
|
||||
async function loadTransferWarehouseOptions() {
|
||||
transferWarehouseLoading.value = true
|
||||
transferWarehouseError.value = null
|
||||
@@ -967,7 +997,7 @@ onMounted(() => { void load() })
|
||||
</div>
|
||||
</section></template>
|
||||
|
||||
<template v-if="statusTab !== '柜子'" #toolbar-actions><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate)" type="primary" plain :disabled="selectedRows.length !== 1 || hasSelectedRejectedReturn() || hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="statusTab === '待分配' && access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion">按柜批量生成</el-button></template>
|
||||
<template v-if="statusTab !== '柜子'" #toolbar-actions><el-button v-if="selectedReturnRows.length" type="warning" @click="openReturnPackingPlan"><PackagePlus :size="16" />创建退货合箱计划({{ selectedReturnRows.length }})</el-button><el-button v-if="access.can(permission.lastMileOrderExport)" plain :loading="exportLoading" @click="exportOrders"><Download :size="16" />导出订单</el-button><el-button v-if="access.can(permission.lastMileOrderTransfer)" plain :disabled="!selectedRows.length || hasSelectedRejectedReturn() || actionLoading" @click="openTransfer">转仓</el-button><el-button v-if="access.can(permission.inboundOrderCreate) && selectedRows.length === 1 && canGenerateInboundPlan(selectedRows[0])" type="primary" plain :disabled="hasSelectedInboundPlan() || actionLoading" @click="openInboundPlan()">生成入库计划</el-button><el-button v-if="statusTab === '待分配' && access.can(permission.inboundOrderCreate)" type="primary" :disabled="actionLoading" @click="openContainerConversion">按柜批量生成</el-button></template>
|
||||
<template #relabelOrderNo="{ row }"><div v-for="application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.relabelNo }}</div><span v-if="!row.relabelApplications?.length">{{ row.relabelOrderNo }}</span></template>
|
||||
<template #relabelStatus="{ row }"><div v-for="application in row.relabelApplications" :key="application.relabelNo" class="relabel-suborder">{{ application.statusLabel }} · {{ application.quantity }} 件</div><span v-if="!row.relabelApplications?.length">{{ row.relabelStatus }}</span></template>
|
||||
<template #actions="{ row }"><RowActions text-actions :actions="lastMileRowActions(row as LastMileOrderListItem)" :more-label="`更多尾程订单操作 · ${(row as LastMileOrderListItem).orderNo || '当前记录'}`" @action="handleLastMileRowAction($event, row as LastMileOrderListItem)" /></template>
|
||||
@@ -976,6 +1006,7 @@ onMounted(() => { void load() })
|
||||
<template #pagination-left>共 {{ total }} 条尾程订单 · 已选 {{ selectedRows.length }} 条</template>
|
||||
</DataTable>
|
||||
</ListPageLayout></AdminPage>
|
||||
<AppDialog class="admin-density-dialog" v-model="packingPlanOpen" title="退货合箱计划" width="min(980px, calc(100vw - 28px))" confirm-text="推送 PDA" :confirm-loading="packingPlanLoading" @confirm="submitReturnPackingPlan"><el-radio-group v-model="packingPlanMode"><el-radio-button label="CUSTOMER_INSTRUCTION">客户合箱指令</el-radio-button><el-radio-button label="WAREHOUSE_RECOMMENDATION">仓库自主合箱</el-radio-button></el-radio-group><div v-for="(box, index) in packingPlanBoxes" :key="index" class="packing-box"><div class="packing-box-head"><strong>第 {{ index + 1 }} 箱</strong><el-button text type="danger" @click="removeReturnPackingBox(index)">删除箱</el-button></div><div v-for="(item, itemIndex) in box.items" :key="itemIndex" class="packing-item"><el-input v-model="item.skuCode" placeholder="SKU" /><el-input-number v-model="item.quantity" :min="0" :precision="0" placeholder="数量" /><el-button text type="danger" @click="removeReturnPackingItem(box, itemIndex)">移除</el-button></div><el-button text type="primary" @click="addReturnPackingItem(box)">+ 添加 SKU</el-button></div><el-button plain type="primary" @click="addReturnPackingBox">+ 添加箱</el-button><el-input v-model="packingPlanRemark" type="textarea" :rows="2" placeholder="合箱备注(选填)" /></AppDialog>
|
||||
<WarehouseTransferReviewDialog v-if="transferReviewTarget" v-model="transferReviewOpen" :customer-id="transferReviewTarget.customerId" :customer-name="transferReviewTarget.owner" :transfer-no="transferReviewTarget.orderNo" @reviewed="load()" />
|
||||
<AppDialog class="admin-density-dialog" v-model="transferOpen" title="转仓" confirm-text="确认转仓" :loading="transferWarehouseLoading" :confirm-loading="actionLoading" :confirm-disabled="!transferForm.warehouseId || Boolean(transferWarehouseError)" @confirm="submitTransfer">
|
||||
<LoadErrorState v-if="transferWarehouseError" title="FBA 仓候选加载失败" :message="transferWarehouseError.message" compact @retry="loadTransferWarehouseOptions">
|
||||
|
||||
Reference in New Issue
Block a user