主要变更: 1. 后端:重构工位管理逻辑,新增工位自定义扩展能力,将工位号上限调整为999并支持14+虚拟工位 2. 前后端:全量替换"BOM"术语为"物料清单",包括提示语、注释和文档 3. WMS模块:新增AGV任务取消接口与前端页面,对接海康RCS取消任务逻辑 4. 权限与种子数据:新增工位新增权限并更新权限配置 5. 修复错误校验逻辑,优化AGV客户端签名与请求处理
207 lines
10 KiB
Vue
207 lines
10 KiB
Vue
<template>
|
||
<div>
|
||
<el-card>
|
||
<el-alert type="info" :closable="false" style="margin-bottom:12px"
|
||
title="产品型号全景流程卡:按产品型号全方位展示制造全流程——工位组合 → 每工位工艺流程与步骤(采集方式 / 检测点 / 拧紧 / 考核标准 / 工艺图纸)→ 物料清单(含装配工位、单台用量、损耗、需求总量)→ 检验卡项 → 各环节时间。可打印或另存为 PDF。" />
|
||
|
||
<el-form inline>
|
||
<el-form-item label="产品型号">
|
||
<el-select v-model="productTypeId" filterable clearable placeholder="请选择产品型号" style="width:260px" @change="onProductChange">
|
||
<el-option v-for="p in productTypes" :key="p.id" :label="p.name + (p.code ? ' / ' + p.code : '')" :value="p.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="产品编号">
|
||
<el-input v-model="productCode" placeholder="也可直接按产品编号生成" clearable style="width:220px" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" :loading="loading" @click="load">生成全景流程卡</el-button>
|
||
<el-button :disabled="!card" @click="printCard">打印 / 导出 PDF</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<el-card v-if="card" style="margin-top:12px">
|
||
<template #header>
|
||
<div class="hd">
|
||
<span class="t">产品型号全景流程卡</span>
|
||
<span class="sub">{{ card.productName }}({{ card.productCode }})</span>
|
||
</div>
|
||
</template>
|
||
|
||
<el-descriptions :column="4" border size="small" style="margin-bottom:14px">
|
||
<el-descriptions-item label="产品名称">{{ card.productName || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="产品编号">{{ card.productCode || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="产品分类">{{ card.category || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="工位数">{{ card.stationCount }}</el-descriptions-item>
|
||
<el-descriptions-item label="工位组合" :span="2">{{ card.processSeq || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="组合来源" :span="2">{{ card.seqSource }}</el-descriptions-item>
|
||
<el-descriptions-item label="参考工单">{{ card.orderNo || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="计划开始">{{ card.planStart || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="计划结束">{{ card.planEnd || '-' }}</el-descriptions-item>
|
||
<el-descriptions-item label="生成时间">{{ card.generatedAt }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-tabs v-model="tab">
|
||
<!-- 工位流程总览 -->
|
||
<el-tab-pane label="工位流程总览" name="stations">
|
||
<el-table :data="card.stations" border size="small">
|
||
<el-table-column prop="stationNo" label="工位号" width="80" align="center" />
|
||
<el-table-column prop="stationName" label="工位名称" min-width="130" show-overflow-tooltip />
|
||
<el-table-column label="工艺流程" min-width="180" show-overflow-tooltip>
|
||
<template #default="{ row }">
|
||
<span v-if="row.flowName">{{ row.flowName }}</span>
|
||
<span v-else class="off">未绑定流程</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="flowStatus" label="流程状态" width="90" align="center" />
|
||
<el-table-column prop="stepCount" label="步骤数" width="80" align="center" />
|
||
<el-table-column label="检验点" width="80" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag v-if="row.checkCount" type="warning" size="small">{{ row.checkCount }}</el-tag>
|
||
<span v-else>0</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="torqueCount" label="拧紧步骤" width="90" align="center" />
|
||
<el-table-column label="工艺图纸" min-width="150" show-overflow-tooltip>
|
||
<template #default="{ row }">
|
||
<span v-if="row.pdfFile">{{ row.pdfFile }}</span>
|
||
<span v-else class="off">-</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="作业时间(本型号实绩)" min-width="190">
|
||
<template #default="{ row }">
|
||
<span v-if="row.refTime">{{ row.refTime }}</span>
|
||
<span v-else class="off">暂无实绩</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
|
||
<!-- 工艺步骤明细 -->
|
||
<el-tab-pane label="工艺流程与步骤" name="steps">
|
||
<el-empty v-if="!card.stations.some((s) => s.steps && s.steps.length)" description="暂无工艺流程步骤" />
|
||
<div v-for="st in card.stations.filter((s) => s.steps && s.steps.length)" :key="st.stationNo" class="blk">
|
||
<div class="blk-t">工位{{ st.stationNo }} {{ st.stationName }} · {{ st.flowName || '未绑定流程' }}</div>
|
||
<el-table :data="st.steps" border size="small">
|
||
<el-table-column prop="seq" label="步骤号" width="80" align="center" />
|
||
<el-table-column prop="name" label="步骤名称" min-width="180" show-overflow-tooltip />
|
||
<el-table-column prop="collectType" label="采集方式" width="110" align="center" />
|
||
<el-table-column label="检测确认" width="100" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag v-if="row.needCheck" type="warning" size="small">需检验</el-tag>
|
||
<span v-else>否</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="拧紧采集" width="100" align="center">
|
||
<template #default="{ row }">
|
||
<span v-if="row.isTorque" class="ok">是</span>
|
||
<span v-else>否</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="criteria" label="考核标准" min-width="220" show-overflow-tooltip />
|
||
</el-table>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- 物料清单 -->
|
||
<el-tab-pane label="物料清单" name="bom">
|
||
<el-empty v-if="!card.bomGroups || !card.bomGroups.length" description="该产品型号暂无物料清单" />
|
||
<div v-for="g in card.bomGroups" :key="g.bomName" class="blk">
|
||
<div class="blk-t">物料清单:{{ g.bomName }}</div>
|
||
<el-table :data="g.items" border size="small">
|
||
<el-table-column prop="materialCode" label="图号" min-width="140" show-overflow-tooltip />
|
||
<el-table-column prop="materialName" label="物料名称" min-width="150" show-overflow-tooltip />
|
||
<el-table-column prop="spec" label="规格" min-width="120" show-overflow-tooltip />
|
||
<el-table-column prop="unit" label="单位" width="70" align="center" />
|
||
<el-table-column prop="manageMode" label="管理方式" width="150" align="center" />
|
||
<el-table-column prop="stationNo" label="装配工位" width="100" align="center" />
|
||
<el-table-column prop="unitQty" label="单台用量" width="90" align="center" />
|
||
<el-table-column prop="lossRate" label="损耗率(%)" width="100" align="center" />
|
||
<el-table-column prop="requiredQty" label="需求总量" width="100" align="center" />
|
||
</el-table>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- 检验卡项 -->
|
||
<el-tab-pane :label="`检验卡项(${(card.checkItems || []).length})`" name="checks">
|
||
<el-empty v-if="!card.checkItems || !card.checkItems.length" description="该产品型号暂无检验卡项(工艺流程未设置「需要检测确认」的步骤)" />
|
||
<el-table v-else :data="card.checkItems" border size="small">
|
||
<el-table-column prop="stationNo" label="工位号" width="90" align="center" />
|
||
<el-table-column prop="stationName" label="工位名称" min-width="140" show-overflow-tooltip />
|
||
<el-table-column prop="stepName" label="检验步骤" min-width="180" show-overflow-tooltip />
|
||
<el-table-column prop="criteria" label="判定标准" min-width="240" show-overflow-tooltip />
|
||
</el-table>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-card>
|
||
|
||
<el-card v-else style="margin-top:12px">
|
||
<el-empty description="请选择产品型号(或填写产品编号)后点击「生成全景流程卡」" />
|
||
</el-card>
|
||
</div>
|
||
<PageHelp :data="helpProcessCard" page="ProcessCard" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import request from '../utils/request'
|
||
import { openPrintableHtml } from '../utils/print'
|
||
import PageHelp from '../components/PageHelp.vue'
|
||
import { helpProcessCard } from '../help'
|
||
|
||
const productTypes = ref([])
|
||
const productTypeId = ref(null)
|
||
const productCode = ref('')
|
||
const card = ref(null)
|
||
const tab = ref('stations')
|
||
const loading = ref(false)
|
||
|
||
function onProductChange(id) {
|
||
const p = productTypes.value.find((x) => x.id === id)
|
||
if (p) productCode.value = p.code || ''
|
||
}
|
||
|
||
async function load() {
|
||
if (!productTypeId.value && !productCode.value) {
|
||
ElMessage.warning('请选择产品型号或填写产品编号')
|
||
return
|
||
}
|
||
loading.value = true
|
||
try {
|
||
card.value = await request.get('/process-card/model', {
|
||
params: { productTypeId: productTypeId.value || 0, productCode: productCode.value, format: 'json' }
|
||
})
|
||
} catch {
|
||
card.value = null
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function printCard() {
|
||
openPrintableHtml('/process-card/model', {
|
||
productTypeId: productTypeId.value || 0,
|
||
productCode: productCode.value
|
||
})
|
||
}
|
||
|
||
onMounted(async () => {
|
||
try {
|
||
productTypes.value = (await request.get('/product-types')) || []
|
||
} catch {
|
||
productTypes.value = []
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.hd { display: flex; align-items: baseline; gap: 12px; }
|
||
.hd .t { font-size: 16px; font-weight: 600; }
|
||
.hd .sub { color: #6b7280; font-size: 13px; }
|
||
.blk { margin-bottom: 16px; }
|
||
.blk-t { font-weight: 600; margin-bottom: 6px; color: #303133; }
|
||
.off { color: #b0b3b8; }
|
||
.ok { color: #15803d; font-weight: 600; }
|
||
</style>
|