餐饮油烟智能监测与监管一体化平台
feiyu02
6 天以前 ccc970e575ef3f3e5c67af8da210263f4ac549f9
src/views/monitor/DataDashboard.vue
@@ -1,5 +1,20 @@
<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">
      <!-- 时间周期选项卡片 -->
@@ -56,49 +71,8 @@
          </div>
        </div>
      </div>
      <div class="cards-container">
        <!-- 浓度预警 -->
        <div class="metric-card">
          <div class="card-header">
            <div class="card-title">{{ getPeriodLabel() }}浓度预警</div>
            <div class="card-icon warning-icon">
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path d="M12 9V13" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
                <path
                  d="M12 17.5V17"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                />
                <path
                  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"
                />
              </svg>
            </div>
          </div>
          <div class="card-value">{{ metrics.overStandardCount }}<el-text>次</el-text></div>
          <div class="card-trend">
            <span
              class="trend-arrow"
              :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
            >
              {{ metrics.overStandardTrend > 0 ? '↑' : '↓' }}
            </span>
            <span class="trend-text">{{ Math.abs(metrics.overStandardTrend) }}%</span>
            <span class="trend-label">{{ getCompareLabel() }}</span>
          </div>
        </div>
        <!-- 设备在线率 -->
        <el-popover placement="right-start" title="设备监控" width="400" trigger="click">
          <div class="popover-content">
@@ -153,7 +127,7 @@
                  </svg>
                </div>
              </div>
              <div class="card-value">{{ metrics.onlineRate }}%</div>
              <div class="card-value">{{ metrics.onlineRate }}<el-text>%</el-text></div>
              <div class="card-trend">
                <span
                  class="trend-arrow"
@@ -186,11 +160,11 @@
          </template>
        </el-popover>
        <!-- 环信码绿码率 -->
        <!-- 浓度预警 -->
        <div class="metric-card">
          <div class="card-header">
            <div class="card-title">环信码绿码率</div>
            <div class="card-icon efficiency-icon">
            <div class="card-title">{{ getPeriodLabel() }}浓度预警</div>
            <div class="card-icon warning-icon">
              <svg
                width="20"
                height="20"
@@ -198,15 +172,15 @@
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path d="M12 9V13" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
                <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 17.5V17"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M12 6V12L16 14"
                  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"
@@ -215,23 +189,20 @@
              </svg>
            </div>
          </div>
          <div class="card-value">{{ metrics.purifierEfficiency }}%</div>
          <div class="card-value">{{ metrics.overStandardCount }}<el-text>次</el-text></div>
          <div class="card-trend">
            <span
              class="trend-arrow"
              :class="{
                up: metrics.purifierEfficiencyTrend > 0,
                down: metrics.purifierEfficiencyTrend < 0,
              }"
              :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
            >
              {{ metrics.purifierEfficiencyTrend > 0 ? '↑' : '↓' }}
              {{ metrics.overStandardTrend > 0 ? '↑' : '↓' }}
            </span>
            <span class="trend-text">{{ Math.abs(metrics.purifierEfficiencyTrend) }}%</span>
            <span class="trend-text">{{ Math.abs(metrics.overStandardTrend) }}%</span>
            <span class="trend-label">{{ getCompareLabel() }}</span>
          </div>
        </div>
        <!-- 巡查点次 -->
        <!-- 现场巡查 -->
        <el-popover placement="right-start" title="现场巡查统计" width="350" trigger="click">
          <div class="inspection-popover-content">
            <!-- 巡查量统计 -->
@@ -318,7 +289,7 @@
          <template #reference>
            <div class="metric-card">
              <div class="card-header">
                <div class="card-title">巡查点次</div>
                <div class="card-title">现场巡查</div>
                <div class="card-icon task-icon">
                  <svg
                    width="20"
@@ -344,7 +315,7 @@
                  </svg>
                </div>
              </div>
              <div class="card-value">{{ metrics.inspectionPoints }}</div>
              <div class="card-value">{{ metrics.inspectionPoints }}<el-text>点次</el-text></div>
              <div class="card-trend">
                <span
                  class="trend-arrow"
