1. 调整物料编码字段为可空,优化注释说明 2. 修复Vite构建自动清空产物目录冲突问题 3. 优化前端提示文案与表单占位符 4. 为各前端项目添加内联SVG图标 5. 重构MES半成品流转逻辑,不再静默吞掉WMS错误并记录日志 6. 生成并添加空的WMS客户端静态文件 7. 完善Ent ORM模型的空值支持与查询谓词 8. 优化半成品入库接口,支持doneProcessCodes数组格式,新增SN反查物料编码逻辑 9. 更新帮助文档与操作手册
234 lines
8.6 KiB
Vue
234 lines
8.6 KiB
Vue
<script setup>
|
||
import { onMounted, reactive, ref } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import request from '../utils/request'
|
||
import { fmtTime } from '../utils/format'
|
||
import { getRealName } from '../utils/auth'
|
||
import PageHelp from '../components/PageHelp.vue'
|
||
import { helpSemi } from '../help'
|
||
|
||
/* ---------- 半成品/成品入库(携带已完成工序,连续扫码) ---------- */
|
||
const materials = ref([])
|
||
|
||
onMounted(async () => {
|
||
try {
|
||
const ms = await request.get('/material/list')
|
||
materials.value = Array.isArray(ms) ? ms : ms?.list || []
|
||
} catch { /* 字典加载失败不阻塞入库 */ }
|
||
})
|
||
|
||
const operator = getRealName()
|
||
|
||
const inForm = reactive({ materialCode: '', zoneCode: '', orderNo: '', completedProcess: '' })
|
||
const snInput = ref('')
|
||
const snList = ref([])
|
||
const inbounding = ref(false)
|
||
|
||
function addSnLines() {
|
||
for (let raw of String(snInput.value).split(/\s+/)) {
|
||
const sn = raw.trim()
|
||
if (sn && !snList.value.includes(sn)) snList.value.push(sn)
|
||
}
|
||
snInput.value = ''
|
||
}
|
||
|
||
function removeSn(i) {
|
||
snList.value.splice(i, 1)
|
||
}
|
||
|
||
async function submitInbound() {
|
||
if (!inForm.materialCode) {
|
||
ElMessage.warning('请选择物料')
|
||
return
|
||
}
|
||
if (!snList.value.length) {
|
||
ElMessage.warning('请扫码录入 SN(回车追加)')
|
||
return
|
||
}
|
||
inbounding.value = true
|
||
let ok = 0
|
||
try {
|
||
for (const sn of snList.value) {
|
||
try {
|
||
await request.post('/semi/inbound', {
|
||
sn,
|
||
materialCode: inForm.materialCode,
|
||
completedProcess: inForm.completedProcess.trim(),
|
||
zoneCode: inForm.zoneCode || undefined,
|
||
orderNo: inForm.orderNo.trim() || undefined,
|
||
operator
|
||
})
|
||
ok++
|
||
} catch { /* 单条失败不中断后续 */ }
|
||
}
|
||
ElMessage.success(`半成品入库完成:成功 ${ok} / ${snList.value.length} 条`)
|
||
snList.value = []
|
||
snInput.value = ''
|
||
} finally {
|
||
inbounding.value = false
|
||
}
|
||
}
|
||
|
||
/* ---------- 半成品出库(重上线) ---------- */
|
||
const outForm = reactive({ sn: '' })
|
||
const outbounding = ref(false)
|
||
|
||
async function submitOutbound() {
|
||
const sn = outForm.sn.trim()
|
||
if (!sn) {
|
||
ElMessage.warning('请输入 SN')
|
||
return
|
||
}
|
||
outbounding.value = true
|
||
try {
|
||
await request.post('/semi/outbound', { sn, operator })
|
||
ElMessage.success(`半成品出库成功:${sn}`)
|
||
outForm.sn = ''
|
||
await loadList()
|
||
} finally {
|
||
outbounding.value = false
|
||
}
|
||
}
|
||
|
||
/* ---------- 半成品查询 ---------- */
|
||
const qForm = reactive({ materialCode: '', status: '' })
|
||
const list = ref([])
|
||
const total = ref(0)
|
||
const loading = ref(false)
|
||
const page = reactive({ current: 1, size: 10 })
|
||
|
||
async function loadList() {
|
||
loading.value = true
|
||
try {
|
||
const data = await request.get('/semi/query', {
|
||
params: {
|
||
materialCode: qForm.materialCode.trim(),
|
||
status: qForm.status,
|
||
page: page.current,
|
||
pageSize: page.size
|
||
}
|
||
})
|
||
list.value = data?.list || []
|
||
total.value = data?.total || 0
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function search() {
|
||
page.current = 1
|
||
loadList()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<el-card shadow="never">
|
||
<el-tabs>
|
||
<el-tab-pane label="半成品/成品入库">
|
||
<el-form label-width="110px" style="max-width:680px">
|
||
<el-form-item label="物料">
|
||
<el-select v-model="inForm.materialCode" filterable placeholder="选择物料编码" style="width:100%">
|
||
<el-option v-for="m in materials" :key="m.id" :value="m.code"
|
||
:label="`${m.code} ${m.name || ''}`" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="已完成工序">
|
||
<el-input v-model="inForm.completedProcess" placeholder="如:1,3,5(可空)" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="工单号">
|
||
<el-input v-model="inForm.orderNo" placeholder="来源工单号(可空)" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="区域">
|
||
<el-input v-model="inForm.zoneCode" placeholder="库区(可空)" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="操作人">
|
||
<el-input :model-value="operator" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="扫码录入">
|
||
<el-input v-model="snInput" type="textarea" :rows="3" autofocus
|
||
placeholder="连续扫描 SN,每次回车追加(可一次多条,空白分隔)"
|
||
@keydown.enter.prevent="addSnLines" />
|
||
</el-form-item>
|
||
</el-form>
|
||
<div style="margin-bottom:8px">待入库 SN:<b>{{ snList.length }}</b> 条</div>
|
||
<el-table :data="snList.map((v, i) => ({ i, v }))" size="small" border max-height="260"
|
||
empty-text="暂无 SN" style="max-width:760px">
|
||
<el-table-column type="index" label="#" width="56" align="center" />
|
||
<el-table-column prop="v" label="SN 序列号" min-width="220" />
|
||
<el-table-column label="操作" width="80" align="center">
|
||
<template #default="{ row }">
|
||
<el-button link type="danger" size="small" @click="removeSn(row.i)">移除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div style="margin-top:12px">
|
||
<el-button type="primary" :loading="inbounding" @click="submitInbound">提交入库</el-button>
|
||
<el-button @click="snList = []">清空</el-button>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="半成品出库">
|
||
<el-form label-width="110px" style="max-width:560px" @submit.prevent="submitOutbound">
|
||
<el-form-item label="SN">
|
||
<el-input v-model="outForm.sn" placeholder="扫描或输入 SN,回车出库" clearable autofocus
|
||
@keyup.enter="submitOutbound" />
|
||
</el-form-item>
|
||
<el-form-item label="操作人">
|
||
<el-input :model-value="operator" disabled />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" :loading="outbounding" @click="submitOutbound">出库(重上线)</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
|
||
<el-tab-pane label="半成品/成品查询">
|
||
<el-form inline @submit.prevent="search">
|
||
<el-form-item label="物料编码">
|
||
<el-input v-model="qForm.materialCode" placeholder="物料编码" clearable style="width:200px" />
|
||
</el-form-item>
|
||
<el-form-item label="状态">
|
||
<el-select v-model="qForm.status" clearable placeholder="请选择状态" style="width:120px">
|
||
<el-option value="在库" label="在库" />
|
||
<el-option value="出库" label="出库" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" :loading="loading" @click="search">查询</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
<el-table :data="list" border size="small" v-loading="loading" empty-text="暂无记录">
|
||
<el-table-column prop="sn" label="SN" min-width="200" />
|
||
<el-table-column prop="materialCode" label="物料编码" min-width="140" />
|
||
<el-table-column prop="completedProcess" label="已完成工序" min-width="120">
|
||
<template #default="{ row }">{{ row.completedProcess || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="zoneCode" label="区域" width="90" align="center">
|
||
<template #default="{ row }">{{ row.zoneCode || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" 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="140">
|
||
<template #default="{ row }">{{ row.orderNo || '-' }}</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" width="160" align="center">
|
||
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div class="pager">
|
||
<el-pagination background layout="total, prev, pager, next" :total="total"
|
||
:current-page="page.current" :page-size="page.size"
|
||
@current-change="(p) => { page.current = p; loadList() }" />
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-card>
|
||
<PageHelp :data="helpSemi" />
|
||
</template>
|
||
|
||
<style scoped>
|
||
.pager { display: flex; justify-content: flex-end; margin-top: 10px; }
|
||
</style> |