feat: 完成客户第12条需求+多系统改造
1. 新增预警表添加工单号字段,支持按工单号检索预警 2. 添加工单过滤在制品/绩效报表/预警中心查询 3. 补全WMS台账字段口径与权限配置 4. 修复备料单与数量不符处理流程 5. 新增配送进度/库位联动/自动出库功能 6. 修复AGV配送状态更新逻辑 7. 优化退料与库存管理细节
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
defineOptions({ name: 'Outbound' })
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { getRealName } from '../utils/auth'
|
||||
@@ -118,6 +118,7 @@ const OWNERSHIP_OPTIONS = [
|
||||
const zones = ref([])
|
||||
const genForm = reactive({
|
||||
materialCode: '', mode: 1, batchNo: '', qty: 1, zoneCode: '',
|
||||
shelfNo: '', layerNo: '', positionNo: '',
|
||||
boxNo: '', contractNo: '', remark: '',
|
||||
outboundCategory: 'other', ownershipType: 'none', ownershipNo: '', targetStation: ''
|
||||
})
|
||||
@@ -155,6 +156,73 @@ const zoneOptions = computed(() => {
|
||||
return [...map.values()]
|
||||
})
|
||||
|
||||
// 库位三级联动(与入库同口径):基于已加载 zones(每行是完整四级货位)派生可选项
|
||||
function zoneShelves(zoneCode) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo) set.add(z.shelfNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
function zoneLayers(zoneCode, shelfNo) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo === shelfNo && z.layerNo) set.add(z.layerNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
function zonePositions(zoneCode, shelfNo, layerNo) {
|
||||
const set = new Set()
|
||||
zones.value.forEach(z => { if (z.zoneCode === zoneCode && z.shelfNo === shelfNo && z.layerNo === layerNo && z.positionNo) set.add(z.positionNo) })
|
||||
return [...set].sort()
|
||||
}
|
||||
function onGenZoneChange() {
|
||||
genForm.shelfNo = ''; genForm.layerNo = ''; genForm.positionNo = ''
|
||||
if (genForm.materialCode || genForm.batchNo) fillZoneFromHistory()
|
||||
}
|
||||
function onGenShelfChange() { genForm.layerNo = ''; genForm.positionNo = '' }
|
||||
function onGenLayerChange() { genForm.positionNo = '' }
|
||||
function locText(row) {
|
||||
const parts = []
|
||||
if (row?.shelfNo) parts.push(row.shelfNo + '架')
|
||||
if (row?.layerNo) parts.push(row.layerNo + '层')
|
||||
if (row?.positionNo) parts.push(row.positionNo + '位')
|
||||
return parts.length ? parts.join('/') : '-'
|
||||
}
|
||||
|
||||
// 出库库位联动(客户诉求 一.9「出库相同」):与入库同口径回填 区域/货架/层/位置 四级。
|
||||
// 优先级:① 指定批次在库的实际库位 ② 该物料最近一次入库库位 ③ 无历史时按所选区域推荐同区空位。
|
||||
async function fillZoneFromHistory() {
|
||||
if (!genForm.materialCode && !genForm.batchNo) return
|
||||
let filled = false
|
||||
try {
|
||||
const loc = await request.get('/outbound/location-hint', {
|
||||
params: { materialCode: genForm.materialCode, batchNo: genForm.batchNo }
|
||||
})
|
||||
if (loc && loc.zoneCode) {
|
||||
genForm.zoneCode = loc.zoneCode
|
||||
genForm.shelfNo = loc.shelfNo || ''
|
||||
genForm.layerNo = loc.layerNo || ''
|
||||
genForm.positionNo = loc.positionNo || ''
|
||||
filled = true
|
||||
}
|
||||
} catch {
|
||||
/* 查询失败不影响主流程 */
|
||||
}
|
||||
if (filled) return
|
||||
// 无历史库位:按区域推荐同区空位(与入库同源接口,返回数组取第一条)
|
||||
try {
|
||||
const v = await request.get('/zone/vacant', { params: { zoneCode: genForm.zoneCode || '', limit: 1 } })
|
||||
const hit = Array.isArray(v) ? v[0] : (v?.list || [])[0]
|
||||
if (hit) {
|
||||
genForm.zoneCode = hit.zoneCode
|
||||
genForm.shelfNo = hit.shelfNo || ''
|
||||
genForm.layerNo = hit.layerNo || ''
|
||||
genForm.positionNo = hit.positionNo || ''
|
||||
}
|
||||
} catch {
|
||||
/* 忽略 */
|
||||
}
|
||||
}
|
||||
watch(() => genForm.materialCode, (v) => { if (v) { genForm.batchNo = ''; fillZoneFromHistory() } })
|
||||
watch(() => genForm.batchNo, (v) => { if (v && genForm.materialCode) fillZoneFromHistory() })
|
||||
|
||||
async function loadGenRows() {
|
||||
loadingGen.value = true
|
||||
try {
|
||||
@@ -180,6 +248,9 @@ async function submitGen() {
|
||||
ownershipNo: genForm.ownershipType !== 'none' ? genForm.ownershipNo.trim() : undefined,
|
||||
targetStation: genForm.targetStation.trim() || undefined,
|
||||
zoneCode: genForm.zoneCode || undefined,
|
||||
shelfNo: genForm.shelfNo || undefined,
|
||||
layerNo: genForm.layerNo || undefined,
|
||||
positionNo: genForm.positionNo || undefined,
|
||||
boxNo: genForm.boxNo.trim() || undefined,
|
||||
contractNo: genForm.contractNo.trim() || undefined,
|
||||
remark: genForm.remark.trim() || undefined
|
||||
@@ -198,7 +269,7 @@ async function submitGen() {
|
||||
try {
|
||||
const res = await request.post('/outbound/general', payload)
|
||||
ElMessage.success(`通用出库成功:${res?.outboundNo}${res?.boxNo ? ' 箱号 ' + res.boxNo : ''}`)
|
||||
Object.assign(genForm, { materialCode: '', mode: 1, batchNo: '', qty: 1, zoneCode: '', boxNo: '', contractNo: '', remark: '', outboundCategory: 'other', ownershipType: 'none', ownershipNo: '', targetStation: '' })
|
||||
Object.assign(genForm, { materialCode: '', mode: 1, batchNo: '', qty: 1, zoneCode: '', shelfNo: '', layerNo: '', positionNo: '', boxNo: '', contractNo: '', remark: '', outboundCategory: 'other', ownershipType: 'none', ownershipNo: '', targetStation: '' })
|
||||
snInput.value = ''
|
||||
snList.value = []
|
||||
genPage.current = 1
|
||||
@@ -208,7 +279,7 @@ async function submitGen() {
|
||||
}
|
||||
|
||||
/* ===================== 出库记录(统一主表,全部 + 导出) ===================== */
|
||||
const recFilters = reactive({ outboundType: '', materialCode: '', boxNo: '', ownershipType: '', dateRange: [] })
|
||||
const recFilters = reactive({ outboundType: '', status: '', materialCode: '', boxNo: '', ownershipType: '', dateRange: [] })
|
||||
const recRows = ref([])
|
||||
const recTotal = ref(0)
|
||||
const loadingRec = ref(false)
|
||||
@@ -221,6 +292,7 @@ async function loadRecords() {
|
||||
try {
|
||||
const params = {
|
||||
outboundType: recFilters.outboundType,
|
||||
status: recFilters.status,
|
||||
materialCode: recFilters.materialCode.trim(),
|
||||
boxNo: recFilters.boxNo.trim(),
|
||||
ownershipType: recFilters.ownershipType,
|
||||
@@ -236,11 +308,12 @@ async function loadRecords() {
|
||||
} finally { loadingRec.value = false }
|
||||
}
|
||||
function searchRec() { recPage.current = 1; loadRecords() }
|
||||
function resetRec() { Object.assign(recFilters, { outboundType: '', materialCode: '', boxNo: '', dateRange: [] }); searchRec() }
|
||||
function resetRec() { Object.assign(recFilters, { outboundType: '', status: '', materialCode: '', boxNo: '', dateRange: [] }); searchRec() }
|
||||
|
||||
function downloadCSV() {
|
||||
exportXlsxFetch('/api/outbound/export', {
|
||||
outboundType: recFilters.outboundType,
|
||||
status: recFilters.status,
|
||||
materialCode: recFilters.materialCode.trim(),
|
||||
boxNo: recFilters.boxNo.trim()
|
||||
}, '出库管理.xlsx')
|
||||
@@ -374,11 +447,24 @@ onMounted(loadDicts)
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="出库区域">
|
||||
<el-select v-model="genForm.zoneCode" filterable clearable placeholder="可选" style="width:100%">
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="genForm.zoneCode" filterable clearable placeholder="选择区域(选物料/批次后自动带出最近库位)" style="width:100%" @change="onGenZoneChange">
|
||||
<el-option v-for="z in zoneOptions" :key="z.zoneCode" :value="z.zoneCode" :label="`${z.zoneCode} ${z.zoneName}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="货架 / 层 / 位置">
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
|
||||
<el-select v-model="genForm.shelfNo" placeholder="货架" clearable style="width:120px" @change="onGenShelfChange">
|
||||
<el-option v-for="s in zoneShelves(genForm.zoneCode)" :key="s" :label="s + ' 架'" :value="s" />
|
||||
</el-select>
|
||||
<el-select v-model="genForm.layerNo" placeholder="层" clearable style="width:100px" :disabled="!genForm.shelfNo" @change="onGenLayerChange">
|
||||
<el-option v-for="l in zoneLayers(genForm.zoneCode, genForm.shelfNo)" :key="l" :label="l + ' 层'" :value="l" />
|
||||
</el-select>
|
||||
<el-select v-model="genForm.positionNo" placeholder="位置" clearable style="width:110px" :disabled="!genForm.shelfNo">
|
||||
<el-option v-for="p in zonePositions(genForm.zoneCode, genForm.shelfNo, genForm.layerNo)" :key="p" :label="p + ' 位'" :value="p" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="箱号">
|
||||
<el-input v-model="genForm.boxNo" placeholder="选填:整箱发货时填写,装箱信息随出库单记录(无需单独录入装箱)" clearable style="width:100%" />
|
||||
</el-form-item>
|
||||
@@ -401,6 +487,9 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="120">
|
||||
<template #default="{ row }">{{ row.batchNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库位" min-width="120" align="center">
|
||||
<template #default="{ row }">{{ row.zoneCode ? row.zoneCode + ' ' + locText(row) : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="SN" min-width="180">
|
||||
<template #default="{ row }">{{ row.snCodes || '-' }}</template>
|
||||
</el-table-column>
|
||||
@@ -440,9 +529,16 @@ onMounted(loadDicts)
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="recFilters.outboundType" clearable placeholder="全部" style="width:140px">
|
||||
<el-option label="备料出库" value="workorder" />
|
||||
<el-option label="工位补料" value="refill" />
|
||||
<el-option label="通用出库" value="general" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="recFilters.status" clearable placeholder="全部" style="width:120px">
|
||||
<el-option label="已出库" value="已出库" />
|
||||
<el-option label="已送达" value="已送达" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="物料">
|
||||
<MaterialSelect v-model="recFilters.materialCode" placeholder="物料" width="180px" />
|
||||
</el-form-item>
|
||||
@@ -466,11 +562,16 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="outboundNo" label="出库单号" min-width="150" />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.outboundType === 'general' ? 'warning' : 'primary'" size="small">
|
||||
{{ row.outboundType === 'general' ? '通用' : '备料' }}
|
||||
<el-tag :type="row.outboundType === 'general' ? 'warning' : row.outboundType === 'refill' ? 'danger' : 'primary'" size="small">
|
||||
{{ row.outboundType === 'general' ? '通用' : row.outboundType === 'refill' ? '补料' : '备料' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '已送达' ? 'success' : 'info'" size="small">{{ row.status || '已出库' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130">
|
||||
<template #default="{ row }">{{ row.orderNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
@@ -478,6 +579,9 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="110">
|
||||
<template #default="{ row }">{{ row.batchNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库位" min-width="120" align="center">
|
||||
<template #default="{ row }">{{ row.zoneCode ? row.zoneCode + ' ' + locText(row) : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="SN" min-width="160">
|
||||
<template #default="{ row }">{{ row.snCodes || '-' }}</template>
|
||||
</el-table-column>
|
||||
|
||||
Reference in New Issue
Block a user