车间能级提升-智能设备管理系统
已添加6个文件
已修改2个文件
2991 ■■■■■ 文件已修改
eims-ui-mobile/src/pages/inspect/insp-record.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/maint/maint-st.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/air-compressor-detail.vue 513 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/cnc-machining-center-detail.vue 505 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/device-detail.vue 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/index.vue 409 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/injection-molding-machine-detail.vue 549 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/smt-machine-detail.vue 509 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
eims-ui-mobile/src/pages/inspect/insp-record.vue
@@ -147,6 +147,7 @@
      <!--      </wd-cell>-->
      <view class="w-full h-[1px] bg-base"></view>
      <wd-input
        v-if="inspSt.status !== '0'"
        label="运行时间"
        label-width="200rpx"
        clearable
@@ -156,6 +157,7 @@
        size="large"
      />
      <wd-input
        v-if="inspSt.status !== '0'"
        label="故障时间"
        label-width="200rpx"
        clearable
@@ -386,6 +388,20 @@
    message.alert('请填写运行次数和故障次数!')
    return false
  }
  // å¦‚果当前时间距上次新时间两小时以内则不允许确认
  console.log('inspSt.updateTime', inspSt.updateTime)
  console.log('new Date().getTime()', new Date().getTime())
  console.log('inspSt.updateTime', new Date(inspSt.updateTime).getTime())
  console.log('new Date().getTime() - new Date(inspSt.updateTime).getTime()', new Date().getTime() - new Date(inspSt.updateTime).getTime())
  console.log("2 * 60 * 60 * 1000", 2 * 60 * 60 * 1000)
  console.log('new Date().getTime() - new Date(inspSt.updateTime).getTime() < 2 * 60 * 60 * 1000', new Date().getTime() - new Date(inspSt.updateTime).getTime() < 2 * 60 * 60 * 1000)
  if (
    new Date().getTime() - new Date(inspSt.updateTime).getTime() < 2 * 60 * 60 * 1000
  ) {
    console.log("new Date().getTime() - new Date(inspSt.updateTime).getTime() < 2 * 60 * 60 * 1000",new Date().getTime() - new Date(inspSt.updateTime).getTime() < 2 * 60 * 60 * 1000)
    message.alert('点检两小时以内不允许确认!')
    return false
  }
  const now = new Date();
  const data: any = Object.assign(
    {},
eims-ui-mobile/src/pages/maint/maint-st.vue
@@ -85,6 +85,17 @@
        </view>
      </wd-card>
    </view>
    <wd-fab
      v-if="status === '1' && isLeader()"
      :draggable="true"
      type="success"
      position="left-bottom"
      :expandable="false"
      inactiveIcon="check"
      @click="handleBatchComplete"
      direction="top"
    />
  </z-paging>
</template>
@@ -92,6 +103,11 @@
import { ref, computed } from 'vue'
import { getMaintStList } from '@/service/maint'
import dayjs from 'dayjs'
import { updateMaintSt } from '@/service/maint'
import { useUserStore } from "@/store";
import { isLeader } from '@/utils/RoleUtils'
import { useToast } from 'wot-design-uni'
const searchValue = ref<string>('')
/**
 * å…¶ä»–页面传过来的数据
@@ -257,6 +273,41 @@
  paging.value.reload()
}
const userStore = useUserStore()
const toast = useToast()
async function handleBatchComplete() {
  console.log('handleBatchComplete', dataList.value.length)
  if (!isLeader()) {
    toast.info('无权限操作')
    return
  }
  // å¦‚果待确认工单列表为空,则提示用户无待确认工单
  if (dataList.value.length < 1) {
    toast.info('无待确认工单')
    return
  }
  const now = new Date()
  const verifyTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
  // å‡è®¾ dataList æ˜¯å½“前待确认的工单列表
  const promises = dataList.value.map(item => {
    return updateMaintSt({
      id: item.id,
      status: '2',
      verifyUser: userStore?.userInfo?.userId,
      verifyTime
    })
  })
  try {
    await Promise.all(promises)
    toast.success('一键确认完成')
    reloadData() // åˆ·æ–°åˆ—表
  } catch (e) {
    toast.error('部分工单确认失败,请重试')
  }
}
onLoad((options) => {
  Object.assign(option, options)
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/air-compressor-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,513 @@
<template>
  <div class="device-detail-container">
    <a-page-header
      title="空压机预测性维护详情"
      :sub-title="deviceInfo.deviceName"
      @back="() => $router.go(-1)"
    />
    <a-row :gutter="16" class="mt-4">
      <!-- è®¾å¤‡åŸºæœ¬ä¿¡æ¯ -->
      <a-col :span="8">
        <a-card title="设备基本信息" class="mb-4" :style="{ height: '440px' }">
          <a-descriptions bordered :column="1">
            <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-card>
      </a-col>
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€ä¸Žç»´æŠ¤å»ºè®® -->
      <a-col :span="16">
        <a-card title="设备健康状态与维护建议" class="mb-4" :style="{ height: '440px' }">
          <a-row :gutter="16">
            <a-col :span="8">
              <a-statistic
                title="整体健康度"
                :value="healthData.overallHealth"
                :precision="0"
                suffix="%"
                :value-style="{ color: healthData.healthColor }"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="预测剩余寿命"
                :value="healthData.predictedLife"
                suffix="天"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="故障风险等级"
                :value="healthData.riskLevel"
                :value-style="{ color: healthData.riskColor }"
              />
            </a-col>
          </a-row>
          <div class="mt-4">
            <a-progress
              :percent="healthData.overallHealth"
              :stroke-color="healthData.healthColor"
              :show-info="false"
            />
          </div>
          <a-divider orientation="left">预测性维护建议</a-divider>
          <a-table
            :columns="maintenanceColumns"
            :data-source="maintenanceData"
            :pagination="false"
            size="small"
          >
            <template #urgency="{ text }">
              <a-tag :color="getUrgencyColor(text)">{{ text }}</a-tag>
            </template>
            <template #action="{ record }">
              <a-button type="link" size="small" @click="handleMaintenance(record)">处理</a-button>
            </template>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
    <!-- å®žæ—¶æ•°æ®è¶‹åŠ¿å›¾ -->
    <a-card title="实时数据趋势图" class="mb-4">
      <a-row :gutter="16">
        <a-col :span="8">
          <div id="exhaustPressureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="exhaustTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="hostVibrationChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="mainBearingTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="mainMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="oilTemperatureChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="oilPressureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="oilLevelChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="inletPressureChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="inletTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="airFlowChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="dryerDewPointChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="coolingWaterTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="coolingWaterFlowChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
    </a-card>
    <a-row :gutter="16">
      <!-- å¤‡ä»¶å¯¿å‘½é¢„测 -->
      <a-col :span="12">
        <a-card title="备件寿命预测" class="mb-4">
          <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 title="历史维护记录" class="mb-4">
          <a-timeline>
            <a-timeline-item v-for="item in historyData" :key="item.id" :color="item.color">
              {{ item.date }} - {{ item.type }}: {{ item.description }}
            </a-timeline-item>
          </a-timeline>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
  name: 'AirCompressorDetail',
  setup() {
    // æ¨¡æ‹Ÿæ•°æ®
    const deviceInfo = reactive({
      deviceName: '空压机 #AC-001',
      deviceType: '空压机',
      deviceId: 'AC-2024-001',
      installDate: '2023-05-10',
      serviceLife: 10,
      status: '运行中',
      statusColor: '#52c41a'
    });
    const healthData = reactive({
      overallHealth: 92,
      healthColor: '#52c41a',
      predictedLife: 3000,
      riskLevel: '低风险',
      riskColor: '#52c41a'
    });
    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: '2024-07-01',
        urgency: '中等'
      },
      {
        key: '2',
        type: '润滑系统维护',
        content: '更换润滑油',
        suggestedTime: '2024-09-01',
        urgency: '高'
      },
      {
        key: '3',
        type: '空气系统维护',
        content: '更换空滤芯',
        suggestedTime: '2024-08-15',
        urgency: '低'
      }
    ]);
    const getUrgencyColor = (urgency: string) => {
      switch (urgency) {
        case '高':
          return 'red';
        case '中等':
          return 'orange';
        case '低':
          return 'green';
        default:
          return 'default';
      }
    };
    const sparePartColumns = [
      {
        title: '备件名称',
        dataIndex: 'name',
        key: 'name'
      },
      {
        title: '当前寿命',
        dataIndex: 'currentLife',
        key: 'currentLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '预测剩余寿命',
        dataIndex: 'remainingLife',
        key: 'remainingLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        slots: { customRender: 'status' }
      }
    ];
    const sparePartData = reactive([
      {
        key: '1',
        name: '空滤芯',
        currentLife: 1000,
        remainingLife: 200,
        status: '预警'
      },
      {
        key: '2',
        name: '油滤芯',
        currentLife: 1500,
        remainingLife: 500,
        status: '良好'
      },
      {
        key: '3',
        name: '油气分离器',
        currentLife: 2000,
        remainingLife: 300,
        status: '预警'
      }
    ]);
    const historyData = reactive([
      {
        id: '1',
        date: '2024-06-10',
        type: '例行保养',
        description: '更换空滤芯、油滤芯',
        color: 'green'
      },
      {
        id: '2',
        date: '2024-03-05',
        type: '故障维修',
        description: '主机轴承润滑脂补充',
        color: 'red'
      },
      {
        id: '3',
        date: '2023-12-01',
        type: '年度保养',
        description: '全面检查设备运行状态',
        color: 'green'
      }
    ]);
    const getStatusColor = (status: string) => {
      switch (status) {
        case '良好':
          return '#52c41a';
        case '预警':
          return '#faad14';
        case '危险':
          return '#f5222d';
        default:
          return '#d9d9d9';
      }
    };
    const handleMaintenance = (record: any) => {
      console.log('处理维护建议:', record);
      // å®žé™…项目中这里会调用API处理维护建议
    };
    return {
      deviceInfo,
      healthData,
      maintenanceColumns,
      maintenanceData,
      sparePartColumns,
      sparePartData,
      historyData,
      getStatusColor,
      getUrgencyColor,
      handleMaintenance
    };
  },
  mounted() {
    // åˆå§‹åŒ–图表
    const initChart = (chartId: string, title: string, chartData: any[], unit: string, baseValue: number, fluctuation: number) => {
      const chart = echarts.init(document.getElementById(chartId));
      // é¢„先生成60个数据点
      for (let i = 0; i < 60; i++) {
        const now = new Date(Date.now() - (60 - 1 - i) * 5000); // å€’推时间
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = (baseValue + (Math.random() * fluctuation * 2 - fluctuation)).toFixed(2);
        chartData.push({
          time,
          value: newValue
        });
      }
      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 > 60) {
          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 => 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();
      });
      onUnmounted(() => {
        clearInterval(intervalId);
      });
    };
    initChart('exhaustPressureChart', '排气压力', [], 'bar', 7.5, 0.2);
    initChart('exhaustTemperatureChart', '排气温度', [], '°C', 85, 2);
    initChart('hostVibrationChart', '主机振动', [], 'mm/s', 3.5, 0.5);
    initChart('mainBearingTemperatureChart', '主轴承温度', [], '°C', 70, 3);
    initChart('mainMotorCurrentChart', '主电机电流', [], 'A', 120, 5);
    initChart('oilTemperatureChart', '油温', [], '°C', 60, 2);
    initChart('oilPressureChart', '油压', [], 'bar', 4.0, 0.1);
    initChart('oilLevelChart', '油位', [], '%', 80, 5);
    initChart('inletPressureChart', '进气压力', [], 'bar', 1.0, 0.05);
    initChart('inletTemperatureChart', '进气温度', [], '°C', 25, 2);
    initChart('airFlowChart', '空气流量', [], 'm³/min', 15, 1);
    initChart('dryerDewPointChart', '干燥器露点', [], '°C', 3, 1);
    initChart('coolingWaterTemperatureChart', '冷却水温度', [], '°C', 30, 2);
    initChart('coolingWaterFlowChart', '冷却水流量', [], 'L/min', 10, 0.5);
    initChart('ambientTemperatureHumidityChart', '环境温湿度', [], '°C', 28, 2);
  }
});
</script>
<style scoped>
.device-detail-container {
  padding: 16px;
  background: #f0f2f5;
}
.mt-4 {
  margin-top: 16px;
}
.mb-4 {
  margin-bottom: 16px;
}
</style>
<style scoped>
.air-compressor-detail {
  padding: 16px;
  background-color: #f0f2f5;
}
.header-card {
  margin-bottom: 16px;
}
</style>
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/cnc-machining-center-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,505 @@
<template>
  <div class="device-detail-container">
    <a-page-header
      title="CNC加工中心预测性维护详情"
      :sub-title="deviceInfo.deviceName"
      @back="() => $router.go(-1)"
    />
    <a-row :gutter="16" class="mt-4">
      <!-- è®¾å¤‡åŸºæœ¬ä¿¡æ¯ -->
      <a-col :span="8">
        <a-card title="设备基本信息" class="mb-4" :style="{ height: '440px' }">
          <a-descriptions bordered :column="1">
            <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-card>
      </a-col>
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€ä¸Žç»´æŠ¤å»ºè®® -->
      <a-col :span="16">
        <a-card title="设备健康状态与维护建议" class="mb-4" :style="{ height: '440px' }">
          <a-row :gutter="16">
            <a-col :span="8">
              <a-statistic
                title="整体健康度"
                :value="healthData.overallHealth"
                :precision="0"
                suffix="%"
                :value-style="{ color: healthData.healthColor }"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="预测剩余寿命"
                :value="healthData.predictedLife"
                suffix="天"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="故障风险等级"
                :value="healthData.riskLevel"
                :value-style="{ color: healthData.riskColor }"
              />
            </a-col>
          </a-row>
          <div class="mt-4">
            <a-progress
              :percent="healthData.overallHealth"
              :stroke-color="healthData.healthColor"
              :show-info="false"
            />
          </div>
          <a-divider orientation="left">预测性维护建议</a-divider>
          <a-table
            :columns="maintenanceColumns"
            :data-source="maintenanceData"
            :pagination="false"
            size="small"
          >
            <template #urgency="{ text }">
              <a-tag :color="getUrgencyColor(text)">{{ text }}</a-tag>
            </template>
            <template #action="{ record }">
              <a-button type="link" size="small" @click="handleMaintenance(record)">处理</a-button>
            </template>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
    <!-- å®žæ—¶æ•°æ®è¶‹åŠ¿å›¾ -->
    <a-card title="实时数据趋势图" class="mb-4">
      <a-row :gutter="16">
        <a-col :span="8">
          <div id="spindleVibrationChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="spindleTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="spindleCurrentChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="spindleSpeedChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="servoMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="servoMotorTemperatureChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="axisMotionSmoothnessChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="guideRailTemperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="guideRailResistanceNoiseChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="hydraulicPressureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="hydraulicFlowChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="hydraulicOilTemperatureChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="airSourcePressureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="coolantFlowChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="coolantTemperatureChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
    </a-card>
    <a-row :gutter="16">
      <!-- å¤‡ä»¶å¯¿å‘½é¢„测 -->
      <a-col :span="12">
        <a-card title="备件寿命预测" class="mb-4">
          <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 title="历史维护记录" class="mb-4">
          <a-timeline>
            <a-timeline-item v-for="item in historyData" :key="item.id" :color="item.color">
              {{ item.date }} - {{ item.type }}: {{ item.description }}
            </a-timeline-item>
          </a-timeline>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
  name: 'CNCMachiningCenterDetail',
  setup() {
    // æ¨¡æ‹Ÿæ•°æ®
    const deviceInfo = reactive({
      deviceName: 'CNC加工中心 #CNC-001',
      deviceType: 'CNC加工中心',
      deviceId: 'CNC-2024-001',
      installDate: '2023-08-20',
      serviceLife: 15,
      status: '运行中',
      statusColor: '#52c41a'
    });
    const healthData = reactive({
      overallHealth: 95,
      healthColor: '#52c41a',
      predictedLife: 4500,
      riskLevel: '低风险',
      riskColor: '#52c41a'
    });
    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: '2024-07-15',
        urgency: '中等'
      },
      {
        key: '2',
        type: '伺服系统维护',
        content: '检查X轴伺服电机',
        suggestedTime: '2024-09-01',
        urgency: '低'
      },
      {
        key: '3',
        type: '导轨维护',
        content: '补充导轨润滑油',
        suggestedTime: '2024-08-01',
        urgency: '高'
      }
    ]);
    const getUrgencyColor = (urgency: string) => {
      switch (urgency) {
        case '高':
          return 'red';
        case '中等':
          return 'orange';
        case '低':
          return 'green';
        default:
          return 'default';
      }
    };
    const sparePartColumns = [
      {
        title: '备件名称',
        dataIndex: 'name',
        key: 'name'
      },
      {
        title: '当前寿命',
        dataIndex: 'currentLife',
        key: 'currentLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '预测剩余寿命',
        dataIndex: 'remainingLife',
        key: 'remainingLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        slots: { customRender: 'status' }
      }
    ];
    const sparePartData = reactive([
      {
        key: '1',
        name: '主轴轴承',
        currentLife: 8000,
        remainingLife: 500,
        status: '预警'
      },
      {
        key: '2',
        name: 'X轴丝杠',
        currentLife: 12000,
        remainingLife: 2000,
        status: '良好'
      },
      {
        key: '3',
        name: '液压油',
        currentLife: 3000,
        remainingLife: 200,
        status: '预警'
      }
    ]);
    const historyData = reactive([
      {
        id: '1',
        date: '2024-05-20',
        type: '例行保养',
        description: '主轴润滑系统检查',
        color: 'green'
      },
      {
        id: '2',
        date: '2024-02-10',
        type: '故障维修',
        description: 'Y轴伺服电机报警处理',
        color: 'red'
      },
      {
        id: '3',
        date: '2023-11-01',
        type: '年度保养',
        description: '全面检查设备运行状态',
        color: 'green'
      }
    ]);
    const getStatusColor = (status: string) => {
      switch (status) {
        case '良好':
          return '#52c41a';
        case '预警':
          return '#faad14';
        case '危险':
          return '#f5222d';
        default:
          return '#d9d9d9';
      }
    };
    const handleMaintenance = (record: any) => {
      console.log('处理维护建议:', record);
      // å®žé™…项目中这里会调用API处理维护建议
    };
    return {
      deviceInfo,
      healthData,
      maintenanceColumns,
      maintenanceData,
      sparePartColumns,
      sparePartData,
      historyData,
      getStatusColor,
      getUrgencyColor,
      handleMaintenance
    };
  },
  mounted() {
    // åˆå§‹åŒ–图表
    const initChart = (chartId: string, title: string, chartData: any[], unit: string, baseValue: number, fluctuation: number) => {
      const chart = echarts.init(document.getElementById(chartId));
      const updateChart = () => {
        // ç”Ÿæˆæ–°çš„æ•°æ®ç‚¹
        const now = new Date();
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = (baseValue + (Math.random() * 2 - 1) * fluctuation).toFixed(2);
        // æ·»åŠ æ–°æ•°æ®ç‚¹ï¼Œæœ€å¤šä¿ç•™60个点(5分钟数据)
        chartData.push({
          time,
          value: newValue
        });
        if (chartData.length > 60) {
          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 => 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);
      };
      // ç”Ÿæˆåˆå§‹æ•°æ®ç‚¹ï¼ˆ60个点,5分钟数据)
      for (let i = 0; i < 60; i++) {
        const now = new Date(Date.now() - (60 - i) * 5000); // ç”Ÿæˆè¿‡åŽ»5分钟的数据
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = (baseValue + (Math.random() * 2 - 1) * fluctuation).toFixed(2);
        chartData.push({
          time,
          value: newValue
        });
      }
      // åˆå§‹æ¸²æŸ“
      updateChart();
      // æ¯5秒更新一次数据
      const intervalId = setInterval(updateChart, 5000);
      window.addEventListener('resize', () => {
        chart.resize();
      });
            onUnmounted(() => {
        clearInterval(intervalId);
      });
    };
    initChart('spindleVibrationChart', '主轴振动', [], 'mm/s', 3.0, 0.5);
    initChart('spindleTemperatureChart', '主轴温度', [], '°C', 50, 3);
    initChart('spindleCurrentChart', '主轴电流', [], 'A', 150, 10);
    initChart('spindleSpeedChart', '主轴转速', [], 'RPM', 8000, 100);
    initChart('servoMotorCurrentChart', '伺服电机电流', [], 'A', 50, 5);
    initChart('servoMotorTemperatureChart', '伺服电机温度', [], '°C', 45, 3);
    initChart('axisMotionSmoothnessChart', '轴运动平稳性', [], '', 0.8, 0.1);
    initChart('guideRailTemperatureChart', '导轨温度', [], '°C', 35, 2);
    initChart('guideRailResistanceNoiseChart', '导轨运动阻力/噪音', [], 'dB', 10, 2);
    initChart('hydraulicPressureChart', '液压系统压力', [], 'bar', 150, 5);
    initChart('hydraulicFlowChart', '液压系统流量', [], 'L/min', 20, 2);
    initChart('hydraulicOilTemperatureChart', '液压油温度', [], '°C', 55, 3);
    initChart('airSourcePressureChart', '气源压力', [], 'bar', 0.7, 0.05);
    initChart('coolantFlowChart', '冷却液流量', [], 'L/min', 30, 3);
    initChart('coolantTemperatureChart', '冷却液温度', [], '°C', 28, 2);
  }
});
</script>
<style scoped>
.device-detail-container {
  padding: 16px;
  background: #f0f2f5;
}
.mt-4 {
  margin-top: 16px;
}
.mb-4 {
  margin-bottom: 16px;
}
</style>
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/device-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,439 @@
<template>
  <div class="device-detail-container">
    <a-page-header
      title="设备预测性维护详情"
      :sub-title="deviceInfo.deviceName"
      @back="() => $router.go(-1)"
    />
    <a-row :gutter="16" class="mt-4">
      <!-- è®¾å¤‡åŸºæœ¬ä¿¡æ¯ -->
      <a-col :span="8">
        <a-card title="设备基本信息" class="mb-4" :style="{ height: '440px' }">
          <a-descriptions bordered :column="1">
            <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-card>
      </a-col>
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€ä¸Žç»´æŠ¤å»ºè®® -->
      <a-col :span="16">
        <a-card title="设备健康状态与维护建议" class="mb-4" :style="{ height: '440px' }">
          <a-row :gutter="16">
            <a-col :span="8">
              <a-statistic
                title="整体健康度"
                :value="healthData.overallHealth"
                :precision="0"
                suffix="%"
                :value-style="{ color: healthData.healthColor }"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="预测剩余寿命"
                :value="healthData.predictedLife"
                suffix="天"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="故障风险等级"
                :value="healthData.riskLevel"
                :value-style="{ color: healthData.riskColor }"
              />
            </a-col>
          </a-row>
          <div class="mt-4">
            <a-progress
              :percent="healthData.overallHealth"
              :stroke-color="healthData.healthColor"
              :show-info="false"
            />
          </div>
          <a-divider orientation="left">预测性维护建议</a-divider>
          <a-table
            :columns="maintenanceColumns"
            :data-source="maintenanceData"
            :pagination="false"
            size="small"
          >
            <template #urgency="{ text }">
              <a-tag :color="getUrgencyColor(text)">{{ text }}</a-tag>
            </template>
            <template #action="{ record }">
              <a-button type="link" size="small" @click="handleMaintenance(record)">处理</a-button>
            </template>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
    <!-- å®žæ—¶æ•°æ®è¶‹åŠ¿å›¾ -->
    <a-card title="实时数据趋势图" class="mb-4">
      <a-row :gutter="16">
        <a-col :span="8">
          <div id="temperatureChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="vibrationChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="currentChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
    </a-card>
    <a-row :gutter="16">
      <!-- å¤‡ä»¶å¯¿å‘½é¢„测 -->
      <a-col :span="12">
        <a-card title="备件寿命预测" class="mb-4">
          <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 title="历史维护记录" class="mb-4">
          <a-timeline>
            <a-timeline-item v-for="item in historyData" :key="item.id" :color="item.color">
              {{ item.date }} - {{ item.type }}: {{ item.description }}
            </a-timeline-item>
          </a-timeline>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
  name: 'DeviceDetail',
  setup() {
    // æ¨¡æ‹Ÿæ•°æ®
    const deviceInfo = reactive({
      deviceName: 'CNC加工中心',
      deviceType: '数控机床',
      deviceId: 'CNC-2023-001',
      installDate: '2023-01-15',
      serviceLife: 10,
      status: '运行中',
      statusColor: '#52c41a'
    });
    const healthData = reactive({
      overallHealth: 85,
      healthColor: '#52c41a',
      predictedLife: 1825,
      riskLevel: '中风险',
      riskColor: '#faad14'
    });
    const maintenanceColumns = [
      {
        title: '维护类型',
        dataIndex: 'type',
        key: 'type'
      },
      {
        title: '维护内容',
        dataIndex: 'content',
        key: 'content'
      },
      {
        title: '建议时间',
        dataIndex: 'suggestedTime',
        key: 'suggestedTime'
      },
      {
        title: '紧急程度',
        dataIndex: 'urgency',
        key: 'urgency'
      },
      {
        title: '操作',
        key: 'action',
        slots: { customRender: 'action' }
      },
      {
        title: '紧急程度',
        dataIndex: 'urgency',
        key: 'urgency',
        slots: { customRender: 'urgency' }
      },
      {
        title: '操作',
        key: 'action',
        slots: { customRender: 'action' }
      }
    ];
    const maintenanceData = reactive([
      {
        key: '1',
        type: '润滑保养',
        content: '主轴润滑系统需要更换润滑油',
        suggestedTime: '2024-03-15',
        urgency: '中等'
      },
      {
        key: '2',
        type: '校准检查',
        content: 'X轴导轨需要重新校准',
        suggestedTime: '2024-04-01',
        urgency: '高'
      }
    ]);
    const getUrgencyColor = (urgency: string) => {
      switch (urgency) {
        case '高':
          return 'red';
        case '中等':
          return 'orange';
        case '低':
          return 'green';
        default:
          return 'default';
      }
    };
    const sparePartColumns = [
      {
        title: '备件名称',
        dataIndex: 'name',
        key: 'name'
      },
      {
        title: '当前寿命',
        dataIndex: 'currentLife',
        key: 'currentLife',
        customRender: ({ text }: { text: number }) => `${text}天`
      },
      {
        title: '预测剩余寿命',
        dataIndex: 'remainingLife',
        key: 'remainingLife',
        customRender: ({ text }: { text: number }) => `${text}天`
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        slots: { customRender: 'status' }
      }
    ];
    const sparePartData = reactive([
      {
        key: '1',
        name: '主轴轴承',
        currentLife: 365,
        remainingLife: 120,
        status: '良好'
      },
      {
        key: '2',
        name: 'X轴导轨',
        currentLife: 730,
        remainingLife: 30,
        status: '预警'
      },
      {
        key: '3',
        name: '刀具夹头',
        currentLife: 180,
        remainingLife: 90,
        status: '良好'
      }
    ]);
    const historyData = reactive([
      {
        id: '1',
        date: '2024-01-10',
        type: '定期保养',
        description: '完成季度保养,更换润滑油和过滤器',
        color: 'green'
      },
      {
        id: '2',
        date: '2023-10-15',
        type: '故障维修',
        description: '修复主轴电机过热问题',
        color: 'red'
      },
      {
        id: '3',
        date: '2023-07-20',
        type: '定期保养',
        description: '完成半年保养,校准各轴精度',
        color: 'green'
      }
    ]);
    const getStatusColor = (status: string) => {
      switch (status) {
        case '良好':
          return '#52c41a';
        case '预警':
          return '#faad14';
        case '危险':
          return '#f5222d';
        default:
          return '#d9d9d9';
      }
    };
    const handleMaintenance = (record: any) => {
      console.log('处理维护建议:', record);
      // å®žé™…项目中这里会调用API处理维护建议
    };
    return {
      deviceInfo,
      healthData,
      maintenanceColumns,
      maintenanceData,
      sparePartColumns,
      sparePartData,
      historyData,
      getStatusColor,
      getUrgencyColor,
      handleMaintenance
    };
  },
  mounted() {
    // åˆå§‹åŒ–图表
    const initChart = (chartId: string, title: string, chartData: any[], unit: string, baseValue: number, fluctuation: number) => {
      const chart = echarts.init(document.getElementById(chartId));
      // é¢„生成60个数据点
      for (let i = 0; i < 60; i++) {
        const now = new Date(Date.now() - (60 - 1 - i) * 5000); // 5秒间隔
        const value = +(Math.random() * fluctuation + baseValue - fluctuation / 2).toFixed(2);
        chartData.push({
          time: `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`,
          value: value
        });
      }
      const updateChart = () => {
        // ç”Ÿæˆæ–°çš„æ•°æ®ç‚¹
        const now = new Date();
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = +(Math.random() * fluctuation + baseValue - fluctuation / 2).toFixed(2);
        // æ·»åŠ æ–°æ•°æ®ç‚¹ï¼Œæœ€å¤šä¿ç•™60个点(5分钟数据)
        chartData.push({
          time,
          value: newValue
        });
        if (chartData.length > 60) {
          chartData.shift();
        }
        const option = {
          title: {
            text: title,
            left: 'center'
          },
          tooltip: {
            trigger: 'axis',
            formatter: (params: any) => {
              return `${params[0].axisValueLabel}<br/>${params[0].marker} ${params[0].seriesName}: ${params[0].data}${unit}`;
            }
          },
          xAxis: {
            type: 'category',
            data: chartData.map(item => item.time)
          },
          yAxis: {
            type: 'value',
            axisLabel: {
              formatter: (value: number) => {
                return `${value}${unit}`;
              }
            }
          },
          series: [
            {
              data: chartData.map(item => parseFloat(item.value)),
              type: 'line',
              smooth: true,
              lineStyle: {
                width: 2,
                color: chartId === 'temperatureChart' ? '#5470C6' : chartId === 'vibrationChart' ? '#91CC75' : '#FAC858' // æ ¹æ®å›¾è¡¨ID设置颜色
              },
              areaStyle: {
                opacity: 0.8,
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                  {
                    offset: 0,
                    color: chartId === 'temperatureChart' ? 'rgba(84,112,198,0.3)' : chartId === 'vibrationChart' ? 'rgba(145,204,117,0.3)' : 'rgba(250,200,88,0.3)'
                  },
                  {
                    offset: 1,
                    color: chartId === 'temperatureChart' ? 'rgba(84,112,198,0)' : chartId === 'vibrationChart' ? 'rgba(145,204,117,0)' : 'rgba(250,200,88,0)'
                  }
                ])
              },
            }
          ]
        };
        chart.setOption(option);
      };
      // åˆå§‹æ¸²æŸ“
      updateChart();
      // æ¯5秒更新一次数据
      const intervalId = setInterval(updateChart, 5000);
      window.addEventListener('resize', () => {
        chart.resize();
      });
            onUnmounted(() => {
        clearInterval(intervalId);
      });
    };
    const temperatureData: any[] = reactive([]);
    const vibrationData: any[] = reactive([]);
    const currentData: any[] = reactive([]);
    initChart('temperatureChart', '温度趋势', temperatureData, '°C', 60, 5);
    initChart('vibrationChart', '振动趋势', vibrationData, 'mm/s', 10, 2);
    initChart('currentChart', '电流趋势', currentData, 'A', 50, 10);
  }
});
</script>
<style scoped>
.device-detail-container {
  padding: 16px;
  background: #f0f2f5;
}
</style>
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,409 @@
<template>
  <Page :auto-content-height="true" :padding="false" class="p-0" content-class="flex flex-col w-full gap-4">
    <div class="p-5">
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€å¯è§†åŒ– -->
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
        <Card title="设备健康度分布" >
          <!-- è¿™é‡Œå°†æ”¾ç½®å¥åº·åº¦åˆ†å¸ƒå›¾è¡¨ -->
          <div ref="chartRef" class="h-64 w-full"></div>
        </Card>
        <Card title="关键指标" >
          <div class="grid grid-cols-2 gap-2">
            <div class="p-2 bg-blue-50 rounded">
              <p class="text-sm text-gray-600">健康设备</p>
              <p class="text-xl font-bold">85%</p>
            </div>
            <div class="p-2 bg-yellow-50 rounded">
              <p class="text-sm text-gray-600">预警设备</p>
              <p class="text-xl font-bold">12%</p>
            </div>
            <div class="p-2 bg-red-50 rounded">
              <p class="text-sm text-gray-600">故障设备</p>
              <p class="text-xl font-bold">3%</p>
            </div>
            <div class="p-2 bg-green-50 rounded">
              <p class="text-sm text-gray-600">维护完成</p>
              <p class="text-xl font-bold">92%</p>
            </div>
            <div class="p-2 bg-blue-50 rounded">
              <p class="text-sm text-gray-600">待处理工单</p>
              <p class="text-xl font-bold">{{ healthData.pendingWorkOrders }}</p>
            </div>
            <div class="p-2 bg-yellow-50 rounded">
              <p class="text-sm text-gray-600">备件库存预警</p>
              <p class="text-xl font-bold">{{ healthData.sparePartWarnings }}</p>
            </div>
          </div>
        </Card>
      </div>
      <!-- æ•…障预测与预警 -->
      <Card title="故障预测与预警" class="mb-6">
        <div class="grid grid-cols-1 gap-4">
            <!-- è¿™é‡Œå°†æ”¾ç½®é¢„警表格 -->
            <a-table :columns="warningColumns" :data-source="warningData" :pagination="false" class="w-full">
              <template #bodyCell="{ column, record }">
                <template v-if="column.key === 'status'">
                  <a-tag :color="getStatusColor(record.status)">
                    {{ record.status }}
                  </a-tag>
                </template>
                <template v-else-if="column.key === 'action'">
                  <a-button type="link" @click="handleDetail(record)">详情</a-button>
                  <a-button type="link" @click="generateWorkOrder(record)" :disabled="record.maintenanceSuggestion === '暂无建议'">生成工单</a-button>
                </template>
              </template>
            </a-table>
          </div>
      </Card>
      <!-- å¤‡ä»¶ä¿¡æ¯ -->
      <div class="grid grid-cols-2 gap-4 mb-6">
        <!-- å¤‡ä»¶å¯¿å‘½é¢„测 -->
        <Card title="备件寿命预测">
          <a-table :columns="lifePredictionColumns" :data-source="lifePredictionData" :pagination="false" class="w-full">
            <template #bodyCell="{ column, record }">
              <template v-if="column.key === 'lifeStatus'">
                <a-tag :color="getLifeStatusColor(record.remainingDays)">
                  {{ getLifeStatus(record.remainingDays) }}
                </a-tag>
              </template>
              <template v-else-if="column.key === 'action'">
                <a-button type="link" @click="showLifePredictionDetail(record)">详情</a-button>
              </template>
            </template>
          </a-table>
        </Card>
        <!-- å¤‡ä»¶åº“存与预警 -->
        <Card title="备件库存与预警">
          <a-table :columns="sparePartColumns" :data-source="sparePartData" :pagination="false" class="w-full">
            <template #bodyCell="{ column, record }">
              <template v-if="column.key === 'stockStatus'">
                <a-tag :color="getStockStatusColor(record.currentStock, record.safetyStock)">
                  {{ getStockStatus(record.currentStock, record.safetyStock) }}
                </a-tag>
              </template>
              <template v-else-if="column.key === 'action'">
                <a-button type="link" @click="showSparePartDetail(record)">详情</a-button>
              </template>
            </template>
          </a-table>
        </Card>
      </div>
    </div>
  </Page>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { Card, Table, Tag, Button, Form, Input, Textarea, Select, message } from 'ant-design-vue';
import * as echarts from 'echarts';
const chartRef = ref<HTMLElement | null>(null);
const healthData = {
  healthy: 85,
  warning: 12,
  critical: 3,
  maintained: 92,
  pendingWorkOrders: 5, // æ¨¡æ‹Ÿå¾…处理工单数
  sparePartWarnings: 2, // æ¨¡æ‹Ÿå¤‡ä»¶åº“存预警数
};
// é«˜é£Žé™©è®¾å¤‡é¢„警数据
const warningColumns = [
  { title: '设备名称', dataIndex: 'name', key: 'name' },
  { title: '关键部件', dataIndex: 'component', key: 'component' },
  { title: '风险指标', dataIndex: 'indicator', key: 'indicator' },
  { title: '当前值', dataIndex: 'value', key: 'value' },
  { title: '阈值', dataIndex: 'threshold', key: 'threshold' },
  { title: '状态', dataIndex: 'status', key: 'status' },
  { title: '维护建议', dataIndex: 'maintenanceSuggestion', key: 'maintenanceSuggestion' },
  { title: '操作', key: 'action' },
];
// æ¨¡æ‹Ÿé«˜é£Žé™©è®¾å¤‡æ•°æ®
const warningData = ref([
  {
    key: '1',
    name: 'SMT贴片机',
    type: 'SMT机器',
    component: '传送带',
    indicator: '振动幅度',
    value: 0.8,
    threshold: 0.5,
    status: '高风险',
    maintenanceSuggestion: '建议更换传送带轴承',
    maintenanceType: '预防性维护',
  },
  {
    key: '2',
    name: 'CNC加工中心',
    type: 'CNC',
    component: '主轴',
    indicator: '温度',
    value: 85,
    threshold: 80,
    status: '中风险',
    maintenanceSuggestion: '建议对主轴进行润滑保养',
    maintenanceType: '润滑维护',
  },
  {
    key: '3',
    name: '注塑机',
    type: '注塑设备',
    component: '液压系统',
    indicator: '压力',
    value: 120,
    threshold: 150,
    status: '低风险',
    maintenanceSuggestion: '建议检查液压系统密封件',
    maintenanceType: '检查维护',
  },
  {
    key: '4',
    name: '空压机',
    type: '空气压缩机',
    component: '电机',
    indicator: '电流',
    value: 49,
    threshold: 50,
    status: '低风险',
    maintenanceSuggestion: '暂无建议',
    maintenanceType: '无',
  },
]);
// å¤‡ä»¶åº“存数据
const sparePartColumns = [
  { title: '备件名称', dataIndex: 'name', key: 'name' },
  { title: '当前库存', dataIndex: 'currentStock', key: 'currentStock' },
  { title: '安全库存', dataIndex: 'safetyStock', key: 'safetyStock' },
  { title: '预测需求', dataIndex: 'predictedDemand', key: 'predictedDemand' },
  { title: '库存状态', dataIndex: 'stockStatus', key: 'stockStatus' },
];
// æ¨¡æ‹Ÿå¤‡ä»¶æ•°æ®
const sparePartData = ref([
  {
    key: '1',
    name: '传送带轴承',
    currentStock: 10,
    safetyStock: 5,
    predictedDemand: 3,
  },
  {
    key: '2',
    name: '主轴润滑油',
    currentStock: 2,
    safetyStock: 3,
    predictedDemand: 1,
  },
  {
    key: '3',
    name: '液压系统密封件',
    currentStock: 20,
    safetyStock: 10,
    predictedDemand: 5,
  },
  {
    key: '4',
    name: '电机碳刷',
    currentStock: 8,
    safetyStock: 10,
    predictedDemand: 2,
  },
]);
const getStatusColor = (status) => {
  switch (status) {
    case '高风险': return 'red';
    case '中风险': return 'orange';
    case '低风险': return 'yellow';
    default: return 'green';
  }
};
// å·¥å•表单数据
const workOrderForm = ref({
  type: '',
  equipment: '',
  content: '',
  priority: 'medium',
});
const selectedMaintenance = ref(null);
const lifePredictionColumns = [
  { title: '设备名称', dataIndex: 'deviceName', key: 'deviceName' },
  { title: '部件名称', dataIndex: 'componentName', key: 'componentName' },
  { title: '备件名称', dataIndex: 'name', key: 'name' },
  { title: '预测寿命 (天)', dataIndex: 'predictedLife', key: 'predictedLife' },
  { title: '剩余寿命 (天)', dataIndex: 'remainingDays', key: 'remainingDays' },
  { title: '状态', dataIndex: 'lifeStatus', key: 'lifeStatus' },
  { title: '操作', dataIndex: 'action', key: 'action' },
];
const lifePredictionData = ref([
  { key: '1', deviceName: 'SMT贴片机', componentName: '传送带', name: '轴承A', predictedLife: 365, remainingDays: 120 },
  { key: '2', deviceName: 'CNC加工中心', componentName: '主轴', name: '齿轮B', predictedLife: 730, remainingDays: 30 },
  { key: '3', deviceName: '注塑机', componentName: '液压系统', name: '滤芯C', predictedLife: 180, remainingDays: 90 },
  { key: '4', deviceName: '空压机', componentName: '电机', name: '传感器D', predictedLife: 500, remainingDays: 10 },
  { key: '5', deviceName: '焊接机器人', componentName: '焊枪', name: '焊嘴E', predictedLife: 240, remainingDays: 60 }
]);
const getLifeStatus = (remainingDays) => {
  if (remainingDays <= 30) {
    return '即将到期';
  } else if (remainingDays <= 90) {
    return '中期预警';
  } else {
    return '寿命充足';
  }
};
const getLifeStatusColor = (remainingDays) => {
  if (remainingDays <= 30) {
    return 'red';
  } else if (remainingDays <= 90) {
    return 'orange';
  } else {
    return 'green';
  }
};
const showLifePredictionDetail = (record) => {
  message.info(`备件名称: ${record.name}, é¢„测寿命: ${record.predictedLife}天, å‰©ä½™å¯¿å‘½: ${record.remainingDays}天`);
};
const getStockStatus = (currentStock, safetyStock) => {
  if (currentStock <= safetyStock) {
    return '库存预警';
  } else {
    return '库存充足';
  }
};
const getStockStatusColor = (currentStock, safetyStock) => {
  if (currentStock <= safetyStock) {
    return 'red';
  } else {
    return 'green';
  }
};
const showSparePartDetail = (record) => {
  message.info(`备件名称: ${record.name}, å½“前库存: ${record.currentStock}, å®‰å…¨åº“å­˜: ${record.safetyStock}, é¢„测需求: ${record.predictedDemand}`);
};
const generateWorkOrder = (record) => {
  selectedMaintenance.value = record;
  workOrderForm.value = {
    type: record.maintenanceType,
    equipment: record.name,
    content: record.maintenanceSuggestion,
    priority: 'medium',
  };
};
const submitWorkOrder = () => {
  console.log('提交工单:', workOrderForm.value);
  // è¿™é‡Œå¯ä»¥æ·»åŠ å·¥å•æäº¤é€»è¾‘
  message.success('工单提交成功');
  selectedMaintenance.value = null;
  workOrderForm.value = {
    type: '',
    equipment: '',
    content: '',
    priority: 'medium',
  };
};
import { useRouter } from 'vue-router';
const router = useRouter();
const handleDetail = (record) => {
  // æ ¹æ®è®¾å¤‡åç§°ä¸åŒè·³è½¬ä¸åŒçš„详情页面
  if (record.name === 'SMT贴片机') {
    console.log('设备ID111:', record.name)
    router.push({ path: '/predictive/smt-detail', query: { deviceId: record.key } });
  } else if (record.name === 'CNC加工中心') {
    router.push({ path: '/predictive/predictive-detail', query: { deviceId: record.key } });
  } else if (record.name === '注塑机') {
    router.push({ path: '/predictive/injection-detail', query: { deviceId: record.key } });
  } else {
    router.push({ path: '/predictive/air-compressor-detail', query: { deviceId: record.key } });
  }
};
onMounted(() => {
  if (chartRef.value) {
    const myChart = echarts.init(chartRef.value);
    const option = {
      tooltip: {
        trigger: 'axis',
        axisPointer: {
          type: 'shadow'
        }
      },
      xAxis: {
        type: 'category',
        data: ['90%-100%', '80%-90%', '70%-80%', '60%-70%', '50%-60%', '40%-50%', '30%-40%', '20%-30%', '10%-20%', '0%-10%'],
        axisLabel: {
          rotate: 45
        }
      },
      yAxis: {
        type: 'value',
        name: '设备数量',
        minInterval: 1
      },
      series: [
        {
          name: '设备数量',
          type: 'bar',
          barWidth: '60%',
          data: [
            { value: 15, itemStyle: { color: '#52c41a' } },
            { value: 20, itemStyle: { color: '#52c41a' } },
            { value: 25, itemStyle: { color: '#faad14' } },
            { value: 15, itemStyle: { color: '#faad14' } },
            { value: 10, itemStyle: { color: '#faad14' } },
            { value: 5, itemStyle: { color: '#ff4d4f' } },
            { value: 5, itemStyle: { color: '#ff4d4f' } },
            { value: 3, itemStyle: { color: '#ff4d4f' } },
            { value: 1, itemStyle: { color: '#ff4d4f' } },
            { value: 1, itemStyle: { color: '#ff4d4f' } }
          ],
          label: {
            show: true,
            position: 'top'
          }
        }
      ]
    };
    myChart.setOption(option);
    // å“åº”式调整图表大小
    window.addEventListener('resize', () => {
      myChart.resize();
    });
  }
});
</script>
<style scoped>
/* å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ ç»„ä»¶æ ·å¼ */
</style>
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/injection-molding-machine-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,549 @@
<script lang="ts">
import { defineComponent, onUnmounted, reactive } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
  name: 'InjectionMoldingMachineDetail',
  setup() {
    // æ¨¡æ‹Ÿæ•°æ®
    const deviceInfo = reactive({
      deviceName: '注塑机 #IM-001',
      deviceType: '注塑机',
      deviceId: 'IM-2024-001',
      installDate: '2023-08-20',
      serviceLife: 15,
      status: '运行中',
      statusColor: '#52c41a'
    });
    const healthData = reactive({
      overallHealth: 88,
      healthColor: '#faad14',
      predictedLife: 1500,
      riskLevel: '中风险',
      riskColor: '#faad14'
    });
    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: '2024-07-15',
        urgency: '中等'
      },
      {
        key: '2',
        type: '故障预警',
        content: '主电机轴承磨损预警',
        suggestedTime: '2024-08-01',
        urgency: '高'
      },
      {
        key: '3',
        type: '冷却系统维护',
        content: '检查冷却水流量',
        suggestedTime: '2024-07-01',
        urgency: '低'
      }
    ]);
    const getUrgencyColor = (urgency: string) => {
      switch (urgency) {
        case '中等': {
          return 'orange';
        }
        case '低': {
          return 'green';
        }
        case '高': {
          return 'red';
        }
        default: {
          return 'default';
        }
      }
    };
    const sparePartColumns = [
      {
        title: '备件名称',
        dataIndex: 'name',
        key: 'name'
      },
      {
        title: '当前寿命',
        dataIndex: 'currentLife',
        key: 'currentLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '预测剩余寿命',
        dataIndex: 'remainingLife',
        key: 'remainingLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        slots: { customRender: 'status' }
      }
    ];
    const sparePartData = reactive([
      {
        key: '1',
        name: '液压泵',
        currentLife: 18_500,
        remainingLife: 1500,
        status: '预警'
      },
      {
        key: '2',
        name: '加热圈',
        currentLife: 12_000,
        remainingLife: 3000,
        status: '良好'
      },
      {
        key: '3',
        name: '螺杆料筒',
        currentLife: 25_000,
        remainingLife: 5000,
        status: '良好'
      }
    ]);
    const historyData = reactive([
      {
        id: '1',
        date: '2023-10-20',
        type: '例行保养',
        description: '更换液压油滤芯',
        color: 'green'
      },
      {
        id: '2',
        date: '2023-08-15',
        type: '故障维修',
        description: '注射单元卡滞,清理异物',
        color: 'red'
      },
      {
        id: '3',
        date: '2023-06-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处理维护建议
    };
    return {
      deviceInfo,
      healthData,
      maintenanceColumns,
      maintenanceData,
      sparePartColumns,
      sparePartData,
      historyData,
      getStatusColor,
      getUrgencyColor,
      handleMaintenance
    };
  },
  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 < 60; i++) { // ç”Ÿæˆ60个点,代表5分钟的数据
          const time = new Date(now.getTime() - (59 - 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 > 60) {
          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();
      });
      onUnmounted(() => {
        clearInterval(intervalId);
      });
    };
    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, 'kN', 1000, 50);
    initChart('injectionPressureChart', '注射压力', injectionPressureData, 'MPa', 1200, 80);
    initChart('injectionSpeedChart', '注射速度', injectionSpeedData, 'mm/s', 150, 10);
    initChart('barrelTemperatureChart', '料筒温度', barrelTemperatureData, '°C', 200, 5);
    initChart('coolingWaterTemperatureChart', '冷却水温度', coolingWaterTemperatureData, '°C', 25, 2);
    initChart('coolingWaterFlowChart', '冷却水流量', coolingWaterFlowData, 'L/min', 30, 3);
    initChart('ejectorPositionChart', '顶出位置', ejectorPositionData, 'mm', 50, 5);
    initChart('cycleTimeChart', '循环时间', cycleTimeData, 's', 30, 2);
  }
});
</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="8">
        <a-card :style="{ height: '440px' }" class="mb-4" title="设备基本信息">
          <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-card>
      </a-col>
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€ä¸Žç»´æŠ¤å»ºè®® -->
      <a-col :span="16">
        <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
            :columns="maintenanceColumns"
            :data-source="maintenanceData"
            :pagination="false"
            size="small"
          >
            <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-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="moldClampingForceChart" 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="barrelTemperatureChart" 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">
              {{ item.date }} - {{ item.type }}: {{ item.description }}
            </a-timeline-item>
          </a-timeline>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>