@@ -379,6 +350,177 @@
            </div>
          </template>
        </el-popover>
        <!-- 信访投诉 -->
        <div class="metric-card">
          <div class="card-header">
            <div class="card-title">信访投诉</div>
            <div class="card-icon warning-icon">
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  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="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"
                  stroke-linejoin="round"
                />
              </svg>
            </div>
          </div>
          <div class="card-value">{{ metrics.overStandardCount }}<el-text>件</el-text></div>
          <div class="card-trend">
            <span
              class="trend-arrow"
              :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
            >
              {{ metrics.overStandardTrend > 0 ? '↑' : '↓' }}
            </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-icon efficiency-icon">
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  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 8V16"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M8 12H16"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
              </svg>
            </div>
          </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"
              :class="{
                up: metrics.purifierEfficiencyTrend > 0,
                down: metrics.purifierEfficiencyTrend < 0,
              }"
            >
              {{ metrics.purifierEfficiencyTrend > 0 ? '↑' : '↓' }}
            </span>
            <span class="trend-text">{{ Math.abs(metrics.purifierEfficiencyTrend) }}%</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-icon warning-icon">
              <svg
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  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="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"
                  stroke-linejoin="round"
                />
              </svg>
            </div>
          </div>
          <div class="card-value">{{ metrics.overStandardCount }}<el-text>次</el-text></div>
          <div class="card-trend">
            <span
              class="trend-arrow"
              :class="{ up: metrics.overStandardTrend > 0, down: metrics.overStandardTrend < 0 }"
            >
              {{ metrics.overStandardTrend > 0 ? '↑' : '↓' }}
            </span>
            <span class="trend-text">{{ Math.abs(metrics.overStandardTrend) }}%</span>
            <span class="trend-label">{{ getCompareLabel() }}</span>
          </div>
        </div>
      </div>
    </div>
@@ -387,10 +529,12 @@
      <!-- 中部GIS地图区 -->
      <div class="map-section">
        <div id="map" class="map-container">
          <BaseMap :showSatellite="true"></BaseMap>
          <BaseMap :showSatellite="false"></BaseMap>
        </div>
      </div>
    </div>
    <!--  -->
    <div class="monitor-control-container">
      <el-button size="large" @click="toggleMonitorControl" class="push-btn">
        <div style="display: flex; flex-direction: column">
@@ -398,10 +542,10 @@
            <ArrowRight v-if="isMonitorControlExpanded" />
            <ArrowLeft v-else />
          </el-icon>
          <div>现</div>
          <!-- <div>现</div>
          <div>场</div>
          <div>巡</div>
          <div>查</div>
          <div>查</div> -->
        </div>
      </el-button>
      <MonitorControl
@@ -417,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="在线状态"
@@ -437,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="离线状态"
@@ -466,11 +612,16 @@
  data() {
    return {
      activeTime: 'day',
      currentDate: new Date('2023-08-01'),
      activeMode: 'pollution', // 默认污染态势模式
      currentDate: new Date('2025-08-01'),
      timeTabs: [
        { label: '日', value: 'day' },
        { label: '周', value: 'week' },
        { label: '月', value: 'month' },
      ],
      mapModes: [
        { label: '污染态势', value: 'pollution' },
        { label: '设备状态', value: 'device' },
      ],
      selectedPoint: {
        enterpriseName: '',
@@ -560,6 +711,9 @@
      this.activeTime = tab.value
      // 模拟切换时间周期后的数据更新
      this.updateMetrics()
    },
    handleModeChange(mode) {
      this.activeMode = mode.value
    },
    navigateTime(direction) {
      const newDate = new Date(this.currentDate)
@@ -877,7 +1031,7 @@
/* 监控控制卡片 */
.monitor-control {
  /* position: absolute; */
  width: 500px;
  width: 400px;
  transition: all 0.3s ease;
  /* top: 0px; */
  /* right: 0px; */
@@ -887,7 +1041,7 @@
.push-btn {
  z-index: 1;
  width: 2.5rem;
  height: initial;
  height: 40px;
  margin: initial;
  display: flex;
  flex-direction: column;
@@ -913,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 {
@@ -1060,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 {
@@ -1391,8 +1608,11 @@
/* 地图图例样式 */
.map-legend {
  position: absolute;
  display: flex;
  align-items: center;
  bottom: 4px;
  left: 4px;
  left: 50%;
  transform: translateX(-50%);
  /* width: 200px; */
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 8px;
@@ -1402,7 +1622,7 @@
}
.legend-header {
  margin-bottom: 12px;
  margin-right: 12px;
}
.legend-header h4 {
@@ -1415,7 +1635,7 @@
.legend-items {
  display: flex;
  flex-direction: column;
  flex-direction: row;
  gap: 8px;
}
@@ -1505,4 +1725,4 @@
    text-align: left;
  }
} */
</style>
</style>