From 20cdb83586daabfb15fc056c4c97eb8e7ccaf928 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期五, 20 三月 2026 17:07:16 +0800
Subject: [PATCH] 2026.3.20
---
src/views/monitor/DataDashboard.vue | 842 ++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 575 insertions(+), 267 deletions(-)
diff --git a/src/views/monitor/DataDashboard.vue b/src/views/monitor/DataDashboard.vue
index 45fa087..e17dcb4 100644
--- a/src/views/monitor/DataDashboard.vue
+++ b/src/views/monitor/DataDashboard.vue
@@ -1,10 +1,25 @@
<template>
<div class="data-dashboard">
+ <!-- 鍦板浘妯″紡鍒囨崲 -->
+ <div class="map-mode-card">
+ <div class="mode-tab-container">
+ <div
+ v-for="mode in mapModes"
+ :key="mode.value"
+ class="mode-tab"
+ :class="{ active: activeMode === mode.value }"
+ @click="handleModeChange(mode)"
+ >
+ {{ mode.label }}
+ </div>
+ </div>
+ </div>
+
<!-- 椤堕儴鎸囨爣鍗$墖鍖� -->
<div class="top-cards">
<!-- 鏃堕棿鍛ㄦ湡閫夐」鍗$墖 -->
<div class="time-period-card">
- <div class="card-title">鏃堕棿閫夋嫨</div>
+ <!-- <div class="card-title">鏃堕棿閫夋嫨</div> -->
<div class="time-controls">
<div class="time-tab-container">
<div
@@ -56,11 +71,99 @@
</div>
</div>
</div>
+
<div class="cards-container">
- <!-- 瓒呮爣鏁� -->
+ <!-- 璁惧鍦ㄧ嚎鐜� -->
+ <el-popover placement="right-start" title="璁惧鐩戞帶" width="400" trigger="click">
+ <div class="popover-content">
+ <div class="overview-items-container">
+ <div class="overview-item">
+ <div class="overview-label">椁愰ギ搴楅摵鎬绘暟</div>
+ <div class="overview-value">{{ overview.totalShops }}</div>
+ </div>
+
+ <div class="overview-item">
+ <div class="overview-label">鍦ㄧ嚎璁惧鏁�</div>
+ <div class="overview-value">{{ overview.onlineDevices }}</div>
+ </div>
+
+ <div class="overview-item">
+ <div class="overview-label">绂荤嚎璁惧鏁�</div>
+ <div class="overview-value">{{ overview.offlineDevices }}</div>
+ </div>
+ </div>
+
+ <!-- 璁惧鐘舵�侀ゼ鍥� -->
+ <div class="device-status-chart">
+ <canvas id="deviceStatusChart"></canvas>
+ </div>
+ </div>
+ <template #reference>
+ <div class="metric-card">
+ <div class="card-header">
+ <div class="card-title">璁惧鍦ㄧ嚎鐜�</div>
+ <div class="card-icon online-icon">
+ <svg
+ width="20"
+ height="20"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M9 12L11 14L15 10"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ </div>
+ </div>
+ <div class="card-value">{{ metrics.onlineRate }}<el-text>%</el-text></div>
+ <div class="card-trend">
+ <span
+ class="trend-arrow"
+ :class="{ up: metrics.onlineRateTrend > 0, down: metrics.onlineRateTrend < 0 }"
+ >
+ {{ metrics.onlineRateTrend > 0 ? '鈫�' : '鈫�' }}
+ </span>
+ <span class="trend-text">{{ Math.abs(metrics.onlineRateTrend) }}%</span>
+ <span class="trend-label">{{ getCompareLabel() }}</span>
+ </div>
+ <div class="view-details">
+ <span>璇︽儏</span>
+ <svg
+ width="12"
+ height="12"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M9 18L15 12L9 6"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ </div>
+ </div>
+ </template>
+ </el-popover>
+
+ <!-- 娴撳害棰勮 -->
<div class="metric-card">
<div class="card-header">
- <div class="card-title">{{ getPeriodLabel() }}瓒呮爣鏁�</div>
+ <div class="card-title">{{ getPeriodLabel() }}娴撳害棰勮</div>
<div class="card-icon warning-icon">
<svg
width="20"
@@ -86,7 +189,7 @@
</svg>
</div>
</div>
- <div class="card-value">{{ metrics.overStandardCount }}</div>
+ <div class="card-value">{{ metrics.overStandardCount }}<el-text>娆�</el-text></div>
<div class="card-trend">
<span
class="trend-arrow"
@@ -99,11 +202,160 @@
</div>
</div>
- <!-- 鍦ㄧ嚎鐜� -->
+ <!-- 鐜板満宸℃煡 -->
+ <el-popover placement="right-start" title="鐜板満宸℃煡缁熻" width="350" trigger="click">
+ <div class="inspection-popover-content">
+ <!-- 宸℃煡閲忕粺璁� -->
+ <div class="inspection-metrics">
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">搴楅摵鎬昏</div>
+ <div class="inspection-metric-value">{{ inspectionStats.totalShops }}</div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">宸℃煡搴楅摵</div>
+ <div class="inspection-metric-value">{{ inspectionStats.inspectedShops }}</div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">宸℃煡鐐规</div>
+ <div class="inspection-metric-value">{{ inspectionStats.inspectionPoints }}</div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">澶嶆煡鐐规</div>
+ <div class="inspection-metric-value">{{ inspectionStats.reviewPoints }}</div>
+ </div>
+ </div>
+
+ <!-- 闂鏁存敼鎯呭喌 -->
+ <div class="inspection-chart-container">
+ <div class="section-header"><h3>闂鏁存敼</h3></div>
+ <canvas id="rectificationChart"></canvas>
+ </div>
+
+ <!-- 闂瀹℃牳鎯呭喌 -->
+ <div class="inspection-table-container" style="display: none">
+ <div class="section-header"><h3>瀹℃牳姹囨��</h3></div>
+ <div class="inspection-metric-label">闂瀹℃牳</div>
+ <div class="inspection-table">
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鏃犻棶棰�</div>
+ <div class="inspection-metric-value">{{ inspectionStats.noProblemShops }}</div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鏈鏍�</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.unreviewedProblemShops }}
+ </div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">閮ㄥ垎瀹℃牳</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.partiallyReviewedProblemShops }}
+ </div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鍏ㄩ儴瀹℃牳</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.fullyReviewedProblemShops }}
+ </div>
+ </div>
+ </div>
+ <div class="inspection-metric-label">鏁存敼瀹℃牳</div>
+ <div class="inspection-table">
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鏈暣鏀�</div>
+ <div class="inspection-metric-value">{{ inspectionStats.unrectifiedShops }}</div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鏈鏍�</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.unreviewedRectifiedShops }}
+ </div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">閮ㄥ垎瀹℃牳</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.partiallyReviewedRectifiedShops }}
+ </div>
+ </div>
+ <div class="inspection-metric-item">
+ <div class="inspection-metric-label">鍏ㄩ儴瀹℃牳</div>
+ <div class="inspection-metric-value">
+ {{ inspectionStats.fullyReviewedRectifiedShops }}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <template #reference>
+ <div class="metric-card">
+ <div class="card-header">
+ <div class="card-title">鐜板満宸℃煡</div>
+ <div class="card-icon task-icon">
+ <svg
+ width="20"
+ height="20"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M22 11.08V12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C15.7376 2 19.0503 4.16113 20.7748 7.33007"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M22 4L12 14.01L9 11.01"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ </div>
+ </div>
+ <div class="card-value">{{ metrics.inspectionPoints }}<el-text>鐐规</el-text></div>
+ <div class="card-trend">
+ <span
+ class="trend-arrow"
+ :class="{
+ up: metrics.inspectionPointsTrend > 0,
+ down: metrics.inspectionPointsTrend < 0,
+ }"
+ >
+ {{ metrics.inspectionPointsTrend > 0 ? '鈫�' : '鈫�' }}
+ </span>
+ <span class="trend-text">{{ Math.abs(metrics.inspectionPointsTrend) }}</span>
+ <span class="trend-label">{{ getCompareLabel() }}</span>
+ </div>
+ <div class="view-details">
+ <span>璇︽儏</span>
+ <svg
+ width="12"
+ height="12"
+ viewBox="0 0 24 24"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M9 18L15 12L9 6"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ </div>
+ </div>
+ </template>
+ </el-popover>
+
+ <!-- 淇¤鎶曡瘔 -->
<div class="metric-card">
<div class="card-header">
- <div class="card-title">鍦ㄧ嚎鐜�</div>
- <div class="card-icon online-icon">
+ <div class="card-title">淇¤鎶曡瘔</div>
+ <div class="card-icon warning-icon">
<svg
width="20"
height="20"
@@ -112,14 +364,28 @@
xmlns="http://www.w3.org/2000/svg"
>
<path
- d="M9 12L11 14L15 10"
+ d="M21 6H3C2.46957 6 1.96086 6.21071 1.58579 6.58579C1.21071 6.96086 1 7.46957 1 8V18C1 19.1046 1.89543 20 3 20H21C22.1046 20 23 19.1046 23 18V8C23 7.46957 22.7893 6.96086 22.4142 6.58579C22.0391 6.21071 21.5304 6 21 6Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
- d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
+ d="M8 12H16"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M8 16H16"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M8 8H16"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
@@ -128,23 +394,23 @@
</svg>
</div>
</div>
- <div class="card-value">{{ metrics.onlineRate }}%</div>
+ <div class="card-value">{{ metrics.overStandardCount }}<el-text>浠�</el-text></div>
<div class="card-trend">
<span
class="trend-arrow"
- :class="{ up: metrics.onlineRateTrend > 0, down: metrics.onlineRateTrend < 0 }"
+ :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
>
- {{ metrics.onlineRateTrend > 0 ? '鈫�' : '鈫�' }}
+ {{ metrics.overStandardTrend > 0 ? '鈫�' : '鈫�' }}
</span>
- <span class="trend-text">{{ Math.abs(metrics.onlineRateTrend) }}%</span>
+ <span class="trend-text">{{ Math.abs(metrics.overStandardTrend) }}%</span>
<span class="trend-label">{{ getCompareLabel() }}</span>
</div>
</div>
- <!-- 鍑�鍖栧櫒杩愯鏁堢巼 -->
+ <!-- 鐜俊鐮� -->
<div class="metric-card">
<div class="card-header">
- <div class="card-title">鍑�鍖栧櫒杩愯鏁堢巼</div>
+ <div class="card-title">鐜俊鐮�</div>
<div class="card-icon efficiency-icon">
<svg
width="20"
@@ -154,14 +420,21 @@
xmlns="http://www.w3.org/2000/svg"
>
<path
- d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2Z"
+ d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
- d="M12 6V12L16 14"
+ d="M12 8V16"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M8 12H16"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
@@ -170,7 +443,14 @@
</svg>
</div>
</div>
- <div class="card-value">{{ metrics.purifierEfficiency }}%</div>
+ <div class="card-value" style="color: #52c41a">
+ <div>{{ metrics.purifierEfficiency }}<el-text>%</el-text></div>
+ <div class="card-subvalues">
+ <span style="color: #faad14; font-size: 14px">榛勭爜锛�3%</span>
+ <span style="color: #f5222d; font-size: 14px; margin-left: 12px">绾㈢爜锛�2%</span>
+ </div>
+ </div>
+
<div class="card-trend">
<span
class="trend-arrow"
@@ -186,11 +466,11 @@
</div>
</div>
- <!-- 宸℃煡鐐规 -->
+ <!-- 琛屾斂澶勭綒 -->
<div class="metric-card">
<div class="card-header">
- <div class="card-title">宸℃煡鐐规</div>
- <div class="card-icon task-icon">
+ <div class="card-title">琛屾斂澶勭綒</div>
+ <div class="card-icon warning-icon">
<svg
width="20"
height="20"
@@ -199,14 +479,28 @@
xmlns="http://www.w3.org/2000/svg"
>
<path
- d="M22 11.08V12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C15.7376 2 19.0503 4.16113 20.7748 7.33007"
+ d="M12 15C15.866 15 19 11.866 19 8C19 4.13401 15.866 1 12 1C8.13401 1 5 4.13401 5 8C5 11.866 8.13401 15 12 15Z"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
- d="M22 4L12 14.01L9 11.01"
+ d="M12 15C12 15 15 21 15 21H9C9 21 12 15 12 15Z"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M11 8H13"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ <path
+ d="M11 11H13"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
@@ -215,18 +509,15 @@
</svg>
</div>
</div>
- <div class="card-value">{{ metrics.inspectionPoints }}</div>
+ <div class="card-value">{{ metrics.overStandardCount }}<el-text>娆�</el-text></div>
<div class="card-trend">
<span
class="trend-arrow"
- :class="{
- up: metrics.inspectionPointsTrend > 0,
- down: metrics.inspectionPointsTrend < 0,
- }"
+ :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
>
- {{ metrics.inspectionPointsTrend > 0 ? '鈫�' : '鈫�' }}
+ {{ metrics.overStandardTrend > 0 ? '鈫�' : '鈫�' }}
</span>
- <span class="trend-text">{{ Math.abs(metrics.inspectionPointsTrend) }}</span>
+ <span class="trend-text">{{ Math.abs(metrics.overStandardTrend) }}%</span>
<span class="trend-label">{{ getCompareLabel() }}</span>
</div>
</div>
@@ -238,75 +529,30 @@
<!-- 涓儴GIS鍦板浘鍖� -->
<div class="map-section">
<div id="map" class="map-container">
- <BaseMap :showSatellite="true"></BaseMap>
+ <BaseMap :showSatellite="false"></BaseMap>
</div>
-
- <!-- 鍦板浘鐐逛綅寮圭獥 -->
- <el-dialog v-model="dialogVisible" title="浼佷笟瀹炴椂鏁版嵁" width="400px">
- <div class="dialog-content">
- <el-descriptions :column="1" border>
- <el-descriptions-item label="浼佷笟鍚嶇О">{{
- selectedPoint.enterpriseName
- }}</el-descriptions-item>
- <el-descriptions-item label="璁惧缂栧彿">{{
- selectedPoint.deviceId
- }}</el-descriptions-item>
- <el-descriptions-item label="娌圭儫娴撳害"
- >{{ selectedPoint.oilSmokeConcentration }} mg/m鲁</el-descriptions-item
- >
- <el-descriptions-item label="棰楃矑鐗�"
- >{{ selectedPoint.particulateMatter }} mg/m鲁</el-descriptions-item
- >
- <el-descriptions-item label="闈炵敳鐑锋�荤儍"
- >{{ selectedPoint.nonMethaneHydrocarbon }} mg/m鲁</el-descriptions-item
- >
- <el-descriptions-item label="鐩戞祴鏃堕棿">{{
- selectedPoint.monitoringTime
- }}</el-descriptions-item>
- <el-descriptions-item label="瓒呮爣鎯呭喌">
- <el-tag :type="selectedPoint.isOverStandard ? 'danger' : 'success'">
- {{ selectedPoint.isOverStandard ? '瓒呮爣' : '姝e父' }}
- </el-tag>
- </el-descriptions-item>
- </el-descriptions>
- </div>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="dialogVisible = false">鍏抽棴</el-button>
- <el-button type="primary" @click="viewDetails">鏌ョ湅璇︽儏</el-button>
- </span>
- </template>
- </el-dialog>
</div>
</div>
- <!-- 鍙充晶瀹炴椂鐩戞祴鎬昏鍖� -->
- <div class="overview-section">
- <div class="section-header">
- <h3>璁惧鐩戞帶</h3>
- <!-- <span class="view-more">鏌ョ湅鏇村</span> -->
- </div>
- <div class="overview-items-container">
- <div class="overview-item">
- <div class="overview-label">椁愰ギ搴楅摵鎬绘暟</div>
- <div class="overview-value">{{ overview.totalShops }}</div>
+ <!-- -->
+ <div class="monitor-control-container">
+ <el-button size="large" @click="toggleMonitorControl" class="push-btn">
+ <div style="display: flex; flex-direction: column">
+ <el-icon>
+ <ArrowRight v-if="isMonitorControlExpanded" />
+ <ArrowLeft v-else />
+ </el-icon>
+ <!-- <div>鐜�</div>
+ <div>鍦�</div>
+ <div>宸�</div>
+ <div>鏌�</div> -->
</div>
-
- <div class="overview-item">
- <div class="overview-label">鍦ㄧ嚎璁惧鏁�</div>
- <div class="overview-value">{{ overview.onlineDevices }}</div>
- </div>
-
- <div class="overview-item">
- <div class="overview-label">绂荤嚎璁惧鏁�</div>
- <div class="overview-value">{{ overview.offlineDevices }}</div>
- </div>
- </div>
-
- <!-- 璁惧鐘舵�侀ゼ鍥� -->
- <div class="device-status-chart">
- <canvas id="deviceStatusChart"></canvas>
- </div>
+ </el-button>
+ <MonitorControl
+ v-if="isMonitorControlExpanded"
+ :class="{ 'monitor-control': true, collapsed: !isMonitorControlExpanded }"
+ style="height: calc(90vh - 40px)"
+ />
</div>
<!-- 鍦板浘鍥句緥 -->
@@ -315,19 +561,21 @@
<h4>鍥句緥</h4>
</div>
<div class="legend-items">
- <div class="legend-item">
+ <!-- 姹℃煋鎬佸娍妯″紡涓嬫樉绀虹殑鍥句緥 -->
+ <div v-if="activeMode === 'pollution'" class="legend-item">
<img src="@/assets/exceed.png" alt="娌圭儫娴撳害瓒呮爣" class="legend-icon" />
<span class="legend-text">娌圭儫娴撳害瓒呮爣</span>
</div>
- <div class="legend-item">
+ <div v-if="activeMode === 'pollution'" class="legend-item">
<img src="@/assets/exception.png" alt="渚涚數寮傚父" class="legend-icon" />
<span class="legend-text">渚涚數寮傚父</span>
</div>
- <div class="legend-item">
+ <div v-if="activeMode === 'pollution'" class="legend-item">
<img src="@/assets/offline.png" alt="璁惧鎴栫綉缁滃紓甯�" class="legend-icon" />
<span class="legend-text">璁惧鎴栫綉缁滃紓甯�</span>
</div>
- <div class="legend-item">
+ <!-- 璁惧鐘舵�佹ā寮忎笅涔熸樉绀哄湪绾跨姸鎬� -->
+ <div v-if="activeMode === 'device'" class="legend-item">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjNTJjNDFhIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiMzODllMGQiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNMTIgMTQgTCAyMSAxNCIgc3Ryb2tlPSIjNTJjNDFhIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMiAxNyBMIDE4IDE3IiBzdHJva2U9IiM1MmM0MWEiIHN0cm9rZS13aWR0aD0iMS41Ii8+PHBhdGggZD0iTTEyIDIwIEwgMTUgMjAiIHN0cm9rZT0iIzUyYzQxYSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48bGluZSB4MT0iMTYiIHkxPSI4IiB4Mj0iMTYiIHkyPSIzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxjaXJjbGUgY3g9IjE2IiBjeT0iMyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjMiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNyIgY3k9IjE2IiByPSIxLjUiIGZpbGw9IiM1MmM0MWEiLz48bGluZSB4MT0iNSIgeTE9IjEzIiB4Mj0iNiIgeTI9IjEzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxsaW5lIHgxPSI1IiB5MT0iMTkiIHgyPSI2IiB5Mj0iMTkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PC9zdmc+"
alt="鍦ㄧ嚎鐘舵��"
@@ -335,7 +583,7 @@
/>
<span class="legend-text">鍦ㄧ嚎鐘舵��</span>
</div>
- <div class="legend-item">
+ <div v-if="activeMode === 'device'" class="legend-item">
<img
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjOGM4YzhjIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48bGluZSB4MT0iMTEiIHkxPSIxMiIgeDI9IjIxIiB5Mj0iMjIiIHN0cm9rZT0iIzhjOGM4YyIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjExIiB5MT0iMjIiIHgyPSIyMSIgeTI9IjEyIiBzdHJva2U9IiM4YzhjOGMiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIHgxPSIxNiIgeTE9IjgiIHgyPSIxNiIgeTI9IjMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGNpcmNsZSBjeD0iMTYiIGN5PSIzIiByPSIxLjUiIGZpbGw9IndoaXRlIi8+PGNpcmNsZSBjeD0iMjciIGN5PSIxNiIgcj0iMyIgZmlsbD0iI2ZmZmZmZiIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjEuNSIgZmlsbD0iIzhjOGM4YyIvPjxsaW5lIHgxPSI1IiB5MT0iMTMiIHgyPSI2IiB5Mj0iMTMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGxpbmUgeDE9IjUiIHkxPSIxOSIgeDI9IjYiIHkyPSIxOSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48L3N2Zz4="
alt="绂荤嚎鐘舵��"
@@ -345,129 +593,6 @@
</div>
</div>
</div>
- <!-- 宸℃煡鎯呭喌缁熻鍗$墖 -->
- <el-scrollbar class="inspection-section">
- <div class="section-header">
- <h3>宸℃煡姹囨��</h3>
- </div>
-
- <!-- 宸℃煡閲忕粺璁� -->
- <div class="inspection-metrics">
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">搴楅摵鎬昏</div>
- <div class="inspection-metric-value">{{ inspectionStats.totalShops }}</div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">宸℃煡搴楅摵</div>
- <div class="inspection-metric-value">{{ inspectionStats.inspectedShops }}</div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">宸℃煡鐐规</div>
- <div class="inspection-metric-value">{{ inspectionStats.inspectionPoints }}</div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">澶嶆煡鐐规</div>
- <div class="inspection-metric-value">{{ inspectionStats.reviewPoints }}</div>
- </div>
- </div>
-
- <!-- 闂鏁存敼鎯呭喌 -->
- <div class="inspection-chart-container">
- <div class="section-header"><h3>鏁存敼姹囨��</h3></div>
- <canvas id="rectificationChart"></canvas>
- </div>
-
- <!-- 闂瀹℃牳鎯呭喌 -->
- <div class="inspection-table-container">
- <div class="section-header"><h3>瀹℃牳姹囨��</h3></div>
- <div class="inspection-metric-label">闂瀹℃牳</div>
- <div class="inspection-table">
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鏃犻棶棰�</div>
- <div class="inspection-metric-value">{{ inspectionStats.noProblemShops }}</div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鏈鏍�</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.unreviewedProblemShops }}
- </div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">閮ㄥ垎瀹℃牳</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.partiallyReviewedProblemShops }}
- </div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鍏ㄩ儴瀹℃牳</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.fullyReviewedProblemShops }}
- </div>
- </div>
- </div>
- <div class="inspection-metric-label">鏁存敼瀹℃牳</div>
- <div class="inspection-table">
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鏈暣鏀�</div>
- <div class="inspection-metric-value">{{ inspectionStats.unrectifiedShops }}</div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鏈鏍�</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.unreviewedRectifiedShops }}
- </div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">閮ㄥ垎瀹℃牳</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.partiallyReviewedRectifiedShops }}
- </div>
- </div>
- <div class="inspection-metric-item">
- <div class="inspection-metric-label">鍏ㄩ儴瀹℃牳</div>
- <div class="inspection-metric-value">
- {{ inspectionStats.fullyReviewedRectifiedShops }}
- </div>
- </div>
- <!-- <div class="table-row">
- <div class="table-cell">鏃犻棶棰樺簵閾烘暟閲�</div>
- <div class="table-cell value">{{ inspectionStats.noProblemShops }}</div>
- </div>
- <div class="table-row">
- <div class="table-cell">闂鏈鏍稿簵閾烘暟閲�</div>
- <div class="table-cell value">{{ inspectionStats.unreviewedProblemShops }}</div>
- </div>
- <div class="table-row">
- <div class="table-cell">闂閮ㄥ垎瀹℃牳搴楅摵鏁伴噺</div>
- <div class="table-cell value">
- {{ inspectionStats.partiallyReviewedProblemShops }}
- </div>
- </div>
- <div class="table-row">
- <div class="table-cell">闂鍏ㄩ儴瀹℃牳搴楅摵鏁伴噺</div>
- <div class="table-cell value">{{ inspectionStats.fullyReviewedProblemShops }}</div>
- </div>
- <div class="table-row">
- <div class="table-cell">鏈暣鏀瑰簵閾烘暟</div>
- <div class="table-cell value">{{ inspectionStats.unrectifiedShops }}</div>
- </div>
- <div class="table-row">
- <div class="table-cell">鏁存敼鏈鏍稿簵閾烘暟</div>
- <div class="table-cell value">{{ inspectionStats.unreviewedRectifiedShops }}</div>
- </div>
- <div class="table-row">
- <div class="table-cell">鏁存敼閮ㄥ垎瀹℃牳搴楅摵鏁�</div>
- <div class="table-cell value">
- {{ inspectionStats.partiallyReviewedRectifiedShops }}
- </div>
- </div>
- <div class="table-row">
- <div class="table-cell">鏁存敼鍏ㄩ儴瀹℃牳搴楅摵鏁�</div>
- <div class="table-cell value">{{ inspectionStats.fullyReviewedRectifiedShops }}</div>
- </div> -->
- </div>
- </div>
- </el-scrollbar>
</div>
</template>
@@ -477,19 +602,27 @@
import districtSearch from '@/utils/map/districtsearch.js'
import marks from '@/utils/map/marks.js'
import { generateTestShops } from '@/debug/debugdata'
+import MonitorControl from '@/views/inspection/MonitorControl.vue'
export default {
name: 'DataDashboard',
+ components: {
+ MonitorControl,
+ },
data() {
return {
activeTime: 'day',
- currentDate: new Date(),
+ activeMode: 'pollution', // 榛樿姹℃煋鎬佸娍妯″紡
+ currentDate: new Date('2023-08-01'),
timeTabs: [
{ label: '鏃�', value: 'day' },
{ label: '鍛�', value: 'week' },
{ label: '鏈�', value: 'month' },
],
- dialogVisible: false,
+ mapModes: [
+ { label: '姹℃煋鎬佸娍', value: 'pollution' },
+ { label: '璁惧鐘舵��', value: 'device' },
+ ],
selectedPoint: {
enterpriseName: '',
deviceId: '',
@@ -504,8 +637,8 @@
overStandardTrend: 5,
onlineRate: 92,
onlineRateTrend: 2,
- purifierEfficiency: 85,
- purifierEfficiencyTrend: -3,
+ purifierEfficiency: 95,
+ purifierEfficiencyTrend: 2,
inspectionPoints: 350,
inspectionPointsTrend: 50,
},
@@ -536,19 +669,20 @@
},
map: null,
refreshTimer: null,
+ isMonitorControlExpanded: true,
}
},
computed: {
currentTimeDisplay() {
const date = this.currentDate
+ let weekStart = new Date(date)
+ let weekEnd = new Date(date)
switch (this.activeTime) {
case 'day':
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
case 'week':
// 绠�鍗曡绠楀懆鏄剧ず锛屽疄闄呴」鐩腑鍙兘闇�瑕佹洿澶嶆潅鐨勫懆璁$畻閫昏緫
- let weekStart = new Date(date)
weekStart.setDate(date.getDate() - date.getDay() + 1)
- let weekEnd = new Date(date)
weekEnd.setDate(date.getDate() + (7 - date.getDay()))
return `${weekStart.getFullYear()}-${String(weekStart.getMonth() + 1).padStart(2, '0')}-${String(weekStart.getDate()).padStart(2, '0')} ~ ${weekEnd.getFullYear()}-${String(weekEnd.getMonth() + 1).padStart(2, '0')}-${String(weekEnd.getDate()).padStart(2, '0')}`
case 'month':
@@ -562,7 +696,7 @@
this.initMap()
this.initDeviceStatusChart()
this.initRectificationChart()
- this.startAutoRefresh()
+ // this.startAutoRefresh()
},
beforeUnmount() {
if (this.refreshTimer) {
@@ -570,10 +704,16 @@
}
},
methods: {
+ toggleMonitorControl() {
+ this.isMonitorControlExpanded = !this.isMonitorControlExpanded
+ },
handleTimeChange(tab) {
this.activeTime = tab.value
// 妯℃嫙鍒囨崲鏃堕棿鍛ㄦ湡鍚庣殑鏁版嵁鏇存柊
this.updateMetrics()
+ },
+ handleModeChange(mode) {
+ this.activeMode = mode.value
},
navigateTime(direction) {
const newDate = new Date(this.currentDate)
@@ -631,12 +771,18 @@
// 杩欓噷搴旇鏍规嵁閫夋嫨鐨勬椂闂村懆鏈熶粠鎺ュ彛鑾峰彇鏁版嵁
// 妯℃嫙鏁版嵁鏇存柊
setTimeout(() => {
+ const m = Math.floor(Math.random() * 50) + 150
+ this.overview = {
+ totalShops: 245,
+ onlineDevices: m,
+ offlineDevices: 245 - m,
+ }
this.metrics = {
overStandardCount: Math.floor(Math.random() * 30),
overStandardTrend: Math.floor(Math.random() * 20) - 10,
- onlineRate: Math.floor(Math.random() * 20) + 80,
+ onlineRate: ((this.overview.onlineDevices / this.overview.totalShops) * 100).toFixed(0),
onlineRateTrend: Math.floor(Math.random() * 10) - 5,
- purifierEfficiency: Math.floor(Math.random() * 30) + 70,
+ purifierEfficiency: Math.floor(Math.random() * 20) + 80,
purifierEfficiencyTrend: Math.floor(Math.random() * 10) - 5,
inspectionPoints: Math.floor(Math.random() * 100) + 300,
inspectionPointsTrend: Math.floor(Math.random() * 100) - 50,
@@ -662,6 +808,7 @@
}
// 鏇存柊鍥捐〃
+ this.initDeviceStatusChart()
this.initRectificationChart()
}, 300)
},
@@ -779,6 +926,7 @@
left: '3%',
right: '4%',
bottom: '3%',
+ top: '5%',
containLabel: true,
},
xAxis: {
@@ -880,6 +1028,34 @@
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
}
+/* 鐩戞帶鎺у埗鍗$墖 */
+.monitor-control {
+ /* position: absolute; */
+ width: 400px;
+ transition: all 0.3s ease;
+ /* top: 0px; */
+ /* right: 0px; */
+ /* z-index: 10; */
+}
+
+.push-btn {
+ z-index: 1;
+ width: 2.5rem;
+ height: 40px;
+ margin: initial;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ /* background-color: white; */
+ /* border-color: white; */
+ /* border-top: 1px solid;
+ border-left: 1px solid;
+ border-bottom: 1px solid; */
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+ /* box-shadow: var(--el-box-shadow-light); */
+}
+
/* 鏃堕棿鍛ㄦ湡鍗$墖 */
.time-period-card {
background-color: #ffffff;
@@ -891,6 +1067,59 @@
justify-content: center;
margin-bottom: 8px;
min-width: 300px;
+}
+
+/* 鍦板浘妯″紡鍗$墖 */
+.map-mode-card {
+ position: absolute;
+ top: 4px;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 10;
+ background-color: #ffffff;
+ border-radius: 8px;
+ padding: 4px 16px;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin-bottom: 8px;
+ min-width: 300px;
+}
+
+.mode-tab-container {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ width: 100%;
+ justify-content: center;
+}
+
+.mode-tab {
+ padding: 4px 8px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ color: #4e5969;
+ text-align: center;
+ border: 1px solid #e8e8e8;
+ background-color: #fafafa;
+ flex: 1;
+}
+
+.mode-tab.active {
+ background-color: #1890ff;
+ color: #ffffff;
+ border-color: #1890ff;
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+}
+
+.mode-tab:hover:not(.active) {
+ color: #1890ff;
+ border-color: #e6f7ff;
+ background-color: #e6f7ff;
}
.time-period-card .card-title {
@@ -993,6 +1222,7 @@
.metric-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ cursor: pointer;
}
.card-header {
@@ -1024,7 +1254,7 @@
}
.efficiency-icon {
- color: #722ed1;
+ color: #52c41a;
}
.task-icon {
@@ -1037,6 +1267,16 @@
margin: 12px 0;
color: #262626;
line-height: 1.2;
+ /* display: flex;
+ justify-content: space-between;
+ align-items: flex-end; */
+}
+
+.card-subvalues {
+ display: flex;
+ align-items: center;
+ margin: 8px 0;
+ font-weight: 500;
}
.card-trend {
@@ -1133,19 +1373,63 @@
}
/* 鍙充晶瀹炴椂鐩戞祴鎬昏鍖� */
-.overview-section {
- position: absolute;
- bottom: 4px;
- left: 4px;
- width: 320px;
- background-color: rgba(255, 255, 255, 0.9);
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- padding: 20px;
+.popover-content {
+ padding: 10px;
+}
+
+.overview-items-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 16px;
+ border-bottom: 1px solid #f0f0f0;
+ margin-bottom: 20px;
+}
+
+.overview-item {
display: flex;
flex-direction: column;
- z-index: 10;
- max-height: calc(100vh - 220px);
+ align-items: center;
+ flex: 1;
+ text-align: center;
+}
+
+.overview-label {
+ font-size: 12px;
+ color: #86909c;
+ font-weight: 500;
+ margin-bottom: 8px;
+}
+
+.overview-value {
+ font-size: 24px;
+ font-weight: bold;
+ color: #262626;
+}
+
+.device-status-chart {
+ flex: 1;
+ min-height: 200px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 16px;
+}
+
+.view-details {
+ position: absolute;
+ bottom: 12px;
+ right: 16px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 12px;
+ color: #1890ff;
+ cursor: pointer;
+}
+
+.view-details:hover {
+ text-decoration: underline;
}
.overview-items-container {
@@ -1177,30 +1461,51 @@
color: #262626;
}
-.device-status-chart {
- flex: 1;
- min-height: 100px;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 16px;
+/* 宸℃煡鎯呭喌缁熻 */
+.inspection-popover-content {
+ padding: 10px;
+ max-height: 400px;
+ overflow-y: auto;
}
-/* 宸℃煡鎯呭喌缁熻 */
-.inspection-section {
+.monitor-control-container {
position: absolute;
top: 4px;
right: 4px;
- width: 320px;
- background-color: rgba(255, 255, 255, 0.9);
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- padding: 20px;
- display: flex;
- flex-direction: column;
z-index: 10;
- max-height: calc(70vh);
- border-top: 1px solid #f0f0f0;
+ transition: all 0.3s ease;
+ /* background-color: rgba(255, 255, 255, 0.9); */
+ display: flex;
+ border-radius: 8px;
+ /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
+ overflow: hidden;
+}
+
+.monitor-control-container.collapsed {
+ width: 60px;
+}
+
+.monitor-control-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 15px;
+ border-bottom: 1px solid #e8e8e8;
+ height: 40px;
+ position: relative;
+}
+
+.monitor-control-header span {
+ font-weight: 600;
+ color: #333;
+ writing-mode: vertical-rl;
+ text-orientation: mixed;
+ letter-spacing: 2px;
+ white-space: nowrap;
+}
+
+.collapse-btn {
+ /* transform: translateY(-50%); */
}
.inspection-metrics {
@@ -1303,18 +1608,21 @@
/* 鍦板浘鍥句緥鏍峰紡 */
.map-legend {
position: absolute;
+ display: flex;
+ align-items: center;
bottom: 4px;
- right: 4px;
- width: 200px;
+ left: 50%;
+ transform: translateX(-50%);
+ /* width: 200px; */
background-color: rgba(255, 255, 255, 0.9);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- padding: 16px;
+ padding: 4px;
z-index: 10;
}
.legend-header {
- margin-bottom: 12px;
+ margin-right: 12px;
}
.legend-header h4 {
@@ -1327,7 +1635,7 @@
.legend-items {
display: flex;
- flex-direction: column;
+ flex-direction: row;
gap: 8px;
}
@@ -1417,4 +1725,4 @@
text-align: left;
}
} */
-</style>
+</style>
\ No newline at end of file
--
Gitblit v1.9.3