Files
bj_power/bj_power_mes/frontend/src/pages/ProcessCard.vue
T
SunYF 4a5e2d7bac feat: 完成多模块迭代更新
- 新增过程巡检按步骤上报、数量不符上报、工位退料功能
- 增加工单工程编号三级归属字段
- 新增物料安全库存与缺货预警
- 新增附件管理、退库单管理模块
- 优化生产看板展示逻辑与前端页面文案
- 清理冗余的工艺路线相关代码与备份文件
2026-09-15 16:37:39 +08:00

207 lines
10 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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">BOM{{ 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>