| | |
| | | # 页颿 é¢
|
| | | VITE_APP_TITLE = æ¿å
å管çç³»ç»
|
| | | VITE_APP_TITLE = 设å¤å¨çº¿çæµè¯æå维修系ç»
|
| | |
|
| | | # å¼åç¯å¢é
ç½®
|
| | | VITE_APP_ENV = 'development'
|
| | |
| | | # 页颿 é¢
|
| | | VITE_APP_TITLE = æ¿å
å管çç³»ç»
|
| | | VITE_APP_TITLE = 设å¤å¨çº¿çæµè¯æå维修系ç»
|
| | |
|
| | | # ç产ç¯å¢é
ç½®
|
| | | VITE_APP_ENV = 'production'
|
| | |
| | | app.config.globalProperties.addDateRange = addDateRange;
|
| | | app.config.globalProperties.selectDictLabel = selectDictLabel;
|
| | | app.config.globalProperties.selectDictLabels = selectDictLabels;
|
| | | app.config.globalProperties.javaApi = "http://10.136.12.71:8018";
|
| | | app.config.globalProperties.javaApi = "http://10.136.12.71:8020";
|
| | | app.config.globalProperties.HaveJson = (val) => {
|
| | | return JSON.parse(JSON.stringify(val));
|
| | | };
|
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="dashboard-container"> |
| | | <!-- ç»è®¡å¡ç --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">设å¤å¨çº¿ç</div> |
| | | <div class="card-value">{{ onlineRate }}%</div> |
| | | <div class="card-desc">å½åå¨çº¿è®¾å¤æ°ï¼{{ onlineCount }} / {{ totalDevices }}</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">æ
éé¢è¦æ°</div> |
| | | <div class="card-value">{{ warningCount }}</div> |
| | | <div class="card-desc">é«é£é©ï¼{{ highRiskCount }} | ä¸é£é©ï¼{{ mediumRiskCount }} | ä½é£é©ï¼{{ lowRiskCount }}</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">å¾
å¤çç»´ä¿®å</div> |
| | | <div class="card-value">{{ pendingOrders }}</div> |
| | | <div class="card-desc">å¤çä¸ï¼{{ processingOrders }} | 已宿ï¼{{ completedOrders }}</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">éç¹è®¾å¤è¿è¡ç¶æ</div> |
| | | <div class="card-value">{{ normalDevices }} æ£å¸¸</div> |
| | | <div class="card-desc">å¼å¸¸ï¼{{ abnormalDevices }} | æ
éï¼{{ faultDevices }}</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- å¾è¡¨åºå --> |
| | | <el-row :gutter="20" style="margin-top: 20px;"> |
| | | <!-- 设å¤å¥åº·åº¦è¶å¿å¾ --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤å¥åº·åº¦è¶å¿å¾</span> |
| | | </div> |
| | | </template> |
| | | <div ref="healthChartRef" class="chart-container"></div> |
| | | </el-card> |
| | | </el-col> |
| | | <!-- è¿7æ¥æ
éç±»åç»è®¡ --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è¿7æ¥æ
éç±»åç»è®¡</span> |
| | | </div> |
| | | </template> |
| | | <div ref="faultChartRef" class="chart-container"></div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- éç¹è®¾å¤è¿è¡ç¶æå¡ç --> |
| | | <el-card shadow="hover" style="margin-top: 20px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>éç¹è®¾å¤è¿è¡ç¶æ</span> |
| | | </div> |
| | | </template> |
| | | <el-table :data="keyDevices" stripe style="width: 100%"> |
| | | <el-table-column prop="name" label="设å¤åç§°" width="180"></el-table-column> |
| | | <el-table-column prop="model" label="åå·" width="120"></el-table-column> |
| | | <el-table-column prop="ip" label="IPå°å" width="150"></el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'online' ? 'success' : scope.row.status === 'warning' ? 'warning' : 'danger'"> |
| | | {{ scope.row.status === 'online' ? 'å¨çº¿' : scope.row.status === 'warning' ? 'é¢è¦' : 'æ
é' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="temperature" label="温度(â)" width="100"></el-table-column> |
| | | <el-table-column prop="pressure" label="åå(MPa)" width="100"></el-table-column> |
| | | <el-table-column prop="speed" label="转é(rpm)" width="100"></el-table-column> |
| | | <el-table-column prop="health" label="å¥åº·åº¦" width="120"> |
| | | <template #default="scope"> |
| | | <el-progress :percentage="scope.row.health" :stroke-width="10"></el-progress> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | |
| | | // ç»è®¡æ°æ® |
| | | const onlineRate = ref(85) |
| | | const onlineCount = ref(170) |
| | | const totalDevices = ref(200) |
| | | const warningCount = ref(23) |
| | | const highRiskCount = ref(5) |
| | | const mediumRiskCount = ref(12) |
| | | const lowRiskCount = ref(6) |
| | | const pendingOrders = ref(15) |
| | | const processingOrders = ref(8) |
| | | const completedOrders = ref(45) |
| | | const normalDevices = ref(162) |
| | | const abnormalDevices = ref(18) |
| | | const faultDevices = ref(10) |
| | | |
| | | // éç¹è®¾å¤å表 |
| | | const keyDevices = ref([ |
| | | { name: 'ç©ºåæºA-001', model: 'KA-200', ip: '192.168.1.101', status: 'online', temperature: 42, pressure: 0.8, speed: 1450, health: 92 }, |
| | | { name: 'å·å´å¡B-002', model: 'CT-300', ip: '192.168.1.102', status: 'warning', temperature: 58, pressure: 0.6, speed: 980, health: 75 }, |
| | | { name: 'æ°´æ³µC-003', model: 'WP-150', ip: '192.168.1.103', status: 'online', temperature: 38, pressure: 1.2, speed: 1200, health: 88 }, |
| | | { name: 'åçµæºD-004', model: 'GE-500', ip: '192.168.1.104', status: 'danger', temperature: 75, pressure: 0.5, speed: 1500, health: 60 }, |
| | | { name: 'ååå¨E-005', model: 'TR-1000', ip: '192.168.1.105', status: 'online', temperature: 45, pressure: 0, speed: 0, health: 95 } |
| | | ]) |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const healthChartRef = ref(null) |
| | | const faultChartRef = ref(null) |
| | | let healthChart = null |
| | | let faultChart = null |
| | | |
| | | // å¥åº·åº¦è¶å¿æ°æ® |
| | | const healthTrendData = { |
| | | dates: ['12-10', '12-11', '12-12', '12-13', '12-14', '12-15', '12-16'], |
| | | values: [88, 90, 85, 87, 92, 91, 93] |
| | | } |
| | | |
| | | // æ
éç±»åç»è®¡æ°æ® |
| | | const faultTypeData = { |
| | | types: ['温度å¼å¸¸', 'ååè¶
æ ', '转éå¼å¸¸', 'æ¯å¨è¿å¤§', 'å
¶ä»'], |
| | | values: [15, 8, 12, 6, 3] |
| | | } |
| | | |
| | | // åå§åå¥åº·åº¦è¶å¿å¾ |
| | | const initHealthChart = () => { |
| | | if (healthChartRef.value) { |
| | | healthChart = echarts.init(healthChartRef.value) |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'cross', |
| | | label: { |
| | | backgroundColor: '#6a7985' |
| | | } |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | boundaryGap: false, |
| | | data: healthTrendData.dates |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | min: 70, |
| | | max: 100, |
| | | name: 'å¥åº·åº¦(%)' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'å¥åº·åº¦', |
| | | type: 'line', |
| | | stack: 'æ»é', |
| | | areaStyle: {}, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: healthTrendData.values, |
| | | itemStyle: { |
| | | color: '#67c23a' |
| | | }, |
| | | lineStyle: { |
| | | width: 3 |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | healthChart.setOption(option) |
| | | } |
| | | } |
| | | |
| | | // åå§åæ
éç±»åç»è®¡é¥¼å¾ |
| | | const initFaultChart = () => { |
| | | if (faultChartRef.value) { |
| | | faultChart = echarts.init(faultChartRef.value) |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'æ
éç±»å', |
| | | type: 'pie', |
| | | radius: '50%', |
| | | data: faultTypeData.types.map((type, index) => ({ |
| | | name: type, |
| | | value: faultTypeData.values[index] |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | faultChart.setOption(option) |
| | | } |
| | | } |
| | | |
| | | // çå¬çªå£å¤§å°ååï¼è°æ´å¾è¡¨å¤§å° |
| | | const handleResize = () => { |
| | | healthChart?.resize() |
| | | faultChart?.resize() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | initHealthChart() |
| | | initFaultChart() |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | window.removeEventListener('resize', handleResize) |
| | | healthChart?.dispose() |
| | | faultChart?.dispose() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dashboard-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .statistics-card { |
| | | height: 180px; |
| | | } |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100%; |
| | | justify-content: center; |
| | | align-items: center; |
| | | } |
| | | |
| | | .card-title { |
| | | font-size: 16px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .card-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .card-desc { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .chart-container { |
| | | width: 100%; |
| | | height: 350px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="device-management-container"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤ç®¡ç</span> |
| | | <div class="header-buttons"> |
| | | <el-button type="primary" @click="showAddDeviceDialog"> |
| | | <el-icon-plus /> æ·»å è®¾å¤ |
| | | </el-button> |
| | | <el-button @click="exportDevices"> |
| | | <el-icon-download /> å¯¼åº |
| | | </el-button> |
| | | <el-button @click="showImportDialog"> |
| | | <el-icon-upload /> 导å
¥ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- ç鿡件 --> |
| | | <el-form :inline="true" :model="filterForm" class="device-filter-form"> |
| | | <el-form-item label="设å¤åç§°"> |
| | | <el-input v-model="filterForm.name" placeholder="请è¾å
¥è®¾å¤åç§°" clearable></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="åå·"> |
| | | <el-input v-model="filterForm.model" placeholder="请è¾å
¥åå·" clearable></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ"> |
| | | <el-select v-model="filterForm.status" placeholder="è¯·éæ©ç¶æ" clearable> |
| | | <el-option label="å¨çº¿" value="online"></el-option> |
| | | <el-option label="离线" value="offline"></el-option> |
| | | <el-option label="æ
é" value="fault"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleFilter">æ¥è¯¢</el-button> |
| | | <el-button @click="resetFilter">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- 设å¤å表 --> |
| | | <el-table :data="filteredDevices" stripe style="width: 100%" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55"></el-table-column> |
| | | <el-table-column prop="id" label="设å¤ID" width="100"></el-table-column> |
| | | <el-table-column prop="name" label="设å¤åç§°" width="180"></el-table-column> |
| | | <el-table-column prop="model" label="åå·" width="120"></el-table-column> |
| | | <el-table-column prop="ip" label="IPå°å" width="150"></el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'online' ? 'success' : scope.row.status === 'offline' ? 'info' : 'danger'"> |
| | | {{ scope.row.status === 'online' ? 'å¨çº¿' : scope.row.status === 'offline' ? '离线' : 'æ
é' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="location" label="å®è£
ä½ç½®" width="180"></el-table-column> |
| | | <el-table-column prop="installDate" label="å®è£
æ¥æ" width="150"></el-table-column> |
| | | <el-table-column prop="manufacturer" label="å¶é å" width="150"></el-table-column> |
| | | <el-table-column label="æä½" width="220" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="text" size="small" @click="showDeviceDetail(scope.row)"> |
| | | 详æ
|
| | | </el-button> |
| | | <el-button type="text" size="small" @click="showEditDeviceDialog(scope.row)"> |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button type="text" size="small" @click="handleDelete(scope.row)"> |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | background |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="filteredDevices.length" |
| | | :current-page="currentPage" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :page-size="pageSize" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | ></el-pagination> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ·»å 设å¤å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="addDeviceDialogVisible" title="æ·»å 设å¤" width="600px"> |
| | | <el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="100px"> |
| | | <el-form-item label="设å¤åç§°" prop="name"> |
| | | <el-input v-model="deviceForm.name" placeholder="请è¾å
¥è®¾å¤åç§°"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="åå·" prop="model"> |
| | | <el-input v-model="deviceForm.model" placeholder="请è¾å
¥åå·"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="IPå°å" prop="ip"> |
| | | <el-input v-model="deviceForm.ip" placeholder="请è¾å
¥IPå°å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å®è£
ä½ç½®" prop="location"> |
| | | <el-input v-model="deviceForm.location" placeholder="请è¾å
¥å®è£
ä½ç½®"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å¶é å" prop="manufacturer"> |
| | | <el-input v-model="deviceForm.manufacturer" placeholder="请è¾å
¥å¶é å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å®è£
æ¥æ" prop="installDate"> |
| | | <el-date-picker v-model="deviceForm.installDate" type="date" placeholder="éæ©å®è£
æ¥æ" style="width: 100%"></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="deviceForm.status" placeholder="è¯·éæ©ç¶æ"> |
| | | <el-option label="å¨çº¿" value="online"></el-option> |
| | | <el-option label="离线" value="offline"></el-option> |
| | | <el-option label="æ
é" value="fault"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="addDeviceDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleAddDevice">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- ç¼è¾è®¾å¤å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="editDeviceDialogVisible" title="ç¼è¾è®¾å¤" width="600px"> |
| | | <el-form :model="deviceForm" :rules="deviceRules" ref="deviceFormRef" label-width="100px"> |
| | | <el-form-item label="设å¤åç§°" prop="name"> |
| | | <el-input v-model="deviceForm.name" placeholder="请è¾å
¥è®¾å¤åç§°"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="åå·" prop="model"> |
| | | <el-input v-model="deviceForm.model" placeholder="请è¾å
¥åå·"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="IPå°å" prop="ip"> |
| | | <el-input v-model="deviceForm.ip" placeholder="请è¾å
¥IPå°å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å®è£
ä½ç½®" prop="location"> |
| | | <el-input v-model="deviceForm.location" placeholder="请è¾å
¥å®è£
ä½ç½®"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å¶é å" prop="manufacturer"> |
| | | <el-input v-model="deviceForm.manufacturer" placeholder="请è¾å
¥å¶é å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å®è£
æ¥æ" prop="installDate"> |
| | | <el-date-picker v-model="deviceForm.installDate" type="date" placeholder="éæ©å®è£
æ¥æ" style="width: 100%"></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="deviceForm.status" placeholder="è¯·éæ©ç¶æ"> |
| | | <el-option label="å¨çº¿" value="online"></el-option> |
| | | <el-option label="离线" value="offline"></el-option> |
| | | <el-option label="æ
é" value="fault"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="editDeviceDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleEditDevice">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 设å¤è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="deviceDetailDialogVisible" title="设å¤è¯¦æ
" width="600px"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="设å¤åç§°">{{ selectedDevice.name }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ID">{{ selectedDevice.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="åå·">{{ selectedDevice.model }}</el-descriptions-item> |
| | | <el-descriptions-item label="IPå°å">{{ selectedDevice.ip }}</el-descriptions-item> |
| | | <el-descriptions-item label="ç¶æ"> |
| | | <el-tag :type="selectedDevice.status === 'online' ? 'success' : selectedDevice.status === 'offline' ? 'info' : 'danger'"> |
| | | {{ selectedDevice.status === 'online' ? 'å¨çº¿' : selectedDevice.status === 'offline' ? '离线' : 'æ
é' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å®è£
ä½ç½®">{{ selectedDevice.location }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¶é å">{{ selectedDevice.manufacturer }}</el-descriptions-item> |
| | | <el-descriptions-item label="å®è£
æ¥æ">{{ selectedDevice.installDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´">{{ selectedDevice.createTime }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="deviceDetailDialogVisible = false">å
³é</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="importDialogVisible" title="导å
¥è®¾å¤" width="400px"> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :on-change="handleFileChange" |
| | | :auto-upload="false" |
| | | accept=".xlsx,.xls" |
| | | > |
| | | <el-button type="primary">éæ©æä»¶</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | åªè½ä¸ä¼ xlsx/xls æä»¶ï¼ä¸ä¸è¶
è¿ 2MB |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="importDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleImport">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed } from 'vue' |
| | | |
| | | // 设å¤åè¡¨æ°æ® |
| | | const devices = ref([ |
| | | { |
| | | id: 'D001', |
| | | name: 'ç©ºåæºA-001', |
| | | model: 'KA-200', |
| | | ip: '192.168.1.101', |
| | | status: 'online', |
| | | location: '车é´A-1åº', |
| | | manufacturer: 'åº·æ®æ¯', |
| | | installDate: '2023-05-10', |
| | | createTime: '2023-05-10 10:30:00' |
| | | }, |
| | | { |
| | | id: 'D002', |
| | | name: 'å·å´å¡B-002', |
| | | model: 'CT-300', |
| | | ip: '192.168.1.102', |
| | | status: 'warning', |
| | | location: '车é´B-2åº', |
| | | manufacturer: 'è¯æº', |
| | | installDate: '2023-06-15', |
| | | createTime: '2023-06-15 14:20:00' |
| | | }, |
| | | { |
| | | id: 'D003', |
| | | name: 'æ°´æ³µC-003', |
| | | model: 'WP-150', |
| | | ip: '192.168.1.103', |
| | | status: 'online', |
| | | location: '车é´C-3åº', |
| | | manufacturer: 'æ ¼å
°å¯', |
| | | installDate: '2023-07-20', |
| | | createTime: '2023-07-20 09:15:00' |
| | | }, |
| | | { |
| | | id: 'D004', |
| | | name: 'åçµæºD-004', |
| | | model: 'GE-500', |
| | | ip: '192.168.1.104', |
| | | status: 'fault', |
| | | location: 'æºæ¿', |
| | | manufacturer: 'å¡ç¹å½¼å', |
| | | installDate: '2023-08-25', |
| | | createTime: '2023-08-25 16:45:00' |
| | | }, |
| | | { |
| | | id: 'D005', |
| | | name: 'ååå¨E-005', |
| | | model: 'TR-1000', |
| | | ip: '192.168.1.105', |
| | | status: 'online', |
| | | location: 'é
çµæ¿', |
| | | manufacturer: 'ABB', |
| | | installDate: '2023-09-30', |
| | | createTime: '2023-09-30 11:20:00' |
| | | } |
| | | ]) |
| | | |
| | | // çé表å |
| | | const filterForm = ref({ |
| | | name: '', |
| | | model: '', |
| | | status: '' |
| | | }) |
| | | |
| | | // åé¡µæ°æ® |
| | | const currentPage = ref(1) |
| | | const pageSize = ref(10) |
| | | |
| | | // å¯¹è¯æ¡ç¶æ |
| | | const addDeviceDialogVisible = ref(false) |
| | | const editDeviceDialogVisible = ref(false) |
| | | const deviceDetailDialogVisible = ref(false) |
| | | const importDialogVisible = ref(false) |
| | | |
| | | // 设å¤è¡¨åæ°æ® |
| | | const deviceForm = ref({ |
| | | id: '', |
| | | name: '', |
| | | model: '', |
| | | ip: '', |
| | | status: 'online', |
| | | location: '', |
| | | manufacturer: '', |
| | | installDate: '' |
| | | }) |
| | | |
| | | // 表åéªè¯è§å |
| | | const deviceRules = ref({ |
| | | name: [{ required: true, message: '请è¾å
¥è®¾å¤åç§°', trigger: 'blur' }], |
| | | model: [{ required: true, message: '请è¾å
¥åå·', trigger: 'blur' }], |
| | | ip: [{ required: true, message: '请è¾å
¥IPå°å', trigger: 'blur' }], |
| | | location: [{ required: true, message: '请è¾å
¥å®è£
ä½ç½®', trigger: 'blur' }], |
| | | manufacturer: [{ required: true, message: '请è¾å
¥å¶é å', trigger: 'blur' }], |
| | | installDate: [{ required: true, message: 'è¯·éæ©å®è£
æ¥æ', trigger: 'change' }], |
| | | status: [{ required: true, message: 'è¯·éæ©ç¶æ', trigger: 'change' }] |
| | | }) |
| | | |
| | | // 表åå¼ç¨ |
| | | const deviceFormRef = ref(null) |
| | | |
| | | // éä¸çè®¾å¤ |
| | | const selectedDevice = ref({}) |
| | | |
| | | // éä¸ç设å¤å表ï¼ç¨äºæ¹éæä½ï¼ |
| | | const selectedDevices = ref([]) |
| | | |
| | | // 导å
¥çæä»¶ |
| | | const importFile = ref(null) |
| | | |
| | | // è¿æ»¤åç设å¤å表 |
| | | const filteredDevices = computed(() => { |
| | | let result = [...devices.value] |
| | | |
| | | // æåç§°çé |
| | | if (filterForm.value.name) { |
| | | result = result.filter(device => device.name.includes(filterForm.value.name)) |
| | | } |
| | | |
| | | // æåå·çé |
| | | if (filterForm.value.model) { |
| | | result = result.filter(device => device.model.includes(filterForm.value.model)) |
| | | } |
| | | |
| | | // æç¶æçé |
| | | if (filterForm.value.status) { |
| | | result = result.filter(device => device.status === filterForm.value.status) |
| | | } |
| | | |
| | | return result |
| | | }) |
| | | |
| | | // æ¾ç¤ºæ·»å 设å¤å¯¹è¯æ¡ |
| | | const showAddDeviceDialog = () => { |
| | | // é置表å |
| | | deviceForm.value = { |
| | | id: '', |
| | | name: '', |
| | | model: '', |
| | | ip: '', |
| | | status: 'online', |
| | | location: '', |
| | | manufacturer: '', |
| | | installDate: '' |
| | | } |
| | | addDeviceDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¾ç¤ºç¼è¾è®¾å¤å¯¹è¯æ¡ |
| | | const showEditDeviceDialog = (device) => { |
| | | deviceForm.value = { ...device } |
| | | editDeviceDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¾ç¤ºè®¾å¤è¯¦æ
|
| | | const showDeviceDetail = (device) => { |
| | | selectedDevice.value = { ...device } |
| | | deviceDetailDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¾ç¤ºå¯¼å
¥å¯¹è¯æ¡ |
| | | const showImportDialog = () => { |
| | | importDialogVisible.value = true |
| | | } |
| | | |
| | | // å¤çæ·»å è®¾å¤ |
| | | const handleAddDevice = () => { |
| | | // æ¨¡ææ·»å è®¾å¤ |
| | | const newDevice = { |
| | | ...deviceForm.value, |
| | | id: `D${String(devices.value.length + 1).padStart(3, '0')}`, |
| | | createTime: new Date().toLocaleString() |
| | | } |
| | | devices.value.push(newDevice) |
| | | addDeviceDialogVisible.value = false |
| | | ElMessage.success('è®¾å¤æ·»å æå') |
| | | } |
| | | |
| | | // å¤çç¼è¾è®¾å¤ |
| | | const handleEditDevice = () => { |
| | | // 模æç¼è¾è®¾å¤ |
| | | const index = devices.value.findIndex(device => device.id === deviceForm.value.id) |
| | | if (index !== -1) { |
| | | devices.value[index] = { ...deviceForm.value } |
| | | editDeviceDialogVisible.value = false |
| | | ElMessage.success('设å¤ç¼è¾æå') |
| | | } |
| | | } |
| | | |
| | | // å¤çå é¤è®¾å¤ |
| | | const handleDelete = (device) => { |
| | | ElMessageBox.confirm('ç¡®å®è¦å é¤è¯¥è®¾å¤åï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | // 模æå é¤è®¾å¤ |
| | | const index = devices.value.findIndex(item => item.id === device.id) |
| | | if (index !== -1) { |
| | | devices.value.splice(index, 1) |
| | | ElMessage.success('设å¤å 餿å') |
| | | } |
| | | }).catch(() => { |
| | | // åæ¶å é¤ |
| | | }) |
| | | } |
| | | |
| | | // å¤ççé |
| | | const handleFilter = () => { |
| | | // çéé»è¾å·²ç»å¨computedä¸å®ç° |
| | | } |
| | | |
| | | // éç½®ç鿡件 |
| | | const resetFilter = () => { |
| | | filterForm.value = { |
| | | name: '', |
| | | model: '', |
| | | status: '' |
| | | } |
| | | } |
| | | |
| | | // å¤çå页大å°åå |
| | | const handleSizeChange = (size) => { |
| | | pageSize.value = size |
| | | currentPage.value = 1 |
| | | } |
| | | |
| | | // å¤çå½å页åå |
| | | const handleCurrentChange = (current) => { |
| | | currentPage.value = current |
| | | } |
| | | |
| | | // å¤çæä»¶ååï¼å¯¼å
¥ï¼ |
| | | const handleFileChange = (file) => { |
| | | importFile.value = file |
| | | } |
| | | |
| | | // å¤ç导å
¥ |
| | | const handleImport = () => { |
| | | // 模æå¯¼å
¥ |
| | | if (importFile.value) { |
| | | importDialogVisible.value = false |
| | | ElMessage.success('设å¤å¯¼å
¥æå') |
| | | importFile.value = null |
| | | } else { |
| | | ElMessage.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶') |
| | | } |
| | | } |
| | | |
| | | // 导åºè®¾å¤ |
| | | const exportDevices = () => { |
| | | // 模æå¯¼åº |
| | | ElMessage.success('设å¤å¯¼åºæå') |
| | | } |
| | | |
| | | // å¤çéæ©ååï¼ç¨äºæ¹éæä½ï¼ |
| | | const handleSelectionChange = (selection) => { |
| | | selectedDevices.value = selection |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .device-management-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .header-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .device-filter-form { |
| | | margin-bottom: 20px; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .pagination-container { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | :deep(.el-icon-plus), |
| | | :deep(.el-icon-download), |
| | | :deep(.el-icon-upload) { |
| | | margin-right: 5px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="fault-diagnosis-container"> |
| | | <el-row :gutter="20"> |
| | | <!-- å·¦ä¾§ï¼æ
éé¢è¦å表 --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ
éé¢è¦å表</span> |
| | | </div> |
| | | </template> |
| | | <el-table :data="warningList" stripe style="width: 100%" @row-click="handleWarningClick"> |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" width="180"></el-table-column> |
| | | <el-table-column prop="warningType" label="é¢è¦ç±»å" width="120"></el-table-column> |
| | | <el-table-column prop="riskLevel" label="é£é©ç级" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.riskLevel === 'high' ? 'danger' : scope.row.riskLevel === 'medium' ? 'warning' : 'info'"> |
| | | {{ scope.row.riskLevel === 'high' ? 'é«' : scope.row.riskLevel === 'medium' ? 'ä¸' : 'ä½' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="occurTime" label="åçæ¶é´" width="180"></el-table-column> |
| | | <el-table-column prop="status" label="å¤çç¶æ" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'pending' ? 'warning' : 'success'"> |
| | | {{ scope.row.status === 'pending' ? 'å¾
å¤ç' : 'å·²å¤ç' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | |
| | | <!-- æ
éåå²è®°å½æ¥è¯¢ --> |
| | | <el-card shadow="hover" style="margin-top: 20px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ
éåå²è®°å½</span> |
| | | </div> |
| | | </template> |
| | | <el-form :inline="true" :model="historyFilterForm" class="history-filter-form"> |
| | | <el-form-item label="设å¤"> |
| | | <el-select v-model="historyFilterForm.deviceId" placeholder="è¯·éæ©è®¾å¤" clearable> |
| | | <el-option |
| | | v-for="device in devices" |
| | | :key="device.id" |
| | | :label="device.name" |
| | | :value="device.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶é´èå´"> |
| | | <el-date-picker |
| | | v-model="historyTimeRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleHistorySearch">æ¥è¯¢</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <el-table :data="historyList" stripe style="width: 100%" size="small"> |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" width="150"></el-table-column> |
| | | <el-table-column prop="faultType" label="æ
éç±»å" width="120"></el-table-column> |
| | | <el-table-column prop="occurTime" label="åçæ¶é´" width="150"></el-table-column> |
| | | <el-table-column prop="dealTime" label="å¤çæ¶é´" width="150"></el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag type="success">{{ scope.row.status }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | background |
| | | layout="total, prev, pager, next" |
| | | :total="historyList.length" |
| | | :page-size="5" |
| | | size="small" |
| | | ></el-pagination> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- å³ä¾§ï¼æ
éè¯æç»æ --> |
| | | <el-col :span="12"> |
| | | <!-- æ
éè¯æç»æ --> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ
éè¯æç»æ</span> |
| | | <el-button type="primary" size="small" @click="handleDiagnosis">éæ°è¯æ</el-button> |
| | | </div> |
| | | </template> |
| | | <div v-if="currentWarning" class="diagnosis-result"> |
| | | <h3>{{ currentWarning.deviceName }} - {{ currentWarning.warningType }}</h3> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="é£é©ç级"> |
| | | <el-tag :type="currentWarning.riskLevel === 'high' ? 'danger' : currentWarning.riskLevel === 'medium' ? 'warning' : 'info'"> |
| | | {{ currentWarning.riskLevel === 'high' ? 'é«' : currentWarning.riskLevel === 'medium' ? 'ä¸' : 'ä½' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åçæ¶é´">{{ currentWarning.occurTime }}</el-descriptions-item> |
| | | <el-descriptions-item label="åå æ¨æµ">{{ diagnosisResult.reason }}</el-descriptions-item> |
| | | <el-descriptions-item label="å½±åèå´">{{ diagnosisResult.impact }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤ç建议">{{ diagnosisResult.suggestion }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <div v-else class="no-selection"> |
| | | <el-empty description="è¯·éæ©ä¸ä¸ªé¢è¦é¡¹æ¥çè¯æç»æ"></el-empty> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 颿µæ§è¯æç»æ --> |
| | | <el-card shadow="hover" style="margin-top: 20px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>颿µæ§è¯æç»æï¼æªæ¥7æ¥æ
éé£é©ï¼</span> |
| | | </div> |
| | | </template> |
| | | <div class="prediction-result"> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="item in predictionList" |
| | | :key="item.date" |
| | | :timestamp="item.date" |
| | | :type="item.riskLevel === 'high' ? 'danger' : item.riskLevel === 'medium' ? 'warning' : 'success'" |
| | | > |
| | | <div class="timeline-content"> |
| | | <h4>{{ item.deviceName }}</h4> |
| | | <p class="risk-level"> |
| | | é£é©ççº§ï¼ |
| | | <el-tag :type="item.riskLevel === 'high' ? 'danger' : item.riskLevel === 'medium' ? 'warning' : 'success'"> |
| | | {{ item.riskLevel === 'high' ? 'é«' : item.riskLevel === 'medium' ? 'ä¸' : 'ä½' }} |
| | | </el-tag> |
| | | </p> |
| | | <p class="fault-type">å¯è½æ
éç±»åï¼{{ item.possibleFault }}</p> |
| | | <p class="probability">åçæ¦çï¼{{ item.probability }}%</p> |
| | | </div> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive } from 'vue' |
| | | |
| | | // 设å¤å表 |
| | | const devices = ref([ |
| | | { id: 'D001', name: 'ç©ºåæºA-001' }, |
| | | { id: 'D002', name: 'å·å´å¡B-002' }, |
| | | { id: 'D003', name: 'æ°´æ³µC-003' }, |
| | | { id: 'D004', name: 'åçµæºD-004' }, |
| | | { id: 'D005', name: 'ååå¨E-005' } |
| | | ]) |
| | | |
| | | // æ
éé¢è¦å表 |
| | | const warningList = ref([ |
| | | { |
| | | id: 1, |
| | | deviceName: 'ç©ºåæºA-001', |
| | | warningType: 'ååå¼å¸¸', |
| | | riskLevel: 'high', |
| | | occurTime: '2024-12-16 14:32:15', |
| | | status: 'pending' |
| | | }, |
| | | { |
| | | id: 2, |
| | | deviceName: 'å·å´å¡B-002', |
| | | warningType: '温度è¿é«', |
| | | riskLevel: 'medium', |
| | | occurTime: '2024-12-16 14:30:45', |
| | | status: 'pending' |
| | | }, |
| | | { |
| | | id: 3, |
| | | deviceName: 'æ°´æ³µC-003', |
| | | warningType: 'æ¯å¨è¿å¤§', |
| | | riskLevel: 'medium', |
| | | occurTime: '2024-12-16 14:28:30', |
| | | status: 'pending' |
| | | }, |
| | | { |
| | | id: 4, |
| | | deviceName: 'åçµæºD-004', |
| | | warningType: 'çµæµå¼å¸¸', |
| | | riskLevel: 'high', |
| | | occurTime: '2024-12-16 14:25:10', |
| | | status: 'pending' |
| | | }, |
| | | { |
| | | id: 5, |
| | | deviceName: 'ååå¨E-005', |
| | | warningType: 'çµåæ³¢å¨', |
| | | riskLevel: 'low', |
| | | occurTime: '2024-12-16 14:20:05', |
| | | status: 'pending' |
| | | } |
| | | ]) |
| | | |
| | | // å½åéä¸çé¢è¦é¡¹ |
| | | const currentWarning = ref(warningList.value[0]) |
| | | |
| | | // æ
éè¯æç»æ |
| | | const diagnosisResult = reactive({ |
| | | reason: 'æ ¹æ®è®¾å¤è¿è¡æ°æ®æ¨æµï¼æ
éåå å¯è½æ¯è®¾å¤å
é¨é¨ä»¶ç£¨æå¯¼è´çååå¼å¸¸ï¼éè¦è¿ä¸æ¥æ£æ¥è®¾å¤çæ´»å¡ç¯åæ°ç¼¸å¥ã', |
| | | impact: '妿ä¸åæ¶å¤çï¼å¯è½å¯¼è´è®¾å¤åæºï¼å½±åçäº§çº¿çæ£å¸¸è¿è¡ï¼é¢è®¡åæºæ¶é´ä¸º4-6å°æ¶ã', |
| | | suggestion: '1. ç«å³å®æç»´ä¿®äººåè¿è¡è®¾å¤æ£æ¥ï¼2. æ£æ¥è®¾å¤çæ´»å¡ç¯åæ°ç¼¸å¥ï¼3. æ´æ¢ç£¨æä¸¥éçé¨ä»¶ï¼4. æ£æ¥è®¾å¤ç润æ»ç³»ç»ï¼ç¡®ä¿æ¶¦æ»æ£å¸¸ã' |
| | | }) |
| | | |
| | | // 颿µæ§è¯æç»æ |
| | | const predictionList = ref([ |
| | | { |
| | | date: '2024-12-17', |
| | | deviceName: 'ç©ºåæºA-001', |
| | | riskLevel: 'medium', |
| | | possibleFault: 'ååå¼å¸¸', |
| | | probability: 65 |
| | | }, |
| | | { |
| | | date: '2024-12-18', |
| | | deviceName: 'å·å´å¡B-002', |
| | | riskLevel: 'high', |
| | | possibleFault: '温度è¿é«', |
| | | probability: 85 |
| | | }, |
| | | { |
| | | date: '2024-12-19', |
| | | deviceName: 'æ°´æ³µC-003', |
| | | riskLevel: 'medium', |
| | | possibleFault: 'æ¯å¨è¿å¤§', |
| | | probability: 70 |
| | | }, |
| | | { |
| | | date: '2024-12-20', |
| | | deviceName: 'åçµæºD-004', |
| | | riskLevel: 'high', |
| | | possibleFault: 'çµæµå¼å¸¸', |
| | | probability: 90 |
| | | }, |
| | | { |
| | | date: '2024-12-21', |
| | | deviceName: 'ååå¨E-005', |
| | | riskLevel: 'low', |
| | | possibleFault: 'çµåæ³¢å¨', |
| | | probability: 45 |
| | | }, |
| | | { |
| | | date: '2024-12-22', |
| | | deviceName: 'ç©ºåæºA-001', |
| | | riskLevel: 'high', |
| | | possibleFault: 'ååå¼å¸¸', |
| | | probability: 80 |
| | | }, |
| | | { |
| | | date: '2024-12-23', |
| | | deviceName: 'å·å´å¡B-002', |
| | | riskLevel: 'medium', |
| | | possibleFault: '温度è¿é«', |
| | | probability: 60 |
| | | } |
| | | ]) |
| | | |
| | | // æ
éåå²è®°å½æ¥è¯¢è¡¨å |
| | | const historyFilterForm = ref({ |
| | | deviceId: '' |
| | | }) |
| | | |
| | | // åå²è®°å½æ¶é´èå´ |
| | | const historyTimeRange = ref([]) |
| | | |
| | | // æ
éåå²è®°å½ |
| | | const historyList = ref([ |
| | | { |
| | | id: 1, |
| | | deviceName: 'ç©ºåæºA-001', |
| | | faultType: 'ååå¼å¸¸', |
| | | occurTime: '2024-12-15 08:30:00', |
| | | dealTime: '2024-12-15 10:45:00', |
| | | status: 'å·²å¤ç' |
| | | }, |
| | | { |
| | | id: 2, |
| | | deviceName: 'å·å´å¡B-002', |
| | | faultType: '温度è¿é«', |
| | | occurTime: '2024-12-14 14:20:00', |
| | | dealTime: '2024-12-14 16:15:00', |
| | | status: 'å·²å¤ç' |
| | | }, |
| | | { |
| | | id: 3, |
| | | deviceName: 'æ°´æ³µC-003', |
| | | faultType: 'æ¯å¨è¿å¤§', |
| | | occurTime: '2024-12-13 09:15:00', |
| | | dealTime: '2024-12-13 11:30:00', |
| | | status: 'å·²å¤ç' |
| | | }, |
| | | { |
| | | id: 4, |
| | | deviceName: 'åçµæºD-004', |
| | | faultType: 'çµæµå¼å¸¸', |
| | | occurTime: '2024-12-12 16:45:00', |
| | | dealTime: '2024-12-12 18:30:00', |
| | | status: 'å·²å¤ç' |
| | | }, |
| | | { |
| | | id: 5, |
| | | deviceName: 'ååå¨E-005', |
| | | faultType: 'çµåæ³¢å¨', |
| | | occurTime: '2024-12-11 11:20:00', |
| | | dealTime: '2024-12-11 13:15:00', |
| | | status: 'å·²å¤ç' |
| | | } |
| | | ]) |
| | | |
| | | // å¤çé¢è¦é¡¹ç¹å» |
| | | const handleWarningClick = (row) => { |
| | | currentWarning.value = row |
| | | } |
| | | |
| | | // éæ°è¯æ |
| | | const handleDiagnosis = () => { |
| | | // 模æéæ°è¯æ |
| | | ElMessage.success('éæ°è¯æå®æ') |
| | | } |
| | | |
| | | // å¤çåå²è®°å½æ¥è¯¢ |
| | | const handleHistorySearch = () => { |
| | | // æ¨¡ææ¥è¯¢åå²è®°å½ |
| | | ElMessage.success('åå²è®°å½æ¥è¯¢æå') |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .fault-diagnosis-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .diagnosis-result h3 { |
| | | margin-bottom: 20px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .no-selection { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 200px; |
| | | } |
| | | |
| | | .prediction-result { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .timeline-content { |
| | | padding: 10px; |
| | | background-color: #fafafa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .timeline-content h4 { |
| | | margin-bottom: 10px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .timeline-content p { |
| | | margin: 5px 0; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .risk-level { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | } |
| | | |
| | | .fault-type { |
| | | color: #606266; |
| | | } |
| | | |
| | | .probability { |
| | | color: #606266; |
| | | } |
| | | |
| | | .history-filter-form { |
| | | margin-bottom: 20px; |
| | | padding: 10px 0; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .pagination-container { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="dashboard"> |
| | | <!-- 顶鍿¨ªå两æ --> |
| | | <div class="dashboard-top"> |
| | | <!-- å·¦ï¼ç³»ç»æ¦è§+æ°æ®å¡ç --> |
| | | <div class="top-left"> |
| | | <div class="system-info"> |
| | | <div class="section-title">æ¿å
å管çç³»ç»</div> |
| | | <div style="display: flex; align-items: center; gap: 20px"> |
| | | <div class="system-card"> |
| | | <div class="system-name">æ¿å
å管çç³»ç»</div> |
| | | <div class="system-meta">èµè´¨å®¡æ ¸ · åå管ç · 绩æè¯ä¼°</div> |
| | | </div> |
| | | <div style="display: flex; align-items: center; gap: 8px"> |
| | | <el-icon color="#5053B5" size="22"><Clock /></el-icon> |
| | | <span>å½åæ¶é´ï¼{{ currentTime }}</span> |
| | | <div class="dashboard-container"> |
| | | <!-- ç»è®¡å¡ç --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">设å¤å¨çº¿ç</div> |
| | | <div class="card-value">{{ onlineRate }}%</div> |
| | | <div class="card-desc"> |
| | | å½åå¨çº¿è®¾å¤æ°ï¼{{ onlineCount }} / {{ totalDevices }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">æ
éé¢è¦æ°</div> |
| | | <div class="card-value">{{ warningCount }}</div> |
| | | <div class="card-desc"> |
| | | é«é£é©ï¼{{ highRiskCount }} | ä¸é£é©ï¼{{ mediumRiskCount }} | |
| | | ä½é£é©ï¼{{ lowRiskCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">å¾
å¤çç»´ä¿®å</div> |
| | | <div class="card-value">{{ pendingOrders }}</div> |
| | | <div class="card-desc"> |
| | | å¤çä¸ï¼{{ processingOrders }} | 已宿ï¼{{ completedOrders }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card class="statistics-card" shadow="hover"> |
| | | <div class="card-content"> |
| | | <div class="card-title">éç¹è®¾å¤è¿è¡ç¶æ</div> |
| | | <div class="card-value">{{ normalDevices }} æ£å¸¸</div> |
| | | <div class="card-desc"> |
| | | å¼å¸¸ï¼{{ abnormalDevices }} | æ
éï¼{{ faultDevices }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- å¾è¡¨åºå --> |
| | | <el-row :gutter="20" style="margin-top: 20px"> |
| | | <!-- 设å¤å¥åº·åº¦è¶å¿å¾ --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>设å¤å¥åº·åº¦è¶å¿å¾</span> |
| | | </div> |
| | | </template> |
| | | <div ref="healthChartRef" class="chart-container"></div> |
| | | </el-card> |
| | | </el-col> |
| | | <!-- è¿7æ¥æ
éç±»åç»è®¡ --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è¿7æ¥æ
éç±»åç»è®¡</span> |
| | | </div> |
| | | </template> |
| | | <div ref="faultChartRef" class="chart-container"></div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- éç¹è®¾å¤è¿è¡ç¶æå¡ç --> |
| | | <el-card shadow="hover" style="margin-top: 20px"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>éç¹è®¾å¤è¿è¡ç¶æ</span> |
| | | </div> |
| | | <div class="data-cards"> |
| | | <div class="data-card total"> |
| | | <div class="data-title">æ»æ¿å
åæ°</div> |
| | | <div class="data-value">{{ contractorStats.total }}</div> |
| | | <div class="data-desc"> |
| | | å·²å®¡æ ¸ {{ contractorStats.approved }} | å¾
å®¡æ ¸ |
| | | {{ contractorStats.pending }} |
| | | </div> |
| | | </div> |
| | | <div class="data-card pending"> |
| | | <div class="data-title">å¾
å®¡æ ¸æ¿å
å</div> |
| | | <div class="data-value">{{ contractorStats.pending }}</div> |
| | | <div class="data-desc"> |
| | | A级 {{ contractorStats.aPending }} | B级 |
| | | {{ contractorStats.bPending }} | C级 |
| | | {{ contractorStats.cPending }} |
| | | </div> |
| | | </div> |
| | | <div class="data-card today"> |
| | | <div class="data-title">æ¬ææ°å¢</div> |
| | | <div class="data-value">{{ contractorStats.monthly }}</div> |
| | | <div class="data-desc"> |
| | | 忝 {{ contractorStats.monthly忝 }}% | ç¯æ¯ |
| | | {{ contractorStats.monthlyç¯æ¯ }}% |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- å³ï¼å¾
å¤çæ¥è¦å表 --> |
| | | <div class="alarm-panel"> |
| | | <div class="section-title">å¾
å®¡æ ¸æ¿å
å</div> |
| | | <ul class="alarm-list" v-if="pendingContractors.length > 0"> |
| | | <li v-for="item in pendingContractors" :key="item.id"> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | gap: 10px; |
| | | </template> |
| | | <el-table :data="keyDevices" stripe style="width: 100%"> |
| | | <el-table-column |
| | | prop="name" |
| | | label="设å¤åç§°" |
| | | width="180" |
| | | ></el-table-column> |
| | | <el-table-column |
| | | prop="model" |
| | | label="åå·" |
| | | width="120" |
| | | ></el-table-column> |
| | | <el-table-column prop="ip" label="IPå°å" width="150"></el-table-column> |
| | | <el-table-column prop="status" label="ç¶æ" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag |
| | | :type=" |
| | | scope.row.status === 'online' |
| | | ? 'success' |
| | | : scope.row.status === 'warning' |
| | | ? 'warning' |
| | | : 'danger' |
| | | " |
| | | > |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | " |
| | | > |
| | | <div class="alarm-title">{{ item.name }} - {{ item.type }}</div> |
| | | <el-tag :type="getContractorLevelType(item.level)" |
| | | >{{ item.level }}级</el-tag |
| | | > |
| | | </div> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | " |
| | | > |
| | | <div class="alarm-value"> |
| | | è系人: {{ item.contact }} | æ³¨åæ¶é´: {{ item.registerDate }} |
| | | </div> |
| | | <div class="alarm-time">{{ item.applyDate }}</div> |
| | | </div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | <div v-else style="text-align: center; color: #909399; padding: 20px"> |
| | | ææ å¾
å®¡æ ¸æ¿å
å |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ä¸é¨æ¨ªå两æ --> |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åå¢é¿è¶å¿</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="lineLegend" |
| | | :series="lineSeries" |
| | | :tooltip="tooltipLine" |
| | | :xAxis="xAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åèµè´¨åå¸</div> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | justify-content: space-evenly; |
| | | height: 300px; |
| | | " |
| | | > |
| | | <div style="width: 50%"> |
| | | <Echarts |
| | | ref="chart" |
| | | :legend="pieLegend" |
| | | :chartStyle="chartStylePie" |
| | | :series="levelPieSeries" |
| | | :tooltip="pieTooltip" |
| | | ></Echarts> |
| | | </div> |
| | | <ul class="level-list" style="width: 50%"> |
| | | <li v-for="item in levelPieSeries[0].data" :key="item.name"> |
| | | <div |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | " |
| | | > |
| | | <div class="line" :style="{ color: item.itemStyle.color }"> |
| | | â{{ item.name }} |
| | | </div> |
| | | <div style="width: 60px">{{ item.value }}å®¶</div> |
| | | <div style="width: 60px">{{ item.rate }}%</div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- åºé¨æ¨ªå两æ --> |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åç±»ååå¸</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :color="barColors" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :series="equipmentBarSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="equipmentXAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="section-title">æ¿å
åå®¡æ ¸æ¶æ</div> |
| | | <Echarts |
| | | ref="chart" |
| | | :color="barColors" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :series="handlingTimeSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="handlingTimeXAxis" |
| | | :yAxis="yAxis" |
| | | style="height: 300px" |
| | | ></Echarts> |
| | | </div> |
| | | </div> |
| | | {{ |
| | | scope.row.status === "online" |
| | | ? "å¨çº¿" |
| | | : scope.row.status === "warning" |
| | | ? "é¢è¦" |
| | | : "æ
é" |
| | | }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="temperature" |
| | | label="温度(â)" |
| | | width="100" |
| | | ></el-table-column> |
| | | <el-table-column |
| | | prop="pressure" |
| | | label="åå(MPa)" |
| | | width="100" |
| | | ></el-table-column> |
| | | <el-table-column |
| | | prop="speed" |
| | | label="转é(rpm)" |
| | | width="100" |
| | | ></el-table-column> |
| | | <el-table-column prop="health" label="å¥åº·åº¦" width="120"> |
| | | <template #default="scope"> |
| | | <el-progress |
| | | :percentage="scope.row.health" |
| | | :stroke-width="10" |
| | | ></el-progress> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, reactive, computed } from "vue"; |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { Clock } from "@element-plus/icons-vue"; |
| | | import { ref, onMounted, onUnmounted } from "vue"; |
| | | import * as echarts from "echarts"; |
| | | |
| | | // å½åæ¶é´ |
| | | const currentTime = computed(() => { |
| | | const now = new Date(); |
| | | return now.toLocaleString("zh-CN"); |
| | | }); |
| | | // ç»è®¡æ°æ® |
| | | const onlineRate = ref(85); |
| | | const onlineCount = ref(170); |
| | | const totalDevices = ref(200); |
| | | const warningCount = ref(23); |
| | | const highRiskCount = ref(5); |
| | | const mediumRiskCount = ref(12); |
| | | const lowRiskCount = ref(6); |
| | | const pendingOrders = ref(15); |
| | | const processingOrders = ref(8); |
| | | const completedOrders = ref(45); |
| | | const normalDevices = ref(162); |
| | | const abnormalDevices = ref(18); |
| | | const faultDevices = ref(10); |
| | | |
| | | // æ¿å
åç»è®¡æ°æ® |
| | | const contractorStats = reactive({ |
| | | total: 156, |
| | | approved: 132, |
| | | pending: 24, |
| | | aPending: 8, |
| | | bPending: 12, |
| | | cPending: 4, |
| | | monthly: 28, |
| | | monthly忝: "+18.5", |
| | | monthlyç¯æ¯: "+5.2", |
| | | }); |
| | | |
| | | // å¾
å®¡æ ¸æ¿å
åå表 |
| | | const pendingContractors = ref([ |
| | | // éç¹è®¾å¤å表 |
| | | const keyDevices = ref([ |
| | | { |
| | | id: 1, |
| | | name: "建çå·¥ç¨æéå
¬å¸", |
| | | type: "å»ºçæ½å·¥", |
| | | contact: "å¼ ä¸", |
| | | registerDate: "2025-01-15", |
| | | applyDate: "2025-12-16 14:30:23", |
| | | level: "A", |
| | | name: "ç©ºåæºA-001", |
| | | model: "KA-200", |
| | | ip: "192.168.1.101", |
| | | status: "online", |
| | | temperature: 42, |
| | | pressure: 0.8, |
| | | speed: 1450, |
| | | health: 92, |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: "æºçµå®è£
å·¥ç¨å
¬å¸", |
| | | type: "æºçµå®è£
", |
| | | contact: "æå", |
| | | registerDate: "2025-03-20", |
| | | applyDate: "2025-12-16 14:28:15", |
| | | level: "B", |
| | | name: "å·å´å¡B-002", |
| | | model: "CT-300", |
| | | ip: "192.168.1.102", |
| | | status: "warning", |
| | | temperature: 58, |
| | | pressure: 0.6, |
| | | speed: 980, |
| | | health: 75, |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: "è£
饰è£
修工ç¨å
¬å¸", |
| | | type: "è£
饰è£
ä¿®", |
| | | contact: "çäº", |
| | | registerDate: "2025-05-10", |
| | | applyDate: "2025-12-16 14:22:18", |
| | | level: "B", |
| | | name: "æ°´æ³µC-003", |
| | | model: "WP-150", |
| | | ip: "192.168.1.103", |
| | | status: "online", |
| | | temperature: 38, |
| | | pressure: 1.2, |
| | | speed: 1200, |
| | | health: 88, |
| | | }, |
| | | { |
| | | id: 4, |
| | | name: "绿åå·¥ç¨æéå
¬å¸", |
| | | type: "åæç»¿å", |
| | | contact: "èµµå
", |
| | | registerDate: "2025-08-05", |
| | | applyDate: "2025-12-16 14:18:55", |
| | | level: "C", |
| | | name: "åçµæºD-004", |
| | | model: "GE-500", |
| | | ip: "192.168.1.104", |
| | | status: "danger", |
| | | temperature: 75, |
| | | pressure: 0.5, |
| | | speed: 1500, |
| | | health: 60, |
| | | }, |
| | | { |
| | | name: "ååå¨E-005", |
| | | model: "TR-1000", |
| | | ip: "192.168.1.105", |
| | | status: "online", |
| | | temperature: 45, |
| | | pressure: 0, |
| | | speed: 0, |
| | | health: 95, |
| | | }, |
| | | ]); |
| | | |
| | | // å¾è¡¨æ ·å¼ |
| | | const chartStyle = { |
| | | width: "100%", |
| | | height: "100%", |
| | | // å¾è¡¨å¼ç¨ |
| | | const healthChartRef = ref(null); |
| | | const faultChartRef = ref(null); |
| | | let healthChart = null; |
| | | let faultChart = null; |
| | | |
| | | // å¥åº·åº¦è¶å¿æ°æ® |
| | | const healthTrendData = { |
| | | dates: ["12-10", "12-11", "12-12", "12-13", "12-14", "12-15", "12-16"], |
| | | values: [88, 90, 85, 87, 92, 91, 93], |
| | | }; |
| | | |
| | | const chartStylePie = { |
| | | width: "100%", |
| | | height: "100%", |
| | | // æ
éç±»åç»è®¡æ°æ® |
| | | const faultTypeData = { |
| | | types: ["温度å¼å¸¸", "ååè¶
æ ", "转éå¼å¸¸", "æ¯å¨è¿å¤§", "å
¶ä»"], |
| | | values: [15, 8, 12, 6, 3], |
| | | }; |
| | | |
| | | const grid = { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }; |
| | | |
| | | // æ¿å
åå¢é¿è¶å¿ - æçº¿å¾ |
| | | const lineLegend = { |
| | | show: true, |
| | | data: ["A级", "B级", "C级"], |
| | | }; |
| | | |
| | | const tooltipLine = { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "cross", |
| | | }, |
| | | }; |
| | | |
| | | const xAxis = ref({ |
| | | type: "category", |
| | | data: ["12-01", "12-02", "12-03", "12-04", "12-05", "12-06", "12-07"], |
| | | }); |
| | | |
| | | const yAxis = ref({ |
| | | type: "value", |
| | | name: "æ¿å
åæ°é", |
| | | }); |
| | | |
| | | const lineSeries = ref([ |
| | | { |
| | | name: "A级", |
| | | type: "line", |
| | | data: [15, 18, 20, 22, 25, 28, 30], |
| | | itemStyle: { |
| | | color: "#f56c6c", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: "B级", |
| | | type: "line", |
| | | data: [45, 52, 58, 65, 70, 75, 80], |
| | | itemStyle: { |
| | | color: "#e6a23c", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: "C级", |
| | | type: "line", |
| | | data: [30, 35, 40, 45, 50, 55, 60], |
| | | itemStyle: { |
| | | color: "#67c23a", |
| | | }, |
| | | lineStyle: { |
| | | width: 2, |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | ]); |
| | | |
| | | // æ¿å
åèµè´¨åå¸ - é¥¼å¾ |
| | | const pieLegend = { |
| | | show: false, |
| | | }; |
| | | |
| | | const pieTooltip = { |
| | | trigger: "item", |
| | | formatter: "{b}: {c}å®¶ ({d}%)", |
| | | }; |
| | | |
| | | const levelPieSeries = ref([ |
| | | { |
| | | type: "pie", |
| | | radius: ["60%", "80%"], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: "#fff", |
| | | borderWidth: 2, |
| | | }, |
| | | label: { |
| | | show: false, |
| | | }, |
| | | data: [ |
| | | { |
| | | name: "A级", |
| | | value: 45, |
| | | rate: 28.85, |
| | | itemStyle: { color: "#f56c6c" }, |
| | | // åå§åå¥åº·åº¦è¶å¿å¾ |
| | | const initHealthChart = () => { |
| | | if (healthChartRef.value) { |
| | | healthChart = echarts.init(healthChartRef.value); |
| | | const option = { |
| | | tooltip: { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "cross", |
| | | label: { |
| | | backgroundColor: "#6a7985", |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | name: "B级", |
| | | value: 70, |
| | | rate: 44.87, |
| | | itemStyle: { color: "#e6a23c" }, |
| | | grid: { |
| | | left: "3%", |
| | | right: "4%", |
| | | bottom: "3%", |
| | | containLabel: true, |
| | | }, |
| | | { |
| | | name: "C级", |
| | | value: 41, |
| | | rate: 26.28, |
| | | itemStyle: { color: "#67c23a" }, |
| | | xAxis: { |
| | | type: "category", |
| | | boundaryGap: false, |
| | | data: healthTrendData.dates, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | // æ¿å
åç±»ååå¸ - æ±ç¶å¾ |
| | | const barColors = ["#409eff", "#67c23a", "#e6a23c", "#f56c6c", "#909399"]; |
| | | |
| | | const tooltip = { |
| | | trigger: "axis", |
| | | axisPointer: { |
| | | type: "shadow", |
| | | }, |
| | | }; |
| | | |
| | | const equipmentXAxis = ref({ |
| | | type: "category", |
| | | data: ["å»ºçæ½å·¥", "æºçµå®è£
", "è£
饰è£
ä¿®", "åæç»¿å", "å
¶ä»"], |
| | | }); |
| | | |
| | | const equipmentBarSeries = ref([ |
| | | { |
| | | name: "æ¿å
åæ°é", |
| | | type: "bar", |
| | | data: [45, 35, 25, 20, 31], |
| | | itemStyle: { |
| | | color: function (params) { |
| | | return barColors[params.dataIndex % barColors.length]; |
| | | yAxis: { |
| | | type: "value", |
| | | min: 70, |
| | | max: 100, |
| | | name: "å¥åº·åº¦(%)", |
| | | }, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: "top", |
| | | }, |
| | | }, |
| | | ]); |
| | | |
| | | // æ¿å
åå®¡æ ¸æ¶æåæ - æ±ç¶å¾ |
| | | const handlingTimeXAxis = ref({ |
| | | type: "category", |
| | | data: ["0-1天", "1-3天", "3-7天", "7天以ä¸"], |
| | | }); |
| | | |
| | | const handlingTimeSeries = ref([ |
| | | { |
| | | name: "å®¡æ ¸æ°é", |
| | | type: "bar", |
| | | data: [85, 45, 20, 10], |
| | | itemStyle: { |
| | | color: function (params) { |
| | | return barColors[params.dataIndex % barColors.length]; |
| | | }, |
| | | }, |
| | | label: { |
| | | show: true, |
| | | position: "top", |
| | | }, |
| | | }, |
| | | ]); |
| | | |
| | | // è·åæ¿å
åçº§å«æ ·å¼ |
| | | const getContractorLevelType = (level) => { |
| | | switch (level) { |
| | | case "A": |
| | | return "danger"; |
| | | case "B": |
| | | return "warning"; |
| | | case "C": |
| | | return "info"; |
| | | default: |
| | | return "info"; |
| | | series: [ |
| | | { |
| | | name: "å¥åº·åº¦", |
| | | type: "line", |
| | | stack: "æ»é", |
| | | areaStyle: {}, |
| | | emphasis: { |
| | | focus: "series", |
| | | }, |
| | | data: healthTrendData.values, |
| | | itemStyle: { |
| | | color: "#67c23a", |
| | | }, |
| | | lineStyle: { |
| | | width: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | healthChart.setOption(option); |
| | | } |
| | | }; |
| | | |
| | | // åå§åæ
éç±»åç»è®¡é¥¼å¾ |
| | | const initFaultChart = () => { |
| | | if (faultChartRef.value) { |
| | | faultChart = echarts.init(faultChartRef.value); |
| | | const option = { |
| | | tooltip: { |
| | | trigger: "item", |
| | | }, |
| | | legend: { |
| | | orient: "vertical", |
| | | left: "left", |
| | | }, |
| | | series: [ |
| | | { |
| | | name: "æ
éç±»å", |
| | | type: "pie", |
| | | radius: "50%", |
| | | data: faultTypeData.types.map((type, index) => ({ |
| | | name: type, |
| | | value: faultTypeData.values[index], |
| | | })), |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: "rgba(0, 0, 0, 0.5)", |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | faultChart.setOption(option); |
| | | } |
| | | }; |
| | | |
| | | // çå¬çªå£å¤§å°ååï¼è°æ´å¾è¡¨å¤§å° |
| | | const handleResize = () => { |
| | | healthChart?.resize(); |
| | | faultChart?.resize(); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | // 页é¢å è½½å®æåçåå§åæä½ |
| | | initHealthChart(); |
| | | initFaultChart(); |
| | | window.addEventListener("resize", handleResize); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | window.removeEventListener("resize", handleResize); |
| | | healthChart?.dispose(); |
| | | faultChart?.dispose(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dashboard { |
| | | background: #f5f7fa; |
| | | .dashboard-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | padding: 20px; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .dashboard-top { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | .statistics-card { |
| | | height: 180px; |
| | | } |
| | | |
| | | .system-info { |
| | | .card-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | padding: 20px; |
| | | min-width: 0; |
| | | background-color: #eff2fb; |
| | | border-radius: 12px; |
| | | height: 138px; |
| | | } |
| | | |
| | | .system-card { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | position: relative; |
| | | padding-right: 15px; |
| | | } |
| | | |
| | | .system-name { |
| | | font-weight: 600; |
| | | font-size: 18px; |
| | | color: #161a9a; |
| | | } |
| | | |
| | | .system-meta { |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #818185; |
| | | } |
| | | |
| | | .data-cards { |
| | | display: flex; |
| | | gap: 16px; |
| | | justify-content: flex-start; |
| | | background: #ffffff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .data-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | min-width: 160px; |
| | | box-shadow: 0 2px 8px #eee; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | height: 100%; |
| | | justify-content: center; |
| | | width: 32%; |
| | | height: 140px; |
| | | transition: all 0.3s ease; |
| | | align-items: center; |
| | | } |
| | | |
| | | .data-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
| | | } |
| | | |
| | | .data-card.total { |
| | | background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-card.pending { |
| | | background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-card.today { |
| | | background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | .data-title { |
| | | font-weight: 600; |
| | | .card-title { |
| | | font-size: 16px; |
| | | margin-bottom: 12px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .data-value { |
| | | .card-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .data-desc { |
| | | font-size: 12px; |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | .top-left { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | width: 60%; |
| | | } |
| | | |
| | | .alarm-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | width: 40%; |
| | | } |
| | | |
| | | .alarm-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | .card-desc { |
| | | font-size: 14px; |
| | | overflow-y: auto; |
| | | height: 260px; |
| | | } |
| | | |
| | | .alarm-list li { |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | padding: 12px 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | background: #f8f9fa; |
| | | border-left: 4px solid #409eff; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .alarm-list li:hover { |
| | | background: #e9ecef; |
| | | transform: translateX(4px); |
| | | } |
| | | |
| | | .alarm-title { |
| | | font-weight: 600; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .alarm-value { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .alarm-time { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .dashboard-row { |
| | | .card-header { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .main-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | flex: 1; |
| | | min-width: 0; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .section-title { |
| | | position: relative; |
| | | font-size: 18px; |
| | | color: #333; |
| | | padding-left: 10px; |
| | | margin-bottom: 20px; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .section-title::before { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 4px; |
| | | content: ""; |
| | | width: 4px; |
| | | height: 18px; |
| | | background-color: #409eff; |
| | | border-radius: 2px; |
| | | } |
| | | |
| | | .level-list { |
| | | margin: 0; |
| | | padding: 0; |
| | | list-style: none; |
| | | height: 200px; |
| | | overflow-y: auto; |
| | | .chart-container { |
| | | width: 100%; |
| | | } |
| | | |
| | | .level-list li { |
| | | margin-bottom: 15px; |
| | | padding: 10px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .level-list li:hover { |
| | | background: #e9ecef; |
| | | } |
| | | |
| | | .line { |
| | | position: relative; |
| | | width: 80px; |
| | | font-weight: 500; |
| | | height: 350px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="maintenance-management-container"> |
| | | <!-- 顶鍿使 --> |
| | | <el-card shadow="hover" style="margin-bottom: 20px;"> |
| | | <div class="card-header"> |
| | | <span>维修管ç</span> |
| | | <div class="header-buttons"> |
| | | <el-button type="primary" @click="showCreateWorkOrderDialog"> |
| | | <el-icon-plus /> å建工å |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <el-row :gutter="20"> |
| | | <!-- 左侧ï¼å·¥åå表 --> |
| | | <el-col :span="16"> |
| | | <el-card shadow="hover"> |
| | | <!-- å·¥åç¶ææ ç¾é¡µ --> |
| | | <el-tabs v-model="activeTab" @tab-change="handleTabChange"> |
| | | <el-tab-pane label="å¾
å¤ç" name="pending"></el-tab-pane> |
| | | <el-tab-pane label="å¤çä¸" name="processing"></el-tab-pane> |
| | | <el-tab-pane label="已宿" name="completed"></el-tab-pane> |
| | | </el-tabs> |
| | | |
| | | <!-- å·¥åå表 --> |
| | | <el-table :data="filteredWorkOrders" stripe style="width: 100%" @row-click="handleWorkOrderClick"> |
| | | <el-table-column prop="orderNo" label="å·¥åç¼å·" width="180"></el-table-column> |
| | | <el-table-column prop="deviceName" label="设å¤åç§°" width="150"></el-table-column> |
| | | <el-table-column prop="faultType" label="æ
éç±»å" width="120"></el-table-column> |
| | | <el-table-column prop="createTime" label="å建æ¶é´" width="180"></el-table-column> |
| | | <el-table-column prop="assignee" label="è´è´£äºº" width="120"></el-table-column> |
| | | <el-table-column prop="priority" label="ä¼å
级" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.priority === 'high' ? 'danger' : scope.row.priority === 'medium' ? 'warning' : 'info'"> |
| | | {{ scope.row.priority === 'high' ? 'é«' : scope.row.priority === 'medium' ? 'ä¸' : 'ä½' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="150"> |
| | | <template #default="scope"> |
| | | <el-button size="small" @click="showEditWorkOrderDialog(scope.row)">ç¼è¾</el-button> |
| | | <el-button type="danger" size="small" @click="handleDeleteWorkOrder(scope.row.id)">å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | background |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="filteredWorkOrders.length" |
| | | :current-page="currentPage" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | :page-size="pageSize" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | ></el-pagination> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- å³ä¾§ï¼ç»´ä¿®ç»è®¡åå¤ä»¶æ¨è --> |
| | | <el-col :span="8"> |
| | | <!-- ç»´ä¿®åå²ç»è®¡ --> |
| | | <el-card shadow="hover" style="margin-bottom: 20px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>ç»´ä¿®åå²ç»è®¡</span> |
| | | </div> |
| | | </template> |
| | | <div class="statistics-content"> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">æ¬æå®æå·¥å</div> |
| | | <div class="stat-value">{{ monthlyCompleted }}</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">å¹³åç»´ä¿®æ¶é¿</div> |
| | | <div class="stat-value">{{ averageRepairTime }}å°æ¶</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">è®¾å¤æ
éç</div> |
| | | <div class="stat-value">{{ failureRate }}%</div> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <div class="stat-label">常ç¨ç»´ä¿®è®¾å¤</div> |
| | | <div class="stat-value">{{ commonDevice }}</div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 常ç¨å¤ä»¶å
³èæ¨è --> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>常ç¨å¤ä»¶æ¨è</span> |
| | | </div> |
| | | </template> |
| | | <div class="spare-parts-content"> |
| | | <el-table :data="spareParts" stripe style="width: 100%" size="small"> |
| | | <el-table-column prop="name" label="å¤ä»¶åç§°" width="120"></el-table-column> |
| | | <el-table-column prop="model" label="åå·" width="100"></el-table-column> |
| | | <el-table-column prop="stock" label="åºå" width="80"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.stock < 10 ? 'danger' : 'success'"> |
| | | {{ scope.row.stock }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="usageCount" label="ä½¿ç¨æ¬¡æ°" width="80"></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- å建工åå¯¹è¯æ¡ --> |
| | | <el-dialog v-model="createWorkOrderDialogVisible" title="å建维修工å" width="600px"> |
| | | <el-form :model="workOrderForm" :rules="workOrderRules" ref="workOrderFormRef" label-width="100px"> |
| | | <el-form-item label="设å¤" prop="deviceId"> |
| | | <el-select v-model="workOrderForm.deviceId" placeholder="è¯·éæ©è®¾å¤"> |
| | | <el-option |
| | | v-for="device in devices" |
| | | :key="device.id" |
| | | :label="device.name" |
| | | :value="device.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ
éç±»å" prop="faultType"> |
| | | <el-input v-model="workOrderForm.faultType" placeholder="请è¾å
¥æ
éç±»å"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æ
éæè¿°" prop="faultDescription"> |
| | | <el-input v-model="workOrderForm.faultDescription" type="textarea" placeholder="è¯·è¯¦ç»æè¿°æ
éæ
åµ" :rows="3"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="ä¼å
级" prop="priority"> |
| | | <el-select v-model="workOrderForm.priority" placeholder="è¯·éæ©ä¼å
级"> |
| | | <el-option label="é«" value="high"></el-option> |
| | | <el-option label="ä¸" value="medium"></el-option> |
| | | <el-option label="ä½" value="low"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="createWorkOrderDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleCreateWorkOrder">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- ç¼è¾å·¥åå¯¹è¯æ¡ --> |
| | | <el-dialog v-model="editWorkOrderDialogVisible" title="ç¼è¾ç»´ä¿®å·¥å" width="600px"> |
| | | <el-form :model="workOrderForm" :rules="workOrderRules" ref="workOrderFormRef" label-width="100px"> |
| | | <el-form-item label="å·¥åç¼å·" disabled> |
| | | <el-input v-model="workOrderForm.orderNo"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤" disabled> |
| | | <el-input v-model="workOrderForm.deviceName"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="æ
éç±»å" disabled> |
| | | <el-input v-model="workOrderForm.faultType"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="è´è´£äºº" prop="assignee"> |
| | | <el-select v-model="workOrderForm.assignee" placeholder="è¯·éæ©è´è´£äºº"> |
| | | <el-option |
| | | v-for="user in users" |
| | | :key="user.id" |
| | | :label="user.name" |
| | | :value="user.name" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç»´ä¿®ç¶æ" prop="status"> |
| | | <el-select v-model="workOrderForm.status" placeholder="è¯·éæ©ç¶æ"> |
| | | <el-option label="å¾
å¤ç" value="pending"></el-option> |
| | | <el-option label="å¤çä¸" value="processing"></el-option> |
| | | <el-option label="已宿" value="completed"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç»´ä¿®ç»æ" prop="repairResult"> |
| | | <el-input v-model="workOrderForm.repairResult" type="textarea" placeholder="请填åç»´ä¿®ç»æ" :rows="3"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ä»¶ä½¿ç¨" prop="usedParts"> |
| | | <el-input v-model="workOrderForm.usedParts" type="textarea" placeholder="请填å使ç¨çå¤ä»¶" :rows="2"></el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="editWorkOrderDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleEditWorkOrder">ç¡®å®</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, computed } from 'vue' |
| | | |
| | | // 设å¤å表 |
| | | const devices = ref([ |
| | | { id: 'D001', name: 'ç©ºåæºA-001' }, |
| | | { id: 'D002', name: 'å·å´å¡B-002' }, |
| | | { id: 'D003', name: 'æ°´æ³µC-003' }, |
| | | { id: 'D004', name: 'åçµæºD-004' }, |
| | | { id: 'D005', name: 'ååå¨E-005' } |
| | | ]) |
| | | |
| | | // ç¨æ·å表ï¼ç¨äºåé
è´è´£äººï¼ |
| | | const users = ref([ |
| | | { id: 'U001', name: 'å¼ ä¸' }, |
| | | { id: 'U002', name: 'æå' }, |
| | | { id: 'U003', name: 'çäº' }, |
| | | { id: 'U004', name: 'èµµå
' } |
| | | ]) |
| | | |
| | | // å·¥ååè¡¨æ°æ® |
| | | const workOrders = ref([ |
| | | { |
| | | id: 1, |
| | | orderNo: 'WO20241216001', |
| | | deviceId: 'D001', |
| | | deviceName: 'ç©ºåæºA-001', |
| | | faultType: 'ååå¼å¸¸', |
| | | faultDescription: '设å¤è¿è¡æ¶ååè¶
è¿è®¾å®éå¼ï¼ä¼´æå¼å¸¸åªé³', |
| | | priority: 'high', |
| | | assignee: '', |
| | | status: 'pending', |
| | | repairResult: '', |
| | | usedParts: '', |
| | | createTime: '2024-12-16 14:32:15', |
| | | startTime: '', |
| | | endTime: '' |
| | | }, |
| | | { |
| | | id: 2, |
| | | orderNo: 'WO20241216002', |
| | | deviceId: 'D002', |
| | | deviceName: 'å·å´å¡B-002', |
| | | faultType: '温度è¿é«', |
| | | faultDescription: 'å·å´å¡åºæ°´æ¸©åº¦è¶
è¿è®¾å®å¼ï¼å·å´ææä¸ä½³', |
| | | priority: 'medium', |
| | | assignee: 'å¼ ä¸', |
| | | status: 'processing', |
| | | repairResult: '', |
| | | usedParts: '', |
| | | createTime: '2024-12-16 14:30:45', |
| | | startTime: '2024-12-16 15:00:00', |
| | | endTime: '' |
| | | }, |
| | | { |
| | | id: 3, |
| | | orderNo: 'WO20241215001', |
| | | deviceId: 'D003', |
| | | deviceName: 'æ°´æ³µC-003', |
| | | faultType: 'æ¯å¨è¿å¤§', |
| | | faultDescription: 'æ°´æ³µè¿è¡æ¶æ¯å¨å¼è¶
è¿æ åï¼å¯è½å½±å设å¤å¯¿å½', |
| | | priority: 'medium', |
| | | assignee: 'æå', |
| | | status: 'completed', |
| | | repairResult: 'æ´æ¢äºæ°´æ³µè½´æ¿ï¼è°æ´äºèè½´å¨ï¼æ¯å¨å¼æ¢å¤æ£å¸¸', |
| | | usedParts: 'è½´æ¿Ã2ï¼èè½´å¨Ã1', |
| | | createTime: '2024-12-15 08:30:00', |
| | | startTime: '2024-12-15 09:00:00', |
| | | endTime: '2024-12-15 10:45:00' |
| | | }, |
| | | { |
| | | id: 4, |
| | | orderNo: 'WO20241214001', |
| | | deviceId: 'D004', |
| | | deviceName: 'åçµæºD-004', |
| | | faultType: 'çµæµå¼å¸¸', |
| | | faultDescription: 'åçµæºè¿è¡æ¶çµæµæ³¢å¨è¾å¤§ï¼å¯è½åå¨çè·¯é£é©', |
| | | priority: 'high', |
| | | assignee: 'çäº', |
| | | status: 'completed', |
| | | repairResult: 'æ£æ¥å¹¶ä¿®å¤äºåçµæºç»ç»çè·¯é®é¢ï¼çµæµæ¢å¤æ£å¸¸', |
| | | usedParts: 'ç»ç¼ææÃ1ï¼å¯¼çº¿Ã5ç±³', |
| | | createTime: '2024-12-14 14:20:00', |
| | | startTime: '2024-12-14 14:30:00', |
| | | endTime: '2024-12-14 16:15:00' |
| | | }, |
| | | { |
| | | id: 5, |
| | | orderNo: 'WO20241213001', |
| | | deviceId: 'D005', |
| | | deviceName: 'ååå¨E-005', |
| | | faultType: 'çµåæ³¢å¨', |
| | | faultDescription: 'ååå¨è¾åºçµåæ³¢å¨è¾å¤§ï¼å½±å䏿¸¸è®¾å¤è¿è¡', |
| | | priority: 'low', |
| | | assignee: 'èµµå
', |
| | | status: 'completed', |
| | | repairResult: 'è°æ´äºååå¨åæ¥å¼å
³ï¼çµå稳å®å¨æ£å¸¸èå´å
', |
| | | usedParts: '', |
| | | createTime: '2024-12-13 09:15:00', |
| | | startTime: '2024-12-13 10:00:00', |
| | | endTime: '2024-12-13 11:30:00' |
| | | } |
| | | ]) |
| | | |
| | | // å½åæ¿æ´»çæ ç¾é¡µï¼å·¥åç¶æï¼ |
| | | const activeTab = ref('pending') |
| | | |
| | | // åé¡µæ°æ® |
| | | const currentPage = ref(1) |
| | | const pageSize = ref(10) |
| | | |
| | | // å¯¹è¯æ¡ç¶æ |
| | | const createWorkOrderDialogVisible = ref(false) |
| | | const editWorkOrderDialogVisible = ref(false) |
| | | |
| | | // å·¥åè¡¨åæ°æ® |
| | | const workOrderForm = ref({ |
| | | id: '', |
| | | orderNo: '', |
| | | deviceId: '', |
| | | deviceName: '', |
| | | faultType: '', |
| | | faultDescription: '', |
| | | priority: 'medium', |
| | | assignee: '', |
| | | status: 'pending', |
| | | repairResult: '', |
| | | usedParts: '', |
| | | createTime: '', |
| | | startTime: '', |
| | | endTime: '' |
| | | }) |
| | | |
| | | // 表åéªè¯è§å |
| | | const workOrderRules = ref({ |
| | | deviceId: [{ required: true, message: 'è¯·éæ©è®¾å¤', trigger: 'change' }], |
| | | faultType: [{ required: true, message: '请è¾å
¥æ
éç±»å', trigger: 'blur' }], |
| | | faultDescription: [{ required: true, message: 'è¯·è¯¦ç»æè¿°æ
éæ
åµ', trigger: 'blur' }], |
| | | priority: [{ required: true, message: 'è¯·éæ©ä¼å
级', trigger: 'change' }], |
| | | assignee: [{ required: true, message: 'è¯·éæ©è´è´£äºº', trigger: 'change' }], |
| | | status: [{ required: true, message: 'è¯·éæ©ç¶æ', trigger: 'change' }] |
| | | }) |
| | | |
| | | // 表åå¼ç¨ |
| | | const workOrderFormRef = ref(null) |
| | | |
| | | // çéåçå·¥åå表 |
| | | const filteredWorkOrders = computed(() => { |
| | | return workOrders.value.filter(order => order.status === activeTab.value) |
| | | }) |
| | | |
| | | // ç»´ä¿®ç»è®¡æ°æ® |
| | | const monthlyCompleted = ref(28) |
| | | const averageRepairTime = ref(2.5) |
| | | const failureRate = ref(3.2) |
| | | const commonDevice = ref('ç©ºåæºA-001') |
| | | |
| | | // 常ç¨å¤ä»¶æ¨è |
| | | const spareParts = ref([ |
| | | { id: 1, name: 'è½´æ¿', model: '6308', stock: 15, usageCount: 23 }, |
| | | { id: 2, name: 'å¯å°ä»¶', model: 'MS-25', stock: 8, usageCount: 18 }, |
| | | { id: 3, name: 'èè½´å¨', model: 'CL-50', stock: 5, usageCount: 12 }, |
| | | { id: 4, name: 'ä¼ æå¨', model: 'TS-100', stock: 3, usageCount: 15 }, |
| | | { id: 5, name: 'æ¶¦æ»æ²¹', model: 'L-46', stock: 20, usageCount: 30 } |
| | | ]) |
| | | |
| | | // æ¾ç¤ºå建工åå¯¹è¯æ¡ |
| | | const showCreateWorkOrderDialog = () => { |
| | | // é置表å |
| | | workOrderForm.value = { |
| | | id: '', |
| | | orderNo: '', |
| | | deviceId: '', |
| | | deviceName: '', |
| | | faultType: '', |
| | | faultDescription: '', |
| | | priority: 'medium', |
| | | assignee: '', |
| | | status: 'pending', |
| | | repairResult: '', |
| | | usedParts: '', |
| | | createTime: '', |
| | | startTime: '', |
| | | endTime: '' |
| | | } |
| | | createWorkOrderDialogVisible.value = true |
| | | } |
| | | |
| | | // æ¾ç¤ºç¼è¾å·¥åå¯¹è¯æ¡ |
| | | const showEditWorkOrderDialog = (order) => { |
| | | workOrderForm.value = { ...order } |
| | | editWorkOrderDialogVisible.value = true |
| | | } |
| | | |
| | | // å¤çå·¥åç¹å» |
| | | const handleWorkOrderClick = (row) => { |
| | | // å¯ä»¥å¨è¿éæ·»å æ¥çå·¥å详æ
çé»è¾ |
| | | } |
| | | |
| | | // å¤çæ ç¾é¡µåæ¢ |
| | | const handleTabChange = (tab) => { |
| | | activeTab.value = tab |
| | | currentPage.value = 1 // 忢æ ç¾é¡µæ¶é置页ç |
| | | } |
| | | |
| | | // å¤çå建工å |
| | | const handleCreateWorkOrder = () => { |
| | | // 模æå建工å |
| | | const newOrder = { |
| | | ...workOrderForm.value, |
| | | id: workOrders.value.length + 1, |
| | | orderNo: `WO${new Date().getFullYear()}${String(new Date().getMonth() + 1).padStart(2, '0')}${String(new Date().getDate()).padStart(2, '0')}${String(workOrders.value.length + 1).padStart(3, '0')}`, |
| | | deviceName: devices.value.find(d => d.id === workOrderForm.value.deviceId)?.name || '', |
| | | createTime: new Date().toLocaleString(), |
| | | status: 'pending' |
| | | } |
| | | workOrders.value.unshift(newOrder) |
| | | createWorkOrderDialogVisible.value = false |
| | | ElMessage.success('å·¥åå建æå') |
| | | } |
| | | |
| | | // å¤çç¼è¾å·¥å |
| | | const handleEditWorkOrder = () => { |
| | | // 模æç¼è¾å·¥å |
| | | const index = workOrders.value.findIndex(order => order.id === workOrderForm.value.id) |
| | | if (index !== -1) { |
| | | // å¦æç¶æä»å¾
å¤çå为å¤çä¸ï¼è®¾ç½®å¼å§æ¶é´ |
| | | if (workOrders.value[index].status === 'pending' && workOrderForm.value.status === 'processing') { |
| | | workOrderForm.value.startTime = new Date().toLocaleString() |
| | | } |
| | | // å¦æç¶æä»å¤çä¸åä¸ºå·²å®æï¼è®¾ç½®ç»ææ¶é´ |
| | | if (workOrders.value[index].status === 'processing' && workOrderForm.value.status === 'completed') { |
| | | workOrderForm.value.endTime = new Date().toLocaleString() |
| | | } |
| | | workOrders.value[index] = { ...workOrderForm.value } |
| | | editWorkOrderDialogVisible.value = false |
| | | ElMessage.success('å·¥åç¼è¾æå') |
| | | } |
| | | } |
| | | |
| | | // å¤çå é¤å·¥å |
| | | const handleDeleteWorkOrder = (id) => { |
| | | ElMessageBox.confirm('ç¡®å®è¦å é¤è¯¥å·¥ååï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | // 模æå é¤å·¥å |
| | | const index = workOrders.value.findIndex(order => order.id === id) |
| | | if (index !== -1) { |
| | | workOrders.value.splice(index, 1) |
| | | ElMessage.success('å·¥åå 餿å') |
| | | } |
| | | }).catch(() => { |
| | | // åæ¶å é¤ |
| | | }) |
| | | } |
| | | |
| | | // å¤çå页大å°åå |
| | | const handleSizeChange = (size) => { |
| | | pageSize.value = size |
| | | currentPage.value = 1 |
| | | } |
| | | |
| | | // å¤çå½å页åå |
| | | const handleCurrentChange = (current) => { |
| | | currentPage.value = current |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .maintenance-management-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .header-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .pagination-container { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .statistics-content { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 20px; |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .stat-item { |
| | | text-align: center; |
| | | padding: 15px; |
| | | background-color: #fafafa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .stat-label { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .stat-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | |
| | | .spare-parts-content { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | :deep(.el-icon-plus) { |
| | | margin-right: 5px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="real-time-monitoring-container"> |
| | | <!-- 设å¤éæ© --> |
| | | <el-card shadow="hover" style="margin-bottom: 20px;"> |
| | | <el-form :inline="true" :model="deviceSelectForm" class="device-select-form"> |
| | | <el-form-item label="设å¤"> |
| | | <el-select v-model="deviceSelectForm.deviceId" placeholder="è¯·éæ©è®¾å¤" @change="handleDeviceChange"> |
| | | <el-option |
| | | v-for="device in devices" |
| | | :key="device.id" |
| | | :label="device.name" |
| | | :value="device.id" |
| | | ></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶é´èå´"> |
| | | <el-date-picker |
| | | v-model="timeRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | :default-time="['00:00:00', '23:59:59']" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleHistoryQuery">æ¥è¯¢å岿°æ®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-card> |
| | | |
| | | <!-- 宿¶åæ°å¡ç --> |
| | | <el-row :gutter="20" style="margin-bottom: 20px;"> |
| | | <el-col :span="8" v-for="param in realTimeParams" :key="param.name"> |
| | | <el-card class="param-card" :shadow="param.isAlarm ? 'always' : 'hover'" :class="{ 'alarm-card': param.isAlarm }"> |
| | | <div class="param-content"> |
| | | <div class="param-title"> |
| | | <span>{{ param.name }}</span> |
| | | <el-tag v-if="param.isAlarm" type="danger">åè¦</el-tag> |
| | | </div> |
| | | <div class="param-value">{{ param.value }} {{ param.unit }}</div> |
| | | <div class="param-range"> |
| | | æ£å¸¸èå´ï¼{{ param.min }} - {{ param.max }} {{ param.unit }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- åè¦æç¤º --> |
| | | <el-card shadow="hover" v-if="alarmList.length > 0" style="margin-bottom: 20px;"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åè¦æç¤º</span> |
| | | </div> |
| | | </template> |
| | | <el-scrollbar height="200px"> |
| | | <div class="alarm-item" v-for="alarm in alarmList" :key="alarm.id"> |
| | | <el-alert |
| | | :title="alarm.message" |
| | | :type="alarm.level === 'high' ? 'error' : alarm.level === 'medium' ? 'warning' : 'info'" |
| | | show-icon |
| | | closable |
| | | @close="handleAlarmClose(alarm.id)" |
| | | > |
| | | <template #default> |
| | | <div class="alarm-detail"> |
| | | <span>设å¤ï¼{{ alarm.deviceName }}</span> |
| | | <span>æ¶é´ï¼{{ alarm.time }}</span> |
| | | </div> |
| | | </template> |
| | | </el-alert> |
| | | </div> |
| | | </el-scrollbar> |
| | | </el-card> |
| | | |
| | | <!-- å岿°æ®è¶å¿å¾ --> |
| | | <el-card shadow="hover"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å岿°æ®è¶å¿</span> |
| | | <div class="param-tabs"> |
| | | <el-radio-group v-model="activeParam" size="small" @change="handleParamChange"> |
| | | <el-radio-button v-for="param in historyParams" :key="param" :label="param"> |
| | | {{ param }} |
| | | </el-radio-button> |
| | | </el-radio-group> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div ref="historyChartRef" class="chart-container"></div> |
| | | </el-card> |
| | | |
| | | <!-- å设å¤åæ°è¯¦æ
页 --> |
| | | <el-dialog v-model="deviceDetailVisible" title="设å¤åæ°è¯¦æ
" width="800px"> |
| | | <div class="device-detail-content"> |
| | | <h3>{{ selectedDevice.name }} - 宿¶åæ°</h3> |
| | | <el-row :gutter="20" style="margin-bottom: 20px;"> |
| | | <el-col :span="12" v-for="param in realTimeParams" :key="param.name"> |
| | | <div class="detail-param-item"> |
| | | <span class="param-label">{{ param.name }}ï¼</span> |
| | | <span class="param-value" :class="{ 'alarm-value': param.isAlarm }"> |
| | | {{ param.value }} {{ param.unit }} |
| | | </span> |
| | | <span class="param-range"> |
| | | ï¼{{ param.min }} - {{ param.max }} {{ param.unit }}ï¼ |
| | | </span> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <h3>åæ°éå¼è®¾ç½®</h3> |
| | | <el-form :model="thresholdForm" label-width="100px" style="margin-top: 20px;"> |
| | | <el-form-item v-for="param in realTimeParams" :key="param.name" :label="param.name"> |
| | | <el-input-number |
| | | v-model="thresholdForm[param.name + 'Min']" |
| | | :min="0" |
| | | :max="1000" |
| | | size="small" |
| | | style="width: 120px; margin-right: 10px;" |
| | | placeholder="æå°å¼" |
| | | ></el-input-number> |
| | | <span style="margin: 0 10px;">-</span> |
| | | <el-input-number |
| | | v-model="thresholdForm[param.name + 'Max']" |
| | | :min="0" |
| | | :max="1000" |
| | | size="small" |
| | | style="width: 120px; margin-right: 10px;" |
| | | placeholder="æå¤§å¼" |
| | | ></el-input-number> |
| | | <span>{{ param.unit }}</span> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="deviceDetailVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveThresholds">ä¿å设置</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, computed } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | |
| | | // 设å¤å表 |
| | | const devices = ref([ |
| | | { id: 'D001', name: 'ç©ºåæºA-001' }, |
| | | { id: 'D002', name: 'å·å´å¡B-002' }, |
| | | { id: 'D003', name: 'æ°´æ³µC-003' }, |
| | | { id: 'D004', name: 'åçµæºD-004' }, |
| | | { id: 'D005', name: 'ååå¨E-005' } |
| | | ]) |
| | | |
| | | // 设å¤éæ©è¡¨å |
| | | const deviceSelectForm = ref({ |
| | | deviceId: 'D001' // é»è®¤éæ©ç¬¬ä¸ä¸ªè®¾å¤ |
| | | }) |
| | | |
| | | // æ¶é´èå´ |
| | | const timeRange = ref([]) |
| | | |
| | | // å½åéä¸çè®¾å¤ |
| | | const selectedDevice = ref(devices.value[0]) |
| | | |
| | | // 宿¶åæ°æ°æ® |
| | | const realTimeParams = ref([ |
| | | { name: '温度', value: 45, unit: 'â', min: 0, max: 60, isAlarm: false }, |
| | | { name: 'åå', value: 0.85, unit: 'MPa', min: 0.5, max: 0.8, isAlarm: true }, |
| | | { name: '转é', value: 1450, unit: 'rpm', min: 1000, max: 1500, isAlarm: false }, |
| | | { name: 'æ¯å¨', value: 3.2, unit: 'mm/s', min: 0, max: 2.5, isAlarm: true }, |
| | | { name: 'çµæµ', value: 25.6, unit: 'A', min: 0, max: 30, isAlarm: false }, |
| | | { name: 'çµå', value: 380, unit: 'V', min: 360, max: 400, isAlarm: false } |
| | | ]) |
| | | |
| | | // åè¦å表 |
| | | const alarmList = ref([ |
| | | { id: 1, deviceName: 'ç©ºåæºA-001', message: 'ååè¶
æ ', level: 'high', time: '2024-12-16 14:32:15' }, |
| | | { id: 2, deviceName: 'ç©ºåæºA-001', message: 'æ¯å¨è¿å¤§', level: 'medium', time: '2024-12-16 14:30:45' }, |
| | | { id: 3, deviceName: 'å·å´å¡B-002', message: '温度å¼å¸¸', level: 'warning', time: '2024-12-16 14:28:30' } |
| | | ]) |
| | | |
| | | // åå²åæ°é项 |
| | | const historyParams = ref(['温度', 'åå', '转é', 'æ¯å¨', 'çµæµ', 'çµå']) |
| | | const activeParam = ref('温度') |
| | | |
| | | // å岿°æ® |
| | | const historyData = ref({ |
| | | dates: [], |
| | | values: [] |
| | | }) |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const historyChartRef = ref(null) |
| | | let historyChart = null |
| | | |
| | | // 设å¤è¯¦æ
å¯¹è¯æ¡ |
| | | const deviceDetailVisible = ref(false) |
| | | |
| | | // éå¼è®¾ç½®è¡¨å |
| | | const thresholdForm = ref({}) |
| | | |
| | | // åå§åéå¼è¡¨å |
| | | const initThresholdForm = () => { |
| | | const form = {} |
| | | realTimeParams.value.forEach(param => { |
| | | form[param.name + 'Min'] = param.min |
| | | form[param.name + 'Max'] = param.max |
| | | }) |
| | | thresholdForm.value = form |
| | | } |
| | | |
| | | // å¤ç设å¤åæ´ |
| | | const handleDeviceChange = (deviceId) => { |
| | | selectedDevice.value = devices.value.find(device => device.id === deviceId) || devices.value[0] |
| | | // 模æåæ¢è®¾å¤åæ´æ°å®æ¶åæ° |
| | | updateRealTimeParams() |
| | | // å·æ°å岿°æ®å¾è¡¨ |
| | | initHistoryChart() |
| | | } |
| | | |
| | | // æ´æ°å®æ¶åæ°ï¼æ¨¡æï¼ |
| | | const updateRealTimeParams = () => { |
| | | realTimeParams.value = realTimeParams.value.map(param => { |
| | | // çæéæºå¼ï¼é¨ååæ°å¯è½è§¦ååè¦ |
| | | const randomFactor = Math.random() * 0.2 - 0.1 // -0.1 å° 0.1 ä¹é´çéæºæ° |
| | | const baseValue = (param.min + param.max) / 2 |
| | | const newValue = baseValue + baseValue * randomFactor |
| | | const isAlarm = newValue < param.min || newValue > param.max |
| | | |
| | | return { |
| | | ...param, |
| | | value: Number(newValue.toFixed(2)), |
| | | isAlarm |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // å¤çå岿°æ®æ¥è¯¢ |
| | | const handleHistoryQuery = () => { |
| | | // æ¨¡ææ¥è¯¢å岿°æ® |
| | | generateHistoryData() |
| | | initHistoryChart() |
| | | ElMessage.success('å岿°æ®æ¥è¯¢æå') |
| | | } |
| | | |
| | | // çæå岿°æ®ï¼æ¨¡æï¼ |
| | | const generateHistoryData = () => { |
| | | const dates = [] |
| | | const values = [] |
| | | |
| | | // çæè¿å»7å¤©çæ¥æåå¯¹åºæ°æ® |
| | | for (let i = 6; i >= 0; i--) { |
| | | const date = new Date() |
| | | date.setDate(date.getDate() - i) |
| | | dates.push(date.toLocaleDateString()) |
| | | |
| | | // çæéæºå¼ |
| | | const baseValue = (realTimeParams.value.find(p => p.name === activeParam.value).min + |
| | | realTimeParams.value.find(p => p.name === activeParam.value).max) / 2 |
| | | const randomValue = baseValue + (Math.random() - 0.5) * baseValue * 0.4 |
| | | values.push(Number(randomValue.toFixed(2))) |
| | | } |
| | | |
| | | historyData.value = { |
| | | dates, |
| | | values |
| | | } |
| | | } |
| | | |
| | | // åå§åå岿°æ®è¶å¿å¾ |
| | | const initHistoryChart = () => { |
| | | if (historyChartRef.value) { |
| | | historyChart = echarts.init(historyChartRef.value) |
| | | |
| | | // å¦ææ²¡æå岿°æ®ï¼çææ¨¡ææ°æ® |
| | | if (historyData.value.dates.length === 0) { |
| | | generateHistoryData() |
| | | } |
| | | |
| | | const paramConfig = realTimeParams.value.find(p => p.name === activeParam.value) |
| | | const option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'cross', |
| | | label: { |
| | | backgroundColor: '#6a7985' |
| | | } |
| | | } |
| | | }, |
| | | grid: { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | boundaryGap: false, |
| | | data: historyData.value.dates |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: `${activeParam.value}(${paramConfig.unit})`, |
| | | min: paramConfig.min * 0.9, |
| | | max: paramConfig.max * 1.1 |
| | | }, |
| | | series: [ |
| | | { |
| | | name: activeParam.value, |
| | | type: 'line', |
| | | stack: 'æ»é', |
| | | areaStyle: {}, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: historyData.value.values, |
| | | itemStyle: { |
| | | color: '#409eff' |
| | | }, |
| | | lineStyle: { |
| | | width: 3 |
| | | } |
| | | } |
| | | ] |
| | | } |
| | | historyChart.setOption(option) |
| | | } |
| | | } |
| | | |
| | | // å¤ç忰忢 |
| | | const handleParamChange = () => { |
| | | // çææ°åæ°çå岿°æ® |
| | | generateHistoryData() |
| | | initHistoryChart() |
| | | } |
| | | |
| | | // å¤çåè¦å
³é |
| | | const handleAlarmClose = (alarmId) => { |
| | | const index = alarmList.value.findIndex(alarm => alarm.id === alarmId) |
| | | if (index !== -1) { |
| | | alarmList.value.splice(index, 1) |
| | | } |
| | | } |
| | | |
| | | // æå¼è®¾å¤è¯¦æ
页 |
| | | const openDeviceDetail = () => { |
| | | initThresholdForm() |
| | | deviceDetailVisible.value = true |
| | | } |
| | | |
| | | // ä¿åéå¼è®¾ç½® |
| | | const saveThresholds = () => { |
| | | // 模æä¿åéå¼ |
| | | realTimeParams.value = realTimeParams.value.map(param => { |
| | | return { |
| | | ...param, |
| | | min: thresholdForm.value[param.name + 'Min'], |
| | | max: thresholdForm.value[param.name + 'Max'] |
| | | } |
| | | }) |
| | | deviceDetailVisible.value = false |
| | | ElMessage.success('éå¼è®¾ç½®ä¿åæå') |
| | | } |
| | | |
| | | // çå¬çªå£å¤§å°ååï¼è°æ´å¾è¡¨å¤§å° |
| | | const handleResize = () => { |
| | | historyChart?.resize() |
| | | } |
| | | |
| | | // æ¯5ç§æ´æ°ä¸æ¬¡å®æ¶åæ°ï¼æ¨¡æï¼ |
| | | let updateTimer = null |
| | | const startRealTimeUpdate = () => { |
| | | updateTimer = setInterval(() => { |
| | | updateRealTimeParams() |
| | | }, 5000) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | // åå§åå¾è¡¨ |
| | | initHistoryChart() |
| | | // åå§åéå¼è¡¨å |
| | | initThresholdForm() |
| | | // å¼å§å®æ¶æ´æ° |
| | | startRealTimeUpdate() |
| | | // çå¬çªå£å¤§å°åå |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | // æ¸
é¤å®æ¶å¨ |
| | | if (updateTimer) { |
| | | clearInterval(updateTimer) |
| | | } |
| | | // 鿝å¾è¡¨ |
| | | historyChart?.dispose() |
| | | // ç§»é¤äºä»¶çå¬ |
| | | window.removeEventListener('resize', handleResize) |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .real-time-monitoring-container { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .device-select-form { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .param-tabs { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .param-card { |
| | | transition: all 0.3s; |
| | | } |
| | | |
| | | .alarm-card { |
| | | border-left: 4px solid #f56c6c; |
| | | box-shadow: 0 2px 12px 0 rgba(245, 108, 108, 0.1); |
| | | } |
| | | |
| | | .param-content { |
| | | text-align: center; |
| | | } |
| | | |
| | | .param-title { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | gap: 10px; |
| | | font-size: 16px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .param-value { |
| | | font-size: 32px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 10px; |
| | | display: block; |
| | | } |
| | | |
| | | .alarm-value { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .param-range { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .alarm-item { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .alarm-detail { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-top: 5px; |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .chart-container { |
| | | width: 100%; |
| | | height: 400px; |
| | | } |
| | | |
| | | .device-detail-content { |
| | | padding: 10px 0; |
| | | } |
| | | |
| | | .device-detail-content h3 { |
| | | margin-bottom: 20px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .detail-param-item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | padding: 10px; |
| | | background-color: #fafafa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .param-label { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | width: 80px; |
| | | } |
| | | |
| | | .param-range { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-left: 10px; |
| | | } |
| | | </style> |
| | |
| | | const { VITE_APP_ENV } = env;
|
| | | const baseUrl =
|
| | | VITE_APP_ENV == "development"
|
| | | ? "http://127.0.0.1:8018" // å¼åç¯å¢å端æ¥å£
|
| | | : "http://10.136.12.71:8018"; // ç产ç¯å¢å端æ¥å£
|
| | | ? "http://127.0.0.1:8020" // å¼åç¯å¢å端æ¥å£
|
| | | : "http://10.136.12.71:8020"; // ç产ç¯å¢å端æ¥å£
|
| | |
|
| | | return {
|
| | | // é¨ç½²ç产ç¯å¢åå¼åç¯å¢ä¸çURLã
|