| ¶Ô±ÈÐÂÎļþ | 
 |  |  | 
 |  |  | <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> |