docs(deployment): 更新部署手册和业务架构说明

- 更新MES和WMS服务端口配置说明
- 详细说明产线工位设备配置(扫码枪+拧紧枪)
- 明确AGV配送和接驳台的模拟模式与真实对接方案
- 更新WMS中AGV配送默认模拟模式说明
- 完善厂内物流(AGV/接驳台)的业务架构约束
- 修订临时文档为详细的项目问题和解决方案规划
This commit is contained in:
SunYF
2026-09-20 14:52:36 +08:00
parent 5a33028003
commit f35e9b5b75
57 changed files with 6096 additions and 1000 deletions
+79 -29
View File
@@ -10,6 +10,7 @@ import { exportXlsxFetch } from '../utils/export'
import { can } from '../utils/perm'
import PageHelp from '../components/PageHelp.vue'
import AttachmentPanel from '../components/AttachmentPanel.vue'
import MaterialSelect from '../components/MaterialSelect.vue'
import { helpInspection } from '../help'
// ===== 多 Tab:录入在前默认打开,记录在后点击才见(参照出库管理) =====
@@ -19,9 +20,13 @@ const activeTab = ref('entry') // entry=质量检验(录入) / records=检验记
const route = useRoute()
const router = useRouter()
const pageType = String(route.meta.inspectionType || '')
// 三检边界(需求23/30):来料检=入库物料验收;过程检=产线在制巡检;完工检=成品 SN 终检。
const isFinal = pageType === 'finished'
const isIncoming = pageType === 'incoming'
/* ===================== Tab1 质量检验(录入,默认打开) ===================== */
const targetType = ref('BATCH')
// 完工检对象一定是成品 SN,无「批次」选项(临时.md L101)
const targetType = ref(isFinal ? 'SN' : 'BATCH')
const status = ref('合格')
const inspector = ref(getRealName())
const submitting = ref(false)
@@ -32,8 +37,8 @@ const passQty = ref(0)
const checkDesc = ref('')
const inspectionNo = ref('')
const reportNo = ref('')
// 新增字段(客户诉求 L107/L150/L494
const ownershipType = ref('none')
// 新增字段(客户诉求 L107/L150/L494;需求22:归属默认「其他」
const ownershipType = ref('other')
const ownershipNo = ref('')
const relatedStandard = ref('')
const manufacturer = ref('')
@@ -63,9 +68,10 @@ async function loadPicker(kw = '') {
pickerLoading.value = true
try {
const mm = targetType.value === 'BATCH' ? 1 : 2
const data = await request.get('/stock/query', {
params: { manageMode: mm, keyword: kw || '', page: 1, pageSize: 100 }
})
const params = { manageMode: mm, keyword: kw || '', page: 1, pageSize: 100 }
// 完工检只从成品库存(category=3)拾取 SN
if (isFinal) params.category = 3
const data = await request.get('/stock/query', { params })
pickerOptions.value = data?.list || []
} catch (e) {
pickerOptions.value = []
@@ -112,7 +118,7 @@ async function submitInspection() {
}
// 判定不合格必须选择处置方式(不合格不入库、不上产线)
if (status.value === '不合格' && disposalType.value === 'NONE') {
ElMessage.warning('判定不合格时必须选择处置方式(退货 / 报废 / 部分入库)')
ElMessage.warning('判定不合格时必须选择处置方式(退货 / 换修 / 让步接收 / 报废 / 部分入库)')
return
}
if (status.value === '不合格' && disposalType.value === 'RETURN' && !returnTrackingNo.value.trim()) {
@@ -123,6 +129,17 @@ async function submitInspection() {
ElMessage.warning('「部分入库」一次只能处理 1 个批次,请分开提交')
return
}
// 报检数守恒校验(临时.md §六:报检数 = 合格数 + 不合格数)
const iq = Number(inspectQty.value) || 0
const pq = Number(passQty.value) || 0
if (iq > 0 && pq > iq) {
ElMessage.warning('合格数不能大于报检数(报检数 = 合格数 + 不合格数)')
return
}
if (status.value === '合格' && iq > 0 && pq !== iq) {
ElMessage.warning('判定合格时,合格数应等于报检数(否则请改判不合格并选处置方式)')
return
}
submitting.value = true
try {
const data = await request.post('/inspection/batch-flip', {
@@ -154,7 +171,7 @@ async function submitInspection() {
reportNo.value = ''
disposalType.value = 'NONE'
returnTrackingNo.value = ''
ownershipType.value = 'none'
ownershipType.value = 'other'
ownershipNo.value = ''
relatedStandard.value = ''
manufacturer.value = ''
@@ -225,13 +242,26 @@ const filters = reactive({
const loading = ref(false)
const statsList = ref([])
const statsLoading = ref(false)
// 需求13/58:统计必须先选物料再点「统计」,不做全表实时聚合
const statsMaterialCode = ref('')
const statsQueried = ref(false)
async function loadStats() {
if (!statsMaterialCode.value) {
ElMessage.warning('请先选择物料再统计')
return
}
statsLoading.value = true
try {
const data = await request.get('/inspection/stats', {
params: { inspectionType: filters.inspectionType, startDate: filters.startDate, endDate: filters.endDate }
params: {
inspectionType: filters.inspectionType,
materialCode: statsMaterialCode.value,
startDate: filters.startDate,
endDate: filters.endDate
}
})
statsList.value = data?.list || []
statsQueried.value = true
} finally {
statsLoading.value = false
}
@@ -249,9 +279,14 @@ function targetTypeLabel(t) {
}
// 不合格处置方式显示(NONE=未处置)
function disposalLabel(t) {
const m = { RETURN: '退货', SCRAP: '报废', PARTIAL: '部分入库' }
const m = { RETURN: '退货', SCRAP: '报废', PARTIAL: '部分入库', REWORK: '换修', CONCESSION: '让步接收' }
return m[t] || '未处置'
}
// 归属类型显示(需求22:工单编号/科技项目号/其他;旧 none/空 → 其他)
function ownershipTypeLabel(t) {
const m = { workorder: '工单编号', project: '科技项目号' }
return m[t] || '其他'
}
async function loadRecords() {
loading.value = true
@@ -321,9 +356,16 @@ onMounted(() => {
<!-- ============ 质量检验录入默认打开 ============ -->
<el-tab-pane label="质量检验录入" name="entry">
<el-card shadow="never" style="max-width:860px;margin:0 auto">
<el-alert :closable="false" show-icon class="mb12" :type="isFinal ? 'success' : 'info'">
<template #title>
<span v-if="isIncoming">来料检验对入库物料验收按入库批次/SN合格才能入库上架不合格隔离退货</span>
<span v-else-if="isFinal">完工检验 MES 回流的成品 SN 做终检对象固定为 SN合格后成品方可发货不合格可返修/让步接收</span>
<span v-else>过程检验对产线在制物料/半成品巡检判定质量状态</span>
</template>
</el-alert>
<el-form label-width="96px">
<!-- 目标类型直接改变输入框标题与占位直观体现当前录入对象 -->
<el-form-item label="目标类型">
<!-- 目标类型直接改变输入框标题与占位完工检对象固定为成品 SN不展示切换 -->
<el-form-item v-if="!isFinal" label="目标类型">
<el-radio-group v-model="targetType" @change="() => loadPicker()">
<el-radio-button value="BATCH">批次 (结构件)</el-radio-button>
<el-radio-button value="SN">序列号 SN (电气件)</el-radio-button>
@@ -336,11 +378,13 @@ onMounted(() => {
</el-radio-group>
</el-form-item>
<!-- 不合格品处置判定不合格时才出现退货/报废/部分入库 -->
<!-- 不合格品处置判定不合格时才出现退货/换修/让步接收/报废/部分入库 -->
<template v-if="status === '不合格'">
<el-form-item label="处置方式">
<el-radio-group v-model="disposalType">
<el-radio-button value="RETURN">退货</el-radio-button>
<el-radio-button value="REWORK">换修</el-radio-button>
<el-radio-button value="CONCESSION">让步接收</el-radio-button>
<el-radio-button value="SCRAP">报废</el-radio-button>
<el-radio-button value="PARTIAL">部分入库</el-radio-button>
</el-radio-group>
@@ -350,33 +394,33 @@ onMounted(() => {
</el-form-item>
<el-form-item label=" ">
<el-alert type="warning" :closable="false" style="max-width:560px"
title="不合格品自动隔离:库存质量状态置「不合格/待退」,禁止领料上线退货必填货运单号。" />
title="退货/换修/报废:库存隔离(待退/不合格),禁止领料上线退货必填货运单号;让步接收:审批后放行,质量状态置合格。" />
</el-form-item>
</template>
<el-form-item label="记录人">
<el-input v-model="inspector" placeholder="记录人姓名" clearable style="width:260px" />
<el-form-item :label="isFinal ? '审核人' : '记录人'">
<el-input v-model="inspector" :placeholder="isFinal ? '完工检审核人姓名' : '记录人姓名'" clearable style="width:260px" />
</el-form-item>
<el-form-item label="归属">
<div style="display:flex;gap:8px;width:100%;max-width:560px">
<el-select v-model="ownershipType" style="width:150px">
<el-option label="" value="none" />
<el-option label="工单号" value="workorder" />
<el-option label="工单编号" value="workorder" />
<el-option label="科技项目号" value="project" />
<el-option label="其他" value="other" />
</el-select>
<el-input v-if="ownershipType !== 'none'" v-model="ownershipNo"
placeholder="归属编号" clearable />
<el-input v-model="ownershipNo" maxlength="64" show-word-limit
placeholder="归属编号(手动输入,不校验格式)" clearable />
</div>
</el-form-item>
<el-form-item label="相关标准">
<el-input v-model="relatedStandard" placeholder="检验细则/试验方案号(可空)" clearable style="width:420px" />
</el-form-item>
<el-form-item label="生产厂家">
<el-form-item v-if="!isFinal" label="生产厂家">
<el-input v-model="manufacturer" placeholder="生产厂家(可空)" clearable style="width:420px" />
</el-form-item>
<el-form-item label="检测单号">
<el-form-item v-if="!isFinal" label="检测单号">
<el-input v-model="inspectionNo" placeholder="检验报告单号(可空,与入库单关联)" clearable style="width:420px" />
</el-form-item>
<el-form-item label="报检单号">
<el-form-item v-if="!isFinal" label="报检单号">
<el-input v-model="reportNo" placeholder="生产报检单号(可空,与报检流程关联)" clearable style="width:420px" />
</el-form-item>
@@ -538,8 +582,11 @@ onMounted(() => {
<el-table-column prop="reportNo" label="报检单号" min-width="130" align="center">
<template #default="{ row }">{{ row.reportNo || '-' }}</template>
</el-table-column>
<el-table-column label="归属单号" min-width="130" align="center">
<template #default="{ row }">{{ row.ownershipNo || '-' }}</template>
<el-table-column label="归属" min-width="160" align="center">
<template #default="{ row }">
<span>{{ ownershipTypeLabel(row.ownershipType) }}</span>
<span v-if="row.ownershipNo">{{ row.ownershipNo }}</span>
</template>
</el-table-column>
<el-table-column prop="inspector" label="记录人" width="90" align="center">
<template #default="{ row }">{{ row.inspector || '-' }}</template>
@@ -574,11 +621,12 @@ onMounted(() => {
<!-- ============ 按物料统计(点击才加载) ============ -->
<el-tab-pane label="按物料统计" name="stats">
<el-card shadow="never" header="检验次数 / 合格率按物料">
<div style="margin-bottom:10px">
<el-button type="primary" :loading="statsLoading" @click="loadStats">统计</el-button>
<span style="margin-left:8px;color:#909399;font-size:12px">按当前时间区间与检验类别统计;每次检验独立计数,不合并记录</span>
<div style="margin-bottom:10px;display:flex;gap:10px;align-items:center;flex-wrap:wrap">
<MaterialSelect v-model="statsMaterialCode" placeholder="先选择物料必填" style="width:280px" />
<el-button type="primary" :loading="statsLoading" :disabled="!statsMaterialCode" @click="loadStats">统计</el-button>
<span style="color:#909399;font-size:12px">需先选物料再统计(按当前时间区间与检验类别,每次检验独立计数,不合并记录)</span>
</div>
<el-table :data="statsList" border size="small" v-loading="statsLoading" empty-text="统计查看结果">
<el-table :data="statsList" border size="small" v-loading="statsLoading" :empty-text="statsQueried ? '该物料区间内无检验记录' : '请先选择物料再点「统计」'">
<el-table-column prop="materialCode" label="图号" min-width="160" />
<el-table-column prop="total" label="检验次数" width="110" align="center" />
<el-table-column prop="pass" label="合格次数" width="110" align="center" />
@@ -609,6 +657,8 @@ onMounted(() => {
<el-form-item label="处置方式" required>
<el-radio-group v-model="disposeDialog.type">
<el-radio-button value="RETURN">退货</el-radio-button>
<el-radio-button value="REWORK">换修</el-radio-button>
<el-radio-button value="CONCESSION">让步接收</el-radio-button>
<el-radio-button value="SCRAP">报废</el-radio-button>
<el-radio-button value="PARTIAL">部分入库</el-radio-button>
</el-radio-group>