| | |
| | | /> |
| | | </div> |
| | | |
| | | <a-divider /> |
| | | <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> |
| | |
| | | <a-card title="设备数据" class="mb-4"> |
| | | <a-row :gutter="[16, 16]"> |
| | | <a-col :span="4"> |
| | | <a-statistic title="X轴总移动距离" :value="healthData.xAxisTravel" suffix="km"> |
| | | <a-statistic title="X轴总移动距离" :value="healthData.xAxisTravel.toFixed(2)" suffix="km"> |
| | | <template #prefix> |
| | | <LineChartOutlined /> |
| | | </template> |
| | | </a-statistic> |
| | | </a-col> |
| | | <a-col :span="4"> |
| | | <a-statistic title="Y轴总移动距离" :value="healthData.yAxisTravel" suffix="km"> |
| | | <a-statistic title="Y轴总移动距离" :value="healthData.yAxisTravel.toFixed(2)" suffix="km"> |
| | | <template #prefix> |
| | | <LineChartOutlined /> |
| | | </template> |
| | | </a-statistic> |
| | | </a-col> |
| | | <a-col :span="4"> |
| | | <a-statistic title="卡带次数" :value="healthData.tapeJamCount" suffix="次"> |
| | | <a-statistic title="卡带次数" :value="healthData.tapeJamCount.toFixed(0)" suffix="次"> |
| | | <template #prefix> |
| | | <WarningOutlined /> |
| | | </template> |
| | | </a-statistic> |
| | | </a-col> |
| | | <a-col :span="4"> |
| | | <a-statistic title="卡料次数" :value="healthData.materialJamCount" suffix="次"> |
| | | <a-statistic title="卡料次数" :value="healthData.materialJamCount.toFixed(0)" suffix="次"> |
| | | <template #prefix> |
| | | <WarningOutlined /> |
| | | </template> |
| | | </a-statistic> |
| | | </a-col> |
| | | <a-col :span="4"> |
| | | <a-statistic title="拼板数" :value="healthData.panelCount" suffix="块"> |
| | | <a-statistic title="拼板数" :value="healthData.panelCount.toFixed(0)" suffix="块"> |
| | | <template #prefix> |
| | | <AppstoreOutlined /> |
| | | </template> |
| | | </a-statistic> |
| | | </a-col> |
| | | <a-col :span="4"> |
| | | <a-statistic title="出错停机时间" :value="healthData.downtime" suffix="秒"> |
| | | <a-statistic title="出错停机时间" :value="healthData.downtime.toFixed(1)" suffix="秒"> |
| | | <template #prefix> |
| | | <FieldTimeOutlined /> |
| | | </template> |
| | |
| | | </a-row> --> |
| | | <a-row :gutter="16" class="mt-4"> |
| | | <a-col :span="8"> |
| | | <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div> |
| | | |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="motorTemperatureChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="motorVibrationChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="nozzleVacuumChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | |
| | | </a-row> |
| | | |
| | | <!-- 贴装头/吸嘴 --> |
| | | <a-row :gutter="16" class="mt-4"> |
| | | |
| | | <a-col :span="8"> |
| | | <div id="nozzleVacuumChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="nozzleFlowChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="placementSpeedChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div> |
| | | </a-col> |
| | | |
| | | </a-row> |
| | | |
| | | |
| | |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { defineComponent, reactive, onMounted, onUnmounted } from 'vue'; |
| | | import { defineComponent, reactive, onMounted, onUnmounted, ref, nextTick } from 'vue'; |
| | | import * as echarts from 'echarts'; |
| | | import { |
| | | LineChartOutlined, |
| | |
| | | AppstoreOutlined, |
| | | FieldTimeOutlined |
| | | } from '@ant-design/icons-vue'; |
| | | import img from '#/assets/images/JUKI.png' |
| | | import { getDeviceDataSmt, updateDeviceData, initDeviceData } from '#/api/eims/equ/deviceData' |
| | | |
| | | export default defineComponent({ |
| | | name: 'SmtMachineDetail', |
| | |
| | | deviceType: '贴片机', |
| | | deviceId: 'GPC2012A101', |
| | | installDate: '2012-06-08', |
| | | serviceLife: 8, |
| | | serviceLife: 15, |
| | | status: '运行中', |
| | | statusColor: '#52c41a', |
| | | imageUrl: '/src/assets/images/JUKI.png' // 添加设备图片路径 |
| | | imageUrl: img // 添加设备图片路径 |
| | | }); |
| | | |
| | | const healthData = reactive({ |
| | |
| | | healthColor: '#52c41a', |
| | | predictedLife: 635, |
| | | riskLevel: '低风险', |
| | | riskColor: '#52c41a', |
| | | riskColor: '#1a7ac4', |
| | | xAxisTravel: 300.179, |
| | | yAxisTravel: 233.392, |
| | | tapeJamCount: 15, |
| | | tapeJamCount: 6, |
| | | materialJamCount: 15, |
| | | panelCount: 2480, |
| | | downtime: 4.5 |
| | | downtime: 4.5, |
| | | // // 用于累加的数据 |
| | | // accumulatedXAxisTravel: 300.179, |
| | | // accumulatedYAxisTravel: 233.392, |
| | | // accumulatedTapeJamCount: 6, |
| | | // accumulatedMaterialJamCount: 15, |
| | | // accumulatedPanelCount: 2480, |
| | | // accumulatedDowntime: 4.5 |
| | | }); |
| | | |
| | | const maintenanceColumns = [ |
| | |
| | | { |
| | | key: '1', |
| | | |
| | | type: '贴装头维护', |
| | | content: '吸嘴真空压力校准', |
| | | suggestedTime: '2024-04-05', |
| | | type: '1号贴装系统维护', |
| | | content: '吸嘴真空压力偏低', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '中等' |
| | | }, |
| | | { |
| | | key: '6', |
| | | |
| | | type: '4号贴装系统维护', |
| | | content: 'T轴马达寿命告急', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '中等' |
| | | }, |
| | | { |
| | | key: '2', |
| | | type: '运动系统保养', |
| | | content: 'X/Y轴伺服电机润滑检查', |
| | | suggestedTime: '2024-03-20', |
| | | suggestedTime: '2025-06-20', |
| | | urgency: '低' |
| | | }, |
| | | { |
| | | key: '3', |
| | | type: '供料系统检查', |
| | | content: '飞达卡带/卡料次数清零', |
| | | suggestedTime: '2024-03-10', |
| | | suggestedTime: '2025-06-10', |
| | | urgency: '低' |
| | | } |
| | | }, |
| | | { |
| | | key: '4', |
| | | |
| | | type: '2号贴装系统维护', |
| | | content: '真空压力不稳定', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '低' |
| | | }, |
| | | { |
| | | key: '5', |
| | | type: '3号贴装系统维护', |
| | | content: 'Z轴马达电流异常', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '低' |
| | | }, |
| | | |
| | | { |
| | | key: '7', |
| | | |
| | | type: '5号贴装头维护', |
| | | content: '真空压力不稳定', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '低' |
| | | }, |
| | | { |
| | | key: '8', |
| | | |
| | | type: '6号贴装头维护', |
| | | content: '真空压力不稳定', |
| | | suggestedTime: '2025-07-05', |
| | | urgency: '低' |
| | | }, |
| | | ]); |
| | | |
| | | const getUrgencyColor = (urgency: string) => { |
| | |
| | | } |
| | | }; |
| | | |
| | | |
| | | const fetchDeviceData = async () => { |
| | | try { |
| | | const res = await getDeviceDataSmt(); |
| | | 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: '部件名称', |
| | |
| | | key: 'name' |
| | | }, |
| | | { |
| | | title: '当前寿命', |
| | | title: '理论寿命', |
| | | dataIndex: 'currentLife', |
| | | key: 'currentLife', |
| | | customRender: ({ text }: { text: number }) => `${text}小时` |
| | | // customRender: ({ text }: { text: number }) => `${text}小时` |
| | | }, |
| | | { |
| | | title: '预测剩余寿命', |
| | | dataIndex: 'remainingLife', |
| | | key: 'remainingLife', |
| | | customRender: ({ text }: { text: number }) => `${text}小时` |
| | | // customRender: ({ text }: { text: number }) => `${text}小时` |
| | | }, |
| | | { |
| | | title: '状态', |
| | |
| | | const sparePartData = reactive([ |
| | | { |
| | | key: '1', |
| | | name: 'X轴伺服电机', |
| | | currentLife: 5000, |
| | | remainingLife: 1500, |
| | | name: '1号贴装系统T轴伺服电机', |
| | | currentLife: '15000小时', |
| | | remainingLife: '1451小时', |
| | | status: '预警' |
| | | }, |
| | | { |
| | | key: '5', |
| | | name: '1号贴装系统Z轴伺服电机', |
| | | currentLife: '15000小时', |
| | | remainingLife: '7521小时', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '9', |
| | | name: '1号贴装系统真空电磁阀', |
| | | currentLife: '10000小时', |
| | | remainingLife: '2154小时', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '2', |
| | | name: '吸嘴', |
| | | currentLife: 1000, |
| | | remainingLife: 50, |
| | | status: '预警' |
| | | name: '1号贴装头', |
| | | currentLife: '1000000次', |
| | | remainingLife: '425542次', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '6', |
| | | name: '2号贴装系统T轴伺服电机', |
| | | currentLife: '15000小时', |
| | | remainingLife: '7540小时', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '7', |
| | | name: '2号贴装系统Z轴伺服电机', |
| | | currentLife: '15000小时', |
| | | remainingLife: '7521小时', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '9', |
| | | name: '2号贴装系统真空电磁阀', |
| | | currentLife: '10000小时', |
| | | remainingLife: '2154小时', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '8', |
| | | name: '2号贴装头', |
| | | currentLife: '1000000次', |
| | | remainingLife: '751251次', |
| | | status: '良好' |
| | | }, |
| | | { |
| | | key: '3', |
| | | name: '飞达', |
| | | currentLife: 8000, |
| | | remainingLife: 2000, |
| | | currentLife: '96个月', |
| | | remainingLife: '43个月', |
| | | status: '良好' |
| | | } |
| | | }, |
| | | |
| | | ]); |
| | | |
| | | const historyData = reactive([ |
| | | { |
| | | id: '1', |
| | | date: '2024-02-15', |
| | | date: '2025-07-22', |
| | | type: '定期保养', |
| | | description: '完成月度保养,丝杆导轨注油,抛料清理', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '1', |
| | | date: '2025-06-18', |
| | | type: '定期保养', |
| | | description: '完成月度保养,易损件更换', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '1', |
| | | date: '2025-05-15', |
| | | type: '定期保养', |
| | | description: '完成季度保养,检查运动系统润滑', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '1', |
| | | date: '2025-04-20', |
| | | type: '定期保养', |
| | | description: '完成月度保养,防尘过滤网清理,导轨注油', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '1', |
| | | date: '2025-03-16', |
| | | type: '定期保养', |
| | | description: '完成月度保养,真空值校准', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '1', |
| | | date: '2025-02-13', |
| | | type: '定期保养', |
| | | description: '完成月度保养,吸嘴检查更换,抛料清理,相机参数校准', |
| | | color: 'green' |
| | | }, |
| | | { |
| | | id: '2', |
| | | date: '2024-01-20', |
| | | date: '2025-01-20', |
| | | type: '故障维修', |
| | | description: '修复吸嘴堵塞问题', |
| | | color: 'red' |
| | | }, |
| | | { |
| | | id: '3', |
| | | date: '2023-12-01', |
| | | date: '2025-01-15', |
| | | type: '定期保养', |
| | | description: '完成年度保养,校准视觉系统', |
| | | description: '完成年度保养,贴装系统校准,易损件更换,校准视觉系统', |
| | | color: 'green' |
| | | } |
| | | ]); |
| | |
| | | // 实际项目中这里会调用API处理维护建议 |
| | | }; |
| | | |
| | | let healthDataInterval: number | undefined; |
| | | let scrollInterval: number | undefined; |
| | | const maintenanceTable = ref<HTMLElement | null>(null); |
| | | |
| | | 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; // 每次滚动1px |
| | | if (currentScrollTop >= scrollHeight - clientHeight) { |
| | | currentScrollTop = 0; // 滚动到底部后回到顶部 |
| | | } |
| | | tableBody.scrollTop = currentScrollTop; |
| | | }, 50); // 每50毫秒滚动一次 |
| | | }; |
| | | |
| | | // const updateHealthData = () => { |
| | | // healthData.accumulatedXAxisTravel = parseFloat((healthData.accumulatedXAxisTravel + Math.random() * 0.01).toFixed(3)); |
| | | // healthData.accumulatedYAxisTravel = parseFloat((healthData.accumulatedYAxisTravel + Math.random() * 0.01).toFixed(3)); |
| | | // healthData.accumulatedTapeJamCount = healthData.accumulatedTapeJamCount + Math.random() * 0.005; |
| | | // healthData.accumulatedMaterialJamCount = healthData.accumulatedMaterialJamCount + Math.random() * 0.005; |
| | | // healthData.accumulatedPanelCount =healthData.accumulatedPanelCount + Math.random() * 0.1; |
| | | // healthData.accumulatedDowntime = parseFloat((healthData.accumulatedDowntime + Math.random() * 0.01).toFixed(1)); |
| | | |
| | | // // 更新显示的数据 |
| | | // healthData.xAxisTravel = healthData.accumulatedXAxisTravel; |
| | | // healthData.yAxisTravel = healthData.accumulatedYAxisTravel; |
| | | // healthData.tapeJamCount = Math.round(healthData.accumulatedTapeJamCount); |
| | | // healthData.materialJamCount = Math.round(healthData.accumulatedMaterialJamCount); |
| | | // healthData.panelCount = Math.round( healthData.accumulatedPanelCount); |
| | | // healthData.downtime = healthData.accumulatedDowntime; |
| | | // }; |
| | | |
| | | onMounted(() => { |
| | | // 初始更新一次数据 |
| | | // updateHealthData(); |
| | | // 每5秒更新一次设备数据 |
| | | // healthDataInterval = setInterval(updateHealthData, 5000); |
| | | |
| | | nextTick(() => { |
| | | startScroll(); |
| | | }); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (healthDataInterval) { |
| | | clearInterval(healthDataInterval); |
| | | } |
| | | if (scrollInterval) { |
| | | clearInterval(scrollInterval); |
| | | } |
| | | if (deviceDataInterval) { |
| | | clearInterval(deviceDataInterval); |
| | | } |
| | | }); |
| | | |
| | | return { |
| | | deviceInfo, |
| | | healthData, |
| | |
| | | historyData, |
| | | getStatusColor, |
| | | getUrgencyColor, |
| | | handleMaintenance |
| | | handleMaintenance, |
| | | maintenanceTable |
| | | }; |
| | | }, |
| | | components: { |
| | |
| | | time, |
| | | value: newValue |
| | | }); |
| | | if (s.data.length > 60) { |
| | | if (s.data.length > 24) { |
| | | s.data.shift(); |
| | | } |
| | | }); |
| | |
| | | seriesConfig.forEach(s => { |
| | | s.data = []; |
| | | // 生成初始数据点(60个点,5分钟数据) |
| | | for (let i = 0; i < 60; i++) { |
| | | const now = new Date(Date.now() - (60 - i) * 5000); // 生成过去5分钟的数据 |
| | | for (let i = 0; i < 24; i++) { |
| | | const now = new Date(Date.now() - (24 - i) * 5000); // 生成过去5分钟的数据 |
| | | const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`; |
| | | const newValue = (s.baseValue + (Math.random() * 2 - 1) * s.fluctuation).toFixed(2); |
| | | s.data.push({ |
| | |
| | | // initChart('xAxisMotorCurrentChart', 'X轴电机电流', [], 'A', 10, 1); |
| | | // initChart('yAxisMotorCurrentChart', 'Y轴电机电流', [], 'A', 10, 1); |
| | | // initChart('zAxisMotorCurrentChart', 'Z轴电机电流', [], 'A', 8, 0.8); |
| | | initChart('motorTemperatureChart', '电机温度', [ |
| | | { name: '温度', data: [], unit: '°C', baseValue: 45, fluctuation: 2, color: '#5470C6' }, |
| | | initChart('motorTemperatureChart', '产量', [ |
| | | { name: '产量', data: [], unit: 'pcs/h', baseValue: 175, fluctuation: 25, color: '#5470C6' }, |
| | | ]); |
| | | initChart('motorVibrationChart', '电机振动', [ |
| | | { name: '振动', data: [], unit: 'mm/s', baseValue: 5, fluctuation: 0.5, color: '#5470C6' }, |
| | | initChart('motorVibrationChart', '抛料率', [ |
| | | { name: '抛料率', data: [], unit: '%', baseValue: 0.15, fluctuation: 0.01, color: '#5470C6' }, |
| | | ], ); |
| | | initChart('nozzleVacuumChart', '吸嘴真空压力', [ |
| | | { name: '压力', data: [], unit: 'kPa', baseValue: -39, fluctuation: 5, color: '#5470C6' }, |
| | | { name: '压力', data: [], unit: 'kPa', baseValue: -45, fluctuation: 5, color: '#5470C6' }, |
| | | ],); |
| | | // initChart('beltTensionChart', '皮带张力', [], 'N', 50, 5); |
| | | |
| | | // 贴装头/吸嘴监测 |
| | | |
| | | initChart('nozzleFlowChart', '吸嘴流量', [ |
| | | { name: '流量', data: [], unit: 'L/min', baseValue: 5, fluctuation: 0.5, color: '#5470C6' }, |
| | | initChart('nozzleFlowChart', '吸嘴吹气压力', [ |
| | | { name: '压力', data: [], unit: 'kPa', baseValue: 20, fluctuation: 5, color: '#5470C6' }, |
| | | ]); |
| | | initChart('placementSpeedChart', '贴装速度', [ |
| | | { name: '速度', data: [], unit: 'mm/s', baseValue: 100, fluctuation: 10, color: '#5470C6' }, |
| | | { name: '速度', data: [], unit: 'chips/h', baseValue: 9000, fluctuation: 1500, color: '#5470C6' }, |
| | | ]); |
| | | const ambientTemperatureData: any[] = []; |
| | | const ambientHumidityData: any[] = []; |
| | | initChart('ambientTemperatureHumidityChart', '环境温湿度', [ |
| | | { name: '温度', data: ambientTemperatureData, unit: '°C', baseValue: 25, fluctuation: 2, color: '#5470C6' }, |
| | | { name: '温度', data: ambientTemperatureData, unit: '°C', baseValue: 25, fluctuation: 1, color: '#5470C6' }, |
| | | { name: '湿度', data: ambientHumidityData, unit: '%', baseValue: 60, fluctuation: 5, color: '#91cc75' } |
| | | ]); |
| | | // initChart('placementAccuracyChart', '贴装精度', [], 'μm', 50, 5); |