Files
bj_power/bj_power_mes/frontend/src/pages/ProcessCard.vue
T

207 lines
10 KiB
Vue
Raw Normal View History

2026-09-15 16:37:39 +08:00
<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>
2026-09-15 16:37:39 +08:00
<el-table :data="g.items" border size="small">
<el-table-column prop="materialCode" label="图号" min-width="140" show-overflow-tooltip />
2026-09-15 16:37:39 +08:00
<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>