¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <!-- 页颿 é¢ --> |
| | | <div class="page-header"> |
| | | <h2>è½æºé©¾é©¶è±</h2> |
| | | <div class="header-info"> |
| | | <span class="update-time">æåæ´æ°ï¼{{ lastUpdateTime }}</span> |
| | | <el-button type="primary" size="small" @click="refreshData"> |
| | | <el-icon><Refresh /></el-icon> |
| | | å·æ°æ°æ® |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 宿¶è½èçæ§ --> |
| | | <div class="real-time-monitor"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-card class="monitor-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>çµåæ¶è</span> |
| | | <el-tag type="success" size="small">宿¶</el-tag> |
| | | </div> |
| | | </template> |
| | | <div class="monitor-content"> |
| | | <div class="monitor-value"> |
| | | <span class="value">{{ electricityConsumption }}</span> |
| | | <span class="unit">kW·h</span> |
| | | </div> |
| | | <div class="monitor-trend"> |
| | | <span class="trend-label">è¶å¿ï¼</span> |
| | | <el-tag :type="getTrendType(electricityTrend)" size="small"> |
| | | {{ electricityTrend > 0 ? 'â' : 'â' }} {{ Math.abs(electricityTrend) }}% |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-card class="monitor-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ°´æ¶è</span> |
| | | <el-tag type="primary" size="small">宿¶</el-tag> |
| | | </div> |
| | | </template> |
| | | <div class="monitor-content"> |
| | | <div class="monitor-value"> |
| | | <span class="value">{{ waterConsumption }}</span> |
| | | <span class="unit">m³</span> |
| | | </div> |
| | | <div class="monitor-trend"> |
| | | <span class="trend-label">è¶å¿ï¼</span> |
| | | <el-tag :type="getTrendType(waterTrend)" size="small"> |
| | | {{ waterTrend > 0 ? 'â' : 'â' }} {{ Math.abs(waterTrend) }}% |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-card class="monitor-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>æ°ä½æ¶è</span> |
| | | <el-tag type="warning" size="small">宿¶</el-tag> |
| | | </div> |
| | | </template> |
| | | <div class="monitor-content"> |
| | | <div class="monitor-value"> |
| | | <span class="value">{{ gasConsumption }}</span> |
| | | <span class="unit">m³</span> |
| | | </div> |
| | | <div class="monitor-trend"> |
| | | <span class="trend-label">è¶å¿ï¼</span> |
| | | <el-tag :type="getTrendType(gasTrend)" size="small"> |
| | | {{ gasTrend > 0 ? 'â' : 'â' }} {{ Math.abs(gasTrend) }}% |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- è½èè¶å¿åæ --> |
| | | <div class="trend-analysis"> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>è½èè¶å¿åæ</span> |
| | | <div class="time-selector"> |
| | | <el-radio-group v-model="trendTimeUnit" @change="handleTrendTimeChange"> |
| | | <el-radio value="hour">å°æ¶</el-radio> |
| | | <el-radio value="day">æ¥</el-radio> |
| | | <el-radio value="week">å¨</el-radio> |
| | | <el-radio value="month">æ</el-radio> |
| | | <el-radio value="year">å¹´</el-radio> |
| | | </el-radio-group> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div class="chart-container"> |
| | | <div ref="trendChart" style="width: 100%; height: 400px;"></div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- è½èç»è®¡ä¸æå --> |
| | | <div class="statistics-ranking"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-card class="statistics-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="card-title">è½èç»è®¡æ¥è¡¨</span> |
| | | <div class="header-actions"> |
| | | <el-select v-model="statisticsPeriod" @change="handleStatisticsChange" size="small" style="width: 100px;"> |
| | | <el-option label="æ¥ç»è®¡" value="day" /> |
| | | <el-option label="å¨ç»è®¡" value="week" /> |
| | | <el-option label="æç»è®¡" value="month" /> |
| | | <el-option label="å¹´ç»è®¡" value="year" /> |
| | | </el-select> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <div class="statistics-content"> |
| | | <div class="statistics-item"> |
| | | <span class="label">æ»è½èï¼</span> |
| | | <span class="value">{{ totalEnergyConsumption }} kW·h</span> |
| | | </div> |
| | | <div class="statistics-item"> |
| | | <span class="label">忝ï¼</span> |
| | | <span class="value" :class="getComparisonClass(yearOverYear)"> |
| | | {{ yearOverYear > 0 ? '+' : '' }}{{ yearOverYear }}% |
| | | </span> |
| | | </div> |
| | | <div class="statistics-item"> |
| | | <span class="label">ç¯æ¯ï¼</span> |
| | | <span class="value" :class="getComparisonClass(monthOverMonth)"> |
| | | {{ monthOverMonth > 0 ? '+' : '' }}{{ monthOverMonth }}% |
| | | </span> |
| | | </div> |
| | | <div class="statistics-item"> |
| | | <span class="label">èè½çï¼</span> |
| | | <span class="value success">{{ energySavingRate }}%</span> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card class="ranking-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="card-title">è½èæå</span> |
| | | <el-select v-model="rankingType" @change="handleRankingChange" size="small" style="width: 120px;"> |
| | | <el-option label="é¨é¨æå" value="department" /> |
| | | <el-option label="è½¦é´æå" value="workshop" /> |
| | | <el-option label="è®¾å¤æå" value="equipment" /> |
| | | </el-select> |
| | | </div> |
| | | </template> |
| | | <div class="ranking-list"> |
| | | <div v-for="(item, index) in rankingList" :key="index" class="ranking-item"> |
| | | <div class="ranking-number" :class="getRankingClass(index + 1)">{{ index + 1 }}</div> |
| | | <div class="ranking-info"> |
| | | <div class="ranking-name">{{ item.name }}</div> |
| | | <div class="ranking-value">{{ item.value }} kW·h</div> |
| | | </div> |
| | | <div class="ranking-trend"> |
| | | <el-tag :type="getTrendType(item.trend)" size="small"> |
| | | {{ item.trend > 0 ? 'â' : 'â' }} {{ Math.abs(item.trend) }}% |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- å¼å¸¸åæä¸æºè½æ§å¶ --> |
| | | <div class="analysis-control"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-card class="abnormal-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="card-title">å¼å¸¸åæ</span> |
| | | <el-tag type="danger" size="small">{{ abnormalCount }}个å¼å¸¸</el-tag> |
| | | </div> |
| | | </template> |
| | | <div class="abnormal-list"> |
| | | <div v-for="(item, index) in abnormalList" :key="index" class="abnormal-item"> |
| | | <div class="abnormal-icon"> |
| | | <el-icon :color="getAbnormalColor(item.level)"> |
| | | <Warning v-if="item.level === 'warning'" /> |
| | | <CircleClose v-else /> |
| | | </el-icon> |
| | | </div> |
| | | <div class="abnormal-content"> |
| | | <div class="abnormal-title">{{ item.title }}</div> |
| | | <div class="abnormal-desc">{{ item.description }}</div> |
| | | <div class="abnormal-time">{{ item.time }}</div> |
| | | </div> |
| | | <div class="abnormal-action"> |
| | | <el-button link size="small" @click="handleAbnormal(item)">å¤ç</el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-card class="control-card"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span class="card-title">æºè½æ§å¶ç³»ç»</span> |
| | | <el-switch v-model="autoControlEnabled" @change="handleAutoControlChange" /> |
| | | </div> |
| | | </template> |
| | | <div class="control-content"> |
| | | <div class="control-item"> |
| | | <span class="label">å³°è°·å¹³çµä»·ç®¡çï¼</span> |
| | | <el-tag :type="getPriceType(currentPriceType)" size="small"> |
| | | {{ getPriceTypeText(currentPriceType) }} |
| | | </el-tag> |
| | | </div> |
| | | <div class="control-item"> |
| | | <span class="label">è´è·é¢æµï¼</span> |
| | | <span class="value">{{ loadForecast }} kW</span> |
| | | </div> |
| | | <div class="control-item"> |
| | | <span class="label">èªå¨å¯åï¼</span> |
| | | <el-tag :type="autoStartStop ? 'success' : 'info'" size="small"> |
| | | {{ autoStartStop ? 'å·²å¯ç¨' : 'å·²ç¦ç¨' }} |
| | | </el-tag> |
| | | </div> |
| | | <div class="control-item"> |
| | | <span class="label">æºè½è°èï¼</span> |
| | | <el-progress :percentage="intelligentAdjustment" :color="getProgressColor" /> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | |
| | | <!-- ç¯ä¿ææ --> |
| | | <div class="environmental-indicators"> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>ç¯ä¿ææ çæ§</span> |
| | | </div> |
| | | </template> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <div class="indicator-item"> |
| | | <div class="indicator-title">ç¢³ææ¾é</div> |
| | | <div class="indicator-value">{{ carbonEmission }} kg</div> |
| | | <div class="indicator-trend"> |
| | | <span>忝ï¼</span> |
| | | <span :class="getComparisonClass(carbonEmissionTrend)"> |
| | | {{ carbonEmissionTrend > 0 ? '+' : '' }}{{ carbonEmissionTrend }}% |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="indicator-item"> |
| | | <div class="indicator-title">ç¯ä¿è¾¾æ ç</div> |
| | | <div class="indicator-value">{{ environmentalCompliance }}%</div> |
| | | <div class="indicator-trend"> |
| | | <span>ç®æ ï¼</span> |
| | | <span class="success">95%</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="indicator-item"> |
| | | <div class="indicator-title">绿è²è½æºå æ¯</div> |
| | | <div class="indicator-value">{{ greenEnergyRatio }}%</div> |
| | | <div class="indicator-trend"> |
| | | <span>ç®æ ï¼</span> |
| | | <span class="success">30%</span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- å¤ç»´åº¦æ¥è¡¨ --> |
| | | <div class="multi-dimensional-reports"> |
| | | <el-card> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å¤ç»´åº¦æ¥è¡¨</span> |
| | | </div> |
| | | </template> |
| | | <div class="report-filters"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="6"> |
| | | <el-form-item label="æ¶é´ç»´åº¦"> |
| | | <el-select v-model="reportTimeDimension" placeholder="éæ©æ¶é´ç»´åº¦"> |
| | | <el-option label="å°æ¶" value="hour" /> |
| | | <el-option label="æ¥" value="day" /> |
| | | <el-option label="å¨" value="week" /> |
| | | <el-option label="æ" value="month" /> |
| | | <el-option label="å¹´" value="year" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="é¨é¨ç»´åº¦"> |
| | | <el-select v-model="reportDepartmentDimension" placeholder="éæ©é¨é¨"> |
| | | <el-option label="å
¨é¨é¨é¨" value="all" /> |
| | | <el-option label="ç产é¨" value="production" /> |
| | | <el-option label="ææ¯é¨" value="technology" /> |
| | | <el-option label="è¡æ¿é¨" value="administration" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item label="设å¤ç»´åº¦"> |
| | | <el-select v-model="reportEquipmentDimension" placeholder="éæ©è®¾å¤ç±»å"> |
| | | <el-option label="å
¨é¨è®¾å¤" value="all" /> |
| | | <el-option label="çµå设å¤" value="electricity" /> |
| | | <el-option label="æ°´å¤ç设å¤" value="water" /> |
| | | <el-option label="æ°ä½è®¾å¤" value="gas" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="generateReport">çææ¥è¡¨</el-button> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <div class="report-preview"> |
| | | <div class="report-data"> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <div class="data-card"> |
| | | <div class="data-title">çµåæ¶è</div> |
| | | <div class="data-value">{{ reportData.electricity }} kW·h</div> |
| | | <div class="data-trend"> |
| | | <span :class="getTrendClass(reportData.electricityTrend)"> |
| | | {{ reportData.electricityTrend > 0 ? 'â' : 'â' }} {{ Math.abs(reportData.electricityTrend) }}% |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="data-card"> |
| | | <div class="data-title">æ°´æ¶è</div> |
| | | <div class="data-value">{{ reportData.water }} m³</div> |
| | | <div class="data-trend"> |
| | | <span :class="getTrendClass(reportData.waterTrend)"> |
| | | {{ reportData.waterTrend > 0 ? 'â' : 'â' }} {{ Math.abs(reportData.waterTrend) }}% |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <div class="data-card"> |
| | | <div class="data-title">æ°ä½æ¶è</div> |
| | | <div class="data-value">{{ reportData.gas }} m³</div> |
| | | <div class="data-trend"> |
| | | <span :class="getTrendClass(reportData.gasTrend)"> |
| | | {{ reportData.gasTrend > 0 ? 'â' : 'â' }} {{ Math.abs(reportData.gasTrend) }}% |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <div class="report-chart"> |
| | | <div class="chart-title">è½èè¶å¿å¾</div> |
| | | <div class="chart-bars"> |
| | | <div v-for="(item, index) in reportData.chartData" :key="index" class="chart-bar"> |
| | | <div class="bar-label">{{ item.label }}</div> |
| | | <div class="bar-container"> |
| | | <div class="bar-fill" :style="{ height: item.percentage + '%', backgroundColor: item.color }"></div> |
| | | </div> |
| | | <div class="bar-value">{{ item.value }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="report-summary"> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">æ»è½èï¼</span> |
| | | <span class="summary-value">{{ reportData.totalEnergy }} kW·h</span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">å¹³åè½èï¼</span> |
| | | <span class="summary-value">{{ reportData.averageEnergy }} kW·h</span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">è½èæçï¼</span> |
| | | <span class="summary-value">{{ reportData.efficiency }}%</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import * as echarts from 'echarts' |
| | | import { |
| | | Refresh, |
| | | Download, |
| | | Warning, |
| | | CircleClose, |
| | | Document, |
| | | Edit, |
| | | Bell |
| | | } from '@element-plus/icons-vue' |
| | | |
| | | // ååºå¼æ°æ® |
| | | const lastUpdateTime = ref('') |
| | | const electricityConsumption = ref(0) |
| | | const waterConsumption = ref(0) |
| | | const gasConsumption = ref(0) |
| | | const electricityTrend = ref(0) |
| | | const waterTrend = ref(0) |
| | | const gasTrend = ref(0) |
| | | |
| | | // è¶å¿åæ |
| | | const trendTimeUnit = ref('day') |
| | | const trendChart = ref(null) |
| | | let chartInstance = null |
| | | |
| | | // ç»è®¡æ¥è¡¨ |
| | | const statisticsPeriod = ref('month') |
| | | const totalEnergyConsumption = ref(0) |
| | | const yearOverYear = ref(0) |
| | | const monthOverMonth = ref(0) |
| | | const energySavingRate = ref(0) |
| | | |
| | | // è½èæå |
| | | const rankingType = ref('department') |
| | | const rankingList = ref([]) |
| | | |
| | | // å¼å¸¸åæ |
| | | const abnormalCount = ref(0) |
| | | const abnormalList = ref([]) |
| | | |
| | | // æºè½æ§å¶ |
| | | const autoControlEnabled = ref(true) |
| | | const currentPriceType = ref('peak') |
| | | const loadForecast = ref(0) |
| | | const autoStartStop = ref(true) |
| | | const intelligentAdjustment = ref(0) |
| | | |
| | | // ç¯ä¿ææ |
| | | const carbonEmission = ref(0) |
| | | const carbonEmissionTrend = ref(0) |
| | | const environmentalCompliance = ref(0) |
| | | const greenEnergyRatio = ref(0) |
| | | |
| | | // å¤ç»´åº¦æ¥è¡¨ |
| | | const reportTimeDimension = ref('month') |
| | | const reportDepartmentDimension = ref('all') |
| | | const reportEquipmentDimension = ref('all') |
| | | const reportData = ref({ |
| | | electricity: 0, |
| | | water: 0, |
| | | gas: 0, |
| | | electricityTrend: 0, |
| | | waterTrend: 0, |
| | | gasTrend: 0, |
| | | totalEnergy: 0, |
| | | averageEnergy: 0, |
| | | efficiency: 0, |
| | | chartData: [] |
| | | }) |
| | | |
| | | // 宿¶å¨ |
| | | let updateTimer = null |
| | | |
| | | // è·åè¶å¿ç±»åæ ·å¼ |
| | | const getTrendType = (trend) => { |
| | | if (trend > 0) return 'danger' |
| | | if (trend < 0) return 'success' |
| | | return 'info' |
| | | } |
| | | |
| | | // è·å对æ¯ç±»åæ ·å¼ |
| | | const getComparisonClass = (value) => { |
| | | if (value > 0) return 'danger' |
| | | if (value < 0) return 'success' |
| | | return 'info' |
| | | } |
| | | |
| | | // è·åæåæ ·å¼ |
| | | const getRankingClass = (rank) => { |
| | | if (rank === 1) return 'ranking-first' |
| | | if (rank === 2) return 'ranking-second' |
| | | if (rank === 3) return 'ranking-third' |
| | | return 'ranking-normal' |
| | | } |
| | | |
| | | // è·åå¼å¸¸é¢è² |
| | | const getAbnormalColor = (level) => { |
| | | return level === 'warning' ? '#E6A23C' : '#F56C6C' |
| | | } |
| | | |
| | | // è·åçµä»·ç±»åæ ·å¼ |
| | | const getPriceType = (type) => { |
| | | const typeMap = { |
| | | peak: 'danger', |
| | | normal: 'warning', |
| | | valley: 'success' |
| | | } |
| | | return typeMap[type] || 'info' |
| | | } |
| | | |
| | | // è·åçµä»·ç±»åææ¬ |
| | | const getPriceTypeText = (type) => { |
| | | const typeMap = { |
| | | peak: 'å³°æ¶', |
| | | normal: 'å¹³æ¶', |
| | | valley: 'è°·æ¶' |
| | | } |
| | | return typeMap[type] || 'æªç¥' |
| | | } |
| | | |
| | | // è·åè¿åº¦æ¡é¢è² |
| | | const getProgressColor = (percentage) => { |
| | | if (percentage < 50) return '#67C23A' |
| | | if (percentage < 80) return '#E6A23C' |
| | | return '#F56C6C' |
| | | } |
| | | |
| | | // è·åè¶å¿æ ·å¼ |
| | | const getTrendClass = (trend) => { |
| | | if (trend > 0) return 'trend-up' |
| | | if (trend < 0) return 'trend-down' |
| | | return 'trend-stable' |
| | | } |
| | | |
| | | // æ¨¡ææ°æ®çæ |
| | | const generateMockData = () => { |
| | | // 宿¶è½èæ°æ® |
| | | electricityConsumption.value = Math.floor(Math.random() * 1000) + 2000 |
| | | waterConsumption.value = Math.floor(Math.random() * 100) + 150 |
| | | gasConsumption.value = Math.floor(Math.random() * 50) + 80 |
| | | |
| | | // è¶å¿æ°æ® |
| | | electricityTrend.value = (Math.random() * 20 - 10).toFixed(1) |
| | | waterTrend.value = (Math.random() * 15 - 7.5).toFixed(1) |
| | | gasTrend.value = (Math.random() * 12 - 6).toFixed(1) |
| | | |
| | | // ç»è®¡æ°æ® |
| | | totalEnergyConsumption.value = Math.floor(Math.random() * 50000) + 100000 |
| | | yearOverYear.value = (Math.random() * 20 - 10).toFixed(1) |
| | | monthOverMonth.value = (Math.random() * 15 - 7.5).toFixed(1) |
| | | energySavingRate.value = (Math.random() * 10 + 5).toFixed(1) |
| | | |
| | | // æåæ°æ® |
| | | rankingList.value = [ |
| | | { name: 'ç产车é´A', value: Math.floor(Math.random() * 5000) + 10000, trend: (Math.random() * 20 - 10).toFixed(1) }, |
| | | { name: 'ç产车é´B', value: Math.floor(Math.random() * 4000) + 8000, trend: (Math.random() * 20 - 10).toFixed(1) }, |
| | | { name: 'ææ¯ç åé¨', value: Math.floor(Math.random() * 3000) + 6000, trend: (Math.random() * 20 - 10).toFixed(1) }, |
| | | { name: 'è¡æ¿åå
¬åº', value: Math.floor(Math.random() * 2000) + 4000, trend: (Math.random() * 20 - 10).toFixed(1) }, |
| | | { name: 'åå¤ä¿éåº', value: Math.floor(Math.random() * 1500) + 3000, trend: (Math.random() * 20 - 10).toFixed(1) } |
| | | ].sort((a, b) => b.value - a.value) |
| | | |
| | | // å¼å¸¸æ°æ® |
| | | abnormalCount.value = Math.floor(Math.random() * 5) + 1 |
| | | abnormalList.value = [ |
| | | { level: 'warning', title: 'çµåè´è·è¿é«', description: 'ç产车é´Açµåè´è·è¾¾å°85%ï¼å»ºè®®æ£æ¥è®¾å¤è¿è¡ç¶æ', time: '2åéå' }, |
| | | { level: 'error', title: 'æ°´åå¼å¸¸', description: 'æ°´å¤ç设å¤ååå¼å¸¸ï¼å½ååå0.3MPaï¼ä½äºæ£å¸¸èå´', time: '5åéå' } |
| | | ] |
| | | |
| | | // æºè½æ§å¶æ°æ® |
| | | loadForecast.value = Math.floor(Math.random() * 500) + 1500 |
| | | intelligentAdjustment.value = Math.floor(Math.random() * 30) + 60 |
| | | |
| | | // ç¯ä¿ææ |
| | | carbonEmission.value = Math.floor(Math.random() * 1000) + 5000 |
| | | carbonEmissionTrend.value = (Math.random() * 15 - 7.5).toFixed(1) |
| | | environmentalCompliance.value = (Math.random() * 5 + 95).toFixed(1) |
| | | greenEnergyRatio.value = (Math.random() * 10 + 25).toFixed(1) |
| | | |
| | | // æ´æ°æåæ´æ°æ¶é´ |
| | | lastUpdateTime.value = new Date().toLocaleString() |
| | | |
| | | // åæ¶æ´æ°æ¥è¡¨æ°æ® |
| | | generateReportData() |
| | | } |
| | | |
| | | // åå§åè¶å¿å¾è¡¨ |
| | | const initTrendChart = () => { |
| | | if (chartInstance) { |
| | | chartInstance.dispose() |
| | | } |
| | | |
| | | chartInstance = echarts.init(trendChart.value) |
| | | |
| | | const option = { |
| | | title: { |
| | | text: 'è½èè¶å¿åæ', |
| | | left: 'center' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'axis' |
| | | }, |
| | | legend: { |
| | | data: ['çµå', 'æ°´', 'æ°ä½'], |
| | | bottom: 10 |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: generateTimeData() |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | name: 'æ¶èé' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: 'çµå', |
| | | type: 'line', |
| | | data: generateSeriesData(), |
| | | smooth: true |
| | | }, |
| | | { |
| | | name: 'æ°´', |
| | | type: 'line', |
| | | data: generateSeriesData(), |
| | | smooth: true |
| | | }, |
| | | { |
| | | name: 'æ°ä½', |
| | | type: 'line', |
| | | data: generateSeriesData(), |
| | | smooth: true |
| | | } |
| | | ] |
| | | } |
| | | |
| | | chartInstance.setOption(option) |
| | | } |
| | | |
| | | // çææ¶é´æ°æ® |
| | | const generateTimeData = () => { |
| | | const data = [] |
| | | const now = new Date() |
| | | |
| | | switch (trendTimeUnit.value) { |
| | | case 'hour': |
| | | for (let i = 23; i >= 0; i--) { |
| | | const time = new Date(now.getTime() - i * 60 * 60 * 1000) |
| | | data.unshift(time.getHours() + ':00') |
| | | } |
| | | break |
| | | case 'day': |
| | | for (let i = 29; i >= 0; i--) { |
| | | const time = new Date(now.getTime() - i * 24 * 60 * 60 * 1000) |
| | | data.unshift(time.getDate() + 'æ¥') |
| | | } |
| | | break |
| | | case 'week': |
| | | for (let i = 11; i >= 0; i--) { |
| | | data.unshift(`第${12 - i}å¨`) |
| | | } |
| | | break |
| | | case 'month': |
| | | for (let i = 11; i >= 0; i--) { |
| | | const month = (12 - i) % 12 || 12 |
| | | data.unshift(`${month}æ`) |
| | | } |
| | | break |
| | | case 'year': |
| | | for (let i = 4; i >= 0; i--) { |
| | | const year = new Date().getFullYear() - i |
| | | data.unshift(`${year}å¹´`) |
| | | } |
| | | break |
| | | } |
| | | |
| | | return data |
| | | } |
| | | |
| | | // çæç³»åæ°æ® |
| | | const generateSeriesData = () => { |
| | | const data = [] |
| | | const count = trendTimeUnit.value === 'hour' ? 24 : |
| | | trendTimeUnit.value === 'day' ? 30 : |
| | | trendTimeUnit.value === 'week' ? 12 : |
| | | trendTimeUnit.value === 'month' ? 12 : 5 |
| | | |
| | | for (let i = 0; i < count; i++) { |
| | | data.push(Math.floor(Math.random() * 1000) + 500) |
| | | } |
| | | |
| | | return data |
| | | } |
| | | |
| | | // å¤çè¶å¿æ¶é´åå |
| | | const handleTrendTimeChange = () => { |
| | | nextTick(() => { |
| | | initTrendChart() |
| | | }) |
| | | } |
| | | |
| | | // å¤çç»è®¡å¨æåå |
| | | const handleStatisticsChange = () => { |
| | | generateMockData() |
| | | } |
| | | |
| | | // å¤çæåç±»ååå |
| | | const handleRankingChange = () => { |
| | | // æ ¹æ®ç±»åéæ°çææåæ°æ® |
| | | generateMockData() |
| | | } |
| | | |
| | | // å¤çèªå¨æ§å¶åå |
| | | const handleAutoControlChange = (value) => { |
| | | ElMessage.success(`æºè½æ§å¶ç³»ç»å·²${value ? 'å¯ç¨' : 'ç¦ç¨'}`) |
| | | } |
| | | |
| | | // å¤çå¼å¸¸ |
| | | const handleAbnormal = (item) => { |
| | | ElMessage.info(`æ£å¨å¤çå¼å¸¸ï¼${item.title}`) |
| | | } |
| | | |
| | | // å·æ°æ°æ® |
| | | const refreshData = () => { |
| | | generateMockData() |
| | | if (chartInstance) { |
| | | initTrendChart() |
| | | } |
| | | ElMessage.success('æ°æ®å·²å·æ°') |
| | | } |
| | | |
| | | // 导åºç»è®¡ |
| | | const exportStatistics = () => { |
| | | ElMessage.success('ç»è®¡æ°æ®å¯¼åºæå') |
| | | } |
| | | |
| | | // 导åºç¯ä¿æ¥å |
| | | const exportEnvironmentalReport = () => { |
| | | ElMessage.success('ç¯ä¿æ¥åå¯¼åºæå') |
| | | } |
| | | |
| | | // çæèªå®ä¹æ¥è¡¨ |
| | | const generateCustomReport = () => { |
| | | ElMessage.info('èªå®ä¹æ¥è¡¨åè½å¼åä¸...') |
| | | } |
| | | |
| | | // 订é
æ¥è¡¨ |
| | | const subscribeReport = () => { |
| | | ElMessage.info('æ¥è¡¨è®¢é
åè½å¼åä¸...') |
| | | } |
| | | |
| | | // çææ¥è¡¨æ°æ® |
| | | const generateReportData = () => { |
| | | // çæåºç¡æ°æ® |
| | | reportData.value.electricity = Math.floor(Math.random() * 5000) + 8000 |
| | | reportData.value.water = Math.floor(Math.random() * 200) + 300 |
| | | reportData.value.gas = Math.floor(Math.random() * 100) + 150 |
| | | |
| | | // çæè¶å¿æ°æ® |
| | | reportData.value.electricityTrend = (Math.random() * 20 - 10).toFixed(1) |
| | | reportData.value.waterTrend = (Math.random() * 15 - 7.5).toFixed(1) |
| | | reportData.value.gasTrend = (Math.random() * 12 - 6).toFixed(1) |
| | | |
| | | // è®¡ç®æ»è½èåå¹³åè½è |
| | | reportData.value.totalEnergy = reportData.value.electricity + reportData.value.water * 0.1 + reportData.value.gas * 0.05 |
| | | reportData.value.averageEnergy = Math.floor(reportData.value.totalEnergy / 3) |
| | | reportData.value.efficiency = (Math.random() * 20 + 80).toFixed(1) |
| | | |
| | | // çæå¾è¡¨æ°æ® |
| | | const labels = ['å¨ä¸', 'å¨äº', 'å¨ä¸', 'å¨å', 'å¨äº', 'å¨å
', '卿¥'] |
| | | const colors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#9c27b0', '#ff9800'] |
| | | |
| | | reportData.value.chartData = labels.map((label, index) => ({ |
| | | label, |
| | | value: Math.floor(Math.random() * 1000) + 500, |
| | | percentage: Math.floor(Math.random() * 40) + 30, |
| | | color: colors[index] |
| | | })) |
| | | } |
| | | |
| | | // çææ¥è¡¨ |
| | | const generateReport = () => { |
| | | generateReportData() |
| | | ElMessage.success('æ¥è¡¨çææå') |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | // å¯å¨å®æ¶æ´æ° |
| | | const startAutoUpdate = () => { |
| | | updateTimer = setInterval(() => { |
| | | generateMockData() |
| | | if (chartInstance) { |
| | | initTrendChart() |
| | | } |
| | | }, 60000) // æ¯åéæ´æ°ä¸æ¬¡ |
| | | } |
| | | |
| | | // 忢宿¶æ´æ° |
| | | const stopAutoUpdate = () => { |
| | | if (updateTimer) { |
| | | clearInterval(updateTimer) |
| | | updateTimer = null |
| | | } |
| | | } |
| | | |
| | | // ç»ä»¶æè½½ |
| | | onMounted(() => { |
| | | generateMockData() |
| | | nextTick(() => { |
| | | initTrendChart() |
| | | }) |
| | | startAutoUpdate() |
| | | }) |
| | | |
| | | // ç»ä»¶å¸è½½ |
| | | onUnmounted(() => { |
| | | stopAutoUpdate() |
| | | if (chartInstance) { |
| | | chartInstance.dispose() |
| | | } |
| | | }) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .app-container { |
| | | padding: 12px; |
| | | background: #f5f5f5; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | padding: 16px; |
| | | background: white; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | |
| | | h2 { |
| | | margin: 0; |
| | | color: #303133; |
| | | font-size: 22px; |
| | | } |
| | | |
| | | .header-info { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | |
| | | .update-time { |
| | | color: #909399; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .real-time-monitor { |
| | | margin-bottom: 12px; |
| | | |
| | | .monitor-card { |
| | | .monitor-content { |
| | | text-align: center; |
| | | padding: 16px 0; |
| | | |
| | | .monitor-value { |
| | | margin-bottom: 12px; |
| | | |
| | | .value { |
| | | font-size: 28px; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | } |
| | | |
| | | .unit { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-left: 4px; |
| | | } |
| | | } |
| | | |
| | | .monitor-trend { |
| | | .trend-label { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-right: 6px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .trend-analysis { |
| | | margin-bottom: 12px; |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .time-selector { |
| | | .el-radio-group { |
| | | .el-radio { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .chart-container { |
| | | padding: 16px 0; |
| | | } |
| | | } |
| | | |
| | | .statistics-ranking { |
| | | margin-bottom: 12px; |
| | | |
| | | .statistics-card, .ranking-card { |
| | | height: 100%; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); |
| | | border-radius: 8px; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); |
| | | } |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | background: #fafafa; |
| | | |
| | | .card-title { |
| | | font-size: 15px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .statistics-content { |
| | | padding: 16px; |
| | | |
| | | .statistics-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | padding: 10px 12px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | transition: background-color 0.3s ease; |
| | | |
| | | &:hover { |
| | | background: #e9ecef; |
| | | } |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .label { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .value { |
| | | font-weight: bold; |
| | | font-size: 15px; |
| | | |
| | | &.success { |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .ranking-list { |
| | | padding: 16px; |
| | | |
| | | .ranking-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 12px; |
| | | margin-bottom: 6px; |
| | | background: #f8f9fa; |
| | | border-radius: 6px; |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | background: #e9ecef; |
| | | transform: translateX(4px); |
| | | } |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .ranking-number { |
| | | width: 32px; |
| | | height: 32px; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | margin-right: 12px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | |
| | | &.ranking-first { |
| | | background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | &.ranking-second { |
| | | background: linear-gradient(135deg, #c0c0c0 0%, #d4d4d4 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | &.ranking-third { |
| | | background: linear-gradient(135deg, #cd7f32 0%, #daa520 100%); |
| | | color: #fff; |
| | | } |
| | | |
| | | &.ranking-normal { |
| | | background: linear-gradient(135deg, #f5f5f5 0%, #e9ecef 100%); |
| | | color: #909399; |
| | | } |
| | | } |
| | | |
| | | .ranking-info { |
| | | flex: 1; |
| | | |
| | | .ranking-name { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .ranking-value { |
| | | color: #606266; |
| | | font-size: 13px; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | .ranking-trend { |
| | | margin-left: 12px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .analysis-control { |
| | | margin-bottom: 20px; |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .abnormal-list { |
| | | .abnormal-item { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | padding: 15px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .abnormal-icon { |
| | | margin-right: 15px; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .abnormal-content { |
| | | flex: 1; |
| | | |
| | | .abnormal-title { |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .abnormal-desc { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | margin-bottom: 5px; |
| | | } |
| | | |
| | | .abnormal-time { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .abnormal-action { |
| | | margin-left: 15px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .control-content { |
| | | .control-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .label { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .value { |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .environmental-indicators { |
| | | margin-bottom: 20px; |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | } |
| | | |
| | | .indicator-item { |
| | | text-align: center; |
| | | padding: 20px 0; |
| | | |
| | | .indicator-title { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .indicator-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #409eff; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .indicator-trend { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | |
| | | .success { |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .multi-dimensional-reports { |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 10px; |
| | | } |
| | | } |
| | | |
| | | .report-filters { |
| | | padding: 20px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .report-preview { |
| | | .report-data { |
| | | padding: 20px 0; |
| | | |
| | | .data-card { |
| | | text-align: center; |
| | | padding: 16px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | margin-bottom: 16px; |
| | | |
| | | .data-title { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 20px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .data-trend { |
| | | font-size: 12px; |
| | | |
| | | .trend-up { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .trend-down { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .trend-stable { |
| | | color: #909399; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .report-chart { |
| | | margin: 20px 0; |
| | | padding: 20px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | |
| | | .chart-title { |
| | | text-align: center; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .chart-bars { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: flex-end; |
| | | height: 120px; |
| | | |
| | | .chart-bar { |
| | | text-align: center; |
| | | flex: 1; |
| | | margin: 0 8px; |
| | | |
| | | .bar-label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .bar-container { |
| | | height: 80px; |
| | | background: #e9ecef; |
| | | border-radius: 4px; |
| | | position: relative; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .bar-fill { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | border-radius: 4px; |
| | | transition: height 0.3s ease; |
| | | } |
| | | |
| | | .bar-value { |
| | | font-size: 12px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .report-summary { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | padding: 20px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | |
| | | .summary-item { |
| | | text-align: center; |
| | | |
| | | .summary-label { |
| | | display: block; |
| | | color: #606266; |
| | | font-size: 14px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 18px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // éç¨æ ·å¼ |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .success { |
| | | color: #67c23a; |
| | | } |
| | | |
| | | .danger { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .warning { |
| | | color: #e6a23c; |
| | | } |
| | | |
| | | .info { |
| | | color: #909399; |
| | | } |
| | | </style> |