docs(deployment): 更新部署手册和业务架构说明
- 更新MES和WMS服务端口配置说明 - 详细说明产线工位设备配置(扫码枪+拧紧枪) - 明确AGV配送和接驳台的模拟模式与真实对接方案 - 更新WMS中AGV配送默认模拟模式说明 - 完善厂内物流(AGV/接驳台)的业务架构约束 - 修订临时文档为详细的项目问题和解决方案规划
This commit is contained in:
@@ -7,6 +7,7 @@ import { getRealName } from '../utils/auth'
|
||||
import { fmtTime } from '../utils/format'
|
||||
import { exportXlsxFetch } from '../utils/export'
|
||||
import { can } from '../utils/perm'
|
||||
import { confirmAction } from '../utils/confirm'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
import MaterialSelect from '../components/MaterialSelect.vue'
|
||||
import { helpOutbound } from '../help'
|
||||
@@ -108,19 +109,24 @@ const CATEGORY_OPTIONS = [
|
||||
{ value: 'deliver', label: '发货' },
|
||||
{ value: 'other', label: '其他' }
|
||||
]
|
||||
// 归属口径(需求22):工单编号/科技项目号/其他,默认其他;工单编号是独立维度标签,非 MES 工单号
|
||||
const OWNERSHIP_OPTIONS = [
|
||||
{ value: 'none', label: '无' },
|
||||
{ value: 'workorder', label: '工单号' },
|
||||
{ value: 'workorder', label: '工单编号' },
|
||||
{ value: 'project', label: '科技项目号' },
|
||||
{ value: 'other', label: '其他' }
|
||||
]
|
||||
// 物料下拉由 MaterialSelect 组件按需远程搜索(最近 100 条)
|
||||
const zones = ref([])
|
||||
// 目标工位下拉(需求20:来自 MES 工位列表,不允许手输);可用批次下拉(问题18:批次不手输,从该物料在库批次中选)
|
||||
const stationOptions = ref([])
|
||||
const batchOptions = ref([])
|
||||
// 选中物料的展示类型(问题18:类型含「其他」):other=其他品类(按批次管理)/sn=电气件/batch=结构件
|
||||
const pickedType = ref('')
|
||||
const genForm = reactive({
|
||||
materialCode: '', mode: 1, batchNo: '', qty: 1, zoneCode: '',
|
||||
shelfNo: '', layerNo: '', positionNo: '',
|
||||
boxNo: '', contractNo: '', remark: '',
|
||||
outboundCategory: 'other', ownershipType: 'none', ownershipNo: '', targetStation: ''
|
||||
outboundCategory: 'other', ownershipType: 'other', ownershipNo: '', targetStation: ''
|
||||
})
|
||||
const snInput = ref('')
|
||||
const snList = ref([])
|
||||
@@ -141,11 +147,44 @@ function addSnLines() {
|
||||
function removeSn(i) { snList.value.splice(i, 1) }
|
||||
|
||||
async function loadDicts() {
|
||||
loadStations()
|
||||
if (zones.value.length) return
|
||||
// 物料下拉由 MaterialSelect 组件按需远程搜索(最近 100 条),此处只加载区域
|
||||
const zs = await request.get('/zone/picker')
|
||||
zones.value = Array.isArray(zs) ? zs : zs?.list || []
|
||||
}
|
||||
// 目标工位选项(需求20):来自后端派生的 MES 产线工位列表
|
||||
async function loadStations() {
|
||||
if (stationOptions.value.length) return
|
||||
try {
|
||||
const d = await request.get('/outbound/stations')
|
||||
stationOptions.value = d?.list || []
|
||||
} catch { stationOptions.value = [] }
|
||||
}
|
||||
// 可用批次选项(问题18:批次号不手输,从所选物料的在库批次中下拉选择):FIFO 排序,默认带出首个可用批次
|
||||
async function loadBatchOptions(code) {
|
||||
if (!code) { batchOptions.value = []; return }
|
||||
try {
|
||||
const stock = await request.get('/stock/query', { params: { type: 'batch', materialCode: code, page: 1, pageSize: 200 } })
|
||||
batchOptions.value = (stock?.batch || [])
|
||||
.map(b => ({ ...b, avail: (b.quantity || 0) - (b.lockedQty || 0) }))
|
||||
.filter(b => b.avail > 0)
|
||||
.sort((a, b) => String(a.batchNo).localeCompare(String(b.batchNo)))
|
||||
if (batchOptions.value.length && !genForm.batchNo) genForm.batchNo = batchOptions.value[0].batchNo
|
||||
} catch { batchOptions.value = [] }
|
||||
}
|
||||
// 物料选中(问题18:出库类型由物料档案自动判定,杜绝客户手选「电气件/结构件」)
|
||||
function onMaterialPick(m) {
|
||||
genForm.mode = m && m.manageMode === 2 ? 2 : 1
|
||||
pickedType.value = m ? (m.itemType === 4 ? 'other' : (m.manageMode === 2 ? 'sn' : 'batch')) : ''
|
||||
genForm.batchNo = ''
|
||||
snList.value = []
|
||||
snInput.value = ''
|
||||
batchOptions.value = []
|
||||
if (!m) return
|
||||
fillZoneFromHistory()
|
||||
if (genForm.mode === 1) loadBatchOptions(m.code)
|
||||
}
|
||||
// 区域下拉选项:picker 每行是一个货位,同一区域会重复出现,按区域编码去重后只展示一条
|
||||
const zoneOptions = computed(() => {
|
||||
const map = new Map()
|
||||
@@ -185,6 +224,11 @@ function locText(row) {
|
||||
if (row?.positionNo) parts.push(row.positionNo + '位')
|
||||
return parts.length ? parts.join('/') : '-'
|
||||
}
|
||||
// 出库类别标签(需求17:类别是纯标签记录,列表/出库单都要显示清楚)
|
||||
function categoryLabel(v) {
|
||||
const m = { return: '退料', supplier_return: '退货', sample: '样品', scrap: '报废', deliver: '发货', other: '其他' }
|
||||
return m[v] || (v || '-')
|
||||
}
|
||||
|
||||
// 出库库位联动(客户诉求 一.9「出库相同」):与入库同口径回填 区域/货架/层/位置 四级。
|
||||
// 优先级:① 指定批次在库的实际库位 ② 该物料最近一次入库库位 ③ 无历史时按所选区域推荐同区空位。
|
||||
@@ -220,7 +264,6 @@ async function fillZoneFromHistory() {
|
||||
/* 忽略 */
|
||||
}
|
||||
}
|
||||
watch(() => genForm.materialCode, (v) => { if (v) { genForm.batchNo = ''; fillZoneFromHistory() } })
|
||||
watch(() => genForm.batchNo, (v) => { if (v && genForm.materialCode) fillZoneFromHistory() })
|
||||
|
||||
async function loadGenRows() {
|
||||
@@ -235,8 +278,8 @@ async function loadGenRows() {
|
||||
async function submitGen() {
|
||||
if (!genForm.materialCode) { ElMessage.warning('请选择物料'); return }
|
||||
if (!genForm.outboundCategory) { ElMessage.warning('请选择出库类别'); return }
|
||||
if (genForm.ownershipType !== 'none' && !genForm.ownershipNo.trim()) {
|
||||
ElMessage.warning('请填写归属编号(工单号 / 科技项目号 / 其他)'); return
|
||||
if (genForm.ownershipType !== 'other' && !genForm.ownershipNo.trim()) {
|
||||
ElMessage.warning('请填写归属编号(工单编号 / 科技项目号)'); return
|
||||
}
|
||||
if (genForm.outboundCategory === 'other' && !genForm.remark.trim()) {
|
||||
ElMessage.warning('出库类别选「其他」时,请在备注中填写具体原因'); return
|
||||
@@ -245,7 +288,7 @@ async function submitGen() {
|
||||
materialCode: genForm.materialCode, operator,
|
||||
outboundCategory: genForm.outboundCategory,
|
||||
ownershipType: genForm.ownershipType,
|
||||
ownershipNo: genForm.ownershipType !== 'none' ? genForm.ownershipNo.trim() : undefined,
|
||||
ownershipNo: genForm.ownershipNo.trim() || undefined,
|
||||
targetStation: genForm.targetStation.trim() || undefined,
|
||||
zoneCode: genForm.zoneCode || undefined,
|
||||
shelfNo: genForm.shelfNo || undefined,
|
||||
@@ -256,22 +299,26 @@ async function submitGen() {
|
||||
remark: genForm.remark.trim() || undefined
|
||||
}
|
||||
if (genForm.mode === 1) {
|
||||
if (!genForm.batchNo.trim()) { ElMessage.warning('请输入批次号'); return }
|
||||
if (!genForm.batchNo) { ElMessage.warning('请从该物料可用批次中选择出库批次'); return }
|
||||
if (!genForm.qty || genForm.qty <= 0) { ElMessage.warning('请输入有效数量'); return }
|
||||
payload.batchNo = genForm.batchNo.trim()
|
||||
payload.batchNo = genForm.batchNo
|
||||
payload.qty = genForm.qty
|
||||
} else {
|
||||
if (!snList.value.length) { ElMessage.warning('请录入至少一个 SN'); return }
|
||||
payload.snList = [...snList.value]
|
||||
payload.qty = snList.value.length
|
||||
}
|
||||
const go = await confirmAction(`确认提交通用出库?物料【${genForm.materialCode}】、数量 ${payload.qty}、类别【${genForm.outboundCategory}】。`, '通用出库确认')
|
||||
if (!go) return
|
||||
savingGen.value = true
|
||||
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: '', shelfNo: '', layerNo: '', positionNo: '', 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: 'other', ownershipNo: '', targetStation: '' })
|
||||
snInput.value = ''
|
||||
snList.value = []
|
||||
batchOptions.value = []
|
||||
pickedType.value = ''
|
||||
genPage.current = 1
|
||||
await loadGenRows()
|
||||
if (activeTab.value === 'records') await loadRecords()
|
||||
@@ -392,13 +439,16 @@ onMounted(loadDicts)
|
||||
<el-card shadow="never" class="mb12" header="通用出库(不依赖工单:退料 / 样品 / 报废 / 发货)">
|
||||
<el-form label-width="96px" style="max-width:880px">
|
||||
<el-form-item label="物料" required>
|
||||
<MaterialSelect v-model="genForm.materialCode" placeholder="选择物料" />
|
||||
<MaterialSelect v-model="genForm.materialCode" placeholder="选择物料(编码/图号/类型/品类)" @item-change="onMaterialPick" />
|
||||
</el-form-item>
|
||||
<el-form-item label="出库类型">
|
||||
<el-radio-group v-model="genForm.mode">
|
||||
<el-radio :value="1">结构件(批次)</el-radio>
|
||||
<el-radio :value="2">电气件(SN)</el-radio>
|
||||
</el-radio-group>
|
||||
<template v-if="genForm.materialCode">
|
||||
<el-tag size="small" :type="pickedType === 'sn' ? 'warning' : 'info'">
|
||||
{{ pickedType === 'sn' ? '电气件(SN 管理)' : pickedType === 'other' ? '其他(按批次管理)' : '结构件(批次管理)' }}
|
||||
</el-tag>
|
||||
<span style="color:#909399;font-size:12px;margin-left:8px">类型由所选物料档案自动判定,无需手选</span>
|
||||
</template>
|
||||
<span v-else style="color:#909399;font-size:12px">请先选择物料,系统自动带出类型(电气件/结构件)</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="出库类别" required>
|
||||
@@ -414,12 +464,20 @@ onMounted(loadDicts)
|
||||
placeholder="归属编号(工单号/科技项目号/其他)" clearable style="width:260px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标工位">
|
||||
<el-input v-model="genForm.targetStation" placeholder="选填:如 1~12 或 DOCK01" clearable style="width:100%" />
|
||||
<el-select v-model="genForm.targetStation" filterable clearable
|
||||
placeholder="选择目标工位(来自 MES 工位列表,不可手输)" style="width:100%">
|
||||
<el-option v-for="s in stationOptions" :key="s.stationNo" :value="String(s.stationNo)"
|
||||
:label="`工位${s.stationNo}${s.name ? ' ' + s.name : ''}`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="genForm.mode === 1">
|
||||
<el-form-item label="批次号" required>
|
||||
<el-input v-model="genForm.batchNo" placeholder="结构件批次号" style="width:100%" />
|
||||
<el-form-item label="批次号">
|
||||
<el-select v-model="genForm.batchNo" filterable clearable
|
||||
placeholder="从该物料可用批次中选择(默认带出 FIFO 首个)" style="width:100%">
|
||||
<el-option v-for="b in batchOptions" :key="b.batchNo" :value="b.batchNo"
|
||||
:label="`${b.batchNo}(可用 ${b.avail}|${b.qualityStatus || '未检'}${b.zoneCode ? '|' + b.zoneCode : ''})`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" required>
|
||||
<el-input-number v-model="genForm.qty" :min="1" style="width:160px" />
|
||||
@@ -483,6 +541,11 @@ onMounted(loadDicts)
|
||||
<el-card shadow="never" header="通用出库记录">
|
||||
<el-table :data="genRows" border size="small" v-loading="loadingGen" empty-text="暂无通用出库记录">
|
||||
<el-table-column prop="outboundNo" label="出库单号" min-width="150" />
|
||||
<el-table-column label="出库类别" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" type="info">{{ categoryLabel(row.outboundCategory) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialCode" label="物料" min-width="130" />
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="120">
|
||||
<template #default="{ row }">{{ row.batchNo || '-' }}</template>
|
||||
@@ -496,10 +559,10 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="quantity" label="数量" width="80" align="center" />
|
||||
<el-table-column label="归属" min-width="130">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.ownershipType === 'workorder'">工单号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-if="row.ownershipType === 'workorder'">工单编号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else-if="row.ownershipType === 'project'">科技项目号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else-if="row.ownershipType === 'other'">其他 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else>无</span>
|
||||
<span v-else>其他</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="targetStation" label="目标工位" width="100" align="center">
|
||||
@@ -575,6 +638,12 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130">
|
||||
<template #default="{ row }">{{ row.orderNo || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出库类别" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.outboundType === 'general'">{{ categoryLabel(row.outboundCategory) }}</span>
|
||||
<span v-else style="color:#909399">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialCode" label="物料" min-width="120" />
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="110">
|
||||
<template #default="{ row }">{{ row.batchNo || '-' }}</template>
|
||||
@@ -588,10 +657,10 @@ onMounted(loadDicts)
|
||||
<el-table-column prop="quantity" label="数量" width="70" align="center" />
|
||||
<el-table-column label="归属" min-width="130">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.ownershipType === 'workorder'">工单号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-if="row.ownershipType === 'workorder'">工单编号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else-if="row.ownershipType === 'project'">科技项目号 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else-if="row.ownershipType === 'other'">其他 {{ row.ownershipNo || '' }}</span>
|
||||
<span v-else>无</span>
|
||||
<span v-else>其他</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="targetStation" label="目标工位" width="100" align="center">
|
||||
|
||||
Reference in New Issue
Block a user