160 lines
11 KiB
Vue
160 lines
11 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted, reactive, ref } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
import { getInboundPlan, listInboundPlans, type InboundPlanDetail, type InboundPlanSummary } from '../api/inboundPlans'
|
|
import { overseasWarehouseLabel } from '../inboundPlanDisplay'
|
|
|
|
const loading = ref(false)
|
|
const error = ref('')
|
|
const rows = ref<InboundPlanSummary[]>([])
|
|
const detail = ref<InboundPlanDetail | null>(null)
|
|
const total = ref(0)
|
|
const inboundStatusOptions = [
|
|
{ label: '待分配', value: 'PENDING_ASSIGNMENT' },
|
|
{ label: '已分配', value: 'ASSIGNED' },
|
|
{ label: '待收货', value: 'PDA_PENDING' },
|
|
{ label: '已签收', value: 'SIGNED' },
|
|
{ label: '拣货中', value: 'PICKING' },
|
|
{ label: '部分入库', value: 'PARTIAL_INBOUND' },
|
|
{ label: '已完成', value: 'COMPLETED' },
|
|
]
|
|
const query = reactive({
|
|
page: 1, size: 20, planNo: '', inboundCode: '', destinationWarehouseCode: '', status: '',
|
|
appointmentStatus: '', dispatchStatus: '', createdDates: [] as string[],
|
|
})
|
|
|
|
function text(value: unknown) { return value === null || value === undefined || value === '' ? '—' : String(value) }
|
|
function destinationWarehouseLabel(...values: unknown[]) {
|
|
const parts = values
|
|
.filter((value): value is string => typeof value === 'string' && Boolean(value.trim()))
|
|
.map((value) => value.trim())
|
|
return [...new Set(parts)].join(' · ') || '—'
|
|
}
|
|
function dateTime(value: unknown) { return value ? new Date(String(value)).toLocaleString('zh-CN', { hour12: false }) : '—' }
|
|
function statusLabel(value: unknown) {
|
|
return ({
|
|
PENDING: '待处理', PROCESSING: '处理中', PENDING_ASSIGNMENT: '待分配', PDA_PENDING: '待收货',
|
|
ASSIGNED: '已分配', SIGNED: '已签收', PICKING: '拣货中', PARTIAL_INBOUND: '部分入库',
|
|
COMPLETED: '已完成', CANCELLED: '已取消', UNAPPOINTED: '未预约', SUCCESS: '预约成功',
|
|
FAILED: '预约失败', SCHEDULED: '已排车', UNSCHEDULED: '未排车',
|
|
} as Record<string, string>)[String(value)] || text(value)
|
|
}
|
|
function errorMessage(cause: unknown) { return cause instanceof Error ? cause.message : '请求失败,请稍后重试' }
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
error.value = ''
|
|
try {
|
|
const result = await listInboundPlans({
|
|
page: query.page, size: query.size, planNo: query.planNo || undefined, inboundCode: query.inboundCode || undefined,
|
|
destinationWarehouseCode: query.destinationWarehouseCode || undefined, status: query.status || undefined,
|
|
appointmentStatus: query.appointmentStatus || undefined, dispatchStatus: query.dispatchStatus || undefined,
|
|
createdFrom: query.createdDates[0] ? `${query.createdDates[0]}T00:00:00` : undefined,
|
|
createdTo: query.createdDates[1] ? `${query.createdDates[1]}T23:59:59` : undefined,
|
|
})
|
|
rows.value = result.records
|
|
total.value = result.total
|
|
} catch (cause) {
|
|
error.value = errorMessage(cause)
|
|
rows.value = []
|
|
total.value = 0
|
|
} finally { loading.value = false }
|
|
}
|
|
|
|
function resetQuery() {
|
|
Object.assign(query, { page: 1, planNo: '', inboundCode: '', destinationWarehouseCode: '', status: '', appointmentStatus: '', dispatchStatus: '', createdDates: [] })
|
|
void load()
|
|
}
|
|
|
|
async function openDetail(candidate: unknown) {
|
|
const row = candidate as InboundPlanSummary
|
|
loading.value = true
|
|
try { detail.value = await getInboundPlan(row.planNo) }
|
|
catch (cause) { ElMessage.error(errorMessage(cause)) }
|
|
finally { loading.value = false }
|
|
}
|
|
|
|
onMounted(() => void load())
|
|
</script>
|
|
|
|
<template>
|
|
<section v-loading="loading" class="inbound-orders" :aria-busy="loading">
|
|
<header class="page-head">
|
|
<div><h2>{{ detail ? '入库订单详情' : '入库订单列表' }}</h2><p>{{ detail ? `计划号:${detail.planNo}` : '查询当前企业在海外仓的入库计划与作业进度' }}</p></div>
|
|
<el-button v-if="detail" @click="detail = null">返回列表</el-button>
|
|
</header>
|
|
|
|
<template v-if="!detail">
|
|
<el-alert v-if="error" :title="error" type="error" show-icon :closable="false"><template #default><el-button size="small" @click="load">重新加载</el-button></template></el-alert>
|
|
<el-card shadow="never" class="filter-card">
|
|
<el-form inline @submit.prevent="query.page = 1; load()">
|
|
<el-form-item label="计划号"><el-input v-model.trim="query.planNo" clearable placeholder="请输入计划号" /></el-form-item>
|
|
<el-form-item label="入仓码"><el-input v-model.trim="query.inboundCode" clearable placeholder="请输入入仓码" /></el-form-item>
|
|
<el-form-item label="目的仓"><el-input v-model.trim="query.destinationWarehouseCode" clearable placeholder="请输入仓库代码" /></el-form-item>
|
|
<el-form-item label="作业状态"><el-select v-model="query.status" clearable placeholder="全部"><el-option v-for="option in inboundStatusOptions" :key="option.value" :label="option.label" :value="option.value" /></el-select></el-form-item>
|
|
<el-form-item label="预约状态"><el-select v-model="query.appointmentStatus" clearable placeholder="全部"><el-option label="未预约" value="UNAPPOINTED" /><el-option label="待处理" value="PENDING" /><el-option label="成功" value="SUCCESS" /><el-option label="失败" value="FAILED" /><el-option label="已取消" value="CANCELLED" /></el-select></el-form-item>
|
|
<el-form-item label="排车状态"><el-select v-model="query.dispatchStatus" clearable placeholder="全部"><el-option label="未排车" value="UNSCHEDULED" /><el-option label="已排车" value="SCHEDULED" /></el-select></el-form-item>
|
|
<el-form-item label="创建时间"><el-date-picker v-model="query.createdDates" type="daterange" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" /></el-form-item>
|
|
<el-form-item><el-button type="primary" native-type="submit">查询</el-button><el-button @click="resetQuery">重置</el-button></el-form-item>
|
|
</el-form>
|
|
</el-card>
|
|
<el-card shadow="never">
|
|
<el-table :data="rows" border empty-text="暂无入库订单">
|
|
<el-table-column label="操作" width="90" fixed="left"><template #default="scope"><el-button link type="primary" @click="openDetail(scope.row)">查看</el-button></template></el-table-column>
|
|
<el-table-column prop="planNo" label="计划号" min-width="180" show-overflow-tooltip />
|
|
<el-table-column prop="inboundCode" label="入仓码" min-width="170" show-overflow-tooltip />
|
|
<el-table-column label="海外接柜仓" min-width="220" show-overflow-tooltip><template #default="scope">{{ overseasWarehouseLabel(scope.row.overseasWarehouseCode, scope.row.overseasWarehouseName) }}</template></el-table-column>
|
|
<el-table-column label="目的仓" min-width="150" show-overflow-tooltip><template #default="scope">{{ text(scope.row.fbaWarehouseCode) }}</template></el-table-column>
|
|
<el-table-column prop="packageCount" label="计划箱数" width="100" />
|
|
<el-table-column prop="pickedPackageCount" label="已拣箱数" width="100" />
|
|
<el-table-column prop="palletCount" label="托盘数" width="90" />
|
|
<el-table-column label="状态" width="120" show-overflow-tooltip><template #default="scope">{{ statusLabel(scope.row.status) }}</template></el-table-column>
|
|
<el-table-column label="预约状态" width="120" show-overflow-tooltip><template #default="scope">{{ statusLabel(scope.row.appointmentStatus) }}</template></el-table-column>
|
|
<el-table-column label="排车状态" width="110" show-overflow-tooltip><template #default="scope">{{ statusLabel(scope.row.dispatchStatus) }}</template></el-table-column>
|
|
<el-table-column label="创建时间" width="180" show-overflow-tooltip><template #default="scope">{{ dateTime(scope.row.createdAt) }}</template></el-table-column>
|
|
</el-table>
|
|
<el-pagination v-model:current-page="query.page" v-model:page-size="query.size" class="pagination" :total="total" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next" @change="load" />
|
|
</el-card>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<el-card shadow="never">
|
|
<dl class="detail-grid">
|
|
<div><dt>计划号</dt><dd>{{ detail.planNo }}</dd></div><div><dt>集装箱号</dt><dd>{{ text(detail.containerNo) }}</dd></div>
|
|
<div><dt>海外接柜仓</dt><dd>{{ overseasWarehouseLabel(detail.overseasWarehouseCode, detail.overseasWarehouseName) }}</dd></div>
|
|
<div><dt>状态</dt><dd>{{ statusLabel(detail.status) }}</dd></div><div><dt>入仓码数</dt><dd>{{ detail.lineCount }}</dd></div>
|
|
<div><dt>托盘数</dt><dd>{{ detail.palletCount }}</dd></div><div><dt>总体积(m³)</dt><dd>{{ detail.totalVolumeCbm }}</dd></div>
|
|
<div><dt>计划箱数</dt><dd>{{ detail.expectedPackageCount }}</dd></div><div><dt>已拣箱数</dt><dd>{{ detail.pickedPackageCount }}</dd></div>
|
|
</dl>
|
|
</el-card>
|
|
<el-card shadow="never" header="入仓明细">
|
|
<el-table :data="detail.lines" border empty-text="暂无入仓明细">
|
|
<el-table-column prop="inboundCode" label="入仓码" min-width="180" /><el-table-column label="目的仓" min-width="220" show-overflow-tooltip><template #default="scope">{{ destinationWarehouseLabel(scope.row.fbaWarehouseCode, scope.row.destinationWarehouseCode, scope.row.destinationWarehouseName) }}</template></el-table-column>
|
|
<el-table-column prop="requiredPackageCount" label="计划箱数" width="100" />
|
|
<el-table-column prop="plannedPalletCount" label="计划托数" width="100" /><el-table-column prop="volumeCbm" label="体积(m³)" width="110" />
|
|
</el-table>
|
|
</el-card>
|
|
<el-card shadow="never" header="托盘明细">
|
|
<el-table :data="detail.pallets" border empty-text="暂无托盘">
|
|
<el-table-column prop="palletNo" label="托盘码" min-width="180" /><el-table-column label="目的仓" min-width="220" show-overflow-tooltip><template #default="scope">{{ destinationWarehouseLabel(scope.row.destinationWarehouseCode, scope.row.destinationWarehouseName) }}</template></el-table-column>
|
|
<el-table-column prop="capacityCbm" label="容量(m³)" width="120" /><el-table-column prop="allocatedVolumeCbm" label="已分配体积(m³)" width="140" />
|
|
</el-table>
|
|
</el-card>
|
|
</template>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.inbound-orders { display: grid; gap: 18px; }
|
|
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
|
|
.page-head h2 { margin: 0; color: #111827; font-size: 26px; }
|
|
.page-head p { margin: 7px 0 0; color: #64748b; font-size: 14px; }
|
|
.filter-card :deep(.el-form-item) { margin-bottom: 12px; }
|
|
.filter-card :deep(.el-input), .filter-card :deep(.el-select) { width: 190px; }
|
|
.pagination { justify-content: flex-end; margin-top: 18px; }
|
|
.detail-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; }
|
|
.detail-grid div { min-width: 0; padding: 18px 20px; border-right: 1px solid #ebeef5; border-bottom: 1px solid #ebeef5; }
|
|
.detail-grid dt { margin-bottom: 7px; color: #8492a6; font-size: 13px; }
|
|
.detail-grid dd { margin: 0; overflow: hidden; color: #1f2937; text-overflow: ellipsis; white-space: nowrap; }
|
|
</style>
|