¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="device-detail-container"> |
| | | <el-page-header @back="goBack"> |
| | | <template #content> |
| | | <span class="text-large font-600 mr-3"> SMTè´´çæºé¢æµæ§ç»´æ¤è¯¦æ
</span> |
| | | </template> |
| | | </el-page-header> |
| | | |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <!-- 设å¤å¾çå设å¤åºæ¬ä¿¡æ¯åå¹¶ --> |
| | | <el-col :span="10"> |
| | | <el-card class="mb-4" :style="{ height: '440px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤ä¿¡æ¯</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="12"> |
| | | <div class="device-image-container"> |
| | | <img :src="deviceInfo.imageUrl" alt="设å¤å¾ç" class="device-image" /> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-descriptions border :column="1" class="custom-descriptions"> |
| | | <el-descriptions-item label="设å¤åç§°">{{ deviceInfo.deviceName }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç±»å">{{ deviceInfo.deviceType }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç¼å·">{{ deviceInfo.deviceId }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®è£
æ¥æ">{{ deviceInfo.installDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="使ç¨å¹´é">{{ deviceInfo.serviceLife }}å¹´</el-descriptions-item> |
| | | <el-descriptions-item label="å½åç¶æ"> |
| | | <el-tag :type="getStatusTagType(deviceInfo.status)">{{ deviceInfo.status }}</el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- 设å¤å¥åº·ç¶æä¸ç»´æ¤å»ºè®® --> |
| | | <el-col :span="14"> |
| | | <el-card class="mb-4" :style="{ height: '440px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤å¥åº·ç¶æä¸ç»´æ¤å»ºè®®</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="æ´ä½å¥åº·åº¦" |
| | | :value="healthData.overallHealth" |
| | | :precision="0" |
| | | :suffix="'%'" |
| | | :value-style="{ color: healthData.healthColor }" |
| | | /> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="颿µå©ä½å¯¿å½" |
| | | :value="healthData.predictedLife" |
| | | :suffix="'天'" |
| | | /> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-statistic |
| | | title="æ
éé£é©ç级" |
| | | :value="healthData.riskLevel" |
| | | :value-style="{ color: healthData.riskColor }" |
| | | /> |
| | | </el-col> |
| | | </el-row> |
| | | <div class="mt-4"> |
| | | <el-progress |
| | | :percentage="healthData.overallHealth" |
| | | :color="healthData.healthColor" |
| | | :show-text="false" |
| | | /> |
| | | </div> |
| | | |
| | | <el-divider content-position="left">颿µæ§ç»´æ¤å»ºè®®</el-divider> |
| | | <div class="table-container"> |
| | | |
| | | <el-table |
| | | ref="maintenanceTable" |
| | | :data="displayMaintenanceData" |
| | | height="246" |
| | | size="large" |
| | | stripe |
| | | > |
| | | <el-table-column prop="type" label="ç»´æ¤ç±»å" /> |
| | | <el-table-column prop="content" label="ç»´æ¤å
容" /> |
| | | <el-table-column prop="suggestedTime" label="建议æ¶é´" /> |
| | | <el-table-column prop="urgency" label="ç´§æ¥ç¨åº¦"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getUrgencyTagType(row.urgency)">{{ row.urgency }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" size="small" @click="handleMaintenance(row)">å¤ç</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <el-col :span="24"> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è®¾å¤æ°æ®</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16"> |
| | | <el-col :span="4"> |
| | | <el-statistic title="Xè½´æ»ç§»å¨è·ç¦»" :value="healthData.xAxisTravel" :precision="2" :suffix="'km'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><TrendCharts /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-statistic title="Yè½´æ»ç§»å¨è·ç¦»" :value="healthData.yAxisTravel" :precision="2" :suffix="'km'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><TrendCharts /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-statistic title="å¡å¸¦æ¬¡æ°" :value="healthData.tapeJamCount" :suffix="'次'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><Warning /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-statistic title="å¡ææ¬¡æ°" :value="healthData.materialJamCount" :suffix="'次'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><Warning /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-statistic title="æ¼æ¿æ°" :value="healthData.panelCount" :suffix="'å'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><Grid /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-statistic title="åºéåæºæ¶é´" :value="healthData.downtime" :suffix="'ç§'"> |
| | | <template #prefix> |
| | | <el-icon style="vertical-align: -0.125em"><Clock /></el-icon> |
| | | </template> |
| | | </el-statistic> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 宿¶æ°æ®è¶å¿å¾ --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>宿¶æ°æ®è¶å¿å¾</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <el-col :span="8"> |
| | | <div id="ambientTemperatureHumidityChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="motorTemperatureChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="motorVibrationChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- è´´è£
头/å¸å´ --> |
| | | <el-row :gutter="16" class="mt-4"> |
| | | <el-col :span="8"> |
| | | <div id="nozzleVacuumChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="nozzleFlowChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div id="placementSpeedChart" style="height: 300px;"></div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | |
| | | <el-row :gutter="16"> |
| | | <!-- é¨ä»¶å¯¿å½é¢æµ --> |
| | | <el-col :span="12"> |
| | | <el-card class="mb-4" :style="{ height: '560px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>é¨ä»¶å¯¿å½é¢æµ</span> |
| | | </div> |
| | | </template> |
| | | <el-table |
| | | :data="sparePartData" |
| | | size="large" |
| | | stripe |
| | | > |
| | | <el-table-column prop="name" label="é¨ä»¶åç§°" /> |
| | | <el-table-column prop="currentLife" label="ç论寿å½" /> |
| | | <el-table-column prop="remainingLife" label="颿µå©ä½å¯¿å½" /> |
| | | <el-table-column prop="status" label="ç¶æ"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusTagType(row.status)">{{ row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- åå²ç»´æ¤è®°å½ --> |
| | | <el-col :span="12"> |
| | | <el-card class="mb-4" :style="{ height: '560px' }"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åå²ç»´æ¤è®°å½</span> |
| | | </div> |
| | | </template> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="item in historyData" |
| | | :key="item.id" |
| | | :type="getTimelineItemType(item.color)" |
| | | :timestamp="item.date" |
| | | > |
| | | {{ item.type }}: {{ item.description }} |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { defineComponent, reactive, onMounted, onUnmounted, ref, nextTick } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import * as echarts from 'echarts'; |
| | | import { |
| | | TrendCharts, |
| | | Warning, |
| | | Grid, |
| | | Clock |
| | | } from '@element-plus/icons-vue'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import img from '@/assets/images/JUKI.png'; |
| | | |
| | | |
| | | export default defineComponent({ |
| | | name: 'SmtMachineDetail', |
| | | components: { |
| | | TrendCharts, |
| | | Warning, |
| | | Grid, |
| | | Clock |
| | | }, |
| | | setup() { |
| | | const router = useRouter(); |
| | | |
| | | const goBack = () => { |
| | | router.go(-1); |
| | | }; |
| | | |
| | | // æ¨¡ææ°æ® |
| | | const deviceInfo = reactive({ |
| | | deviceName: 'SMTè´´çæº', |
| | | deviceType: 'è´´çæº', |
| | | deviceId: 'GPC2012A101', |
| | | installDate: '2012-06-08', |
| | | serviceLife: 15, |
| | | status: 'è¿è¡ä¸', |
| | | statusColor: '#52c41a', |
| | | imageUrl: img |
| | | }); |
| | | |
| | | const healthData = reactive({ |
| | | overallHealth: 82, |
| | | healthColor: '#52c41a', |
| | | predictedLife: 635, |
| | | riskLevel: 'ä½é£é©', |
| | | riskColor: '#1a7ac4', |
| | | xAxisTravel: 300.179, |
| | | yAxisTravel: 233.39, |
| | | tapeJamCount: 6, |
| | | materialJamCount: 15, |
| | | panelCount: 2480, |
| | | downtime: 4.5, |
| | | }); |
| | | |
| | | const maintenanceData = reactive([ |
| | | { |
| | | key: '1', |
| | | 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: '2025-06-20', |
| | | urgency: 'ä½' |
| | | }, |
| | | { |
| | | key: '3', |
| | | type: 'ä¾æç³»ç»æ£æ¥', |
| | | content: 'é£è¾¾å¡å¸¦/å¡ææ¬¡æ°æ¸
é¶', |
| | | 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 getUrgencyTagType = (urgency: string) => { |
| | | switch (urgency) { |
| | | case 'é«': return 'danger'; |
| | | case 'ä¸ç': return 'warning'; |
| | | case 'ä½': return 'success'; |
| | | default: return 'info'; |
| | | } |
| | | }; |
| | | |
| | | const getStatusTagType = (status: string) => { |
| | | switch (status) { |
| | | case 'è¿è¡ä¸': return 'success'; |
| | | case 'é¢è¦': return 'warning'; |
| | | case 'å±é©': return 'danger'; |
| | | case 'è¯å¥½': return 'success'; |
| | | default: return 'info'; |
| | | } |
| | | }; |
| | | |
| | | const getTimelineItemType = (color: string) => { |
| | | switch (color) { |
| | | case 'green': return 'success'; |
| | | case 'red': return 'danger'; |
| | | default: return 'primary'; |
| | | } |
| | | }; |
| | | |
| | | // const fetchDeviceData = async () => { |
| | | // try { |
| | | // const res = await getDeviceDataSmt(); |
| | | // Object.assign(healthData, res); |
| | | // } catch (error) { |
| | | // console.error('è·åè®¾å¤æ°æ®å¤±è´¥:', error); |
| | | // } |
| | | // }; |
| | | |
| | | let deviceDataInterval: number | undefined; |
| | | // fetchDeviceData(); |
| | | // deviceDataInterval = setInterval(fetchDeviceData, 3000); |
| | | |
| | | const sparePartData = reactive([ |
| | | { |
| | | key: '1', |
| | | 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: '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: '96个æ', |
| | | remainingLife: '43个æ', |
| | | status: 'è¯å¥½' |
| | | }, |
| | | ]); |
| | | |
| | | const historyData = reactive([ |
| | | { |
| | | id: '1', |
| | | 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: '2025-01-20', |
| | | type: 'æ
éç»´ä¿®', |
| | | description: 'ä¿®å¤å¸å´å µå¡é®é¢', |
| | | color: 'red' |
| | | }, |
| | | { |
| | | id: '3', |
| | | date: '2025-01-15', |
| | | type: '宿ä¿å
»', |
| | | description: 'å®æå¹´åº¦ä¿å
»ï¼è´´è£
ç³»ç»æ ¡åï¼ææä»¶æ´æ¢ï¼æ ¡åè§è§ç³»ç»', |
| | | color: 'green' |
| | | } |
| | | ]); |
| | | |
| | | const handleMaintenance = (record: any) => { |
| | | console.log('å¤çç»´æ¤å»ºè®®:', record); |
| | | ElMessage.info(`å¤çç»´æ¤å»ºè®®: ${record.type}`); |
| | | }; |
| | | |
| | | const maintenanceTable = ref(); |
| | | |
| | | // ç¨äºæ¾ç¤ºçç»´æ¤æ°æ®ï¼æ¯ææ éæ»å¨ï¼ |
| | | const displayMaintenanceData = ref([...maintenanceData]); |
| | | let scrollInterval: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | // å¯å¨èªå¨æ»å¨ |
| | | const startAutoScroll = () => { |
| | | scrollInterval = setInterval(() => { |
| | | if (displayMaintenanceData.value.length > 0) { |
| | | // å°ç¬¬ä¸é¡¹ç§»å°æå |
| | | const firstItem = displayMaintenanceData.value.shift(); |
| | | if (firstItem) { |
| | | displayMaintenanceData.value.push(firstItem); |
| | | } |
| | | } |
| | | }, 1000); // æ¯3ç§æ»å¨ä¸æ¬¡ |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | nextTick(() => { |
| | | initCharts(); |
| | | startAutoScroll(); // å¯å¨èªå¨æ»å¨ |
| | | }); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | if (deviceDataInterval) { |
| | | clearInterval(deviceDataInterval); |
| | | } |
| | | if (scrollInterval) { |
| | | clearInterval(scrollInterval); |
| | | } |
| | | }); |
| | | |
| | | const initCharts = () => { |
| | | // å¾è¡¨åå§å代ç ï¼ä¸ä¹åç¸åï¼ |
| | | // åå§åå¾è¡¨ |
| | | const initChart = (chartId: string, title: string, seriesConfig: Array<{ name: string, data: any[], unit: string, baseValue: number, fluctuation: number, color: string }>) => { |
| | | const chart = echarts.init(document.getElementById(chartId)); |
| | | |
| | | const updateChart = () => { |
| | | // çææ°çæ°æ®ç¹ |
| | | const now = new Date(); |
| | | const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`; |
| | | |
| | | seriesConfig.forEach(s => { |
| | | const newValue = (s.baseValue + (Math.random() * 2 - 1) * s.fluctuation).toFixed(2); |
| | | s.data.push({ |
| | | time, |
| | | value: newValue |
| | | }); |
| | | if (s.data.length > 24) { |
| | | s.data.shift(); |
| | | } |
| | | }); |
| | | |
| | | const option = { |
| | | title: { |
| | | text: title, |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | formatter: (params) => { |
| | | let result = `${params[0].axisValueLabel}<br/>`; |
| | | params.forEach(param => { |
| | | result += `${param.marker} ${param.seriesName}: ${param.data}${seriesConfig[param.seriesIndex].unit}<br/>`; |
| | | }); |
| | | return result; |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '8%', // å¢å å·¦ä¾§è¾¹è· |
| | | right: '5%', |
| | | bottom: '10%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: seriesConfig[0].data.map(item => item.time) |
| | | }, |
| | | yAxis: seriesConfig.map((s, index) => ({ |
| | | type: 'value', |
| | | name: s.name, |
| | | position: index === 0 ? 'left' : 'right', |
| | | axisLine: { |
| | | show: true, |
| | | }, |
| | | axisLabel: { |
| | | formatter: (value) => `${value}${s.unit}` |
| | | } |
| | | })), |
| | | series: seriesConfig.map((s, index) => ({ |
| | | name: s.name, |
| | | data: s.data.map(item => parseFloat(item.value)), |
| | | type: 'line', |
| | | smooth: true, |
| | | yAxisIndex: index, |
| | | lineStyle: { |
| | | width: 2, |
| | | color: s.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); |
| | | }; |
| | | |
| | | seriesConfig.forEach(s => { |
| | | s.data = []; |
| | | // çæåå§æ°æ®ç¹ï¼60个ç¹ï¼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({ |
| | | time, |
| | | value: newValue |
| | | }); |
| | | } |
| | | }) |
| | | |
| | | |
| | | // åå§æ¸²æ |
| | | updateChart(); |
| | | |
| | | // æ¯5ç§æ´æ°ä¸æ¬¡æ°æ® |
| | | const intervalId = setInterval(updateChart, 5000); |
| | | |
| | | window.addEventListener('resize', () => { |
| | | chart.resize(); |
| | | }); |
| | | |
| | | |
| | | }; |
| | | |
| | | // åå§åå个å¾è¡¨ |
| | | initChart('motorTemperatureChart', '产é', [ |
| | | { name: '产é', data: [], unit: 'pcs/h', baseValue: 175, fluctuation: 25, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('motorVibrationChart', 'ææç', [ |
| | | { name: 'ææç', data: [], unit: '%', baseValue: 0.15, fluctuation: 0.01, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('nozzleVacuumChart', 'å¸å´ç空åå', [ |
| | | { name: 'åå', data: [], unit: 'kPa', baseValue: -45, fluctuation: 5, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('nozzleFlowChart', 'å¸å´å¹æ°åå', [ |
| | | { name: 'åå', data: [], unit: 'kPa', baseValue: 20, fluctuation: 5, color: '#5470C6' }, |
| | | ]); |
| | | |
| | | initChart('placementSpeedChart', 'è´´è£
é度', [ |
| | | { 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: 1, color: '#5470C6' }, |
| | | { name: '湿度', data: ambientHumidityData, unit: '%', baseValue: 60, fluctuation: 5, color: '#91cc75' } |
| | | ]); |
| | | }; |
| | | |
| | | return { |
| | | deviceInfo, |
| | | healthData, |
| | | maintenanceData, |
| | | displayMaintenanceData, |
| | | sparePartData, |
| | | historyData, |
| | | goBack, |
| | | getUrgencyTagType, |
| | | getStatusTagType, |
| | | getTimelineItemType, |
| | | handleMaintenance, |
| | | maintenanceTable, |
| | | |
| | | }; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .device-detail-container { |
| | | padding: 16px; |
| | | background: #f0f2f5; |
| | | } |
| | | |
| | | .page-header-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .page-header-title { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .page-header-subtitle { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | .card-header { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .device-image-container { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 330px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .device-image { |
| | | max-width: 100%; |
| | | max-height: 100%; |
| | | object-fit: contain; |
| | | } |
| | | |
| | | .mt-4 { |
| | | margin-top: 1rem; |
| | | } |
| | | |
| | | .mb-4 { |
| | | margin-bottom: 1rem; |
| | | } |
| | | |
| | | |
| | | .custom-descriptions { |
| | | height: 357px; /* 设置æ´ä¸ªæè¿°å表çé«åº¦ */ |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__body) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table) { |
| | | height: 100%; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table tbody) { |
| | | height: 100%; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table tr) { |
| | | flex: 1; |
| | | display: flex; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table td) { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table .el-descriptions__label) { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .custom-descriptions :deep(.el-descriptions__table .el-descriptions__content) { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | /* |
| | | .table-container { |
| | | position: relative; |
| | | height: 246px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .table-container :deep(.el-table__body-wrapper) { |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .table-container :deep(.el-table__body) { |
| | | animation: scrollTable 20s linear infinite; |
| | | } |
| | | |
| | | .table-container:hover :deep(.el-table__body) { |
| | | animation-play-state: paused; |
| | | } |
| | | |
| | | @keyframes scrollTable { |
| | | 0% { |
| | | transform: translateY(0); |
| | | } |
| | | 100% { |
| | | transform: translateY(-100%); |
| | | } |
| | | } |
| | | |
| | | |
| | | .table-container :deep(.el-table__header-wrapper) { |
| | | position: sticky; |
| | | top: 0; |
| | | background: white; |
| | | z-index: 10; |
| | | } |
| | | */ |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="p-5"> |
| | | <!-- 设å¤å¥åº·ç¶æå¯è§å --> |
| | | <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> |
| | | <el-card shadow="hover" class="h-full"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤å¥åº·åº¦è¯å</span> |
| | | </div> |
| | | </template> |
| | | <!-- è¿éå°æ¾ç½®å¥åº·åº¦åå¸å¾è¡¨ --> |
| | | <div ref="chartRef" class="h-400 w-full"></div> |
| | | </el-card> |
| | | |
| | | <el-card shadow="hover" class="h-full"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å
³é®ææ </span> |
| | | </div> |
| | | </template> |
| | | <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> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- æ
é颿µä¸é¢è¦ --> |
| | | <el-card shadow="hover" class="mb-6"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ
é颿µä¸é¢è¦</span> |
| | | </div> |
| | | </template> |
| | | <div class="grid grid-cols-1 gap-4"> |
| | | <!-- è¿éå°æ¾ç½®é¢è¦è¡¨æ ¼ --> |
| | | <el-table |
| | | :data="warningData" |
| | | style="width: 100%" |
| | | :border="true" |
| | | stripe |
| | | > |
| | | <el-table-column prop="name" label="设å¤åç§°" /> |
| | | <el-table-column prop="component" label="å
³é®é¨ä»¶" /> |
| | | <el-table-column prop="indicator" label="é£é©ææ " /> |
| | | <el-table-column prop="value" label="å½åå¼" /> |
| | | <el-table-column prop="threshold" label="éå¼" /> |
| | | <el-table-column prop="status" label="ç¶æ"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStatusTagType(row.status)"> |
| | | {{ row.status }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="maintenanceSuggestion" label="ç»´æ¤å»ºè®®" /> |
| | | <el-table-column label="æä½" width="180"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" @click="handleDetail(row)">详æ
</el-button> |
| | | <el-button |
| | | link |
| | | type="primary" |
| | | @click="generateWorkOrder(row)" |
| | | :disabled="row.maintenanceSuggestion === 'ææ å»ºè®®'" |
| | | > |
| | | çæå·¥å |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- å¤ä»¶ä¿¡æ¯ --> |
| | | <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> |
| | | <!-- 设å¤é¨ä»¶å¯¿å½é¢æµ --> |
| | | <el-card shadow="hover" class="h-500"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤é¨ä»¶å¯¿å½é¢æµ</span> |
| | | </div> |
| | | </template> |
| | | <el-table |
| | | :data="lifePredictionData" |
| | | style="width: 100%" |
| | | :border="true" |
| | | stripe |
| | | height="410" |
| | | > |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" /> |
| | | <el-table-column prop="name" label="é¨ä»¶åç§°" /> |
| | | <el-table-column prop="predictedLife" label="颿µå¯¿å½ (天)" /> |
| | | <el-table-column prop="remainingDays" label="å©ä½å¯¿å½ (天)" /> |
| | | <el-table-column prop="lifeStatus" label="ç¶æ"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getLifeStatusTagType(row.remainingDays)"> |
| | | {{ getLifeStatus(row.remainingDays) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" @click="showLifePredictionDetail(row)">详æ
</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | |
| | | <!-- å¤ä»¶åºåä¸é¢è¦ --> |
| | | <el-card shadow="hover" class="h-500"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å¤ä»¶åºåä¸é¢è¦</span> |
| | | </div> |
| | | </template> |
| | | <el-table |
| | | :data="sparePartData" |
| | | style="width: 100%" |
| | | :border="true" |
| | | stripe |
| | | height="410" |
| | | > |
| | | <el-table-column prop="name" label="å¤ä»¶åç§°" /> |
| | | <el-table-column prop="currentStock" label="å½ååºå" /> |
| | | <el-table-column prop="safetyStock" label="å®å
¨åºå" /> |
| | | <el-table-column prop="predictedDemand" label="颿µéæ±" /> |
| | | <el-table-column prop="stockStatus" label="åºåç¶æ"> |
| | | <template #default="{ row }"> |
| | | <el-tag :type="getStockStatusTagType(row.currentStock, row.safetyStock)"> |
| | | {{ getStockStatus(row.currentStock, row.safetyStock) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="primary" @click="showSparePartDetail(row)">详æ
</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { onMounted, ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import * as echarts from 'echarts'; |
| | | |
| | | const chartRef = ref<HTMLElement | null>(null); |
| | | const router = useRouter(); |
| | | |
| | | const healthData = { |
| | | healthy: 85, |
| | | warning: 12, |
| | | critical: 3, |
| | | maintained: 92, |
| | | pendingWorkOrders: 5, |
| | | sparePartWarnings: 2, |
| | | }; |
| | | |
| | | // 模æé«é£é©è®¾å¤æ°æ® |
| | | const warningData = ref([ |
| | | { |
| | | key: '1', |
| | | name: 'SMTè´´çæº', |
| | | type: 'SMTæºå¨', |
| | | component: 'å·¦3-Head', |
| | | indicator: 'ç空åå', |
| | | value: 32, |
| | | threshold: 35, |
| | | 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: 28, |
| | | threshold: 30, |
| | | status: 'ä½é£é©', |
| | | maintenanceSuggestion: 'å»ºè®®æ£æ¥æ¶²åç³»ç»å¯å°ä»¶', |
| | | maintenanceType: 'æ£æ¥ç»´æ¤', |
| | | }, |
| | | ]); |
| | | |
| | | // 模æå¤ä»¶æ°æ® |
| | | const sparePartData = ref([ |
| | | { |
| | | key: '1', |
| | | name: 'ä¼ é带轴æ¿', |
| | | currentStock: 10, |
| | | safetyStock: 15, |
| | | predictedDemand: 8, |
| | | }, |
| | | { |
| | | key: '2', |
| | | name: 'ä¸»è½´æ¶¦æ»æ²¹', |
| | | currentStock: 2, |
| | | safetyStock: 3, |
| | | predictedDemand: 1, |
| | | }, |
| | | { |
| | | key: '3', |
| | | name: 'æ¶²åç³»ç»å¯å°ä»¶', |
| | | currentStock: 20, |
| | | safetyStock: 25, |
| | | predictedDemand: 5, |
| | | }, |
| | | { |
| | | key: '4', |
| | | name: 'çµæºç¢³å·', |
| | | currentStock: 8, |
| | | safetyStock: 10, |
| | | predictedDemand: 2, |
| | | }, |
| | | { |
| | | key: '5', |
| | | name: 'å·ç æºæ²¹å¢¨', |
| | | currentStock: 1, |
| | | safetyStock: 1, |
| | | predictedDemand: 1, |
| | | }, |
| | | { |
| | | key: '6', |
| | | name: 'é¢ç½æ¸
æ´æ¶²', |
| | | currentStock: 2, |
| | | safetyStock: 1, |
| | | predictedDemand: 1, |
| | | }, |
| | | { |
| | | key: '7', |
| | | name: 'åå¯¸ç²¾å¯æ»¤è¯', |
| | | currentStock: 20, |
| | | safetyStock: 4, |
| | | predictedDemand: 0, |
| | | }, |
| | | { |
| | | key: '8', |
| | | name: 'åå¯¸ç²¾å¯æ»¤è¯', |
| | | currentStock: 20, |
| | | safetyStock: 4, |
| | | predictedDemand: 0, |
| | | }, |
| | | { |
| | | key: '9', |
| | | name: 'åå¯¸ç²¾å¯æ»¤è¯', |
| | | currentStock: 20, |
| | | safetyStock: 4, |
| | | predictedDemand: 0, |
| | | }, |
| | | { |
| | | key: '10', |
| | | name: 'åå¯¸ç²¾å¯æ»¤è¯', |
| | | currentStock: 20, |
| | | safetyStock: 4, |
| | | predictedDemand: 0, |
| | | }, |
| | | ]); |
| | | |
| | | const lifePredictionData = ref([ |
| | | { key: '1', deviceName: 'SMTè´´çæº', componentName: 'ä¼ é带', name: 'ç空åçå¨', predictedLife: 700, remainingDays: 83 }, |
| | | { key: '2', deviceName: 'å
è£
æº', componentName: 'ä¼ é带', name: 'åçä¸', predictedLife: 260, remainingDays: 61 }, |
| | | { key: '3', deviceName: 'æ¿å
ææ æº', componentName: 'ä¼ é带', name: 'å·å´æ°´', predictedLife: 180, remainingDays: 43 }, |
| | | { key: '4', deviceName: 'é¢ç½æ¸
æ´æº', componentName: 'ä¼ é带', name: 'æ¸
æ´æ¶²', predictedLife: 180, remainingDays: 95 }, |
| | | { key: '5', deviceName: 'é¢ç½æ¸
æ´æº', componentName: 'ä¼ é带', name: '滤è¯', predictedLife: 180, remainingDays: 95 }, |
| | | { key: '6', deviceName: 'ç«¯åæº', componentName: 'ä¼ é带', name: 'åç', predictedLife: 180, remainingDays: 112 }, |
| | | { key: '7', deviceName: 'çµèå¥çº¿æº', componentName: 'ä¼ é带', name: 'åç', predictedLife: 180, remainingDays: 107 }, |
| | | { key: '8', deviceName: 'CNCå å·¥ä¸å¿', componentName: '主轴', name: 'åç夹', predictedLife: 1100, remainingDays: 130 }, |
| | | { key: '9', deviceName: 'ç©ºåæº', componentName: 'å®å
¨é', name: 'å®å
¨é', predictedLife: 365, remainingDays: 130 }, |
| | | { key: '10', deviceName: 'å·ç æº', componentName: 'ä¼ é带', name: '墨水', predictedLife: 365, remainingDays: 184 }, |
| | | { key: '11', deviceName: 'æ³¨å¡æº', componentName: 'æ¶²åç³»ç»', name: 'æ¶²åæ²¹', predictedLife: 1100, remainingDays: 395 }, |
| | | { key: '12', deviceName: 'çæ¥æºå¨äºº', componentName: 'çæª', name: 'çéè¯', predictedLife: 1000, remainingDays: 512 } |
| | | ]); |
| | | |
| | | const getStatusTagType = (status) => { |
| | | switch (status) { |
| | | case 'é«é£é©': return 'danger'; |
| | | case 'ä¸é£é©': return 'warning'; |
| | | case 'ä½é£é©': return 'primary'; |
| | | default: return 'success'; |
| | | } |
| | | }; |
| | | |
| | | const getLifeStatus = (remainingDays) => { |
| | | if (remainingDays <= 90) { |
| | | return 'å³å°å°æ'; |
| | | } else if (remainingDays <= 150) { |
| | | return '䏿é¢è¦'; |
| | | } else { |
| | | return '寿å½å
è¶³'; |
| | | } |
| | | }; |
| | | |
| | | const getLifeStatusTagType = (remainingDays) => { |
| | | if (remainingDays <= 90) { |
| | | return 'warning'; |
| | | } else if (remainingDays <= 150) { |
| | | return 'primary'; |
| | | } else { |
| | | return 'success'; |
| | | } |
| | | }; |
| | | |
| | | const showLifePredictionDetail = (record) => { |
| | | ElMessage.info(`å¤ä»¶åç§°: ${record.name}, 颿µå¯¿å½: ${record.predictedLife}天, å©ä½å¯¿å½: ${record.remainingDays}天`); |
| | | }; |
| | | |
| | | const getStockStatus = (currentStock, safetyStock) => { |
| | | if (currentStock < safetyStock) { |
| | | return 'åºåé¢è¦'; |
| | | } else { |
| | | return 'åºåå
è¶³'; |
| | | } |
| | | }; |
| | | |
| | | const getStockStatusTagType = (currentStock, safetyStock) => { |
| | | if (currentStock < safetyStock) { |
| | | return 'warning'; |
| | | } else { |
| | | return 'success'; |
| | | } |
| | | }; |
| | | |
| | | const showSparePartDetail = (record) => { |
| | | ElMessage.info(`å¤ä»¶åç§°: ${record.name}, å½ååºå: ${record.currentStock}, å®å
¨åºå: ${record.safetyStock}, 颿µéæ±: ${record.predictedDemand}`); |
| | | }; |
| | | |
| | | const generateWorkOrder = (record) => { |
| | | // å·¥åçæé»è¾ |
| | | ElMessage.success(`已为 ${record.name} çæå·¥å`); |
| | | }; |
| | | |
| | | const handleDetail = (record) => { |
| | | router.push({ path: '/ai/detail', query: { deviceId: 15 } }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | if (chartRef.value) { |
| | | const myChart = echarts.init(chartRef.value); |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: ['10å', '9å', '8å', '7å', '6å', '5å', '4å', '3å', '2å', '1å'], |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'è®¾å¤æ°é', |
| | | minInterval: 1 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'è®¾å¤æ°é', |
| | | type: 'bar', |
| | | barWidth: '60%', |
| | | data: [ |
| | | { value: 48, itemStyle: { color: '#52c41a' } }, |
| | | { value: 53, itemStyle: { color: '#52c41a' } }, |
| | | { value: 37, itemStyle: { color: '#52c41a' } }, |
| | | { value: 29, itemStyle: { color: '#52c41a' } }, |
| | | { value: 21, itemStyle: { color: '#52c41a' } }, |
| | | { value: 8, itemStyle: { color: '#faad14' } }, |
| | | { value: 5, itemStyle: { color: '#faad14' } }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | | position: 'top', |
| | | formatter: '{c}å°' |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | myChart.setOption(option); |
| | | |
| | | window.addEventListener('resize', () => { |
| | | myChart.resize(); |
| | | }); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .app-container { |
| | | padding: 0; |
| | | } |
| | | |
| | | .h-500 { |
| | | height: 500px; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .grid { |
| | | display: grid; |
| | | } |
| | | |
| | | .grid-cols-1 { |
| | | grid-template-columns: repeat(1, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .grid-cols-2 { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .gap-4 { |
| | | gap: 1rem; |
| | | } |
| | | |
| | | .mb-6 { |
| | | margin-bottom: 1.5rem; |
| | | } |
| | | |
| | | .p-5 { |
| | | padding: 1.25rem; |
| | | } |
| | | |
| | | .w-full { |
| | | width: 100%; |
| | | } |
| | | |
| | | .h-400 { |
| | | height: 400px; |
| | | } |
| | | |
| | | .bg-blue-50 { |
| | | background-color: #eff6ff; |
| | | } |
| | | |
| | | .bg-yellow-50 { |
| | | background-color: #fffbeb; |
| | | } |
| | | |
| | | .bg-red-50 { |
| | | background-color: #fef2f2; |
| | | } |
| | | |
| | | .bg-green-50 { |
| | | background-color: #f0fdf4; |
| | | } |
| | | |
| | | .rounded { |
| | | border-radius: 0.25rem; |
| | | } |
| | | |
| | | .text-sm { |
| | | font-size: 0.875rem; |
| | | } |
| | | |
| | | .text-xl { |
| | | font-size: 1.25rem; |
| | | } |
| | | |
| | | .font-bold { |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .text-gray-600 { |
| | | color: #4b5563; |
| | | } |
| | | |
| | | @media (min-width: 768px) { |
| | | .md\:grid-cols-2 { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | </style> |