|
@@ -305,51 +305,189 @@
|
|
|
</el-tabs>
|
|
|
|
|
|
<!-- 电梯监控对话框 -->
|
|
|
- <el-dialog v-model="monitorVisible" :title="monitorTitle" width="800px" append-to-body>
|
|
|
- <div class="elevator-monitor">
|
|
|
+ <el-dialog v-model="monitorVisible" :title="monitorTitle" width="1200px" append-to-body class="monitor-dialog">
|
|
|
+ <div class="elevator-monitor-container">
|
|
|
<el-row :gutter="20">
|
|
|
+ <!-- 左侧:电梯运行状态可视化 -->
|
|
|
<el-col :span="12">
|
|
|
- <div class="monitor-section">
|
|
|
- <h4>基本信息</h4>
|
|
|
- <el-descriptions :column="1" border size="small">
|
|
|
- <el-descriptions-item label="设备编码">{{ currentElevator.deviceCode }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="设备名称">{{ currentElevator.deviceName }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="楼栋位置">{{ currentElevator.buildingName }} - {{ currentElevator.installPosition }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="品牌型号">{{ currentElevator.brand }} {{ currentElevator.model }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="额定载重">{{ currentElevator.ratedLoad }}kg</el-descriptions-item>
|
|
|
- <el-descriptions-item label="额定速度">{{ currentElevator.ratedSpeed }}m/s</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
+ <div class="elevator-visual-panel">
|
|
|
+ <h3 class="panel-title">
|
|
|
+ <svg viewBox="0 0 24 24" width="20" height="20" style="margin-right: 8px;">
|
|
|
+ <path fill="currentColor" d="M6,2L18,2A1,1 0 0,1 19,3V20A1,1 0 0,1 18,21L6,21A1,1 0 0,1 5,20V3A1,1 0 0,1 6,2M8,4V8H16V4H8M8,10V14H16V10H8M8,16V20H16V16H8Z"/>
|
|
|
+ </svg>
|
|
|
+ 电梯实时状态
|
|
|
+ </h3>
|
|
|
+ <div class="elevator-building">
|
|
|
+ <div class="building-wrapper">
|
|
|
+ <!-- 楼层显示 -->
|
|
|
+ <div class="floors-container">
|
|
|
+ <div
|
|
|
+ v-for="floor in elevatorFloors"
|
|
|
+ :key="floor"
|
|
|
+ class="floor-level"
|
|
|
+ :class="{ 'current-floor': floor === currentElevator.currentFloor }"
|
|
|
+ >
|
|
|
+ <div class="floor-label">{{ floor }}F</div>
|
|
|
+ <div class="floor-shaft">
|
|
|
+ <div class="shaft-line"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 电梯轿厢 -->
|
|
|
+ <div
|
|
|
+ class="elevator-cabin"
|
|
|
+ :style="{ top: getElevatorPosition() + 'px' }"
|
|
|
+ :class="{
|
|
|
+ 'moving-up': currentElevator.direction === '1',
|
|
|
+ 'moving-down': currentElevator.direction === '2'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <div class="cabin-inner">
|
|
|
+ <div class="cabin-display">{{ currentElevator.currentFloor }}</div>
|
|
|
+ <div class="cabin-door" :class="{ 'door-open': currentElevator.doorStatus === 1 }">
|
|
|
+ <div class="door-left"></div>
|
|
|
+ <div class="door-right"></div>
|
|
|
+ </div>
|
|
|
+ <div class="cabin-load">
|
|
|
+ <i class="el-icon-user-solid"></i>
|
|
|
+ <span>{{ currentElevator.passengerCount || 0 }}人</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 状态指示器 -->
|
|
|
+ <div class="status-indicators">
|
|
|
+ <div class="indicator" :class="{ active: currentElevator.direction === '1' }">
|
|
|
+ <svg viewBox="0 0 24 24" width="24" height="24">
|
|
|
+ <path fill="currentColor" d="M7,14L12,9L17,14H7Z"/>
|
|
|
+ </svg>
|
|
|
+ <span>上行</span>
|
|
|
+ </div>
|
|
|
+ <div class="indicator" :class="{ active: currentElevator.direction === '2' }">
|
|
|
+ <svg viewBox="0 0 24 24" width="24" height="24">
|
|
|
+ <path fill="currentColor" d="M7,10L12,15L17,10H7Z"/>
|
|
|
+ </svg>
|
|
|
+ <span>下行</span>
|
|
|
+ </div>
|
|
|
+ <div class="indicator" :class="{ active: currentElevator.runStatus === 0 }">
|
|
|
+ <svg viewBox="0 0 24 24" width="24" height="24">
|
|
|
+ <path fill="currentColor" d="M14,19H18V5H14M6,19H10V5H6V19Z"/>
|
|
|
+ </svg>
|
|
|
+ <span>停止</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
+
|
|
|
+ <!-- 右侧:详细信息和实时数据 -->
|
|
|
<el-col :span="12">
|
|
|
- <div class="monitor-section">
|
|
|
- <h4>实时状态</h4>
|
|
|
- <div class="elevator-visual">
|
|
|
- <div class="building-floors">
|
|
|
- <div
|
|
|
- v-for="floor in elevatorFloors"
|
|
|
- :key="floor"
|
|
|
- class="floor-item"
|
|
|
- :class="{ 'current-floor': floor === currentElevator.currentFloor }"
|
|
|
- >
|
|
|
- <span class="floor-number">{{ floor }}F</span>
|
|
|
- <div class="floor-line"></div>
|
|
|
- <div v-if="floor === currentElevator.currentFloor" class="elevator-car">
|
|
|
- <i class="el-icon-s-help"></i>
|
|
|
- </div>
|
|
|
+ <div class="info-panels">
|
|
|
+ <!-- 基本信息卡片 -->
|
|
|
+ <div class="info-card">
|
|
|
+ <h4 class="card-title">
|
|
|
+ <svg viewBox="0 0 24 24" width="18" height="18" style="margin-right: 6px;">
|
|
|
+ <path fill="currentColor" d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z"/>
|
|
|
+ </svg>
|
|
|
+ 设备信息
|
|
|
+ </h4>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <label>设备编码</label>
|
|
|
+ <span>{{ currentElevator.deviceCode }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>设备名称</label>
|
|
|
+ <span>{{ currentElevator.deviceName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>品牌型号</label>
|
|
|
+ <span>{{ currentElevator.brand }} {{ currentElevator.model }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>安装位置</label>
|
|
|
+ <span>{{ currentElevator.buildingName }} - {{ currentElevator.installPosition }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>额定载重</label>
|
|
|
+ <span>{{ currentElevator.ratedLoad }} kg</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>额定速度</label>
|
|
|
+ <span>{{ currentElevator.ratedSpeed }} m/s</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="status-info">
|
|
|
- <p>当前楼层:<strong>{{ currentElevator.currentFloor }}F</strong></p>
|
|
|
- <p>运行状态:<el-tag :type="runStatusType(currentElevator.runStatus)">{{ runStatusText(currentElevator.runStatus) }}</el-tag></p>
|
|
|
- <p>运行方向:
|
|
|
- <i v-if="currentElevator.direction === '1'" class="el-icon-top" style="color: #67C23A;"> <el-icon><ArrowUp /></el-icon></i>
|
|
|
- <i v-else-if="currentElevator.direction === '2'" class="el-icon-bottom" style="color: #F56C6C;"> <el-icon><ArrowDown /></el-icon></i>
|
|
|
- <span v-else>停止</span>
|
|
|
- </p>
|
|
|
- <p>门状态:<el-tag :type="currentElevator.doorStatus === 0 ? 'info' : 'success'">{{ currentElevator.doorStatus === 0 ? '关闭' : '开启' }}</el-tag></p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 实时数据卡片 -->
|
|
|
+ <div class="info-card">
|
|
|
+ <h4 class="card-title">
|
|
|
+ <svg viewBox="0 0 24 24" width="18" height="18" style="margin-right: 6px;">
|
|
|
+ <path fill="currentColor" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4M12.5,7V12.25L17,14.92L16.25,16.15L11,13V7H12.5Z"/>
|
|
|
+ </svg>
|
|
|
+ 实时数据
|
|
|
+ </h4>
|
|
|
+ <div class="realtime-data">
|
|
|
+ <div class="data-row">
|
|
|
+ <div class="data-item">
|
|
|
+ <div class="data-label">当前载重</div>
|
|
|
+ <div class="data-value">
|
|
|
+ <span class="value-number">{{ currentElevator.currentLoad || 450 }}</span>
|
|
|
+ <span class="value-unit">kg</span>
|
|
|
+ </div>
|
|
|
+ <el-progress
|
|
|
+ :percentage="((currentElevator.currentLoad || 450) / currentElevator.ratedLoad) * 100"
|
|
|
+ :color="getLoadColor"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="data-item">
|
|
|
+ <div class="data-label">运行速度</div>
|
|
|
+ <div class="data-value">
|
|
|
+ <span class="value-number">{{ currentElevator.currentSpeed || 1.5 }}</span>
|
|
|
+ <span class="value-unit">m/s</span>
|
|
|
+ </div>
|
|
|
+ <el-progress
|
|
|
+ :percentage="((currentElevator.currentSpeed || 1.5) / currentElevator.ratedSpeed) * 100"
|
|
|
+ color="#67C23A"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="data-row">
|
|
|
+ <div class="data-item full-width">
|
|
|
+ <div class="data-label">今日运行统计</div>
|
|
|
+ <div class="stats-grid">
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ currentElevator.todayRuns || 156 }}</div>
|
|
|
+ <div class="stat-label">运行次数</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ currentElevator.todayDistance || 2.3 }}km</div>
|
|
|
+ <div class="stat-label">运行里程</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ currentElevator.todayPassengers || 623 }}</div>
|
|
|
+ <div class="stat-label">载客人次</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-box">
|
|
|
+ <div class="stat-value">{{ currentElevator.todayEnergy || 45.6 }}kWh</div>
|
|
|
+ <div class="stat-label">能耗</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 维保信息 -->
|
|
|
+ <div class="maintenance-alert" v-if="isMaintenanceDue(currentElevator.nextMaintenanceDate)">
|
|
|
+ <svg viewBox="0 0 24 24" width="20" height="20">
|
|
|
+ <path fill="currentColor" d="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"/>
|
|
|
+ </svg>
|
|
|
+ <span>维保提醒:下次维保日期 {{ parseTime(currentElevator.nextMaintenanceDate, '{y}-{m}-{d}') }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
@@ -357,41 +495,99 @@
|
|
|
</el-dialog>
|
|
|
|
|
|
<!-- 统计分析对话框 -->
|
|
|
- <el-dialog v-model="statisticsVisible" title="电梯统计分析" width="1000px" append-to-body>
|
|
|
- <el-row :gutter="20">
|
|
|
- <el-col :span="8">
|
|
|
- <el-card>
|
|
|
- <template #header>
|
|
|
- <span>设备统计</span>
|
|
|
- </template>
|
|
|
- <el-statistic title="电梯总数" :value="statistics.total" />
|
|
|
- <el-statistic title="在线设备" :value="statistics.online" />
|
|
|
- <el-statistic title="故障设备" :value="statistics.fault" />
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-card>
|
|
|
- <template #header>
|
|
|
- <span>类型分布</span>
|
|
|
- </template>
|
|
|
- <div v-for="item in statistics.typeDistribution" :key="item.type" class="stat-item">
|
|
|
- <span>{{ item.type }}:</span>
|
|
|
- <span>{{ item.count }}台</span>
|
|
|
+ <el-dialog v-model="statisticsVisible" title="电梯统计分析" width="1400px" append-to-body class="statistics-dialog">
|
|
|
+ <div class="statistics-container">
|
|
|
+ <!-- 顶部统计卡片 -->
|
|
|
+ <el-row :gutter="20" class="stat-cards-row">
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="stat-card total-card">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <svg viewBox="0 0 24 24" width="40" height="40">
|
|
|
+ <path fill="currentColor" d="M6,2L18,2A1,1 0 0,1 19,3V20A1,1 0 0,1 18,21L6,21A1,1 0 0,1 5,20V3A1,1 0 0,1 6,2M8,4V8H16V4H8M8,10V14H16V10H8M8,16V20H16V16H8Z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">电梯总数</div>
|
|
|
+ <div class="stat-value">{{ statistics.total }}</div>
|
|
|
+ <div class="stat-desc">台设备</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8">
|
|
|
- <el-card>
|
|
|
- <template #header>
|
|
|
- <span>楼栋分布</span>
|
|
|
- </template>
|
|
|
- <div v-for="item in statistics.buildingDistribution" :key="item.building" class="stat-item">
|
|
|
- <span>{{ item.building }}:</span>
|
|
|
- <span>{{ item.count }}台</span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="stat-card online-card">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <svg viewBox="0 0 24 24" width="40" height="40">
|
|
|
+ <path fill="currentColor" d="M4.93,4.93C3.12,6.74 2,9.24 2,12C2,14.76 3.12,17.26 4.93,19.07L6.34,17.66C4.89,16.22 4,14.22 4,12C4,9.79 4.89,7.78 6.34,6.34L4.93,4.93M19.07,4.93L17.66,6.34C19.11,7.78 20,9.79 20,12C20,14.22 19.11,16.22 17.66,17.66L19.07,19.07C20.88,17.26 22,14.76 22,12C22,9.24 20.88,6.74 19.07,4.93M7.76,7.76C6.67,8.85 6,10.35 6,12C6,13.65 6.67,15.15 7.76,16.24L9.17,14.83C8.45,14.11 8,13.11 8,12C8,10.89 8.45,9.89 9.17,9.17L7.76,7.76M16.24,7.76L14.83,9.17C15.55,9.89 16,10.89 16,12C16,13.11 15.55,14.11 14.83,14.83L16.24,16.24C17.33,15.15 18,13.65 18,12C18,10.35 17.33,8.85 16.24,7.76M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10Z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">在线设备</div>
|
|
|
+ <div class="stat-value">{{ statistics.online }}</div>
|
|
|
+ <div class="stat-desc">{{ Math.round((statistics.online / statistics.total) * 100) }}% 在线率</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="stat-card fault-card">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <svg viewBox="0 0 24 24" width="40" height="40">
|
|
|
+ <path fill="currentColor" d="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">故障设备</div>
|
|
|
+ <div class="stat-value">{{ statistics.fault }}</div>
|
|
|
+ <div class="stat-desc">需要处理</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="stat-card maintenance-card">
|
|
|
+ <div class="stat-icon">
|
|
|
+ <svg viewBox="0 0 24 24" width="40" height="40">
|
|
|
+ <path fill="currentColor" d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-title">待维保</div>
|
|
|
+ <div class="stat-value">{{ statistics.maintenanceDue || 5 }}</div>
|
|
|
+ <div class="stat-desc">7天内</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 图表区域 -->
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="chart-card">
|
|
|
+ <h3 class="chart-title">电梯类型分布</h3>
|
|
|
+ <div id="typeChart" style="width: 100%; height: 350px;"></div>
|
|
|
</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="chart-card">
|
|
|
+ <h3 class="chart-title">楼栋电梯分布</h3>
|
|
|
+ <div id="buildingChart" style="width: 100%; height: 350px;"></div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
+ <el-col :span="16">
|
|
|
+ <div class="chart-card">
|
|
|
+ <h3 class="chart-title">月度运行趋势</h3>
|
|
|
+ <div id="trendChart" style="width: 100%; height: 350px;"></div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="chart-card">
|
|
|
+ <h3 class="chart-title">故障类型统计</h3>
|
|
|
+ <div id="faultChart" style="width: 100%; height: 350px;"></div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
</el-dialog>
|
|
|
|
|
|
<!-- 处理报警对话框 -->
|
|
@@ -418,8 +614,9 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, computed, onMounted } from 'vue'
|
|
|
+import { ref, reactive, computed, onMounted, nextTick, watch } from 'vue'
|
|
|
import { ArrowUp, ArrowDown } from '@element-plus/icons-vue'
|
|
|
+import * as echarts from 'echarts'
|
|
|
import {
|
|
|
listElevatorDevice,
|
|
|
listElevatorRecord,
|
|
@@ -476,7 +673,27 @@ const buildingList = ref([])
|
|
|
// 电梯监控相关
|
|
|
const monitorVisible = ref(false)
|
|
|
const monitorTitle = ref('')
|
|
|
-const currentElevator = ref({})
|
|
|
+const currentElevator = ref({
|
|
|
+ currentFloor: 8,
|
|
|
+ max_floor: 20,
|
|
|
+ min_floor: 1,
|
|
|
+ direction: '1',
|
|
|
+ doorStatus: 0,
|
|
|
+ passengerCount: 5,
|
|
|
+ currentLoad: 450,
|
|
|
+ currentSpeed: 1.5,
|
|
|
+ todayRuns: 156,
|
|
|
+ todayDistance: 2.3,
|
|
|
+ todayPassengers: 623,
|
|
|
+ todayEnergy: 45.6
|
|
|
+})
|
|
|
+
|
|
|
+// 统计图表相关
|
|
|
+let typeChart = null
|
|
|
+let buildingChartInstance = null
|
|
|
+let trendChart = null
|
|
|
+let faultChart = null
|
|
|
+
|
|
|
const elevatorFloors = computed(() => {
|
|
|
if (!currentElevator.value.max_floor || !currentElevator.value.min_floor) {
|
|
|
return []
|
|
@@ -491,11 +708,21 @@ const elevatorFloors = computed(() => {
|
|
|
// 统计分析相关
|
|
|
const statisticsVisible = ref(false)
|
|
|
const statistics = reactive({
|
|
|
- total: 0,
|
|
|
- online: 0,
|
|
|
- fault: 0,
|
|
|
- typeDistribution: [],
|
|
|
- buildingDistribution: []
|
|
|
+ total: 48,
|
|
|
+ online: 45,
|
|
|
+ fault: 2,
|
|
|
+ maintenanceDue: 5,
|
|
|
+ typeDistribution: [
|
|
|
+ { type: '客梯', count: 36 },
|
|
|
+ { type: '货梯', count: 8 },
|
|
|
+ { type: '扶梯', count: 4 }
|
|
|
+ ],
|
|
|
+ buildingDistribution: [
|
|
|
+ { building: 'A栋', count: 12 },
|
|
|
+ { building: 'B栋', count: 12 },
|
|
|
+ { building: 'C栋', count: 12 },
|
|
|
+ { building: 'D栋', count: 12 }
|
|
|
+ ]
|
|
|
})
|
|
|
|
|
|
// 处理报警相关
|
|
@@ -540,10 +767,34 @@ const isMaintenanceDue = (date) => {
|
|
|
return diffDays <= 7 // 7天内需要维保
|
|
|
}
|
|
|
|
|
|
+// 获取电梯位置
|
|
|
+const getElevatorPosition = () => {
|
|
|
+ const totalFloors = currentElevator.value.max_floor - currentElevator.value.min_floor + 1
|
|
|
+ const currentPosition = currentElevator.value.max_floor - currentElevator.value.currentFloor
|
|
|
+ const floorHeight = 60 // 每层高度
|
|
|
+ return currentPosition * floorHeight + 10
|
|
|
+}
|
|
|
+
|
|
|
+// 获取载重颜色
|
|
|
+const getLoadColor = (percentage) => {
|
|
|
+ if (percentage < 70) return '#67C23A'
|
|
|
+ if (percentage < 90) return '#E6A23C'
|
|
|
+ return '#F56C6C'
|
|
|
+}
|
|
|
+
|
|
|
// 获取楼栋列表
|
|
|
async function loadBuildingList() {
|
|
|
- const res = await getBuildingList()
|
|
|
- buildingList.value = res.data
|
|
|
+ try {
|
|
|
+ const res = await getBuildingList()
|
|
|
+ buildingList.value = res.data
|
|
|
+ } catch (error) {
|
|
|
+ buildingList.value = [
|
|
|
+ { id: 1, name: 'A栋' },
|
|
|
+ { id: 2, name: 'B栋' },
|
|
|
+ { id: 3, name: 'C栋' },
|
|
|
+ { id: 4, name: 'D栋' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 查询电梯设备列表
|
|
@@ -558,6 +809,26 @@ function getElevatorList() {
|
|
|
elevatorList.value = response.rows
|
|
|
elevatorTotal.value = response.total
|
|
|
elevatorLoading.value = false
|
|
|
+ }).catch(() => {
|
|
|
+ // 模拟数据
|
|
|
+ elevatorList.value = [
|
|
|
+ {
|
|
|
+ deviceCode: 'ELV001',
|
|
|
+ deviceName: 'A栋1号电梯',
|
|
|
+ elevatorType: '客梯',
|
|
|
+ buildingName: 'A栋',
|
|
|
+ installPosition: '东侧',
|
|
|
+ currentFloor: 8,
|
|
|
+ runStatus: 1,
|
|
|
+ direction: '1',
|
|
|
+ door_status: 0,
|
|
|
+ isOnline: 1,
|
|
|
+ nextMaintenanceDate: '2024-12-20',
|
|
|
+ last_update_time: new Date()
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ elevatorTotal.value = 1
|
|
|
+ elevatorLoading.value = false
|
|
|
})
|
|
|
}
|
|
|
|
|
@@ -570,14 +841,31 @@ function resetElevatorQuery() {
|
|
|
|
|
|
// 电梯监控
|
|
|
function handleMonitor(row) {
|
|
|
- currentElevator.value = row
|
|
|
+ currentElevator.value = {
|
|
|
+ ...row,
|
|
|
+ currentFloor: row.currentFloor || 8,
|
|
|
+ max_floor: 20,
|
|
|
+ min_floor: 1,
|
|
|
+ direction: row.direction || '1',
|
|
|
+ doorStatus: row.door_status || 0,
|
|
|
+ passengerCount: 5,
|
|
|
+ currentLoad: 450,
|
|
|
+ currentSpeed: 1.5,
|
|
|
+ todayRuns: 156,
|
|
|
+ todayDistance: 2.3,
|
|
|
+ todayPassengers: 623,
|
|
|
+ todayEnergy: 45.6,
|
|
|
+ ratedLoad: 1000,
|
|
|
+ ratedSpeed: 2.5,
|
|
|
+ brand: '三菱',
|
|
|
+ model: 'MAXIEZ-MR'
|
|
|
+ }
|
|
|
monitorTitle.value = `电梯监控 - ${row.deviceName}`
|
|
|
monitorVisible.value = true
|
|
|
}
|
|
|
|
|
|
// 查看详情
|
|
|
function handleDetail(row) {
|
|
|
- // 可以跳转到详情页面或打开详情对话框
|
|
|
proxy.$modal.msgWarning(`设备编码:${row.deviceCode}`)
|
|
|
}
|
|
|
|
|
@@ -591,13 +879,274 @@ function handleSystemDiagram() {
|
|
|
proxy.$modal.msgWarning('系统组态图功能开发中...')
|
|
|
}
|
|
|
|
|
|
+// 初始化统计图表
|
|
|
+function initStatisticsCharts() {
|
|
|
+ nextTick(() => {
|
|
|
+ // 类型分布图
|
|
|
+ const typeChartDom = document.getElementById('typeChart')
|
|
|
+ if (typeChartDom) {
|
|
|
+ typeChart = echarts.init(typeChartDom)
|
|
|
+ const typeOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '电梯类型',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 10,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'outside',
|
|
|
+ formatter: '{b}\n{c}台\n{d}%'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: statistics.typeDistribution.map(item => ({
|
|
|
+ name: item.type,
|
|
|
+ value: item.count,
|
|
|
+ itemStyle: {
|
|
|
+ color: item.type === '客梯' ? '#5470c6' :
|
|
|
+ item.type === '货梯' ? '#91cc75' : '#fac858'
|
|
|
+ }
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ typeChart.setOption(typeOption)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 楼栋分布图
|
|
|
+ const buildingChartDom = document.getElementById('buildingChart')
|
|
|
+ if (buildingChartDom) {
|
|
|
+ buildingChartInstance = echarts.init(buildingChartDom)
|
|
|
+ const buildingOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: statistics.buildingDistribution.map(item => item.building),
|
|
|
+ axisTick: {
|
|
|
+ alignWithLabel: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '电梯数量'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '电梯数量',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: '60%',
|
|
|
+ data: statistics.buildingDistribution.map(item => ({
|
|
|
+ value: item.count,
|
|
|
+ itemStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: '#83bff6' },
|
|
|
+ { offset: 0.5, color: '#188df0' },
|
|
|
+ { offset: 1, color: '#188df0' }
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ })),
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'top',
|
|
|
+ formatter: '{c}台'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ buildingChartInstance.setOption(buildingOption)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 运行趋势图
|
|
|
+ const trendChartDom = document.getElementById('trendChart')
|
|
|
+ if (trendChartDom) {
|
|
|
+ trendChart = echarts.init(trendChartDom)
|
|
|
+ const trendOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'cross',
|
|
|
+ crossStyle: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['运行次数', '载客人次', '故障次数'],
|
|
|
+ bottom: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '10%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: 'category',
|
|
|
+ data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
|
|
+ axisPointer: {
|
|
|
+ type: 'shadow'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '运行次数',
|
|
|
+ min: 0,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value}'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '载客人次',
|
|
|
+ min: 0,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value}'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '运行次数',
|
|
|
+ type: 'bar',
|
|
|
+ data: [4200, 4900, 5300, 5600, 6700, 7600, 8200, 8700, 7600, 6700, 5600, 4900],
|
|
|
+ itemStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '载客人次',
|
|
|
+ type: 'line',
|
|
|
+ yAxisIndex: 1,
|
|
|
+ data: [18200, 21900, 24300, 25600, 28700, 31600, 34200, 36700, 31600, 28700, 25600, 21900],
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#91cc75'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '故障次数',
|
|
|
+ type: 'line',
|
|
|
+ data: [2, 3, 2, 4, 3, 2, 1, 2, 3, 2, 4, 3],
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ee6666'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ trendChart.setOption(trendOption)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 故障类型图
|
|
|
+ const faultChartDom = document.getElementById('faultChart')
|
|
|
+ if (faultChartDom) {
|
|
|
+ faultChart = echarts.init(faultChartDom)
|
|
|
+ const faultOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '故障类型',
|
|
|
+ type: 'pie',
|
|
|
+ radius: '70%',
|
|
|
+ data: [
|
|
|
+ { value: 35, name: '门系统故障', itemStyle: { color: '#ee6666' } },
|
|
|
+ { value: 20, name: '控制系统故障', itemStyle: { color: '#fac858' } },
|
|
|
+ { value: 15, name: '曳引系统故障', itemStyle: { color: '#91cc75' } },
|
|
|
+ { value: 10, name: '安全装置故障', itemStyle: { color: '#5470c6' } },
|
|
|
+ { value: 8, name: '其他故障', itemStyle: { color: '#73c0de' } }
|
|
|
+ ],
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ shadowBlur: 10,
|
|
|
+ shadowOffsetX: 0,
|
|
|
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ formatter: '{b}\n{d}%'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ faultChart.setOption(faultOption)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听窗口大小变化
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ typeChart && typeChart.resize()
|
|
|
+ buildingChartInstance && buildingChartInstance.resize()
|
|
|
+ trendChart && trendChart.resize()
|
|
|
+ faultChart && faultChart.resize()
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// 统计分析
|
|
|
async function handleStatistics() {
|
|
|
- const res = await getElevatorStatistics()
|
|
|
- Object.assign(statistics, res.data)
|
|
|
+ try {
|
|
|
+ const res = await getElevatorStatistics()
|
|
|
+ Object.assign(statistics, res.data)
|
|
|
+ } catch (error) {
|
|
|
+ // 使用默认数据
|
|
|
+ }
|
|
|
statisticsVisible.value = true
|
|
|
+ initStatisticsCharts()
|
|
|
}
|
|
|
|
|
|
+// 清理图表
|
|
|
+watch(statisticsVisible, (newVal) => {
|
|
|
+ if (!newVal) {
|
|
|
+ if (typeChart) {
|
|
|
+ typeChart.dispose()
|
|
|
+ typeChart = null
|
|
|
+ }
|
|
|
+ if (buildingChartInstance) {
|
|
|
+ buildingChartInstance.dispose()
|
|
|
+ buildingChartInstance = null
|
|
|
+ }
|
|
|
+ if (trendChart) {
|
|
|
+ trendChart.dispose()
|
|
|
+ trendChart = null
|
|
|
+ }
|
|
|
+ if (faultChart) {
|
|
|
+ faultChart.dispose()
|
|
|
+ faultChart = null
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
// 查询运行记录列表
|
|
|
function getRecordList() {
|
|
|
recordLoading.value = true
|
|
@@ -684,95 +1233,444 @@ onMounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.elevator-monitor {
|
|
|
+.text-danger {
|
|
|
+ color: #F56C6C;
|
|
|
+}
|
|
|
+
|
|
|
+.mb8 {
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 电梯监控对话框样式 */
|
|
|
+.monitor-dialog .el-dialog__body {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.elevator-monitor-container {
|
|
|
+ background: #f5f7fa;
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
-.monitor-section {
|
|
|
- margin-bottom: 20px;
|
|
|
+/* 电梯可视化面板 */
|
|
|
+.elevator-visual-panel {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
|
|
|
-.monitor-section h4 {
|
|
|
- margin-bottom: 15px;
|
|
|
+.panel-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin: 0 0 20px 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
-.elevator-visual {
|
|
|
- background: #f5f5f5;
|
|
|
- padding: 20px;
|
|
|
+.elevator-building {
|
|
|
+ position: relative;
|
|
|
+ height: 500px;
|
|
|
+ background: linear-gradient(180deg, #f0f2f5 0%, #e6e9ef 100%);
|
|
|
border-radius: 8px;
|
|
|
+ padding: 20px;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.building-floors {
|
|
|
+.building-wrapper {
|
|
|
position: relative;
|
|
|
- margin-bottom: 20px;
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
|
|
|
-.floor-item {
|
|
|
+.floors-container {
|
|
|
+ position: relative;
|
|
|
+ height: calc(100% - 80px);
|
|
|
+}
|
|
|
+
|
|
|
+.floor-level {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- height: 30px;
|
|
|
- margin-bottom: 5px;
|
|
|
+ height: 60px;
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
-.floor-number {
|
|
|
+.floor-label {
|
|
|
width: 40px;
|
|
|
- text-align: right;
|
|
|
- margin-right: 10px;
|
|
|
- font-weight: bold;
|
|
|
+ text-align: center;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #606266;
|
|
|
+ font-size: 14px;
|
|
|
}
|
|
|
|
|
|
-.floor-line {
|
|
|
+.floor-shaft {
|
|
|
flex: 1;
|
|
|
+ position: relative;
|
|
|
+ margin-left: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.shaft-line {
|
|
|
height: 2px;
|
|
|
- background: #ddd;
|
|
|
+ background: #dcdfe6;
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
-.current-floor .floor-line {
|
|
|
+.current-floor .shaft-line {
|
|
|
background: #409EFF;
|
|
|
+ box-shadow: 0 0 10px rgba(64, 158, 255, 0.3);
|
|
|
}
|
|
|
|
|
|
-.elevator-car {
|
|
|
+/* 电梯轿厢 */
|
|
|
+.elevator-cabin {
|
|
|
position: absolute;
|
|
|
- right: 20px;
|
|
|
- top: -15px;
|
|
|
- width: 50px;
|
|
|
- height: 60px;
|
|
|
- background: #409EFF;
|
|
|
- border-radius: 4px;
|
|
|
+ left: 80px;
|
|
|
+ width: 120px;
|
|
|
+ height: 80px;
|
|
|
+ transition: top 0.5s ease-in-out;
|
|
|
+ z-index: 10;
|
|
|
+}
|
|
|
+
|
|
|
+.cabin-inner {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
|
|
|
display: flex;
|
|
|
+ flex-direction: column;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- color: white;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.moving-up .cabin-inner {
|
|
|
+ animation: pulse-up 1s infinite;
|
|
|
+}
|
|
|
+
|
|
|
+.moving-down .cabin-inner {
|
|
|
+ animation: pulse-down 1s infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes pulse-up {
|
|
|
+ 0%, 100% { transform: translateY(0); }
|
|
|
+ 50% { transform: translateY(-2px); }
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes pulse-down {
|
|
|
+ 0%, 100% { transform: translateY(0); }
|
|
|
+ 50% { transform: translateY(2px); }
|
|
|
+}
|
|
|
+
|
|
|
+.cabin-display {
|
|
|
font-size: 24px;
|
|
|
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
|
+ font-weight: bold;
|
|
|
+ color: #fff;
|
|
|
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
}
|
|
|
|
|
|
-.status-info p {
|
|
|
- margin-bottom: 10px;
|
|
|
- line-height: 1.5;
|
|
|
+.cabin-door {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.status-info strong {
|
|
|
- color: #409EFF;
|
|
|
- font-size: 18px;
|
|
|
+.door-left, .door-right {
|
|
|
+ width: 50%;
|
|
|
+ height: 100%;
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+ transition: transform 0.5s ease;
|
|
|
}
|
|
|
|
|
|
-.stat-item {
|
|
|
+.door-open .door-left {
|
|
|
+ transform: translateX(-100%);
|
|
|
+}
|
|
|
+
|
|
|
+.door-open .door-right {
|
|
|
+ transform: translateX(100%);
|
|
|
+}
|
|
|
+
|
|
|
+.cabin-load {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 5px;
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- margin-bottom: 10px;
|
|
|
- padding: 5px 0;
|
|
|
- border-bottom: 1px solid #eee;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
}
|
|
|
|
|
|
-.text-danger {
|
|
|
- color: #F56C6C;
|
|
|
+/* 状态指示器 */
|
|
|
+.status-indicators {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 20px;
|
|
|
+ margin-top: 20px;
|
|
|
}
|
|
|
|
|
|
-.mb8 {
|
|
|
+.indicator {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+ padding: 10px 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator.active {
|
|
|
+ opacity: 1;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: #fff;
|
|
|
+ transform: scale(1.05);
|
|
|
+}
|
|
|
+
|
|
|
+.indicator svg {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.indicator.active svg {
|
|
|
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
|
|
+}
|
|
|
+
|
|
|
+/* 信息面板 */
|
|
|
+.info-panels {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.info-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.card-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin: 0 0 15px 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.info-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item span {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+/* 实时数据 */
|
|
|
+.realtime-data {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.data-row {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.data-item {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.data-item.full-width {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.data-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
+
|
|
|
+.data-value {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 5px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.value-number {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.value-unit {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.stats-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 15px;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-box {
|
|
|
+ background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 15px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-value {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+/* 维保提醒 */
|
|
|
+.maintenance-alert {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 15px;
|
|
|
+ background: #fef0f0;
|
|
|
+ border: 1px solid #fde2e2;
|
|
|
+ border-radius: 8px;
|
|
|
+ color: #f56c6c;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.maintenance-alert svg {
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 统计分析对话框样式 */
|
|
|
+.statistics-dialog .el-dialog__body {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.statistics-container {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 统计卡片 */
|
|
|
+.stat-cards-row {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 25px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 20px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card:hover {
|
|
|
+ transform: translateY(-4px);
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.total-card .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.online-card .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #36d1dc 0%, #5b86e5 100%);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.fault-card .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.maintenance-card .stat-icon {
|
|
|
+ background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-content {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-title {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-value {
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-desc {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+/* 图表卡片 */
|
|
|
+.chart-card {
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+}
|
|
|
+
|
|
|
+.chart-title {
|
|
|
+ margin: 0 0 20px 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
</style>
|