<style scoped>
.device-detail-container {
  padding: 16px;
  background: #f0f2f5;
}
.mt-4 {
  margin-top: 16px;
}
.mb-4 {
  margin-bottom: 16px;
}
</style>
<style scoped>
.injection-molding-machine-detail {
  padding: 16px;
  background-color: #f0f2f5;
}
.header-card {
  margin-bottom: 16px;
}
.device-info h2 {
  font-size: 24px;
  margin-bottom: 10px;
}
.device-info p {
  margin-bottom: 5px;
}
.health-status h3 {
  font-size: 20px;
  margin-bottom: 10px;
}
.ant-statistic-content {
  font-size: 20px !important;
}
.ant-list-item-meta-title {
  font-size: 16px;
}
.ant-list-item-meta-description {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.65);
}
</style>
eims-ui/apps/web-antd/src/views/eims/predictive-maintenance/smt-machine-detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,509 @@
<template>
  <div class="device-detail-container">
    <a-page-header
      title="SMT贴片机预测性维护详情"
      :sub-title="deviceInfo.deviceName"
      @back="() => $router.go(-1)"
    />
    <a-row :gutter="16" class="mt-4">
      <!-- è®¾å¤‡åŸºæœ¬ä¿¡æ¯ -->
      <a-col :span="8">
        <a-card title="设备基本信息" class="mb-4" :style="{ height: '440px' }">
          <a-descriptions bordered :column="1">
            <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-card>
      </a-col>
      <!-- è®¾å¤‡å¥åº·çŠ¶æ€ä¸Žç»´æŠ¤å»ºè®® -->
      <a-col :span="16">
        <a-card title="设备健康状态与维护建议" class="mb-4" :style="{ height: '440px' }">
          <a-row :gutter="16">
            <a-col :span="8">
              <a-statistic
                title="整体健康度"
                :value="healthData.overallHealth"
                :precision="0"
                suffix="%"
                :value-style="{ color: healthData.healthColor }"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="预测剩余寿命"
                :value="healthData.predictedLife"
                suffix="天"
              />
            </a-col>
            <a-col :span="8">
              <a-statistic
                title="故障风险等级"
                :value="healthData.riskLevel"
                :value-style="{ color: healthData.riskColor }"
              />
            </a-col>
          </a-row>
          <div class="mt-4">
            <a-progress
              :percent="healthData.overallHealth"
              :stroke-color="healthData.healthColor"
              :show-info="false"
            />
          </div>
          <a-divider orientation="left">预测性维护建议</a-divider>
          <a-table
            :columns="maintenanceColumns"
            :data-source="maintenanceData"
            :pagination="false"
            size="small"
          >
            <template #urgency="{ text }">
              <a-tag :color="getUrgencyColor(text)">{{ text }}</a-tag>
            </template>
            <template #action="{ record }">
              <a-button type="link" size="small" @click="handleMaintenance(record)">处理</a-button>
            </template>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
    <!-- å®žæ—¶æ•°æ®è¶‹åŠ¿å›¾ -->
    <a-card title="实时数据趋势图" class="mb-4">
      <!-- è¿åŠ¨ç³»ç»Ÿ -->
      <a-row :gutter="16">
        <a-col :span="8">
          <div id="xAxisMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="yAxisMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="zAxisMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <a-row :gutter="16" class="mt-4">
        <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="beltTensionChart" 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-row>
      <!-- è§†è§‰ç³»ç»Ÿ -->
      <a-row :gutter="16" class="mt-4">
                <a-col :span="8">
          <div id="placementAccuracyChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="visionAlignmentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="lightIntensityChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
      <!-- çŽ¯å¢ƒå‚æ•° -->
      <a-row :gutter="16" class="mt-4">
        <a-col :span="8">
          <div id="feederMotorCurrentChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="esdChart" style="height: 300px;"></div>
        </a-col>
        <a-col :span="8">
          <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div>
        </a-col>
      </a-row>
    </a-card>
    <a-row :gutter="16">
      <!-- å¤‡ä»¶å¯¿å‘½é¢„测 -->
      <a-col :span="12">
        <a-card title="备件寿命预测" class="mb-4">
          <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 title="历史维护记录" class="mb-4">
          <a-timeline>
            <a-timeline-item v-for="item in historyData" :key="item.id" :color="item.color">
              {{ item.date }} - {{ item.type }}: {{ item.description }}
            </a-timeline-item>
          </a-timeline>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>
