feat: 完成多模块迭代更新

- 新增过程巡检按步骤上报、数量不符上报、工位退料功能
- 增加工单工程编号三级归属字段
- 新增物料安全库存与缺货预警
- 新增附件管理、退库单管理模块
- 优化生产看板展示逻辑与前端页面文案
- 清理冗余的工艺路线相关代码与备份文件
This commit is contained in:
SunYF
2026-09-15 16:37:39 +08:00
parent 951f3dfecd
commit 4a5e2d7bac
494 changed files with 24702 additions and 139223 deletions
+90 -13
View File
@@ -8,6 +8,7 @@ import { can } from '../utils/perm'
import { exportXlsxFetch } from '../utils/export'
import { getToken } from '../utils/auth'
import PageHelp from '../components/PageHelp.vue'
import AttachmentPanel from '../components/AttachmentPanel.vue'
import { helpZone, helpMaterial } from '../help'
// mode: 'zone' = 区域维护;'material' = 物料档案(同一组件拆成两个独立菜单)
@@ -40,7 +41,8 @@ const zonePage = reactive({ current: 1, size: 10 })
const zoneDialog = ref(false)
const zoneEditingId = ref(0)
const zoneForm = reactive({ zoneCode: '', zoneName: '', description: '' })
// 区域库位:区域 → 货架号 → 第几层 → 位置号(客户诉求 问题记录 L32/L169,手填)
const zoneForm = reactive({ zoneCode: '', zoneName: '', description: '', shelfNo: '', layerNo: '', positionNo: '' })
const zoneSaving = ref(false)
async function loadZones() {
@@ -69,14 +71,15 @@ function searchZones() {
function openZoneCreate() {
zoneEditingId.value = 0
Object.assign(zoneForm, { zoneCode: '', zoneName: '', description: '' })
Object.assign(zoneForm, { zoneCode: '', zoneName: '', description: '', shelfNo: '', layerNo: '', positionNo: '' })
zoneDialog.value = true
}
function openZoneEdit(row) {
zoneEditingId.value = row.id
Object.assign(zoneForm, {
zoneCode: row.zoneCode, zoneName: row.zoneName, description: row.description || ''
zoneCode: row.zoneCode, zoneName: row.zoneName, description: row.description || '',
shelfNo: row.shelfNo || '', layerNo: row.layerNo || '', positionNo: row.positionNo || ''
})
zoneDialog.value = true
}
@@ -115,7 +118,8 @@ const matDialog = ref(false)
const mEditingId = ref(0)
const mForm = reactive({
code: '', name: '', shortName: '', spec: '', unit: '',
manageMode: 1, templateCode: '', description: '', drawingNo: ''
manageMode: 1, templateCode: '', description: '', drawingNo: '',
itemType: 1, safetyStock: 0
})
const mSaving = ref(false)
@@ -151,7 +155,8 @@ function resetMForm() {
mEditingId.value = 0
Object.assign(mForm, {
code: '', name: '', shortName: '', spec: '', unit: '',
manageMode: 1, templateCode: '', description: '', drawingNo: ''
manageMode: 1, templateCode: '', description: '', drawingNo: '',
itemType: 1, safetyStock: 0
})
}
@@ -166,7 +171,9 @@ function openMatEdit(row) {
code: row.code, name: row.name, shortName: row.shortName || '',
spec: row.spec || '', unit: row.unit || '', manageMode: row.manageMode,
templateCode: row.templateCode || '',
drawingNo: row.drawingNo || '',
drawingNo: row.drawingNo || '',
itemType: row.itemType || 1,
safetyStock: row.safetyStock || 0,
description: row.description || ''
})
matDialog.value = true
@@ -177,18 +184,25 @@ async function saveMaterial() {
ElMessage.warning('物料编码和名称必填')
return
}
// 其他类物料规格强制(客户诉求 问题记录 L50)
if (Number(mForm.itemType) === 4 && !String(mForm.spec || '').trim()) {
ElMessage.warning('「其他」类物料必须填写规格型号')
return
}
mSaving.value = true
try {
if (mEditingId.value) {
await request.post('/material/update', {
id: mEditingId.value, name: mForm.name, shortName: mForm.shortName,
spec: mForm.spec, unit: mForm.unit, description: mForm.description,
drawingNo: mForm.drawingNo,
drawingNo: mForm.drawingNo,
itemType: Number(mForm.itemType),
safetyStock: Number(mForm.safetyStock) || 0,
manageMode: mForm.manageMode
})
ElMessage.success('物料修改成功')
} else {
await request.post('/material/create', { ...mForm })
await request.post('/material/create', { ...mForm, itemType: Number(mForm.itemType), safetyStock: Number(mForm.safetyStock) || 0 })
ElMessage.success('物料新增成功')
}
matDialog.value = false
@@ -198,6 +212,24 @@ async function saveMaterial() {
}
}
// 物料附件(bizType=material,bizId=物料编码):零件图纸 PDF 等
const attachDrawer = reactive({ visible: false, code: '', name: '' })
function openMatAttach(row) {
Object.assign(attachDrawer, { visible: true, code: row.code, name: row.name })
}
// 品类显示(1原材料/2半成品/3成品/4其他)
function itemTypeText(t) {
return t === 3 ? '成品' : t === 2 ? '半成品' : t === 4 ? '其他' : '原材料'
}
function itemTypeTagType(t) {
return t === 3 ? 'success' : t === 2 ? 'warning' : t === 4 ? 'danger' : 'info'
}
// 品类切换:其他类强制按数量管理(不做 SN)
function onItemTypeChange(v) {
if (Number(v) === 4) mForm.manageMode = 1
}
// 区域维护 / 物料档案共用同一组件:切换菜单时复用,watch(mode, immediate) 保证进入即加载
watch(
() => props.mode,
@@ -299,6 +331,15 @@ async function onMaterialFile(e) {
<el-table-column label="ID" prop="id" width="60" align="center" />
<el-table-column prop="zoneCode" label="区域编码" min-width="120" />
<el-table-column prop="zoneName" label="区域名称" min-width="150" />
<el-table-column prop="shelfNo" label="货架号" width="90" align="center">
<template #default="{ row }">{{ row.shelfNo || '-' }}</template>
</el-table-column>
<el-table-column prop="layerNo" label="层" width="70" align="center">
<template #default="{ row }">{{ row.layerNo || '-' }}</template>
</el-table-column>
<el-table-column prop="positionNo" label="位置号" width="90" align="center">
<template #default="{ row }">{{ row.positionNo || '-' }}</template>
</el-table-column>
<el-table-column label="状态" width="90" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '停用' ? 'info' : 'success'" size="small">
@@ -334,6 +375,15 @@ async function onMaterialFile(e) {
<el-form-item label="区域名称" required>
<el-input v-model="zoneForm.zoneName" placeholder="如 待检区" clearable />
</el-form-item>
<el-form-item label="货架号">
<el-input v-model="zoneForm.shelfNo" placeholder="如 A / 03(可空,手填)" clearable />
</el-form-item>
<el-form-item label="第几层">
<el-input v-model="zoneForm.layerNo" placeholder="如 3(可空,手填)" clearable />
</el-form-item>
<el-form-item label="位置号">
<el-input v-model="zoneForm.positionNo" placeholder="如 12(可空,手填)" clearable />
</el-form-item>
<el-form-item label="说明">
<el-input v-model="zoneForm.description" type="textarea" :rows="2" placeholder="备注(可空)" />
</el-form-item>
@@ -366,6 +416,14 @@ async function onMaterialFile(e) {
<el-option label="电气件(SN)" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="品类">
<el-select v-model="matFilter.itemType" placeholder="全部" clearable style="width:120px" @change="searchMaterials">
<el-option label="原材料" :value="1" />
<el-option label="半成品" :value="2" />
<el-option label="成品" :value="3" />
<el-option label="其他" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="matFilter.startDate" type="date" value-format="YYYY-MM-DD" placeholder="开始"
style="width:130px" @change="searchMaterials" />
@@ -400,9 +458,7 @@ async function onMaterialFile(e) {
</el-table-column>
<el-table-column label="品类" width="90" align="center">
<template #default="{ row }">
<el-tag :type="row.itemType === 3 ? 'success' : (row.itemType === 2 ? 'warning' : 'info')" size="small">
{{ row.itemType === 3 ? '成品' : (row.itemType === 2 ? '半成品' : '原材料') }}
</el-tag>
<el-tag :type="itemTypeTagType(row.itemType)" size="small">{{ itemTypeText(row.itemType) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="类型" width="140" align="center">
@@ -412,12 +468,16 @@ async function onMaterialFile(e) {
</el-tag>
</template>
</el-table-column>
<el-table-column label="安全库存" width="100" align="center">
<template #default="{ row }">{{ row.safetyStock || 0 }}</template>
</el-table-column>
<el-table-column label="创建时间" width="155" align="center">
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<el-table-column label="操作" width="150" align="center">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="openMatEdit(row)">编辑</el-button>
<el-button link type="success" size="small" @click="openMatAttach(row)">附件</el-button>
</template>
</el-table-column>
</el-table>
@@ -449,12 +509,24 @@ async function onMaterialFile(e) {
<el-form-item label="图号">
<el-input v-model="mForm.drawingNo" placeholder="图纸编号(可空)" clearable />
</el-form-item>
<el-form-item label="品类">
<el-select v-model="mForm.itemType" placeholder="请选择" style="width:100%" @change="onItemTypeChange">
<el-option :value="1" label="原材料" />
<el-option :value="2" label="半成品" />
<el-option :value="3" label="成品" />
<el-option :value="4" label="其他(辅料/工装/试验设备)" />
</el-select>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="mForm.manageMode" placeholder="请选择" style="width:100%">
<el-select v-model="mForm.manageMode" placeholder="请选择" style="width:100%" :disabled="Number(mForm.itemType) === 4">
<el-option :value="1" label="结构件(批次管理)" />
<el-option :value="2" label="电气件(SN 管理)" />
</el-select>
</el-form-item>
<el-form-item label="安全库存">
<el-input-number v-model="mForm.safetyStock" :min="0" :step="1" step-strictly style="width:100%" />
<span style="color:#909399;font-size:12px;margin-left:8px">低于该值触发缺货预警(0=不预警)</span>
</el-form-item>
<el-form-item label="说明">
<el-input v-model="mForm.description" type="textarea" :rows="2" placeholder="备注(可空)" />
</el-form-item>
@@ -466,6 +538,11 @@ async function onMaterialFile(e) {
</el-dialog>
</template>
</el-card>
<!-- 物料附件抽屉(图纸 PDF 等) -->
<el-drawer v-model="attachDrawer.visible" :title="`物料附件:${attachDrawer.code} ${attachDrawer.name}`" size="620px">
<AttachmentPanel biz-type="material" :biz-id="attachDrawer.code" title="附件(零件图纸 / 资料)" />
</el-drawer>
<PageHelp :data="helpData" :page="props.mode === 'material' ? 'Material' : 'Zone'" />
</div>
</template>