<script lang="ts">
|
import { defineComponent, reactive, onMounted, onUnmounted, ref, nextTick } from 'vue';
|
|
import { AppstoreOutlined, FieldTimeOutlined, LineChartOutlined, WarningOutlined, ThunderboltOutlined, CheckCircleOutlined } from '@ant-design/icons-vue';
|
import * as echarts from 'echarts';
|
import img from '#/assets/images/2t.png';
|
import { getDeviceDataInj, updateDeviceData, initDeviceData } from '#/api/eims/equ/deviceData'
|
|
export default defineComponent({
|
name: 'InjectionMoldingMachineDetail',
|
components: {
|
LineChartOutlined,
|
WarningOutlined,
|
AppstoreOutlined,
|
CheckCircleOutlined,
|
FieldTimeOutlined,
|
ThunderboltOutlined
|
},
|
setup() {
|
// 模拟数据
|
const deviceInfo = reactive({
|
deviceName: '立式注塑机',
|
deviceType: '注塑机',
|
deviceId: 'GPC2013C027',
|
installDate: '2014-01-06',
|
serviceLife: 15,
|
status: '运行中',
|
statusColor: '#52c41a',
|
imageUrl: img // 添加设备图片路径
|
});
|
|
const healthData = reactive({
|
overallHealth: 88,
|
healthColor: '#52c41a',
|
predictedLife: 1753,
|
riskLevel: '低风险',
|
riskColor: '#1a7ac4',
|
injectionPressure: 120, // 注塑压力
|
clampingForce: 80, // 锁模力
|
moldTemperature: 45, // 模具温度
|
screwSpeed: 150, // 螺杆转速
|
meltTemperature: 220, // 熔融温度
|
coolingTime: 25, // 冷却时间
|
injectionCount: 495 * 24 * 60 + 19 * 60 + 44, // 注射次数,转换为分钟
|
clampingCount: 545636, // 合模次数 // 跟随产量 50s加一次
|
productionCycle: 45, // 生产周期
|
energyConsumption: 64, // 能耗
|
yieldRate: 354, // 产量 50秒增加一模
|
downtime: 120, // 出错停机时间
|
// 用于累加增长的字段
|
// accumulatedInjectionCount: 12180,
|
// accumulatedClampingCount: 545636,
|
// accumulatedEnergyConsumption: 64
|
});
|
|
const maintenanceColumns = [
|
{
|
title: '维护类型',
|
dataIndex: 'type',
|
key: 'type'
|
},
|
{
|
title: '维护内容',
|
dataIndex: 'content',
|
key: 'content'
|
},
|
{
|
title: '建议时间',
|
dataIndex: 'suggestedTime',
|
key: 'suggestedTime'
|
},
|
{
|
title: '紧急程度',
|
dataIndex: 'urgency',
|
key: 'urgency',
|
slots: { customRender: 'urgency' }
|
},
|
{
|
title: '操作',
|
key: 'action',
|
slots: { customRender: 'action' }
|
}
|
];
|
|
const maintenanceData = reactive([
|
{
|
key: '1',
|
type: '例行保养',
|
content: '检查液压油乳化程度',
|
suggestedTime: '2025-07-02',
|
urgency: '中等'
|
},
|
{
|
key: '2',
|
type: '故障预警',
|
content: '主油缸密封圈寿命预警',
|
suggestedTime: '2025-07-01',
|
urgency: '低'
|
},{
|
key: '2',
|
type: '液压油检查',
|
content: '检查确认液压油温度是否正常',
|
suggestedTime: '2025-07-01',
|
urgency: '低'
|
},
|
{
|
key: '3',
|
type: '冷却系统检查',
|
content: '检查冷水机制冷效率是否达标',
|
suggestedTime: '2025-06-11',
|
urgency: '低'
|
},{
|
key: '3',
|
type: '液压系统检查',
|
content: '检查各液压管道压力表与电脑显示是否一致',
|
suggestedTime: '2025-06-11',
|
urgency: '低'
|
},{
|
key: '3',
|
type: '温控系统检查',
|
content: '检查各段加热圈加热、热电耦是否异常',
|
suggestedTime: '2025-06-11',
|
urgency: '低'
|
}
|
]);
|
|
const getUrgencyColor = (urgency: string) => {
|
switch (urgency) {
|
case '中等': {
|
return 'orange';
|
}
|
case '低': {
|
return 'green';
|
}
|
case '高': {
|
return 'red';
|
}
|
default: {
|
return 'default';
|
}
|
}
|
};
|
|
const fetchDeviceData = async () => {
|
try {
|
const res = await getDeviceDataInj();
|
Object.assign(healthData, res);
|
} catch (error) {
|
console.error('获取设备数据失败:', error);
|
}
|
};
|
|
let deviceDataInterval: number | undefined;
|
fetchDeviceData();
|
|
deviceDataInterval = setInterval(async () => {
|
try {
|
fetchDeviceData();
|
} catch (error) {
|
console.error('更新设备数据失败:', error);
|
}
|
}, 3000);
|
|
|
const sparePartColumns = [
|
{
|
title: '部件名称',
|
dataIndex: 'name',
|
key: 'name'
|
},
|
{
|
title: '预计寿命',
|
dataIndex: 'currentLife',
|
key: 'currentLife'
|
|
},
|
{
|
title: '预测剩余寿命',
|
dataIndex: 'remainingLife',
|
key: 'remainingLife'
|
|
},
|
{
|
title: '状态',
|
dataIndex: 'status',
|
key: 'status',
|
slots: { customRender: 'status' }
|
}
|
];
|
|
const sparePartData = reactive([
|
{
|
key: '1',
|
name: '液压油',
|
currentLife: 8_500,
|
remainingLife: 2515,
|
status: '良好'
|
},
|
{
|
key: '2',
|
name: '加热圈',
|
currentLife: 6_000,
|
remainingLife: 3415,
|
status: '良好'
|
},
|
{
|
key: '3',
|
name: '热电耦',
|
currentLife: 8_000,
|
remainingLife: 5851,
|
status: '良好'
|
},
|
{
|
key: '3',
|
name: '注胶嘴',
|
currentLife: "100000次",
|
remainingLife: "76438次",
|
status: '良好'
|
},
|
{
|
key: '3',
|
name: '锁模夹具',
|
currentLife: "12个月",
|
remainingLife: "8个月",
|
status: '良好'
|
},
|
{
|
key: '3',
|
name: '注胶螺杆',
|
currentLife: "60个月",
|
remainingLife: "48个月",
|
status: '良好'
|
}
|
]);
|
|
const historyData = reactive([
|
{
|
id: '1',
|
date: '2025-08-15',
|
type: '例行保养',
|
description: '液压系统检查,温控系统检查',
|
color: 'green'
|
},
|
{
|
id: '1',
|
date: '2025-07-20',
|
type: '例行保养',
|
description: '液压系统检查,温控系统检查',
|
color: 'green'
|
},
|
{
|
id: '2',
|
date: '2025-07-13',
|
type: '维修保养',
|
description: '第一段温度显示异常,热电耦损坏,更换新配件',
|
color: 'orange'
|
},
|
{
|
id: '1',
|
date: '2025-06-18',
|
type: '例行保养',
|
description: '液压系统检查,温控系统检查',
|
color: 'green'
|
},
|
{
|
id: '2',
|
date: '2025-05-23',
|
type: '维修保养',
|
description: '第三段温度异常,加热圈烧坏,更换新配件',
|
color: 'orange'
|
},
|
{
|
id: '2',
|
date: '2025-05-15',
|
type: '例行保养',
|
description: '液压系统检查,温控系统检查',
|
color: 'green'
|
},
|
{
|
id: '3',
|
date: '2025-04-01',
|
type: '年度保养',
|
description: '全面检查设备运行状态',
|
color: 'blue'
|
}
|
]);
|
|
const getStatusColor = (status: string) => {
|
switch (status) {
|
case '危险': {
|
return '#f5222d';
|
}
|
case '良好': {
|
return '#52c41a';
|
}
|
case '预警': {
|
return '#faad14';
|
}
|
default: {
|
return '#d9d9d9';
|
}
|
}
|
};
|
|
const handleMaintenance = (record: any) => {
|
console.log('处理维护建议:', record);
|
// 实际项目中这里会调用API处理维护建议
|
};
|
|
let intervalId: number | undefined;
|
|
// const updateAccumulatedData = () => {
|
// healthData.accumulatedInjectionCount += + 0.1; // 每次增加1-5分钟
|
// healthData.accumulatedClampingCount += Math.floor(Math.random() * 100) + 10; // 每次增加10-100次
|
// healthData.accumulatedEnergyConsumption += (Math.random() * 0.015 + 0.008); // 每次增加0.1-0.6 kWh
|
|
// // 更新显示值
|
// const totalMinutes = Math.floor(healthData.accumulatedInjectionCount);
|
|
// const minutes = totalMinutes % 60;
|
// healthData.injectionCount = `${healthData.accumulatedInjectionCount}h`;
|
// healthData.clampingCount = healthData.accumulatedClampingCount;
|
// healthData.energyConsumption = parseFloat(healthData.accumulatedEnergyConsumption.toFixed(2));
|
// };
|
const maintenanceTable = ref<HTMLElement | null>(null);
|
// 初始设置一次,避免首次加载时显示为0
|
// updateAccumulatedData();
|
let scrollInterval: number | undefined;
|
|
const startScroll = () => {
|
if (!maintenanceTable.value) return;
|
|
const tableBody = maintenanceTable.value.$el.querySelector('.ant-table-body');
|
if (!tableBody) return;
|
|
const scrollHeight = tableBody.scrollHeight;
|
const clientHeight = tableBody.clientHeight;
|
|
if (scrollHeight <= clientHeight) {
|
return;
|
}
|
|
let currentScrollTop = 0;
|
scrollInterval = setInterval(() => {
|
currentScrollTop += 1;
|
if (currentScrollTop >= scrollHeight - clientHeight) {
|
currentScrollTop = 0;
|
}
|
tableBody.scrollTop = currentScrollTop;
|
}, 50);
|
};
|
|
|
|
onMounted(() => {
|
// 每5秒更新一次累加数据
|
// intervalId = setInterval(updateAccumulatedData, 5000);
|
nextTick(() => {
|
startScroll();
|
});
|
})
|
onUnmounted(() => {
|
if (scrollInterval) {
|
clearInterval(scrollInterval);
|
}
|
if (deviceDataInterval) {
|
clearInterval(deviceDataInterval);
|
}
|
});
|
|
|
return {
|
deviceInfo,
|
healthData,
|
maintenanceColumns,
|
maintenanceData,
|
sparePartColumns,
|
sparePartData,
|
historyData,
|
getStatusColor,
|
getUrgencyColor,
|
handleMaintenance,
|
maintenanceTable
|
};
|
},
|
mounted() {
|
|
// 初始化图表
|
const initChart = (chartId: string, title: string, chartData: any[], unit: string, baseValue: number, fluctuation: number) => {
|
const chart = echarts.init(document.getElementById(chartId));
|
|
// 确保初始数据点足够显示一个完整的趋势
|
if (chartData.length === 0) {
|
const now = new Date();
|
for (let i = 0; i < 24; i++) {
|
// 生成60个点,代表5分钟的数据
|
const time = new Date(now.getTime() - (24 - i) * 5000); // 每个点间隔5秒
|
chartData.push({
|
time: time.toLocaleTimeString(),
|
value: (baseValue + Math.random() * fluctuation * 2 - fluctuation).toFixed(2)
|
});
|
}
|
}
|
|
const updateChart = () => {
|
// 生成新的数据点
|
const now = new Date();
|
const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
|
const newValue = (baseValue + Math.random() * fluctuation * 2 - fluctuation).toFixed(2);
|
|
// 添加新数据点,最多保留60个点(5分钟数据)
|
chartData.push({
|
time,
|
value: newValue
|
});
|
if (chartData.length > 24) {
|
chartData.shift();
|
}
|
|
const option = {
|
title: {
|
text: title,
|
left: 'center'
|
},
|
tooltip: {
|
trigger: 'axis',
|
formatter: (params) => {
|
return `${params[0].axisValueLabel}<br/>${params[0].marker} ${params[0].seriesName}: ${params[0].data}${unit}`;
|
}
|
},
|
grid: {
|
left: '8%', // 增加左侧边距
|
right: '5%',
|
bottom: '10%',
|
containLabel: true
|
},
|
xAxis: {
|
type: 'category',
|
data: chartData.map((item) => item.time)
|
},
|
yAxis: {
|
type: 'value',
|
axisLabel: {
|
formatter: (value) => `${value}${unit}`
|
}
|
},
|
series: [
|
{
|
data: chartData.map((item) => Number.parseFloat(item.value)),
|
type: 'line',
|
smooth: true,
|
lineStyle: {
|
width: 2,
|
color: '#5470C6' // Default color
|
},
|
areaStyle: {
|
opacity: 0.8,
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{
|
offset: 0,
|
color: 'rgba(84,112,198,0.3)'
|
},
|
{
|
offset: 1,
|
color: 'rgba(84,112,198,0)'
|
}
|
])
|
}
|
}
|
]
|
};
|
chart.setOption(option);
|
};
|
|
// 初始渲染
|
updateChart();
|
|
// 每5秒更新一次数据
|
const intervalId = setInterval(updateChart, 5000);
|
|
window.addEventListener('resize', () => {
|
chart.resize();
|
});
|
window.addEventListener('resize', () => {
|
chart.resize();
|
});
|
|
};
|
|
|
|
const hydraulicOilTemperatureData: any[] = [];
|
const hydraulicOilPressureData: any[] = [];
|
const mainMotorCurrentData: any[] = [];
|
const screwSpeedData: any[] = [];
|
const moldClampingForceData: any[] = [];
|
const injectionPressureData: any[] = [];
|
const injectionSpeedData: any[] = [];
|
const barrelTemperatureData: any[] = [];
|
const coolingWaterTemperatureData: any[] = [];
|
const coolingWaterFlowData: any[] = [];
|
const ejectorPositionData: any[] = [];
|
const cycleTimeData: any[] = [];
|
|
// initChart('hydraulicOilTemperatureChart', '液压油温', hydraulicOilTemperatureData, '°C', 50, 2);
|
// initChart('hydraulicOilPressureChart', '液压油压', hydraulicOilPressureData, 'MPa', 150, 5);
|
// initChart('mainMotorCurrentChart', '主电机电流', mainMotorCurrentData, 'A', 80, 3);
|
initChart('screwSpeedChart', '螺杆转速', screwSpeedData, 'rpm', 100, 10);
|
initChart('moldClampingForceChart', '锁模压力', moldClampingForceData, 'Bar', 120, 10);
|
initChart('injectionPressureChart', '注射压力', injectionPressureData, 'Bar', 80, 5);
|
initChart('injectionSpeedChart', '注射速度', injectionSpeedData, 'mm/s', 150, 10);
|
initChart('barrelTemperatureChart', '料筒温度', barrelTemperatureData, '°C', 240, 3);
|
initChart('coolingWaterTemperatureChart', '冷水机温度', coolingWaterTemperatureData, '°C', 20, 2);
|
// initChart('coolingWaterFlowChart', '冷却水流量', coolingWaterFlowData, 'L/min', 30, 3);
|
// initChart('ejectorPositionChart', '顶出位置', ejectorPositionData, 'mm', 50, 5);
|
// initChart('cycleTimeChart', '循环时间', cycleTimeData, 's', 30, 2);
|
},
|
unmounted() {
|
// 清除定时器
|
if (this.intervalId) {
|
clearInterval(this.intervalId);
|
}
|
}
|
});
|
</script>
|
|
<template>
|
<div class="device-detail-container">
|
<a-page-header :sub-title="deviceInfo.deviceName" title="注塑机预测性维护详情" @back="() => $router.go(-1)" />
|
|
<a-row :gutter="16" class="mt-4">
|
<!-- 设备图片和设备基本信息合并 -->
|
<a-col :span="10">
|
<a-card :style="{ height: '440px' }" class="mb-4" title="设备信息">
|
<a-row :gutter="16">
|
<a-col :span="12">
|
<div class="device-image-container">
|
<img :src="deviceInfo.imageUrl" alt="设备图片" class="device-image" />
|
</div>
|
</a-col>
|
<a-col :span="12">
|
<a-descriptions :column="1" bordered>
|
<a-descriptions-item label="设备名称">{{ deviceInfo.deviceName }}</a-descriptions-item>
|
<a-descriptions-item label="设备类型">{{ deviceInfo.deviceType }}</a-descriptions-item>
|
<a-descriptions-item label="设备编号">{{ deviceInfo.deviceId }}</a-descriptions-item>
|
<a-descriptions-item label="安装日期">{{ deviceInfo.installDate }}</a-descriptions-item>
|
<a-descriptions-item label="使用年限">{{ deviceInfo.serviceLife }}年</a-descriptions-item>
|
<a-descriptions-item label="当前状态">
|
<a-tag :color="deviceInfo.statusColor">{{ deviceInfo.status }}</a-tag>
|
</a-descriptions-item>
|
</a-descriptions>
|
</a-col>
|
</a-row>
|
</a-card>
|
</a-col>
|
|
<!-- 设备健康状态与维护建议 -->
|
<a-col :span="14">
|
<a-card :style="{ height: '440px' }" class="mb-4" title="设备健康状态与维护建议">
|
<a-row :gutter="16">
|
<a-col :span="8">
|
<a-statistic
|
:precision="0"
|
:value="healthData.overallHealth"
|
:value-style="{ color: healthData.healthColor }"
|
suffix="%"
|
title="整体健康度"
|
/>
|
</a-col>
|
<a-col :span="8">
|
<a-statistic :value="healthData.predictedLife" suffix="天" title="预测剩余寿命" />
|
</a-col>
|
<a-col :span="8">
|
<a-statistic :value="healthData.riskLevel" :value-style="{ color: healthData.riskColor }" title="故障风险等级" />
|
</a-col>
|
</a-row>
|
<div class="mt-4">
|
<a-progress :percent="healthData.overallHealth" :show-info="false" :stroke-color="healthData.healthColor" />
|
</div>
|
<a-divider orientation="left">预测性维护建议</a-divider>
|
<a-table ref="maintenanceTable" :columns="maintenanceColumns" :data-source="maintenanceData" :pagination="false" size="small" :scroll="{ y: 150 }">
|
<template #urgency="{ text }">
|
<a-tag :color="getUrgencyColor(text)">{{ text }}</a-tag>
|
</template>
|
<template #action="{ record }">
|
<a-button size="small" type="link" @click="handleMaintenance(record)">处理</a-button>
|
</template>
|
</a-table>
|
</a-card>
|
</a-col>
|
</a-row>
|
<a-col :span="24">
|
<a-card class="mb-4" title="设备数据">
|
<a-row :gutter="[16, 16]">
|
<a-col :span="4">
|
<a-statistic :value="healthData.injectionCount.toFixed(2)" suffix="h" title="总开机时间">
|
<template #prefix>
|
<LineChartOutlined />
|
</template>
|
</a-statistic>
|
</a-col>
|
<a-col :span="4">
|
<a-statistic :value="healthData.clampingCount.toFixed(0)" suffix="次" title="总循环次数">
|
<template #prefix>
|
<LineChartOutlined />
|
</template>
|
</a-statistic>
|
</a-col>
|
<a-col :span="4">
|
<a-statistic :value="healthData.productionCycle.toFixed(0)" suffix="s" title="平均生产周期">
|
<template #prefix>
|
<FieldTimeOutlined />
|
</template>
|
</a-statistic>
|
</a-col>
|
<a-col :span="4">
|
<a-statistic :value="healthData.energyConsumption.toFixed(2)" suffix="kWh" title="当日累积能耗">
|
<template #prefix>
|
<ThunderboltOutlined />
|
</template>
|
</a-statistic>
|
</a-col>
|
<a-col :span="4">
|
<a-statistic :value="healthData.yieldRate.toFixed(0)" suffix="模" title="当前产量">
|
<template #prefix>
|
<CheckCircleOutlined />
|
</template>
|
</a-statistic>
|
</a-col>
|
<!-- <a-col :span="4">-->
|
<!-- <a-statistic :value="healthData.downtime" suffix="s" title="出错停机时间">-->
|
<!-- <template #prefix>-->
|
<!-- <FieldTimeOutlined />-->
|
<!-- </template>-->
|
<!-- </a-statistic>-->
|
<!-- </a-col>-->
|
</a-row>
|
</a-card>
|
</a-col>
|
|
<!-- 实时数据趋势图 -->
|
<a-card class="mb-4" title="实时数据趋势图">
|
<!-- <a-row :gutter="16">-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="hydraulicOilTemperatureChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="hydraulicOilPressureChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="mainMotorCurrentChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- </a-row>-->
|
<a-row :gutter="16" class="mt-4">
|
<a-col :span="8">
|
<div id="screwSpeedChart" style="height: 300px"></div>
|
</a-col>
|
<a-col :span="8">
|
<div id="barrelTemperatureChart" style="height: 300px"></div>
|
</a-col>
|
<a-col :span="8">
|
<div id="injectionPressureChart" style="height: 300px"></div>
|
</a-col>
|
</a-row>
|
<a-row :gutter="16" class="mt-4">
|
<a-col :span="8">
|
<div id="injectionSpeedChart" style="height: 300px"></div>
|
</a-col>
|
<a-col :span="8">
|
<div id="moldClampingForceChart" style="height: 300px"></div>
|
|
</a-col>
|
<a-col :span="8">
|
<div id="coolingWaterTemperatureChart" style="height: 300px"></div>
|
</a-col>
|
</a-row>
|
<!-- <a-row :gutter="16" class="mt-4">-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="coolingWaterFlowChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="ejectorPositionChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- <a-col :span="8">-->
|
<!-- <div id="cycleTimeChart" style="height: 300px"></div>-->
|
<!-- </a-col>-->
|
<!-- </a-row>-->
|
</a-card>
|
|
<a-row :gutter="16">
|
<!-- 备件寿命预测 -->
|
<a-col :span="12">
|
<a-card class="mb-4" title="备件寿命预测">
|
<a-table :columns="sparePartColumns" :data-source="sparePartData" :pagination="false" size="small">
|
<template #status="{ text }">
|
<a-tag :color="getStatusColor(text)">{{ text }}</a-tag>
|
</template>
|
</a-table>
|
</a-card>
|
</a-col>
|
|
<!-- 历史维护记录 -->
|
<a-col :span="12">
|
<a-card class="mb-4" title="历史维护记录">
|
<a-timeline>
|
<a-timeline-item v-for="item in historyData" :key="item.id" :color="item.color">
|
<p>{{ item.date }} - {{ item.type }}:{{ item.description }}</p>
|
</a-timeline-item>
|
</a-timeline>
|
</a-card>
|
</a-col>
|
</a-row>
|
</div>
|
</template>
|
|
<style scoped>
|
.device-detail-container {
|
padding: 20px;
|
background-color: #f0f2f5;
|
}
|
|
.device-image-container {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
height: 330px;
|
}
|
|
.device-image {
|
max-width: 100%;
|
max-height: 100%;
|
object-fit: contain;
|
}
|
|
.mb-4 {
|
margin-bottom: 16px;
|
}
|
|
.mt-4 {
|
margin-top: 16px;
|
}
|
</style>
|