<script lang="ts">
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
export default defineComponent({
  name: 'SmtMachineDetail',
  setup() {
    // æ¨¡æ‹Ÿæ•°æ®
    const deviceInfo = reactive({
      deviceName: 'SMT贴片机-001',
      deviceType: '贴片机',
      deviceId: 'SMT-2024-001',
      installDate: '2024-01-01',
      serviceLife: 8,
      status: '运行中',
      statusColor: '#52c41a'
    });
    const healthData = reactive({
      overallHealth: 92,
      healthColor: '#52c41a',
      predictedLife: 2500,
      riskLevel: '低风险',
      riskColor: '#52c41a'
    });
    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: 'X/Y轴伺服电机润滑检查',
        suggestedTime: '2024-03-20',
        urgency: '中等'
      },
      {
        key: '2',
        type: '贴装头维护',
        content: '吸嘴真空压力校准',
        suggestedTime: '2024-04-05',
        urgency: '高'
      },
      {
        key: '3',
        type: '供料系统检查',
        content: '飞达卡带/卡料次数清零',
        suggestedTime: '2024-03-10',
        urgency: '低'
      }
    ]);
    const getUrgencyColor = (urgency: string) => {
      switch (urgency) {
        case '高':
          return 'red';
        case '中等':
          return 'orange';
        case '低':
          return 'green';
        default:
          return 'default';
      }
    };
    const sparePartColumns = [
      {
        title: '备件名称',
        dataIndex: 'name',
        key: 'name'
      },
      {
        title: '当前寿命',
        dataIndex: 'currentLife',
        key: 'currentLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '预测剩余寿命',
        dataIndex: 'remainingLife',
        key: 'remainingLife',
        customRender: ({ text }: { text: number }) => `${text}小时`
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        slots: { customRender: 'status' }
      }
    ];
    const sparePartData = reactive([
      {
        key: '1',
        name: 'X轴伺服电机',
        currentLife: 5000,
        remainingLife: 1500,
        status: '良好'
      },
      {
        key: '2',
        name: '吸嘴',
        currentLife: 1000,
        remainingLife: 50,
        status: '预警'
      },
      {
        key: '3',
        name: '飞达',
        currentLife: 8000,
        remainingLife: 2000,
        status: '良好'
      }
    ]);
    const historyData = reactive([
      {
        id: '1',
        date: '2024-02-15',
        type: '定期保养',
        description: '完成季度保养,检查运动系统润滑',
        color: 'green'
      },
      {
        id: '2',
        date: '2024-01-20',
        type: '故障维修',
        description: '修复吸嘴堵塞问题',
        color: 'red'
      },
      {
        id: '3',
        date: '2023-12-01',
        type: '定期保养',
        description: '完成年度保养,校准视觉系统',
        color: 'green'
      }
    ]);
    const getStatusColor = (status: string) => {
      switch (status) {
        case '良好':
          return '#52c41a';
        case '预警':
          return '#faad14';
        case '危险':
          return '#f5222d';
        default:
          return '#d9d9d9';
      }
    };
    const handleMaintenance = (record: any) => {
      console.log('处理维护建议:', record);
      // å®žé™…项目中这里会调用API处理维护建议
    };
    return {
      deviceInfo,
      healthData,
      maintenanceColumns,
      maintenanceData,
      sparePartColumns,
      sparePartData,
      historyData,
      getStatusColor,
      getUrgencyColor,
      handleMaintenance
    };
  },
  mounted() {
    // åˆå§‹åŒ–图表
    const initChart = (chartId: string, title: string, chartData: any[], unit: string, baseValue: number, fluctuation: number) => {
      const chart = echarts.init(document.getElementById(chartId));
      const updateChart = () => {
        // ç”Ÿæˆæ–°çš„æ•°æ®ç‚¹
        const now = new Date();
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = (baseValue + (Math.random() * 2 - 1) * fluctuation).toFixed(2);
        // æ·»åŠ æ–°æ•°æ®ç‚¹ï¼Œæœ€å¤šä¿ç•™60个点(5分钟数据)
        chartData.push({
          time,
          value: newValue
        });
        if (chartData.length > 60) {
          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 => parseFloat(item.value)),
              type: 'line',
              smooth: true,
              lineStyle: {
                width: 2,
                color: chartId === 'motorCurrentChart' ? '#5470C6' : chartId === 'motorTemperatureChart' ? '#91CC75' : '#FAC858' // æ ¹æ®å›¾è¡¨ID设置颜色
              },
              areaStyle: {
                opacity: 0.8,
                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                  {
                    offset: 0,
                    color: chartId === 'motorCurrentChart' ? 'rgba(84,112,198,0.3)' : chartId === 'motorTemperatureChart' ? 'rgba(145,204,117,0.3)' : 'rgba(250,200,88,0.3)'
                  },
                  {
                    offset: 1,
                    color: chartId === 'motorCurrentChart' ? 'rgba(84,112,198,0)' : chartId === 'motorTemperatureChart' ? 'rgba(145,204,117,0)' : 'rgba(250,200,88,0)'
                  }
                ])
              },
            }
          ]
        };
        chart.setOption(option);
      };
      // ç”Ÿæˆåˆå§‹æ•°æ®ç‚¹ï¼ˆ60个点,5分钟数据)
      for (let i = 0; i < 60; i++) {
        const now = new Date(Date.now() - (60 - i) * 5000); // ç”Ÿæˆè¿‡åŽ»5分钟的数据
        const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
        const newValue = (baseValue + (Math.random() * 2 - 1) * fluctuation).toFixed(2);
        chartData.push({
          time,
          value: newValue
        });
      }
      // åˆå§‹æ¸²æŸ“
      updateChart();
      // æ¯5秒更新一次数据
      const intervalId = setInterval(updateChart, 5000);
      window.addEventListener('resize', () => {
        chart.resize();
      });
            onUnmounted(() => {
        clearInterval(intervalId);
      });
    };
    // è¿åŠ¨ç³»ç»Ÿç›‘æµ‹
    initChart('xAxisMotorCurrentChart', 'X轴电机电流', [], 'A', 10, 1);
    initChart('yAxisMotorCurrentChart', 'Y轴电机电流', [], 'A', 10, 1);
    initChart('zAxisMotorCurrentChart', 'Z轴电机电流', [], 'A', 8, 0.8);
    initChart('motorTemperatureChart', '电机温度', [], '°C', 45, 2);
    initChart('motorVibrationChart', '电机振动', [], 'mm/s', 5, 0.5);
    initChart('beltTensionChart', '皮带张力', [], 'N', 50, 5);
    // è´´è£…头/吸嘴监测
    initChart('nozzleVacuumChart', '吸嘴真空压力', [], 'kPa', 80, 5);
    initChart('nozzleFlowChart', '吸嘴流量', [], 'L/min', 5, 0.5);
    initChart('placementSpeedChart', '贴装速度', [], 'mm/s', 100, 10);
    initChart('placementAccuracyChart', '贴装精度', [], 'μm', 50, 5);
    // è§†è§‰ç³»ç»Ÿç›‘测
    initChart('visionAlignmentChart', '视觉对位精度', [], 'μm', 30, 3);
    initChart('lightIntensityChart', '光源亮度', [], 'lux', 5000, 500);
    // ä¾›æ–™ç³»ç»Ÿç›‘测
    initChart('feederMotorCurrentChart', '供料电机电流', [], 'A', 5, 0.5);
    // çŽ¯å¢ƒç›‘æµ‹
    initChart('esdChart', '静电监测', [], 'kV', 0.1, 0.05);
    initChart('ambientTemperatureHumidityChart', '环境温湿度', [], '°C', 25, 2);
  }
});
</script>
<style scoped>
.device-detail-container {
  padding: 16px;
  background: #f0f2f5;
}
</style>