优化
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import { getDispatch, type DispatchItemDto, type DispatchSummaryDto } from '../api/dispatches'
|
||||
import { completeDispatchPda, getDispatch, getDispatchPdaTask, pickDispatchPda, pushDispatchPda, type DispatchItemDto, type DispatchPdaItemDto, type DispatchPdaTaskDto, type DispatchSummaryDto } from '../api/dispatches'
|
||||
import OverseasDetailShell from '../components/OverseasDetailShell.vue'
|
||||
import { formatOverseasDateTime } from '../lastMilePresentation'
|
||||
|
||||
@@ -25,6 +26,13 @@ const dispatchNo = computed(() => String(route.params.dispatchNo || ''))
|
||||
const detail = ref<DispatchSummaryDto | null>(null)
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
const pdaTask = ref<DispatchPdaTaskDto | null>(null)
|
||||
const pdaLoading = ref(false)
|
||||
const pdaMutating = ref(false)
|
||||
const pdaError = ref<ApiErrorDetails | null>(null)
|
||||
const pdaPushKey = ref('')
|
||||
const pdaReason = ref('')
|
||||
const pickForm = ref({ itemId: 0, positionId: 0, packages: 1, productQuantity: 1, idempotencyKey: '' })
|
||||
|
||||
const dispatchStatusLabels: Record<string, string> = {
|
||||
PENDING_DEPARTURE: '待发车',
|
||||
@@ -126,6 +134,40 @@ async function load() {
|
||||
}
|
||||
function back() { void router.push({ name: 'overseas-dispatches' }) }
|
||||
|
||||
function makeKey(prefix: string) { return `${prefix}-${dispatchNo.value}-${Date.now()}-${Math.random().toString(16).slice(2)}` }
|
||||
function selectPdaItem(item: DispatchPdaItemDto) {
|
||||
const position = item.positions?.find((value) => value.availablePackages > 0) || item.positions?.[0]
|
||||
pickForm.value = { itemId: item.dispatchItemId, positionId: Number(position?.id || 0), packages: 1, productQuantity: 1, idempotencyKey: makeKey('web-pick') }
|
||||
}
|
||||
function selectPdaRow(item: unknown) { selectPdaItem(item as DispatchPdaItemDto) }
|
||||
async function loadPdaTask() {
|
||||
if (!dispatchNo.value) return
|
||||
pdaLoading.value = true; pdaError.value = null
|
||||
try { pdaTask.value = await getDispatchPdaTask(dispatchNo.value); if (!pickForm.value.itemId && pdaTask.value.items?.[0]) selectPdaItem(pdaTask.value.items[0]) }
|
||||
catch (error) { pdaError.value = getApiErrorDetails(error, 'PDA任务加载失败,请先启动拣货任务') }
|
||||
finally { pdaLoading.value = false }
|
||||
}
|
||||
async function startPda() {
|
||||
pdaMutating.value = true; pdaError.value = null
|
||||
try { pdaTask.value = await pushDispatchPda(dispatchNo.value, { idempotencyKey: pdaPushKey.value.trim() || makeKey('web-push') }); pdaPushKey.value = ''; if (pdaTask.value.items?.[0]) selectPdaItem(pdaTask.value.items[0]); ElMessage.success('PDA拣货任务已启动') }
|
||||
catch (error) { pdaError.value = getApiErrorDetails(error, '启动PDA任务失败') }
|
||||
finally { pdaMutating.value = false }
|
||||
}
|
||||
async function submitPick() {
|
||||
const form = pickForm.value
|
||||
if (!form.itemId || !form.positionId || form.packages < 1) { ElMessage.warning('请选择拣货明细和有效库位、数量'); return }
|
||||
pdaMutating.value = true; pdaError.value = null
|
||||
try { await pickDispatchPda(dispatchNo.value, form.itemId, { positionId: form.positionId, packages: form.packages, productQuantity: form.productQuantity || form.packages, idempotencyKey: form.idempotencyKey.trim() || makeKey('web-pick') }); ElMessage.success('拣货成功,库存已扣减'); await loadPdaTask() }
|
||||
catch (error) { pdaError.value = getApiErrorDetails(error, '拣货失败,请检查库位库存') }
|
||||
finally { pdaMutating.value = false }
|
||||
}
|
||||
async function completePda() {
|
||||
pdaMutating.value = true; pdaError.value = null
|
||||
try { await completeDispatchPda(dispatchNo.value, { idempotencyKey: makeKey('web-complete'), reason: pdaReason.value.trim() || '正常完成拣货' }); ElMessage.success('拣货任务已完成'); await loadPdaTask(); await load() }
|
||||
catch (error) { pdaError.value = getApiErrorDetails(error, '完成拣货失败') }
|
||||
finally { pdaMutating.value = false }
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
@@ -139,6 +181,20 @@ onMounted(load)
|
||||
<section class="detail-section document-section" aria-labelledby="dispatch-document-heading"><h2 id="dispatch-document-heading">单据信息</h2><dl class="detail-fields"><div v-for="field in documentFields" :key="field.label"><dt>{{ field.label }}</dt><dd>{{ field.value }}</dd></div></dl></section>
|
||||
</div>
|
||||
<section class="detail-section order-section" aria-labelledby="dispatch-orders-heading"><h2 id="dispatch-orders-heading">关联订单明细</h2><DataTable :data="relatedOrders" :columns="orderColumns" column-settings-mode="hide" row-key="orderNo" empty-text="暂无关联订单明细" /></section>
|
||||
<section class="detail-section pda-section" aria-labelledby="dispatch-pda-heading">
|
||||
<div class="pda-header"><h2 id="dispatch-pda-heading">PDA拣货与库存扣减</h2><div class="pda-actions"><el-input v-model="pdaPushKey" placeholder="启动幂等键(可选)" clearable /><el-button type="primary" :loading="pdaMutating" @click="startPda">启动拣货</el-button><el-button :loading="pdaLoading" @click="loadPdaTask">刷新任务</el-button></div></div>
|
||||
<el-alert v-if="pdaError" :title="pdaError.message" type="error" :closable="false" class="pda-alert" />
|
||||
<div v-if="pdaTask" v-loading="pdaLoading" class="pda-body">
|
||||
<div class="pda-meta">任务号:{{ pdaTask.taskNo }} 状态:<strong>{{ dispatchStatus(pdaTask.status) }}</strong> 仓库:{{ text(pdaTask.warehouseId) }}</div>
|
||||
<el-table :data="pdaTask.items || []" size="small" border @row-click="selectPdaItem">
|
||||
<el-table-column label="托盘" prop="palletNo" min-width="140" /><el-table-column label="目的仓" prop="fbaWarehouseCode" min-width="110" /><el-table-column label="计划箱数" prop="scheduledPackages" width="100" /><el-table-column label="已拣箱数" prop="pickedPackages" width="100" />
|
||||
<el-table-column label="库位(可用)" min-width="220"><template #default="{ row }"><el-button v-for="position in row.positions || []" :key="position.id" link type="primary" @click.stop="selectPdaRow(row); pickForm.positionId = Number(position.id)">{{ position.id }}({{ position.availablePackages }})</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
<div class="pick-form"><span class="pick-label">拣货明细:{{ pickForm.itemId || '未选择' }}</span><el-input-number v-model="pickForm.positionId" :min="1" controls-position="right" placeholder="库位ID" /><el-input-number v-model="pickForm.packages" :min="1" controls-position="right" placeholder="箱数" @change="(value) => { pickForm.productQuantity = Number(value || 1) }" /><el-input v-model="pickForm.idempotencyKey" placeholder="拣货幂等键" /><el-button type="success" :loading="pdaMutating" @click="submitPick">拣货并扣减库存</el-button></div>
|
||||
<div class="pda-complete"><el-input v-model="pdaReason" placeholder="完成原因(可选)" /><el-button type="warning" :loading="pdaMutating" @click="completePda">完成拣货</el-button></div>
|
||||
</div>
|
||||
<el-empty v-else description="尚未加载PDA任务,请点击启动拣货" />
|
||||
</section>
|
||||
</div>
|
||||
</OverseasDetailShell>
|
||||
</template>
|
||||
@@ -158,10 +214,22 @@ onMounted(load)
|
||||
.detail-fields dd { margin: 0; color: var(--cc-text); font-weight: 600; overflow-wrap: anywhere; }
|
||||
.order-section { overflow: hidden; border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
|
||||
.order-section h2 { margin-right: 4px; margin-left: 4px; }
|
||||
.pda-section { border: 1px solid var(--cc-border); border-top: 0; background: #fff; }
|
||||
.pda-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
||||
.pda-header h2 { margin-bottom: 0; }
|
||||
.pda-actions, .pick-form, .pda-complete { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.pda-actions :deep(.el-input) { width: 230px; }
|
||||
.pda-alert { margin: 12px 0; }
|
||||
.pda-meta { margin: 12px 0; color: var(--cc-muted); }
|
||||
.pick-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--cc-border); }
|
||||
.pick-form :deep(.el-input), .pda-complete :deep(.el-input) { width: 220px; }
|
||||
.pick-label { color: var(--cc-muted); }
|
||||
.pda-complete { justify-content: flex-end; margin-top: 12px; }
|
||||
@media (max-width: 960px) {
|
||||
.summary-grid { grid-template-columns: 1fr; }
|
||||
.summary-grid > .detail-section, .summary-grid > .detail-section:nth-child(2) { border-right: 0; }
|
||||
.document-section { grid-column: auto; }
|
||||
.detail-fields > div { grid-template-columns: 132px minmax(0, 1fr); }
|
||||
.pda-actions { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user