|
|
<template>
|
|
|
<div class="tire1-container">
|
|
|
<el-card class="header-card" shadow="never">
|
|
|
<div class="header-row">
|
|
|
<span class="label">胎号:</span>
|
|
|
<el-input v-model="queryTireNo" style="width: 220px" clearable @keyup.enter="handleQuery" />
|
|
|
<el-button type="primary" @click="handleQuery">查询</el-button>
|
|
|
<el-button @click="handleClear">清空</el-button>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
|
|
|
<el-card class="process-card" shadow="never">
|
|
|
<div class="process-wrapper" @click="handleProcessClick">
|
|
|
<img class="process-img" :src="mixTireFull" alt="轮胎工艺流程" />
|
|
|
</div>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 批次列表(默认展示) -->
|
|
|
<el-card v-if="displayMode === 'tree'" class="tree-card" shadow="never">
|
|
|
<template #header>
|
|
|
<div class="blue-header">
|
|
|
<span class="header-title">批次列表</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
<el-tree
|
|
|
ref="treeRef"
|
|
|
:data="treeData"
|
|
|
:props="treeProps"
|
|
|
node-key="id"
|
|
|
default-expand-all
|
|
|
:expand-on-click-node="false"
|
|
|
highlight-current
|
|
|
>
|
|
|
<template #default="{ node, data }">
|
|
|
<span class="custom-tree-node">
|
|
|
<el-icon v-if="!data.children"><Document /></el-icon>
|
|
|
<el-icon v-else><Folder /></el-icon>
|
|
|
<span class="label-text">{{ node.label }}</span>
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-tree>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 生产信息(点击成型后展示) -->
|
|
|
<el-card v-if="displayMode === 'production'" class="detail-card" shadow="never">
|
|
|
<template #header>
|
|
|
<div class="blue-header">
|
|
|
<span class="header-title">生产信息 - {{ tireNo }}</span>
|
|
|
<el-button type="primary" link @click="displayMode = 'tree'">返回批次列表</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
<el-descriptions :column="2" border size="small" class="production-desc">
|
|
|
<el-descriptions-item label="条码">{{ currentProduction.barcode }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="生产日期">{{ currentProduction.productionDate }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="物料名称">{{ currentProduction.materialName }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="机台">{{ currentProduction.machine }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="班次">{{ currentProduction.shift }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="班组">{{ currentProduction.team }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="车次">{{ currentProduction.trainNo }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="设定重量">{{ currentProduction.setWeight }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="实际重量">{{ currentProduction.actualWeight }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
</el-card>
|
|
|
|
|
|
<!-- 质检信息(点击质检区后展示) -->
|
|
|
<el-card v-if="displayMode === 'quality'" class="detail-card" shadow="never">
|
|
|
<template #header>
|
|
|
<div class="blue-header">
|
|
|
<span class="header-title">质检信息 - {{ tireNo }}</span>
|
|
|
<el-button type="primary" link @click="displayMode = 'tree'">返回批次列表</el-button>
|
|
|
</div>
|
|
|
</template>
|
|
|
<el-table :data="currentQuality" border stripe size="small">
|
|
|
<el-table-column prop="reportNo" label="报告单号" min-width="140" align="center" show-overflow-tooltip />
|
|
|
<el-table-column prop="times" label="检验次数" min-width="90" align="center" />
|
|
|
<el-table-column prop="item" label="检验项目" min-width="140" align="center" show-overflow-tooltip />
|
|
|
<el-table-column prop="standard" label="标准" min-width="120" align="center" show-overflow-tooltip />
|
|
|
<el-table-column prop="value" label="检验值" min-width="100" align="center" show-overflow-tooltip />
|
|
|
<el-table-column prop="deviation" label="偏差" min-width="180" align="center" show-overflow-tooltip />
|
|
|
<el-table-column prop="result" label="结果" min-width="80" align="center" />
|
|
|
</el-table>
|
|
|
</el-card>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="MixTraceTire1">
|
|
|
import { computed, ref } from 'vue';
|
|
|
import { Document, Folder } from '@element-plus/icons-vue';
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
import tireData from './data/tire1.json';
|
|
|
import backwardDetailData from './data/backward2.json';
|
|
|
import mixTireFull from './mixTire.png';
|
|
|
|
|
|
const source = tireData as any;
|
|
|
const detailMap = (backwardDetailData as any).detailMap || {};
|
|
|
|
|
|
const tireNo = ref(source.tireNo || '');
|
|
|
const queryTireNo = ref(tireNo.value);
|
|
|
const treeData = ref<any[]>(source.treeData || []);
|
|
|
const treeProps = { children: 'children', label: 'label' };
|
|
|
const treeRef = ref();
|
|
|
|
|
|
/** 页面下方展示模式:tree=批次列表(默认) | production=生产信息 | quality=质检信息 */
|
|
|
type DisplayMode = 'tree' | 'production' | 'quality';
|
|
|
const displayMode = ref<DisplayMode>('tree');
|
|
|
|
|
|
/**
|
|
|
* 流程图各节点在图片中的水平位置比例(基于 mixTire.png 实际布局)
|
|
|
* 半制品(~8%) → 半制品质检(~20%) → 成型(~31%) → 成型质检(~39%) → 硫化(~47%)
|
|
|
* → 质检区[外观/X光/动平衡/均匀性](~62%) → 成品入库(~80%) → 成品出库(~92%)
|
|
|
*
|
|
|
* 硫化左侧 = 反向追溯(上游工序);硫化右侧 = 正向追溯(下游质检/入库/出库)
|
|
|
*/
|
|
|
const REGIONS = {
|
|
|
// 成型节点区域:点击后展示生产信息
|
|
|
molding: { left: 0.25, right: 0.37 },
|
|
|
// 质检区域(外观/X光/动平衡/均匀性):点击后展示质检信息
|
|
|
qualityCheck: { left: 0.52, right: 0.73 }
|
|
|
};
|
|
|
|
|
|
/** 根据胎号(硫化号)从 backward2.json 的 detailMap 中查找对应数据 */
|
|
|
const findDetailByTireNo = (no: string) => {
|
|
|
for (const key of Object.keys(detailMap)) {
|
|
|
const detail = detailMap[key];
|
|
|
if (detail.barcodeType === '4' && detail.production?.barcode === no) {
|
|
|
return detail;
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
};
|
|
|
|
|
|
const currentProduction = computed(() => {
|
|
|
const detail = findDetailByTireNo(tireNo.value);
|
|
|
return detail?.production || {};
|
|
|
});
|
|
|
|
|
|
const currentQuality = computed(() => {
|
|
|
const detail = findDetailByTireNo(tireNo.value);
|
|
|
return detail?.qualityData || [];
|
|
|
});
|
|
|
|
|
|
const handleQuery = () => {
|
|
|
if (!queryTireNo.value.trim()) {
|
|
|
ElMessage.warning('请输入胎号');
|
|
|
return;
|
|
|
}
|
|
|
tireNo.value = queryTireNo.value.trim();
|
|
|
displayMode.value = 'tree';
|
|
|
ElMessage.success(`已定位胎号: ${tireNo.value}`);
|
|
|
};
|
|
|
|
|
|
const handleClear = () => {
|
|
|
queryTireNo.value = '';
|
|
|
displayMode.value = 'tree';
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 点击流程图节点,根据坐标判断所属区域,在页面内切换展示内容
|
|
|
* 点击"成型"→展示生产信息 | 点击"质检区"→展示质检信息
|
|
|
*/
|
|
|
const handleProcessClick = (event: MouseEvent) => {
|
|
|
if (!tireNo.value) {
|
|
|
ElMessage.warning('请先输入胎号并查询');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const target = event.currentTarget as HTMLElement;
|
|
|
const rect = target.getBoundingClientRect();
|
|
|
const x = event.clientX - rect.left;
|
|
|
const ratio = x / rect.width;
|
|
|
|
|
|
if (ratio >= REGIONS.molding.left && ratio <= REGIONS.molding.right) {
|
|
|
// 点击"成型"节点 → 展示该胎号的生产信息
|
|
|
if (!currentProduction.value?.barcode) {
|
|
|
ElMessage.warning(`未找到胎号 ${tireNo.value} 的生产数据`);
|
|
|
return;
|
|
|
}
|
|
|
displayMode.value = 'production';
|
|
|
} else if (ratio >= REGIONS.qualityCheck.left && ratio <= REGIONS.qualityCheck.right) {
|
|
|
// 点击"质检区"(外观/X光/动平衡/均匀性)→ 展示该胎号的质检信息
|
|
|
if (!currentQuality.value?.length) {
|
|
|
ElMessage.warning(`未找到胎号 ${tireNo.value} 的质检数据`);
|
|
|
return;
|
|
|
}
|
|
|
displayMode.value = 'quality';
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.tire1-container {
|
|
|
padding: 16px;
|
|
|
|
|
|
.header-card {
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
|
|
|
.header-row {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
.label {
|
|
|
font-size: 16px;
|
|
|
font-weight: 600;
|
|
|
color: #303133;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.process-card {
|
|
|
margin-bottom: 16px;
|
|
|
background: white;
|
|
|
:deep(.el-card__body) {
|
|
|
padding: 24px 16px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.process-wrapper {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.process-img {
|
|
|
display: block;
|
|
|
width: 100%;
|
|
|
height: 50vh;
|
|
|
user-select: none;
|
|
|
object-fit: contain;
|
|
|
}
|
|
|
|
|
|
.tree-card,
|
|
|
.detail-card {
|
|
|
:deep(.el-card__header) {
|
|
|
padding: 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.blue-header {
|
|
|
background-color: #2f6ea5;
|
|
|
color: #fff;
|
|
|
padding: 12px 16px;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
.header-title {
|
|
|
font-size: 14px;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
.el-button {
|
|
|
color: #fff !important;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.production-desc {
|
|
|
:deep(.el-descriptions__label) {
|
|
|
width: 100px;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.custom-tree-node {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 6px;
|
|
|
font-size: 13px;
|
|
|
|
|
|
.el-icon {
|
|
|
color: #2f6ea5;
|
|
|
}
|
|
|
.label-text {
|
|
|
color: #303133;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</style>
|