<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="time-controls">
|
<div class="time-tab-container">
|
<div
|
v-for="tab in timeTabs"
|
:key="tab.value"
|
class="time-tab"
|
:class="{ active: activeTime === tab.value }"
|
@click="handleTimeChange(tab)"
|
>
|
{{ tab.label }}
|
</div>
|
</div>
|
<div class="time-navigator">
|
<button class="nav-btn" @click="navigateTime(-1)" title="上一个周期">
|
<svg
|
width="16"
|
height="16"
|
viewBox="0 0 24 24"
|
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
>
|
<path
|
d="M15 18L9 12L15 6"
|
stroke="currentColor"
|
stroke-width="2"
|
stroke-linecap="round"
|
stroke-linejoin="round"
|
/>
|
</svg>
|
</button>
|
<div class="current-time">{{ currentTimeDisplay }}</div>
|
<button class="nav-btn" @click="navigateTime(1)" title="下一个周期">
|
<svg
|
width="16"
|
height="16"
|
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>
|
</button>
|
</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-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="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 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>
|
|
<!-- 主要内容区 -->
|
<div class="main-content">
|
<!-- 中部GIS地图区 -->
|
<div class="map-section">
|
<div id="map" class="map-container">
|
<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">
|
<el-icon>
|
<ArrowRight v-if="isMonitorControlExpanded" />
|
<ArrowLeft v-else />
|
</el-icon>
|
<!-- <div>现</div>
|
<div>场</div>
|
<div>巡</div>
|
<div>查</div> -->
|
</div>
|
</el-button>
|
<MonitorControl
|
v-if="isMonitorControlExpanded"
|
:class="{ 'monitor-control': true, collapsed: !isMonitorControlExpanded }"
|
style="height: calc(90vh - 40px)"
|
/>
|
</div>
|
|
<!-- 地图图例 -->
|
<div class="map-legend">
|
<div class="legend-header">
|
<h4>图例</h4>
|
</div>
|
<div class="legend-items">
|
<!-- 污染态势模式下显示的图例 -->
|
<div v-if="activeMode === 'pollution'" class="legend-item">
|
<img src="@/assets/exceed.png" alt="油烟浓度超标" class="legend-icon" />
|
<span class="legend-text">油烟浓度超标</span>
|
</div>
|
<div v-if="activeMode === 'pollution'" class="legend-item">
|
<img src="@/assets/exception.png" alt="供电异常" class="legend-icon" />
|
<span class="legend-text">供电异常</span>
|
</div>
|
<div v-if="activeMode === 'pollution'" class="legend-item">
|
<img src="@/assets/offline.png" alt="设备或网络异常" class="legend-icon" />
|
<span class="legend-text">设备或网络异常</span>
|
</div>
|
<!-- 设备状态模式下也显示在线状态 -->
|
<div v-if="activeMode === 'device'" class="legend-item">
|
<img
|
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjNTJjNDFhIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiMzODllMGQiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNMTIgMTQgTCAyMSAxNCIgc3Ryb2tlPSIjNTJjNDFhIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMiAxNyBMIDE4IDE3IiBzdHJva2U9IiM1MmM0MWEiIHN0cm9rZS13aWR0aD0iMS41Ii8+PHBhdGggZD0iTTEyIDIwIEwgMTUgMjAiIHN0cm9rZT0iIzUyYzQxYSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48bGluZSB4MT0iMTYiIHkxPSI4IiB4Mj0iMTYiIHkyPSIzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxjaXJjbGUgY3g9IjE2IiBjeT0iMyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjMiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNyIgY3k9IjE2IiByPSIxLjUiIGZpbGw9IiM1MmM0MWEiLz48bGluZSB4MT0iNSIgeTE9IjEzIiB4Mj0iNiIgeTI9IjEzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxsaW5lIHgxPSI1IiB5MT0iMTkiIHgyPSI2IiB5Mj0iMTkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PC9zdmc+"
|
alt="在线状态"
|
class="legend-icon"
|
/>
|
<span class="legend-text">在线状态</span>
|
</div>
|
<div v-if="activeMode === 'device'" class="legend-item">
|
<img
|
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjOGM4YzhjIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48bGluZSB4MT0iMTEiIHkxPSIxMiIgeDI9IjIxIiB5Mj0iMjIiIHN0cm9rZT0iIzhjOGM4YyIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjExIiB5MT0iMjIiIHgyPSIyMSIgeTI9IjEyIiBzdHJva2U9IiM4YzhjOGMiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIHgxPSIxNiIgeTE9IjgiIHgyPSIxNiIgeTI9IjMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGNpcmNsZSBjeD0iMTYiIGN5PSIzIiByPSIxLjUiIGZpbGw9IndoaXRlIi8+PGNpcmNsZSBjeD0iMjciIGN5PSIxNiIgcj0iMyIgZmlsbD0iI2ZmZmZmZiIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjEuNSIgZmlsbD0iIzhjOGM4YyIvPjxsaW5lIHgxPSI1IiB5MT0iMTMiIHgyPSI2IiB5Mj0iMTMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGxpbmUgeDE9IjUiIHkxPSIxOSIgeDI9IjYiIHkyPSIxOSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48L3N2Zz4="
|
alt="离线状态"
|
class="legend-icon"
|
/>
|
<span class="legend-text">离线状态</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import * as echarts from 'echarts'
|
import { onMapMounted, satellite } from '@/utils/map/index'
|
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',
|
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: '',
|
deviceId: '',
|
oilSmokeConcentration: 0,
|
particulateMatter: 0,
|
nonMethaneHydrocarbon: 0,
|
monitoringTime: '',
|
isOverStandard: false,
|
},
|
metrics: {
|
overStandardCount: 12,
|
overStandardTrend: 5,
|
onlineRate: 92,
|
onlineRateTrend: 2,
|
purifierEfficiency: 95,
|
purifierEfficiencyTrend: 2,
|
inspectionPoints: 350,
|
inspectionPointsTrend: 50,
|
},
|
overview: {
|
totalShops: 245,
|
onlineDevices: 220,
|
offlineDevices: 25,
|
},
|
inspectionStats: {
|
// 巡查量
|
totalShops: 245,
|
inspectedShops: 180,
|
inspectionPoints: 350,
|
reviewPoints: 80,
|
// 问题整改情况
|
problemCount: 45,
|
rectifiedCount: 38,
|
rectificationRate: 84.4,
|
// 问题审核情况
|
noProblemShops: 135,
|
unreviewedProblemShops: 8,
|
partiallyReviewedProblemShops: 5,
|
fullyReviewedProblemShops: 32,
|
unreviewedRectifiedShops: 3,
|
partiallyReviewedRectifiedShops: 2,
|
fullyReviewedRectifiedShops: 33,
|
unrectifiedShops: 7,
|
},
|
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':
|
// 简单计算周显示,实际项目中可能需要更复杂的周计算逻辑
|
weekStart.setDate(date.getDate() - date.getDay() + 1)
|
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':
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
|
default:
|
return ''
|
}
|
},
|
},
|
mounted() {
|
this.initMap()
|
this.initDeviceStatusChart()
|
this.initRectificationChart()
|
// this.startAutoRefresh()
|
},
|
beforeUnmount() {
|
if (this.refreshTimer) {
|
clearInterval(this.refreshTimer)
|
}
|
},
|
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)
|
switch (this.activeTime) {
|
case 'day':
|
newDate.setDate(newDate.getDate() + direction)
|
break
|
case 'week':
|
newDate.setDate(newDate.getDate() + direction * 7)
|
break
|
case 'month':
|
newDate.setMonth(newDate.getMonth() + direction)
|
break
|
}
|
this.currentDate = newDate
|
// 模拟切换时间后的数据更新
|
this.updateMetrics()
|
},
|
getPeriodLabel() {
|
const today = new Date()
|
const isToday =
|
this.activeTime === 'day' &&
|
this.currentDate.getDate() === today.getDate() &&
|
this.currentDate.getMonth() === today.getMonth() &&
|
this.currentDate.getFullYear() === today.getFullYear()
|
|
if (isToday) {
|
return '今日'
|
}
|
|
switch (this.activeTime) {
|
case 'day':
|
return '当日'
|
case 'week':
|
return '当周'
|
case 'month':
|
return '当月'
|
default:
|
return '当日'
|
}
|
},
|
getCompareLabel() {
|
switch (this.activeTime) {
|
case 'day':
|
return '较昨日'
|
case 'week':
|
return '较上周'
|
case 'month':
|
return '较上月'
|
default:
|
return '较昨日'
|
}
|
},
|
updateMetrics() {
|
// 这里应该根据选择的时间周期从接口获取数据
|
// 模拟数据更新
|
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: ((this.overview.onlineDevices / this.overview.totalShops) * 100).toFixed(0),
|
onlineRateTrend: Math.floor(Math.random() * 10) - 5,
|
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,
|
}
|
|
// 更新巡查统计数据
|
this.inspectionStats = {
|
totalShops: 245,
|
inspectedShops: Math.floor(Math.random() * 50) + 150,
|
inspectionPoints: Math.floor(Math.random() * 100) + 300,
|
reviewPoints: Math.floor(Math.random() * 50) + 50,
|
problemCount: Math.floor(Math.random() * 30) + 20,
|
rectifiedCount: Math.floor(Math.random() * 25) + 15,
|
rectificationRate: Math.round((Math.random() * 30 + 70) * 10) / 10,
|
noProblemShops: Math.floor(Math.random() * 50) + 100,
|
unreviewedProblemShops: Math.floor(Math.random() * 10),
|
partiallyReviewedProblemShops: Math.floor(Math.random() * 8),
|
fullyReviewedProblemShops: Math.floor(Math.random() * 20) + 15,
|
unreviewedRectifiedShops: Math.floor(Math.random() * 5),
|
partiallyReviewedRectifiedShops: Math.floor(Math.random() * 5),
|
fullyReviewedRectifiedShops: Math.floor(Math.random() * 20) + 15,
|
unrectifiedShops: Math.floor(Math.random() * 10),
|
}
|
|
// 更新图表
|
this.initDeviceStatusChart()
|
this.initRectificationChart()
|
}, 300)
|
},
|
initMap() {
|
// setTimeout(() => {
|
districtSearch.removeDistrict()
|
districtSearch.drawDistrictMask('上海市')
|
// districtSearch.districtLayer('310106')
|
// }, 2000)
|
|
onMapMounted(() => {
|
setTimeout(() => {
|
marks.clearMassMarks()
|
const shops = generateTestShops()
|
console.log(shops)
|
|
marks.drawMassMarks(shops)
|
}, 2000)
|
})
|
},
|
initDeviceStatusChart() {
|
const chartDom = document.getElementById('deviceStatusChart')
|
if (chartDom) {
|
const chart = echarts.init(chartDom)
|
const option = {
|
tooltip: {
|
trigger: 'item',
|
formatter: '{b}: {c} ({d}%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
borderColor: '#e8e8e8',
|
borderWidth: 1,
|
textStyle: {
|
color: '#333',
|
},
|
},
|
legend: {
|
bottom: '0%',
|
left: 'center',
|
textStyle: {
|
color: '#86909c',
|
fontSize: 12,
|
},
|
},
|
series: [
|
{
|
name: '设备状态',
|
type: 'pie',
|
radius: ['50%', '75%'],
|
center: ['50%', '45%'],
|
avoidLabelOverlap: false,
|
itemStyle: {
|
borderRadius: 8,
|
borderColor: '#ffffff',
|
borderWidth: 2,
|
shadowBlur: 5,
|
shadowOffsetX: 0,
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
},
|
label: {
|
show: true,
|
position: 'center',
|
formatter: '{d}%',
|
fontSize: 18,
|
fontWeight: 'bold',
|
color: '#262626',
|
},
|
labelLine: {
|
show: false,
|
},
|
data: [
|
{
|
value: this.overview.onlineDevices,
|
name: '在线',
|
itemStyle: {
|
color: '#1890ff',
|
},
|
},
|
{
|
value: this.overview.offlineDevices,
|
name: '离线',
|
itemStyle: {
|
color: '#f5222d',
|
},
|
},
|
],
|
},
|
],
|
}
|
chart.setOption(option)
|
|
// 响应式调整
|
// window.addEventListener('resize', () => {
|
// chart.resize()
|
// })
|
}
|
},
|
initRectificationChart() {
|
const chartDom = document.getElementById('rectificationChart')
|
if (chartDom) {
|
const chart = echarts.init(chartDom)
|
const option = {
|
tooltip: {
|
trigger: 'axis',
|
axisPointer: {
|
type: 'shadow',
|
},
|
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
borderColor: '#e8e8e8',
|
borderWidth: 1,
|
textStyle: {
|
color: '#333',
|
},
|
},
|
grid: {
|
left: '3%',
|
right: '4%',
|
bottom: '3%',
|
top: '5%',
|
containLabel: true,
|
},
|
xAxis: {
|
type: 'category',
|
data: ['问题数', '整改数'],
|
axisLabel: {
|
color: '#86909c',
|
fontSize: 12,
|
},
|
},
|
yAxis: {
|
type: 'value',
|
axisLabel: {
|
color: '#86909c',
|
fontSize: 12,
|
},
|
},
|
series: [
|
{
|
name: '数量',
|
type: 'bar',
|
data: [
|
{
|
value: this.inspectionStats.problemCount,
|
itemStyle: {
|
color: '#fa8c16',
|
},
|
},
|
{
|
value: this.inspectionStats.rectifiedCount,
|
itemStyle: {
|
color: '#52c41a',
|
},
|
},
|
],
|
barWidth: '60%',
|
label: {
|
show: true,
|
position: 'top',
|
color: '#262626',
|
fontSize: 12,
|
},
|
},
|
],
|
}
|
chart.setOption(option)
|
|
// 响应式调整
|
// window.addEventListener('resize', () => {
|
// chart.resize()
|
// })
|
}
|
},
|
startAutoRefresh() {
|
// 每30秒自动刷新数据
|
this.refreshTimer = setInterval(() => {
|
this.updateMetrics()
|
// 这里应该同时更新地图点位数据
|
}, 30000)
|
},
|
viewDetails() {
|
// 跳转到企业监控详情页
|
this.$router.push('/monitor/enterprise-detail')
|
},
|
},
|
}
|
</script>
|
|
<style scoped>
|
/* 全局样式 */
|
.data-dashboard {
|
width: 100%;
|
height: calc(100vh - 70px);
|
background-color: #f5f7fa;
|
color: #333;
|
box-sizing: border-box;
|
font-family:
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
position: relative;
|
}
|
|
/* 顶部指标卡片区 */
|
.top-cards {
|
position: absolute;
|
top: 4px;
|
left: 4px;
|
z-index: 10;
|
margin-bottom: 24px;
|
}
|
|
.cards-container {
|
display: grid;
|
grid-template-columns: repeat(2, 180px);
|
grid-template-rows: auto repeat(2, auto);
|
gap: 8px;
|
/* background-color: rgba(255, 255, 255, 0.9); */
|
/* padding: 16px; */
|
border-radius: 8px;
|
/* 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;
|
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;
|
}
|
|
/* 地图模式卡片 */
|
.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 {
|
font-size: 14px;
|
color: #86909c;
|
font-weight: 500;
|
margin-bottom: 16px;
|
text-align: center;
|
}
|
|
.time-controls {
|
display: flex;
|
flex-direction: column;
|
gap: 16px;
|
}
|
|
.time-tab-container {
|
display: flex;
|
flex-direction: row;
|
gap: 8px;
|
width: 100%;
|
justify-content: center;
|
}
|
|
.time-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;
|
}
|
|
.time-tab.active {
|
background-color: #1890ff;
|
color: #ffffff;
|
border-color: #1890ff;
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
|
}
|
|
.time-tab:hover:not(.active) {
|
color: #1890ff;
|
border-color: #e6f7ff;
|
background-color: #e6f7ff;
|
}
|
|
.time-navigator {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
gap: 12px;
|
padding: 8px 0;
|
}
|
|
.nav-btn {
|
width: 32px;
|
height: 32px;
|
border: 1px solid #e8e8e8;
|
background-color: #fafafa;
|
border-radius: 4px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
color: #4e5969;
|
}
|
|
.nav-btn:hover {
|
border-color: #1890ff;
|
color: #1890ff;
|
background-color: #e6f7ff;
|
}
|
|
.current-time {
|
font-size: 14px;
|
font-weight: 500;
|
color: #262626;
|
min-width: 180px;
|
text-align: center;
|
padding: 0 12px;
|
}
|
|
/* 指标卡片 */
|
.metric-card {
|
background-color: #ffffff;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
transition: all 0.3s ease;
|
position: relative;
|
overflow: hidden;
|
}
|
|
.metric-card:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
cursor: pointer;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 12px;
|
}
|
|
.card-title {
|
font-size: 14px;
|
color: #86909c;
|
font-weight: 500;
|
}
|
|
.card-icon {
|
color: #1890ff;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.warning-icon {
|
color: #fa8c16;
|
}
|
|
.online-icon {
|
color: #52c41a;
|
}
|
|
.efficiency-icon {
|
color: #52c41a;
|
}
|
|
.task-icon {
|
color: #1890ff;
|
}
|
|
.card-value {
|
font-size: 32px;
|
font-weight: bold;
|
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 {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
font-size: 12px;
|
padding-top: 12px;
|
border-top: 1px solid #f0f0f0;
|
}
|
|
.trend-arrow {
|
font-size: 14px;
|
font-weight: bold;
|
}
|
|
.trend-arrow.up {
|
color: #52c41a;
|
}
|
|
.trend-arrow.down {
|
color: #f5222d;
|
}
|
|
.trend-text {
|
color: #262626;
|
font-weight: 500;
|
}
|
|
.trend-label {
|
color: #86909c;
|
font-size: 12px;
|
}
|
|
/* 主要内容区 */
|
.main-content {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
}
|
|
/* 中部GIS地图区 */
|
.map-section {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
overflow: hidden;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 16px;
|
color: #262626;
|
}
|
|
.section-header h3 {
|
font-size: 16px;
|
font-weight: 600;
|
color: #262626;
|
margin: 0;
|
}
|
|
.view-more {
|
font-size: 12px;
|
color: #1890ff;
|
cursor: pointer;
|
display: flex;
|
align-items: center;
|
}
|
|
.view-more:hover {
|
text-decoration: underline;
|
}
|
|
.map-container {
|
flex: 1;
|
position: relative;
|
overflow: hidden;
|
background-color: #fafafa;
|
}
|
|
.map-placeholder {
|
width: 100%;
|
height: 100%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: #86909c;
|
font-size: 16px;
|
}
|
|
/* 右侧实时监测总览区 */
|
.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;
|
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 {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding-bottom: 16px;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.overview-item {
|
display: flex;
|
flex-direction: column;
|
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;
|
}
|
|
/* 巡查情况统计 */
|
.inspection-popover-content {
|
padding: 10px;
|
max-height: 400px;
|
overflow-y: auto;
|
}
|
|
.monitor-control-container {
|
position: absolute;
|
top: 4px;
|
right: 4px;
|
z-index: 10;
|
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 {
|
display: grid;
|
grid-template-columns: repeat(2, 1fr);
|
gap: 12px;
|
margin-bottom: 20px;
|
}
|
|
.inspection-metric-item {
|
background-color: #fafafa;
|
border-radius: 6px;
|
padding: 12px;
|
text-align: center;
|
}
|
|
.inspection-metric-label {
|
font-size: 12px;
|
color: #86909c;
|
margin-bottom: 4px;
|
}
|
|
.inspection-metric-value {
|
font-size: 18px;
|
font-weight: bold;
|
color: #262626;
|
}
|
|
.inspection-chart-container {
|
margin-bottom: 20px;
|
}
|
|
.chart-title {
|
font-size: 14px;
|
font-weight: 500;
|
color: #262626;
|
margin-bottom: 12px;
|
}
|
|
.inspection-table-container {
|
/* max-height: 200px; */
|
/* overflow-y: auto; */
|
}
|
|
.inspection-table {
|
/* width: 100%;
|
border-collapse: collapse; */
|
display: grid;
|
grid-template-columns: repeat(4, 1fr);
|
gap: 2px;
|
margin-bottom: 20px;
|
}
|
|
.table-row {
|
display: flex;
|
border-bottom: 1px solid #f0f0f0;
|
padding: 8px 0;
|
}
|
|
.table-row:last-child {
|
border-bottom: none;
|
}
|
|
.table-cell {
|
flex: 1;
|
font-size: 12px;
|
color: #4e5969;
|
}
|
|
.table-cell.value {
|
font-weight: 500;
|
color: #262626;
|
text-align: right;
|
}
|
|
/* 弹窗样式 */
|
.dialog-content {
|
color: #333;
|
}
|
|
.dialog-footer {
|
text-align: right;
|
}
|
|
/* 闪烁效果 */
|
@keyframes blink {
|
0%,
|
100% {
|
opacity: 1;
|
}
|
50% {
|
opacity: 0.5;
|
}
|
}
|
|
.blink {
|
animation: blink 1s infinite;
|
}
|
|
/* 地图图例样式 */
|
.map-legend {
|
position: absolute;
|
display: flex;
|
align-items: center;
|
bottom: 4px;
|
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: 4px;
|
z-index: 10;
|
}
|
|
.legend-header {
|
margin-right: 12px;
|
}
|
|
.legend-header h4 {
|
font-size: 14px;
|
font-weight: 600;
|
color: #262626;
|
margin: 0;
|
text-align: center;
|
}
|
|
.legend-items {
|
display: flex;
|
flex-direction: row;
|
gap: 8px;
|
}
|
|
.legend-item {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
font-size: 12px;
|
color: #4e5969;
|
}
|
|
.legend-icon {
|
width: 24px;
|
height: 24px;
|
object-fit: contain;
|
}
|
|
/* 响应式设计 */
|
/* @media (max-width: 1200px) {
|
.top-cards {
|
position: relative;
|
margin-bottom: 24px;
|
}
|
|
.cards-container {
|
grid-template-columns: repeat(2, 1fr);
|
grid-template-rows: auto auto;
|
background-color: #ffffff;
|
}
|
|
.main-content {
|
height: calc(100vh - 300px);
|
}
|
|
.overview-section {
|
position: relative;
|
top: 0;
|
right: 0;
|
width: 100%;
|
max-height: none;
|
height: 300px;
|
margin-top: 16px;
|
background-color: #ffffff;
|
}
|
} */
|
|
/* @media (max-width: 768px) {
|
.data-dashboard {
|
padding: 16px;
|
}
|
|
.top-cards {
|
left: 16px;
|
}
|
|
.cards-container {
|
grid-template-columns: 1fr;
|
grid-template-rows: auto repeat(4, auto);
|
}
|
|
.time-period-card {
|
order: -1;
|
}
|
|
.time-tab-container {
|
flex-direction: row;
|
}
|
|
.time-tab {
|
flex: 1;
|
padding: 8px 0;
|
}
|
|
.overview-section {
|
right: 16px;
|
}
|
|
.overview-items-container {
|
flex-direction: column;
|
gap: 16px;
|
}
|
|
.overview-item {
|
flex-direction: row;
|
justify-content: space-between;
|
width: 100%;
|
text-align: left;
|
}
|
} */
|
</style>
|