You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
9.0 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>