凹面大屏展示

<template>
	<div class="curved-dashboard">
		<!-- 顶部标题 -->
		<div class="title"></div>

		<!-- 3D弧形面板容器(6个模块,对称布局) -->
		<div class="dashboard-container">
			<!-- 模块1:左侧数据概览 -->
			<div class="dashboard-module module-1" @click="openDialog(1)">
				<div class="module-header">监视预警</div>
				<div class="module-content">
					<div class="stats-grid">
						<waterStorage />
					</div>
					<div class="stats-grid">
						<safetyMonitoring />
					</div>
					<div class="stats-grid">
						<warningStatistics />
					</div>
				</div>
			</div>

			<!-- 模块2:左中水量调度模块 -->
			<div class="dashboard-module module-2" @click="openDialog(2)">
				<div class="module-header">水量调度模块</div>
				<div class="module-content">
					<div class="stats-grid">
						<waterFlowChart />
					</div>
					<div class="stats-grid">
						<waterSupplyChart />
					</div>
					<div class="stats-grid">
						<waterDemandChart />
					</div>
				</div>
			</div>

			<!-- 模块3:中间3D核心 -->
			<div class="dashboard-module module-3" @click="openDialog(3)">
				<div class="module-header">安全监管模块</div>
				<div class="module-content core">
					<div class="stats-grid">
						<rainMonitorChart />
					</div>
					<div class="stats-grid">
						<waterMonitorChart />
					</div>
					<div>
						<floodProtectionChart />
					</div>
				</div>
			</div>

			<!-- 模块4:右中资产分布 -->
			<div class="dashboard-module module-4" @click="openDialog(4)">
				<div class="module-header">水质保障模块</div>
				<div class="module-content">
					<div class="stats-grid">
						<waterQualityDistribution />
					</div>
					<div class="stats-grid">
						<monitoringDataChart />
					</div>
					<div class="stats-grid">
						<siteComparisonRadar />
					</div>
				</div>
			</div>

			<!-- 模块5:右侧数据质量监控 -->
			<div class="dashboard-module module-5" @click="openDialog(5)">
				<div class="module-header">运行维护</div>
				<div class="module-content">
					<div class="stats-grid">
						<dronePatrol />
					</div>
					<div class="stats-grid">
						<engineering />
					</div>
					<div class="stats-grid">
						<equipmentHealth />
					</div>
				</div>
			</div>

			<!-- 新增模块6:最右侧系统性能监控 -->
			<div class="dashboard-module module-6" @click="openDialog(6)">
				<div class="module-header">应急处置模块</div>
				<div class="module-content">
					<div class="stats-grid">
						<eventTypeWordCloud />
					</div>
					<div class="stats-grid">
						<drillPlanCalendar />
					</div>
					<div class="stats-grid">
						<emergencySupport />
					</div>
				</div>
			</div>
		</div>

		<!-- 弹窗遮罩层 -->
		<div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
			<!-- 弹窗内容容器 -->
			<div class="dialog-container" @click.stop>
				<div class="dialog-header">
					<h2>{{ dialogTitle }}</h2>
					<button class="close-btn" @click="closeDialog">×</button>
				</div>
				<div class="dialog-content">
					<!-- 根据当前模块显示对应内容 -->
					<div v-if="currentModule === 1" class="module-1-content">
						<waterStorageMagnify :isDialog="true" />
						<safetyMonitoringMagnify :isDialog="true" />
						<warningStatisticsMagnify :isDialog="true" />
					</div>

					<div v-if="currentModule === 2" class="module-2-content">
						<waterFlowChartMagnify :isDialog="true" />
						<waterSupplyChartMagnify :isDialog="true" />
						<waterDemandChartMagnify :isDialog="true" />
					</div>

					<div v-if="currentModule === 3" class="module-3-content">
						<rainMonitorChartMagnify :isDialog="true" />
						<waterMonitorChartMagnify :isDialog="true" />
						<floodProtectionChartMagnify :isDialog="true" />
					</div>

					<div v-if="currentModule === 4" class="module-4-content">
						<waterQualityDistributionMagnify :isDialog="true" />
						<monitoringDataChartMagnify :isDialog="true" />
						<siteComparisonRadarMagnify :isDialog="true" />
					</div>

					<div v-if="currentModule === 5" class="module-5-content">
						<dronePatrolMagnify :isDialog="true" />
						<engineeringMagnify :isDialog="true" />
						<equipmentHealthMagnify :isDialog="true" />
					</div>

					<div v-if="currentModule === 6" class="module-6-content">
						<eventTypeWordCloudMagnify :isDialog="true" />
						<drillPlanCalendarMagnify :isDialog="true" />
						<emergencySupportMagnify :isDialog="true" />
					</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup>
	import {
		ref,
		computed
	} from 'vue';
	import waterStorage from "./components/reduce/surveillanceWarning/waterStorage.vue"
	import warningStatistics from "./components/reduce/surveillanceWarning/warningStatistics.vue"
	import safetyMonitoring from "./components/reduce/surveillanceWarning/safetyMonitoring.vue"
	import waterFlowChart from "./components/reduce/waterDispatching/waterFlowChart.vue"
	import waterSupplyChart from "./components/reduce/waterDispatching/waterSupplyChart.vue"
	import waterDemandChart from "./components/reduce/waterDispatching/waterDemandChart.vue"
	import rainMonitorChart from "./components/reduce/safetySupervision/rainMonitorChart.vue"
	import waterMonitorChart from "./components/reduce/safetySupervision/waterMonitorChart.vue"
	import floodProtectionChart from "./components/reduce/safetySupervision/floodProtectionChart.vue"
	import waterQualityDistribution from "./components/reduce/waterQualityAssurance/waterQualityDistribution.vue"
	import monitoringDataChart from "./components/reduce/waterQualityAssurance/monitoringDataChart.vue"
	import siteComparisonRadar from "./components/reduce/waterQualityAssurance/siteComparisonRadar.vue"
	import dronePatrol from "./components/reduce/maintenance/dronePatrol.vue"
	import engineering from "./components/reduce/maintenance/engineering.vue"
	import equipmentHealth from "./components/reduce/maintenance/equipmentHealth.vue"
	import eventTypeWordCloud from "./components/reduce/emergencyResponse/eventTypeWordCloud.vue"
	import drillPlanCalendar from "./components/reduce/emergencyResponse/drillPlanCalendar.vue"
	import emergencySupport from "./components/reduce/emergencyResponse/emergencySupport.vue"

	import waterStorageMagnify from "./components/magnify/surveillanceWarning/waterStorage.vue"
	import safetyMonitoringMagnify from "./components/magnify/surveillanceWarning/safetyMonitoring.vue"
	import warningStatisticsMagnify from "./components/magnify/surveillanceWarning/warningStatistics.vue"
	import waterFlowChartMagnify from "./components/magnify/waterDispatching/waterFlowChart.vue"
	import waterSupplyChartMagnify from "./components/magnify/waterDispatching/waterSupplyChart.vue"
	import waterDemandChartMagnify from "./components/magnify/waterDispatching/waterDemandChart.vue"
	import rainMonitorChartMagnify from "./components/magnify/safetySupervision/rainMonitorChart.vue"
	import waterMonitorChartMagnify from "./components/magnify/safetySupervision/waterMonitorChart.vue"
	import floodProtectionChartMagnify from "./components/magnify/safetySupervision/floodProtectionChart.vue"
	import waterQualityDistributionMagnify from "./components/magnify/waterQualityAssurance/waterQualityDistribution.vue"
	import monitoringDataChartMagnify from "./components/magnify/waterQualityAssurance/monitoringDataChart.vue"
	import siteComparisonRadarMagnify from "./components/magnify/waterQualityAssurance/siteComparisonRadar.vue"
	import dronePatrolMagnify from "./components/magnify/maintenance/dronePatrol.vue"
	import engineeringMagnify from "./components/magnify/maintenance/engineering.vue"
	import equipmentHealthMagnify from "./components/magnify/maintenance/equipmentHealth.vue"
	import eventTypeWordCloudMagnify from "./components/magnify/emergencyResponse/eventTypeWordCloud.vue"
	import drillPlanCalendarMagnify from "./components/magnify/emergencyResponse/drillPlanCalendar.vue"
	import emergencySupportMagnify from "./components/magnify/emergencyResponse/emergencySupport.vue"
	
	// 响应式数据
	const showDialog = ref(false);
	const currentModule = ref(1);

	// 计算属性:根据当前模块返回弹窗标题
	const dialogTitle = computed(() => {
		const titles = {
			1: "监视预警",
			2: "水量调度模块",
			3: "安全监管模块",
			4: "水质保障模块",
			5: "运行维护",
			6: "应急处置模块"
		};
		return titles[currentModule.value] || "模块详情";
	});

	// 方法:打开弹窗
	const openDialog = (moduleId) => {
		currentModule.value = moduleId;
		showDialog.value = true;
	};

	// 方法:关闭弹窗
	const closeDialog = () => {
		showDialog.value = false;
	};
</script>

<style scoped>
	/* 全局背景:深青蓝渐变,匹配原图色调 */
	.curved-dashboard {
		width: 100vw;
		height: 100vh;
		background: url("@/assets/imgs/底图.gif");
		background-size: 100% 100%;
		position: relative;
		overflow: hidden;
	}

	/* 顶部标题:发光边框+科技感样式 */
	.title {
		position: absolute;
		top: 40px;
		left: 20%;
		width: 60%;
		height: 10%;
		background: url("@/assets/imgs/标题@1x.png");
		background-size: 100% 100%;
		padding: 8px 25px;
	}

	/* 3D容器:perspective控制深度,确保弧形自然 */
	.dashboard-container {
		width: 80%;
		height: 680px;
		position: relative;
		perspective: 2200px;
		display: flex;
		justify-content: center;
		align-items: center;
		margin-top: 100px;
		margin-left: 10%;
	}

	/* 单个模块:半透明蓝+发光边框,匹配原图质感 */
	.dashboard-module {
		position: absolute;
		width: 260px;
		height: 420px;
		background: rgba(0, 31, 88, 0.7);
		border: 1px solid #35ABFF;
		border-radius: 8px;
		padding: 18px;
		box-sizing: border-box;
		color: #e0e0e0;
		transform-style: preserve-3d;
		backface-visibility: hidden;
		box-shadow: 0px 4px 10px 0px rgba(65, 176, 255, 0.5), 0px -4px 10px 0px rgba(65, 176, 255, 0.5);
		transition: all 0.3s ease;
		cursor: pointer;
	}

	/* 模块hover:增强发光,突出交互 */
	.dashboard-module:hover {
		box-shadow: 0 0 20px rgba(64, 189, 245, 0.6);
		border-color: #40bdf5;
		background: rgba(0, 31, 88, 0.8);
		z-index: 10;
	}

	/* 6个模块的3D变换参数(对称凹形布局) */
	.module-1 {
		transform: rotateY(50deg) translateX(-855px) translateZ(50px);
		--rotate-y: 5deg;
	}

	.module-2 {
		transform: rotateY(45deg) translateX(-570px) translateZ(90px);
		--rotate-y: -12deg;
	}

	.module-3 {
		transform: rotateY(20deg) translateX(-220px) translateZ(240px);
		--rotate-y: 0deg;
	}

	.module-4 {
		transform: rotateY(-20deg) translateX(220px) translateZ(240px);
		--rotate-y: 12deg;
	}

	.module-5 {
		transform: rotateY(-45deg) translateX(570px) translateZ(90px);
		--rotate-y: 25deg;
	}

	/* 新增模块6:最右侧,与左侧模块1对称 */
	.module-6 {
		transform: rotateY(-50deg) translateX(855px) translateZ(50px);
		--rotate-y: 30deg;
	}

	.module-header {
		font-size: 16px;
		margin-bottom: 10px;
		padding-bottom: 6px;
		border-bottom: 1px solid rgba(64, 189, 245, 0.2);
		color: #40bdf5;
	}

	.module-content {
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		height: calc(100% - 30px);
	}

	/* 图表占位符:形状、颜色1:1还原原图 */
	.chart-placeholder {
		width: 100%;
		height: 120px;
		border-radius: 6px;
		background: rgba(64, 189, 245, 0.1);
		position: relative;
		overflow: hidden;
		border: 1px solid rgba(64, 189, 245, 0.2);
	}

	/* 统计卡片:网格布局+发光边框 */
	.stats-grid,
	.radar-stats {
		margin-bottom: 8px;
	}

	/* 弹窗遮罩层样式 */
	.dialog-overlay {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		background: rgba(0, 0, 0, 0.7);
		backdrop-filter: blur(5px);
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 1000;
		animation: fadeIn 0.3s ease;
	}

	/* 弹窗容器样式 */
	.dialog-container {
		width: 85%;
		height: 85%;
		background: rgba(0, 31, 88, 0.95);
		border: 2px solid #35ABFF;
		border-radius: 12px;
		box-shadow: 0 0 40px rgba(53, 171, 255, 0.5);
		display: flex;
		flex-direction: column;
		overflow: hidden;
		animation: scaleIn 0.3s ease;
	}

	/* 弹窗头部 */
	.dialog-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		padding: 15px 25px;
		background: rgba(0, 20, 60, 0.8);
		border-bottom: 1px solid rgba(53, 171, 255, 0.3);
	}

	.dialog-header h2 {
		color: #40bdf5;
		margin: 0;
		font-size: 22px;
		font-weight: 600;
	}

	.close-btn {
		background: transparent;
		border: none;
		color: #40bdf5;
		font-size: 28px;
		cursor: pointer;
		width: 40px;
		height: 40px;
		border-radius: 50%;
		display: flex;
		align-items: center;
		justify-content: center;
		transition: all 0.3s ease;
	}

	.close-btn:hover {
		background: rgba(64, 189, 245, 0.2);
		transform: scale(1.1);
	}

	/* 弹窗内容区域 */
	.dialog-content {
		flex: 1;
		padding: 25px;
		overflow-y: auto;
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
		gap: 20px;
		align-content: flex-start;
	}

	/* 弹窗中的图表容器样式 */
	.dialog-content :deep(.stats-grid) {
		background: rgba(64, 189, 245, 0.1);
		border: 1px solid rgba(64, 189, 245, 0.3);
		border-radius: 8px;
		padding: 15px;
		height: 200px;
		display: flex;
		/* flex-direction: column; */
	}

	/* 动画效果 */
	@keyframes fadeIn {
		from {
			opacity: 0;
		}

		to {
			opacity: 1;
		}
	}

	@keyframes scaleIn {
		from {
			opacity: 0;
			transform: scale(0.8);
		}

		to {
			opacity: 1;
			transform: scale(1);
		}
	}

	/* 响应式调整 */
	@media (max-width: 1200px) {
		.dialog-content {
			grid-template-columns: repeat(2, 1fr);
		}
	}

	@media (max-width: 768px) {
		.dialog-container {
			width: 95%;
			height: 90%;
		}

		.dialog-content {
			grid-template-columns: 1fr;
		}
	}

	/* 中间3D地球:核心视觉+呼吸动画+连接线条 */
	.core {
		align-items: center;
		justify-content: center;
	}

	.globe {
		position: relative;
		width: 180px;
		height: 180px;
		margin: 0 auto;
	}

	.globe-sphere {
		width: 100%;
		height: 100%;
		border-radius: 50%;
		background: radial-gradient(circle at 30% 30%, #40bdf5, #08152d);
		box-shadow: 0 0 30px rgba(64, 189, 245, 0.6);
		animation: pulse 3s infinite ease-in-out;
	}

	@keyframes pulse {

		0%,
		100% {
			box-shadow: 0 0 30px rgba(64, 189, 245, 0.6);
		}

		50% {
			box-shadow: 0 0 40px rgba(64, 189, 245, 0.8);
		}
	}

	.globe-lines {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		background: repeating-radial-gradient(circle, transparent 0%, rgba(64, 189, 245, 0.2) 5%, transparent 10%);
		border-radius: 50%;
	}

	.globe-connections {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		background:
			linear-gradient(45deg, transparent 49%, rgba(64, 189, 245, 0.3) 50%, transparent 51%),
			linear-gradient(135deg, transparent 49%, rgba(64, 189, 245, 0.3) 50%, transparent 51%);
		border-radius: 50%;
	}

	/* 底部光效与网格:多层环形+科技纹理,匹配原图地面 */
	/* .floor-effect {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  height: 420px;
  background: transparent;
} */
	.floor-rings {
		position: absolute;
		bottom: 0;
		left: 50%;
		transform: translateX(-50%);
		width: 850px;
		height: 320px;
		background: radial-gradient(circle, rgba(64, 189, 245, 0.18) 0%, transparent 70%);
		border-radius: 50%;
	}

	.floor-rings::before,
	.floor-rings::after,
	.floor-rings::before::before {
		content: '';
		position: absolute;
		border: 2px solid rgba(64, 189, 245, 0.35);
		border-radius: 50%;
	}

	.floor-rings::before {
		top: 70px;
		left: 70px;
		right: 70px;
		bottom: 70px;
	}

	.floor-rings::after {
		top: 140px;
		left: 140px;
		right: 140px;
		bottom: 140px;
	}
	.module-1-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
	.module-2-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
	.module-3-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
	.module-4-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
	.module-5-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
	.module-6-content{
		display: flex;
		justify-content: space-between;
		padding-top: 5vh;
		align-items: center;
	}
</style>
//drillPlanCalendar
<template>
  <div class="custom-table-container">
    <table class="custom-table">
      <!-- 表头 -->
      <thead>
        <tr>
          <th>演练次数</th>
          <th>参与人数</th>
          <th>完成率</th>
        </tr>
      </thead>
      <!-- 表体 -->
      <tbody>
        <tr v-for="item in drillPlans" :key="item.id">
          <td>{{ item.typeNum }}(次/年)</td>
          <td>{{ item.participants }}(人)</td>
          <td>{{ item.completionRate }}%</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';

// 表格数据
const drillPlans = ref([
  { id: 1, typeNum: '2', participants: 120, completionRate: 95 },
  { id: 2, typeNum: '5', participants: 45, completionRate: 100 },
  { id: 3, typeNum: '7', participants: 200, completionRate: 60 },
  { id: 4, typeNum: '8', participants: 85, completionRate: 0 },
  { id: 5, typeNum: '2', participants: 30, completionRate: 0 },
]);
</script>

<style lang="css" scoped>
/* 容器样式 */
.custom-table-container {
  width: 12.7vw; /* 缩小后的宽度 */
  height: 13vh; /* 缩小后的高度 */
  margin-left: -0.5vw;
  border-radius: 8px;
  border: 1px solid #004a99;
  overflow: hidden; /* 隐藏外部滚动 */
}

/* 表格整体样式 */
.custom-table {
  width: 100%;
  height: 100%;
  border-collapse: collapse;
  font-size: calc(0.5vw); /* 缩小后的字体大小 */
  color: #ffffff;
  background-color: transparent;
  display: block; /* 改为块级显示 */
}

/* 表头固定 */
.custom-table thead {
  display: block;
  width: 100%;
  background: linear-gradient(180deg, rgba(22, 117, 196, 0) 0%, rgba(22, 117, 196, 0.7) 100%);
}

/* 表头行 */
.custom-table thead tr {
  display: table;
  width: 100%;
  table-layout: fixed;
}

/* 表头单元格 */
.custom-table th {
  text-align: center;
  padding: 2px 4px; /* 缩小内边距 */
  border-right: 1px solid #004a99;
  border-bottom: 1px solid #004a99;
  line-height: 1.2; /* 缩小行高 */
}

/* 表体滚动 */
.custom-table tbody {
  display: block;
  height: calc(100% - 10px); /* 减去表头高度 */
  overflow-y: auto;
  width: 100%;
}

/* 表体行 */
.custom-table tbody tr {
  display: table;
  width: 100%;
  table-layout: fixed;
}

/* 表体单元格 */
.custom-table td {
  padding: 2px 4px; /* 缩小内边距 */
  text-align: center;
  border-right: 1px solid #004a99;
  border-bottom: 1px solid #004a99;
  line-height: 1.2; /* 缩小行高 */
  white-space: nowrap; /* 防止文字换行 */
  overflow: hidden; /* 隐藏超出内容 */
  text-overflow: ellipsis; /* 超出部分显示省略号 */
}

/* 列宽设置 */
.custom-table th:nth-child(1),
.custom-table td:nth-child(1) {
  width: 40%;
}

.custom-table th:nth-child(2),
.custom-table td:nth-child(2) {
  width: 30%;
}

.custom-table th:nth-child(3),
.custom-table td:nth-child(3) {
  width: 30%;
}

/* 移除最后一列的右边框 */
.custom-table th:last-child,
.custom-table td:last-child {
  border-right: none;
}

/* 行 hover 效果 */
.custom-table tbody tr:hover td {
  background-color: rgba(0, 74, 153, 0.3);
}

/* 自定义滚动条样式 */
.custom-table tbody::-webkit-scrollbar {
  width: 6px;
}

.custom-table tbody::-webkit-scrollbar-track {
  background: rgba(0, 74, 153, 0.2);
  border-radius: 3px;
}

.custom-table tbody::-webkit-scrollbar-thumb {
  background: rgba(0, 74, 153, 0.6);
  border-radius: 3px;
}

.custom-table tbody::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 74, 153, 0.8);
}
</style>
// emergencySupport
<template>
  <div class="dashboard">
    <!-- 物资库存仪表盘 -->
    <div class="gauge-item">
      <div class="gauge-title">物资库存 (万件)</div>
      <div ref="materialGauge" class="gauge-chart"></div>
    </div>

    <!-- 队伍人数仪表盘 -->
    <div class="gauge-item">
      <div class="gauge-title">队伍人数 (人)</div>
      <div ref="teamGauge" class="gauge-chart"></div>
    </div>

    <!-- 预案完备率仪表盘 -->
    <div class="gauge-item">
      <div class="gauge-title">预案完备率 (%)</div>
      <div ref="planGauge" class="gauge-chart"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';

// 定义三个仪表盘的引用
const materialGauge = ref(null);
const teamGauge = ref(null);
const planGauge = ref(null);

// 定义图表实例变量
let materialChart = null;
let teamChart = null;
let planChart = null;

// 模拟的当前数据
const emergencyData = ref({
  materialStock: 7.8,    // 物资库存 (万件)
  teamPersonnel: 125,    // 队伍人数 (人)
  planCompletion: 92     // 预案完备率 (%)
});

// 初始化单个仪表盘
const initGauge = (refEl, value, max, unit = '', color = '#5470C6') => {
  if (!refEl.value) return;

  // 销毁已存在的实例
  if (refEl.value.chartInstance) {
    refEl.value.chartInstance.dispose();
  }

  const chart = echarts.init(refEl.value);
  refEl.value.chartInstance = chart;

  const option = {
    tooltip: {
      formatter: '{a} <br/>{b} : {c}' + unit
    },
    series: [
      {
        name: '当前值',
        type: 'gauge',
        startAngle: 90,
        endAngle: -270,
        pointer: {
          show: true,
          width: 3,
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 1,
            y2: 0,
            colorStops: [{
              offset: 0, color: '#fff'
            }, {
              offset: 1, color: color
            }]
          }
        },
        progress: {
          show: true,
          overlap: false,
          roundCap: true,
          clip: false,
          itemStyle: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 1,
              y2: 0,
              colorStops: [{
                offset: 0, color: '#36CFC9'
              }, {
                offset: 1, color: color
              }]
            }
          }
        },
        axisLine: {
          lineStyle: {
            width: 8,
            color: [[1, 'rgba(255, 255, 255, 0.2)']]
          }
        },
        splitLine: {
          show: true,
          distance: 0,
          length: 10,
          lineStyle: {
            color: '#fff',
            width: 1
          }
        },
        axisTick: {
          show: false
        },
        axisLabel: {
          show: false
        },
        title: {
          fontSize: 6,
          color: '#fff'
        },
        detail: {
          width: 60,
          height: 14,
          fontSize: 10,
		  top: 0,
          color: '#fff',
          borderColor: 'auto',
          formatter: '{value}' + unit,
          offsetCenter: [0, '-120%']
        },
        data: [
          {
            value: value,
            name: ''
          }
        ]
      }
    ]
  };

  chart.setOption(option);
  return chart;
};

// 初始化所有仪表盘
const initAllGauges = () => {
  // 物资库存仪表盘 (最大值设为10万件)
  materialChart = initGauge(materialGauge, emergencyData.value.materialStock, 10, '万件', '#5470C6');
  
  // 队伍人数仪表盘 (最大值设为200人)
  teamChart = initGauge(teamGauge, emergencyData.value.teamPersonnel, 200, '人', '#91CC75');
  
  // 预案完备率仪表盘 (最大值设为100%)
  planChart = initGauge(planGauge, emergencyData.value.planCompletion, 100, '%', '#FAC858');
};

// 更新仪表盘数据
const updateGaugeData = () => {
  if (materialChart) {
    materialChart.setOption({
      series: [{
        data: [{ value: emergencyData.value.materialStock }]
      }]
    });
  }
  
  if (teamChart) {
    teamChart.setOption({
      series: [{
        data: [{ value: emergencyData.value.teamPersonnel }]
      }]
    });
  }
  
  if (planChart) {
    planChart.setOption({
      series: [{
        data: [{ value: emergencyData.value.planCompletion }]
      }]
    });
  }
};

// 监听窗口大小变化,调整图表尺寸
const handleResize = () => {
  if (materialChart) materialChart.resize();
  if (teamChart) teamChart.resize();
  if (planChart) planChart.resize();
};

onMounted(() => {
  // 初始化图表
  initAllGauges();
  
  // 添加窗口大小变化监听
  window.addEventListener('resize', handleResize);
  
  // 模拟数据更新 (每5秒更新一次)
  // setInterval(() => {
  //   emergencyData.value.materialStock = (Math.random() * 4 + 6).toFixed(1);
  //   emergencyData.value.teamPersonnel = Math.floor(Math.random() * 50 + 100);
  //   emergencyData.value.planCompletion = Math.floor(Math.random() * 10 + 85);
  //   updateGaugeData();
  // }, 5000);
});

onUnmounted(() => {
  // 移除事件监听
  window.removeEventListener('resize', handleResize);
  
  // 销毁图表实例
  if (materialChart) materialChart.dispose();
  if (teamChart) teamChart.dispose();
  if (planChart) planChart.dispose();
});
</script>

<style scoped>
.dashboard {
  display: flex;
  justify-content: space-around;
  align-items: center;
  width: 13vw;
  height: 13vh;
  margin-left: -0.5vw;
}

.gauge-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 30%;
 /* height: 100%; */
}

.gauge-title {
  font-size: calc(0.5vw);
  color: #fff;
/*  margin-bottom: 10px; */
  text-align: center;
}

.gauge-chart {
  width: 4vw;
  height: 10vh;
}
</style>
// eventTypeWordCloud
<template>
  <div ref="chartRef" class="chart"></div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

export default {
  name: 'EventTypeBarChart',
  setup() {
    const chartRef = ref(null)
    let chartInstance = null
    let resizeHandler = null

    // 事件数据(与原词云图数据一致)
    const eventData = ref([
      { name: '洪水', value: 156 },
      { name: '暴雨', value: 142 },
      { name: '台风', value: 123 },
      { name: '山体滑坡', value: 98 },
      { name: '水质污染', value: 87 },
      { name: '设备故障', value: 76 },
      { name: '电力中断', value: 65 },
      { name: '交通事故', value: 54 },
      { name: '火灾', value: 43 },
      { name: '化学品泄漏', value: 32 },
      { name: '地震', value: 21 },
      { name: '疫情', value: 19 }
    ])

    // 初始化横向条形图
    const initChart = () => {
      if (!chartRef.value) return

      // 销毁旧实例
      if (chartInstance) {
        chartInstance.dispose()
      }

      chartInstance = echarts.init(chartRef.value)

      // 颜色配置(沿用原词云图的颜色方案)
      const colors = [
        '#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae',
        '#749f83', '#ca8622', '#bda29a', '#6e7074', '#546570',
        '#c4ccd3', '#f05b72'
      ]

      const option = {
        // 提示框配置
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow',
            shadowStyle: {
              color: 'rgba(0, 0, 0, 0.05)'
            }
          },
          formatter: '{b}: {c}次',
          backgroundColor: 'rgba(255, 255, 255, 0.9)',
          borderColor: '#ddd',
          borderWidth: 1,
          textStyle: {
            color: '#fff'
          }
        },

        // 网格布局
        grid: {
          left: '0%',  // 左侧留出更多空间显示事件名称
          right: '10%',
          top: '10%',
          bottom: '10%',
          containLabel: true
        },

        // Y轴(事件类型,类目轴)
        yAxis: {
          type: 'category',
          data: eventData.value.map(item => item.name),
          axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
          axisTick: {
            show: false
          },
          axisLabel: {
            color: '#fff',
            fontSize: 8,
          },
          // 事件名称右对齐
          align: 'right'
        },

        // X轴(事件数量,数值轴)
        xAxis: {
          type: 'value',
          axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
          axisTick: {
            show: false
          },
		  splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
          axisLabel: {
            color: '#fff',
            fontSize: 8
          }
        },

        // 系列配置(横向条形图)
        series: [{
          name: '事件数量',
          type: 'bar',
          data: eventData.value.map(item => item.value),
          // 条形图样式
          barWidth: 5,
          borderRadius: [0, 4, 4, 0],  // 只给右侧加圆角
          // 每个条形使用不同颜色
          itemStyle: {
            color: (params) => colors[params.dataIndex % colors.length],
            // hover时的阴影效果
            emphasis: {
              shadowBlur: 10,
              shadowColor: 'rgba(0, 0, 0, 0.3)'
            }
          },
          // 数值标签(显示在条形图右侧)
          label: {
            show: true,
            position: 'right',
            distance: 5,
            color: '#fff',
            fontSize: 7,
            formatter: '{c}次'
          }
        }]
      }

      chartInstance.setOption(option)
    }

    onMounted(() => {
      // 延迟初始化,确保DOM渲染完成
      setTimeout(() => {
        initChart()
      }, 100)

      // 监听窗口大小变化
      resizeHandler = () => {
        if (chartInstance) {
          chartInstance.resize()
        }
      }
      window.addEventListener('resize', resizeHandler)
    })

    onUnmounted(() => {
      // 清理事件监听和图表实例
      window.removeEventListener('resize', resizeHandler)
      if (chartInstance) {
        chartInstance.dispose()
      }
    })

    return {
      chartRef
    }
  }
}
</script>

<style lang="scss" scoped>
.chart {
  width: 13vw;
  height: 13vh;
  margin-left: -1vw;
}
</style>
// dronePatrol
<template>
	<div class="card-count">
		<span>无人机巡航</span>
	</div>
	<div class="monitor-stats">	
		<div class="stat-item">
			<!-- <img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" /> -->
			<img src="@/assets/drone/task/01@1x.png" alt="" class="stat-item-img"/>
			<div class="stat-text">
				<p>机场个数:</p>
				<p class="card-num">10个</p>
			</div>
		</div>
		<div class="stat-item">
			<!-- <img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" /> -->
			<img src="@/assets/drone/task/04@1x.png" alt="" class="stat-item-img"/>
			<div class="stat-text">
				<p>巡航里程:</p>
				<p class="card-num">100km</p>
			</div>
		</div>
		<div class="stat-item">
			<!-- <img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" /> -->
			<img src="@/assets/drone/task/02@1x.png" alt="" class="stat-item-img"/>
			<div class="stat-text">
				<p>飞行时间:</p>
				<p class="card-num">3h</p>
			</div>
		</div>
		<div class="stat-item">
			<!-- <img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" /> -->
			<img src="@/assets/drone/task/03@1x.png" alt="" class="stat-item-img"/>
			<div class="stat-text">
				<p>飞行次数:</p>
				<p class="card-num">6次</p>
			</div>
		</div>
	</div>
</template>

<style lang="scss" scoped>
	.card-count {
		background-image: url("@/assets/drone/device/bg01@1x.png");
		background-size: 100% 100%;
		background-repeat: no-repeat;
		height: 2vh;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: calc(0.6vw);
	
		img {
			margin-right: 0.5vw;
		}
	}
	.card-num {
		color: #17ffe0;
		font-weight: bold;
	}
	.stat-item {
		position: relative;
		height: calc(100% - 4vh);
		font-size: calc(0.43vw);
		width: 48%;
		margin-top: 0.5vh;
	}
	
	.stat-item-img {
		width: 100%;
		height: 4vh;
	}
	.stat-text {
		color: #e0efff;
		display: flex;
		align-items: center;
		margin-left: 2vw;
		position: absolute;
		top: 15%;
	}
	.monitor-stats {
		display: flex;
		// height: calc(100% - 17vh);
		flex-wrap: wrap;
		margin-top: 0.5vh;
		overflow-y: auto;
		justify-content: space-between;
	}
</style>
// engineering
<template>
	<div class="card-menu">
		<div class="menu-div">
			<div class="menu-title">
				合同履约率
			</div>
			<div class="menu-num" style="color:#FFF000">
				80%
			</div>
			<div class="menu-img">
				<img src="@/assets/drone/video/设备@1x.png" alt="" class="img-top" />
				<img src="@/assets/drone/video/底座.png" alt="" class="img-footer" />
			</div>
		</div>
		<div class="menu-div1">
			<div class="menu-title">
				质量合格率
			</div>
			<div class="menu-num" style="color:#17FFE0">
				95%
			</div>
			<div class="menu-img">
				<img src="@/assets/drone/video/在线@1x.png" alt="" class="img-top" />
				<img src="@/assets/drone/video/底座.png" alt="" class="img-footer" />
			</div>
		</div>
		<div class="menu-div2">
			<div class="menu-title">
				安全违规次数
			</div>
			<div class="menu-num" style="color:#FFFFFF">
				2次
			</div>
			<div class="menu-img">
				<img src="@/assets/drone/video/离线@1x.png" alt="" class="img-top" />
				<img src="@/assets/drone/video/底座.png" alt="" class="img-footer" />
			</div>
		</div>
	</div>
</template>
<style lang="scss" scoped>
	.menu-div {
		background: url("@/assets/drone/video/Rectangle 961.png");
		background-size: 100% 100%;
		width: 30%;
		text-align: center;
	}
	
	.menu-div1 {
		background: url("@/assets/drone/video/Rectangle 959.png");
		background-size: 100% 100%;
		width: 30%;
		text-align: center;
	}
	
	.menu-div2 {
		background: url("@/assets/drone/video/Rectangle 960.png");
		background-size: 100% 100%;
		width: 30%;
		text-align: center;
	}
	.card-menu {
		display: flex;
		justify-content: space-between;
		width: 100%;
		height: 13vh;
	}
	.menu-title{
		font-size: calc(0.5vw);
		margin-top: 2vh;
	}
	.menu-num{
		margin-top: 0.5vh;
		font-size: calc(0.65vw);
	}
	.img-top{
		width: 1.5vw;
		height: 3vh;
	}
	.img-footer{
		width: 3vw;
		height: 5vh;
		margin-top: -2vh;
	}
</style>
// equipmentHealth
<template>
	<div ref="durationChartRef" class="echart-container"></div>
</template>

<script setup>
	import {
		ref,
		onMounted
	} from 'vue';
	import * as echarts from "echarts";

	const durationChartRef = ref(null);
	let durationChart = null; // 图表实例

	// 模拟的设备健康状态数据
	const deviceHealthData = ref([{
			name: '台儿庄泵站',
			status: 92
		}, // 健康
		{
			name: '二级坝泵站',
			status: 85
		}, // 良好
		{
			name: '八里湾泵站',
			status: 78
		}, // 一般
		{
			name: '东湖水库',
			status: 95
		} // 优秀
	]);

	// 初始化设备健康状态图表
	const initDurationChart = () => {
		if (durationChartRef.value && !durationChart) {
			durationChart = echarts.init(durationChartRef.value);

			// 定义颜色渐变(从绿色到浅绿色,象征健康状态)
			const colorStops = [{
					offset: 0,
					color: "rgba(0, 200, 100, 1)"
				},
				{
					offset: 1,
					color: "rgba(100, 255, 150, 0.6)"
				}
			];

			const option = {
				// 1. 鼠标悬停提示框配置
				tooltip: {
					trigger: "axis",
					axisPointer: {
						type: "shadow",
					},
					backgroundColor: "rgba(10, 30, 60, 0.9)",
					borderColor: "#00a8ff",
					borderWidth: 1,
					textStyle: {
						color: "#fff"
					},
					// 自定义提示内容:显示站点名称和健康状态值
					formatter: "{b}: {c} 分",
				},
				legend: {
					textStyle: {
						color: 'rgba(255, 255, 255, 0.8)',
						fontSize: 8
					},
					// top: 0,
					itemWidth: 12, // 图例标记的宽度
					itemHeight: 8, // 图例标记的高度
				},
				// 2. 图表网格布局
				grid: {
					top: "20%",
					bottom: "25%",
					left: "12%",
					right: "5%"
				},
				// 3. X轴配置(设备站点名称)
				xAxis: {
					type: "category",
					// 使用模拟数据中的站点名称
					data: deviceHealthData.value.map((item) => item.name),
					axisLine: {
						lineStyle: {
							color: "#00a8ff"
						}
					},
					axisTick: {
						show: false
					},
					axisLabel: {
						color: "#fff",
						fontSize: 8
					},
				},
				// 4. Y轴配置(健康状态值,范围0-100)
				yAxis: {
					name: "健康状态",
					type: "value",
					axisLine: {
						lineStyle: {
							color: "#fff",
							fontSize: 8
						}
					},
					nameTextStyle: {
						color: '#fff',
						fontSize: 8,
					},
					axisTick: {
						show: false
					},
					axisLabel: {
						color: "#fff",
						// 格式化Y轴标签,添加百分号
						formatter: '{value} %',
						fontSize: 8
					},
					splitLine: {
						lineStyle: {
							color: "rgba(0, 168, 255, 0.3)"
						}
					},
				},
				// 5. 系列数据配置(柱状图)
				series: [{
					name: "健康状态",
					type: "bar",
					// 使用模拟数据中的健康状态值
					data: deviceHealthData.value.map((item) => item.status),
					barWidth: 15, // 调整柱子宽度
					itemStyle: {
						// 应用颜色渐变
						color: new echarts.graphic.LinearGradient(0, 0, 0, 1, colorStops),
						barBorderRadius: [4, 4, 0, 0], // 圆角设置
					},
					// 6. 鼠标悬停时柱子高亮效果
					emphasis: {
						itemStyle: {
							// 悬停时使用更亮的渐变色
							color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
									offset: 0,
									color: "rgba(50, 255, 150, 1)"
								},
								{
									offset: 1,
									color: "rgba(150, 255, 200, 0.8)"
								},
							]),
						},
					},
				}],
			};

			durationChart.setOption(option);
		}
	};

	// 窗口大小变化处理
	const handleResize = () => {
		durationChart && durationChart.resize();
	};

	// 生命周期管理
	onMounted(() => {
		initDurationChart();
		window.addEventListener("resize", handleResize);
	});

	// 组件卸载时销毁图表
	onUnmounted(() => {
		window.removeEventListener("resize", handleResize);
		durationChart && durationChart.dispose();
	});
</script>

<style scoped>
	/* 确保图表容器有明确的宽高 */
	.echart-container {
		width: 13vw;
		height: 13vh;
		margin-left: -0.5vw;
	}
</style>
// floodProtectionChart
<template>
	<div ref="chartRef" class="chart"></div>
</template>

<script>
	import {
		ref,
		onMounted,
		onUnmounted
	} from 'vue'
	import * as echarts from 'echarts'

	export default {
		name: 'FloodProtectionChart',
		setup() {
			const chartRef = ref(null)
			let chartInstance = null

			const protectionData = ref({
				teams: {
					total: 245,
					categories: [{
							name: '综合防汛队伍',
							value: 156
						},
						{
							name: '专业应急队伍',
							value: 89
						}
					]
				},
				materials: {
					total: 38560,
					categories: [{
							name: '救生衣',
							value: 12500
						},
						{
							name: '沙袋',
							value: 20000
						},
						{
							name: '水泵',
							value: 1560
						},
						{
							name: '发电机',
							value: 320
						},
						{
							name: '应急照明',
							value: 850
						},
						{
							name: '其他物资',
							value: 3330
						}
					]
				}
			})

			const initChart = () => {
				if (chartRef.value) {
					chartInstance = echarts.init(chartRef.value)

					const option = {
						tooltip: {
							trigger: 'axis',
							axisPointer: {
								type: 'shadow'
							}
						},
						legend: {
							data: ['队伍数量', '物资数量'],
							textStyle: {
								color: 'rgba(255, 255, 255, 0.8)',
								fontSize: 8
							},
							itemWidth: 12, // 图例标记的宽度
							itemHeight: 8, // 图例标记的高度
							itemGap: 8, // 图例项之间的间距
							top: 0
						},
						grid: {
							top: '15%',
							left: '0%',
							right: '5%',
							bottom: '10%',
							containLabel: true
						},
						xAxis: {
							type: 'value',
							axisLine: {
								lineStyle: {
									color: '#fff'
								}
							},
							axisLabel: {
								color: '#fff',
								formatter: '{value}',
								fontSize: 8
							},
							splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
						},
						yAxis: {
							type: 'category',
							data: protectionData.value.teams.categories.map(t => t.name)
								.concat(protectionData.value.materials.categories.map(m => m.name)),
							axisLine: {
								lineStyle: {
									color: '#fff'
								}
							},
							axisLabel: {
								color: '#fff',
								formatter: '{value}',
								fontSize: 8
							},
							splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
						},
						series: [{
								name: '队伍数量',
								type: 'bar',
								data: protectionData.value.teams.categories.map(t => t.value)
									.concat(new Array(protectionData.value.materials.categories.length).fill(
										0)),
								itemStyle: {
									color: '#5470c6'
								}
							},
							{
								name: '物资数量',
								type: 'bar',
								data: new Array(protectionData.value.teams.categories.length).fill(0)
									.concat(protectionData.value.materials.categories.map(m => m.value)),
								itemStyle: {
									color: '#91cc75'
								}
							}
						]
					}

					chartInstance.setOption(option)
				}
			}

			onMounted(() => {
				initChart()
				window.addEventListener('resize', () => {
					if (chartInstance) chartInstance.resize()
				})
			})

			onUnmounted(() => {
				if (chartInstance) {
					chartInstance.dispose()
				}
			})

			return {
				chartRef
			}
		}
	}
</script>
<style scoped>
	.chart {
		width: 13vw;
		height: 13vh;
	}
</style>
// rainMonitorChart
<template>
  <div ref="chartRef" class="chart"></div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

export default {
  name: 'RainMonitorChart',
  setup() {
    const chartRef = ref(null)
    let chartInstance = null

    const rainData = ref({
      total: 156,
      categories: [
        { name: '特大暴雨', value: 3, threshold: '≥250mm', color: '#C8102E' },
        { name: '大暴雨', value: 12, threshold: '100-250mm', color: '#FF3B30' },
        { name: '暴雨', value: 25, threshold: '50-100mm', color: '#FF9500' },
        { name: '大雨', value: 38, threshold: '25-50mm', color: '#FFCC00' },
        { name: '中雨', value: 45, threshold: '10-25mm', color: '#CCFF00' },
        { name: '小雨', value: 33, threshold: '0.1-10mm', color: '#98FB98' }
      ]
    })

    const initChart = () => {
      if (chartRef.value) {
        chartInstance = echarts.init(chartRef.value)
        
        const option = {
          // 提示框样式优化
          tooltip: {
            trigger: 'item',
            backgroundColor: 'rgba(255, 255, 255, 0.9)',
            borderColor: '#fff',
            borderWidth: 1,
            padding: [8, 12],
            textStyle: { color: '#fff' },
            formatter: function(params) {
              const data = rainData.value.categories.find(item => item.name === params.name);
              return `雨情监测 <br/>${params.name}:${params.value}次 (${params.percent.toFixed(1)}%) <br/>阈值:${data.threshold}`;
            }
          },
          series: [
            {
              name: '雨情监测',
              type: 'pie',
              radius: ['40%', '60%'], // 调整环形比例
              center: ['50%', '50%'], // 居中布局
              avoidLabelOverlap: true,
              itemStyle: {
                borderRadius: 8, // 圆角更精致
                borderWidth: 1
              },
              label: {
                show: true, // 显示外部标签
                position: 'outside',
                formatter: '{b}: {c}', // 标签内容:名称+数值
                textStyle: {
                  color: '#fff',
                  fontSize: 10
                }
              },
              labelLine: {
                show: true, // 显示引导线
                length: 10,
                length2: 20,
                lineStyle: {
                  color: '#ccc'
                }
              },
              emphasis: {
                scale: true, //  hover时放大效果
                itemStyle: {
                  shadowBlur: 10, // 阴影增强立体感
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.3)'
                }
              },
              data: rainData.value.categories.map(item => ({
                value: item.value,
                name: item.name,
                itemStyle: { color: item.color }
              }))
            }
          ]
        }
        
        chartInstance.setOption(option)
      }
    }

    onMounted(() => {
      initChart()
      window.addEventListener('resize', () => {
        if (chartInstance) chartInstance.resize()
      })
    })

    onUnmounted(() => {
      if (chartInstance) {
        chartInstance.dispose()
      }
    })

    return {
      chartRef,
      rainData
    }
  }
}
</script>
<style scoped>
	.chart{
		width: 13vw;
		height: 13vh;
	}
</style>
// waterMonitorChart
<template>
  <div ref="chartRef" class="chart"></div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

export default {
  name: 'WaterMonitorChart',
  setup() {
    const chartRef = ref(null)
    let chartInstance = null

    const waterData = ref({
      stations: [
        { name: '长江站', warningLevel: 12.5, data: [11.2, 11.5, 11.8, 12.1, 12.4, 12.6, 12.3] },
        { name: '黄河站', warningLevel: 8.7, data: [7.2, 7.5, 7.9, 8.2, 8.5, 8.6, 8.4] },
        { name: '淮河站', warningLevel: 10.3, data: [9.1, 9.3, 9.6, 9.9, 10.1, 10.2, 10.0] }
      ],
      times: ['06:00', '09:00', '12:00', '15:00', '18:00', '21:00', '00:00']
    })

    const initChart = () => {
      if (chartRef.value) {
        chartInstance = echarts.init(chartRef.value)
        
        const series = waterData.value.stations.map(station => ({
          name: station.name,
          type: 'line',
          data: station.data,
          markLine: {
            data: [
              {
                yAxis: station.warningLevel,
                name: '警戒水位',
                lineStyle: {
                  color: '#ff0000',
                  type: 'dashed'
                },
                label: {
                  formatter: station.name + '警戒线',
                  position: 'insideEndTop',
				  fontSize:10,
                }
              }
            ]
          }
        }))

        const option = {
          tooltip: {
            trigger: 'axis'
          },
		  legend: {
		  	data: waterData.value.stations.map(s => s.name), // 图例数据(区域名称)
		  	textStyle: { color: 'rgba(255, 255, 255, 0.8)', fontSize: 8 },
		  	top: 0,
			itemWidth: 12, // 图例标记的宽度
			itemHeight: 8, // 图例标记的高度
		  },
          grid: {
			top: '20%',
            left: '3%',
            right: '10%',
            bottom: '3%',
            containLabel: true
          },
          xAxis: {
            type: 'category',
            boundaryGap: false,
            data: waterData.value.times,
			// axisLine: {
			//   lineStyle: {
			//     color: '#fff'
			//   }
			// },
			axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
			axisLabel: {
			  color: '#fff',
			  formatter: '{value}',
			  fontSize:8
			},
          },
          yAxis: {
            type: 'value',
            name: '水位(m)',
			nameTextStyle: { color: '#fff',fontSize: 8, },
			axisLine: {
			  lineStyle: {
			    color: '#fff',
			  }
			},
			splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
			axisLabel: {
			  color: '#fff',
			  formatter: '{value}',
			  fontSize:8
			},
          },
          series
        }
        
        chartInstance.setOption(option)
      }
    }

    onMounted(() => {
      initChart()
      window.addEventListener('resize', () => {
        if (chartInstance) chartInstance.resize()
      })
    })

    onUnmounted(() => {
      if (chartInstance) {
        chartInstance.dispose()
      }
    })

    return {
      chartRef
    }
  }
}
</script>
<style scoped>
	.chart{
		width: 13vw;
		height: 13vh;
	}
</style>
// safetyMonitoring
<template>
	<div>
		<div ref="lineChartRef" class="echart-containers"></div>
	</div>
</template>

<script setup>
import * as echarts from "echarts";
import { ref, onMounted, onUnmounted } from "vue";

// 图表DOM引用
const lineChartRef = ref(null);
// 图表实例
let lineChart = null;

// 柱状图配置
const option = {
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'shadow'
    }
  },
  xAxis: {
    type: 'category',
    data: ['渗压告警数量', '应变告警数量', '测缝告警数量', '位移告警数量'],
    axisLine: {
      lineStyle: {
        color: 'rgba(255, 255, 255, 0.2)'
      }
    },
    axisLabel: {
      color: '#fff',
	  fontSize:6.7,
    },
	splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
  },
  yAxis: {
    type: 'value',
    name: '亿m³',
    min: 0,
    max: 500,
    interval: 100,
    axisLine: {
      lineStyle: {
        color: 'rgba(255, 255, 255, 0.1)'
      }
    },
    axisLabel: {
      color: '#fff',
      formatter: '{value}',
	  fontSize:8
    },
    splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
  },
  series: [
    {
      name: '储存量',
      type: 'bar',
      data: [100, 200, 300, 400],
      itemStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: '#83bff6' },
          { offset: 0.5, color: '#188df0' },
          { offset: 1, color: '#188df0' }
        ])
      },
      emphasis: {
        itemStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: '#2378f7' },
            { offset: 0.7, color: '#2378f7' },
            { offset: 1, color: '#83bff6' }
          ])
        }
      },
      barWidth: '40%'
    }
  ],
  grid: {
    left: '0%',
    right: '4%',
    bottom: '0%',
	top: '10%',
    containLabel: true
  }
};

// 初始化图表
const initChart = () => {
  if (lineChartRef.value) {
    // 初始化echarts实例
    lineChart = echarts.init(lineChartRef.value);
    // 设置配置项
    lineChart.setOption(option);
    
    // 添加窗口大小变化监听,实现自适应
    window.addEventListener('resize', handleResize);
  }
};

// 处理窗口大小变化
const handleResize = () => {
  if (lineChart) {
    lineChart.resize();
  }
};

// 组件挂载时初始化图表
onMounted(() => {
  // 使用setTimeout确保DOM已经渲染
  setTimeout(() => {
    initChart();
  }, 0);
});

// 组件卸载时清理资源
onUnmounted(() => {
  if (lineChart) {
    window.removeEventListener('resize', handleResize);
    lineChart.dispose();
    lineChart = null;
  }
});
</script>

<style scoped>
.echart-containers {
  width: 13vw;
  height: 12vh;
  margin-left: -0.5vw;
}
</style>
// warningStatistics
<template>
	<div class="card-count">
		<span>危险源总计:</span>
		<span class="card-num">21</span>
	</div>
	<div class="monitor-stats">
		<div class="stat-item">
			<img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" />
			<div class="stat-text">
				<p>低风险:</p>
				<p class="card-num">10</p>
			</div>
		</div>
		<div class="stat-item">
			<img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" />
			<div class="stat-text">
				<p>一般风险:</p>
				<p class="card-num">2</p>
			</div>
		</div>
		<div class="stat-item">
			<img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" />
			<div class="stat-text">
				<p>较大风险:</p>
				<p class="card-num">3</p>
			</div>
		</div>
		<div class="stat-item">
			<img src="@/assets/drone/device/wait.png" alt="" class="stat-item-img" />
			<div class="stat-text">
				<p>重大风险:</p>
				<p class="card-num">6</p>
			</div>
		</div>
	</div>
</template>

<style lang="scss" scoped>
	.card-count {
		background-image: url("@/assets/drone/device/bg01@1x.png");
		background-size: 100% 100%;
		background-repeat: no-repeat;
		height: 2vh;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: calc(0.6vw);
	
		img {
			margin-right: 0.5vw;
		}
	}
	.card-num {
		color: #F17079;
		font-weight: bold;
	}
	.stat-item {
		position: relative;
		height: calc(100% - 4vh);
		font-size: calc(0.5vw);
		width: 48%;
		margin-top: 0.5vh;
	}
	
	.stat-item-img {
		width: 100%;
	}
	.stat-text {
		color: #e0efff;
		display: flex;
		align-items: center;
		margin-left: 2vw;
		position: absolute;
		top: 5%;
	}
	.monitor-stats {
		display: flex;
		// height: calc(100% - 17vh);
		flex-wrap: wrap;
		margin-top: 0.5vh;
		justify-content: space-between;
	}
</style>
// waterStorage
<template>
	<div>
		<div ref="pieChartRef" class="echart-container"></div>
	</div>
</template>

<script setup>
import * as echarts from "echarts";
import { ref, onMounted, onUnmounted } from "vue";

// 图表DOM引用
const pieChartRef = ref(null);
// 图表实例
let pieChart = null;

// 饼图配置
const option = {
  tooltip: {
    trigger: 'item',
    textStyle: {
      color: '#fff' // 提示文字颜色设为白色
    }
  },
  legend: {
    left: '60%', // 放在右侧
    top: 'center', // 垂直居中
    orient: 'vertical', // 垂直排列
    textStyle: {
      color: '#fff', // 字体颜色设为白色
      fontSize: 7.5 // 调小字体大小
    },
    itemWidth: 7.5, // 图例标记的宽度
    itemHeight: 7.5, // 图例标记的高度
    itemGap: 7.5 // 图例每项之间的间隔
  },
  series: [
    {
      name: 'Access From',
      type: 'pie',
      radius: ['30%', '70%'],
      center: ['30%', '50%'], // 调整饼图位置,为右侧图例留出空间
      avoidLabelOverlap: false,
      label: {
        show: false,
        position: 'center'
      },
      emphasis: {
        label: {
          show: true,
          fontSize: 20,
          fontWeight: 'bold'
        }
      },
      labelLine: {
        show: false
      },
      data: [
        { value: 1048, name: '东平湖蓄水' },
        { value: 735, name: '上级湖蓄水' },
        { value: 580, name: '下级湖蓄水' },
        { value: 484, name: '大屯水库蓄水' },
        { value: 300, name: '东湖水库蓄水' },
		{ value: 300, name: '双王城水库蓄水' }
      ]
    }
  ]
};

// 初始化图表
const initChart = () => {
  if (pieChartRef.value) {
    // 初始化echarts实例
    pieChart = echarts.init(pieChartRef.value);
    // 设置配置项
    pieChart.setOption(option);
    
    // 添加窗口大小变化监听,实现自适应
    window.addEventListener('resize', handleResize);
  }
};

// 处理窗口大小变化
const handleResize = () => {
  if (pieChart) {
    pieChart.resize();
  }
};

// 组件挂载时初始化图表
onMounted(() => {
  // 使用nextTick确保DOM已经渲染
  setTimeout(() => {
    initChart();
  }, 0);
});

// 组件卸载时清理资源
onUnmounted(() => {
  if (pieChart) {
    window.removeEventListener('resize', handleResize);
    pieChart.dispose();
    pieChart = null;
  }
});
</script>

<style scoped>
.echart-container {
  width: 10vw;
  height: 11vh; 
  /* min-height: 300px; */
}
</style>
// waterDemandChart
<template>
	<div class="water-supply-stats">
		<div class="chart-container">
			<div ref="chartRef" class="chart"></div>
		</div>
	</div>
</template>

<script setup lang="ts">
	import { ref, onMounted, onUnmounted, computed } from 'vue';
	import * as echarts from 'echarts';

	// 模拟数据:年份、区域、供水量(万m³)
	const supplyData = [
		{ year: 2020, region: '济南', supply: 105.3 },
		{ year: 2020, region: '青岛', supply: 260.8 },
		{ year: 2020, region: '淄博', supply: 215.2 },
		{ year: 2020, region: '枣庄', supply: 240.5 },
		{ year: 2021, region: '济南', supply: 110.5 },
		{ year: 2021, region: '青岛', supply: 265.2 },
		{ year: 2021, region: '淄博', supply: 220.8 },
		{ year: 2021, region: '枣庄', supply: 245.3 },
		{ year: 2022, region: '济南', supply: 130.8 },
		{ year: 2022, region: '青岛', supply: 278.5 },
		{ year: 2022, region: '淄博', supply: 232.4 },
		{ year: 2022, region: '枣庄', supply: 156.7 },
		{ year: 2023, region: '济南', supply: 256.3 },
		{ year: 2023, region: '青岛', supply: 195.8 },
		{ year: 2023, region: '淄博', supply: 145.2 },
		{ year: 2023, region: '枣庄', supply: 172.5 },
	];

	// 响应式状态:选中的年份(用于筛选总供水量)
	const selectedYear = ref<number | 'all'>('all');

	// 提取唯一的年份和区域
	const years = Array.from(new Set(supplyData.map(item => item.year))).sort((a, b) => a - b);
	const regions = Array.from(new Set(supplyData.map(item => item.region))).sort();

	// 计算总供水量(根据选中的年份筛选)
	const totalSupply = computed(() => {
		if (selectedYear.value === 'all') {
			// 所有年份的总供水量
			return supplyData.reduce((sum, item) => sum + item.supply, 0).toFixed(1);
		} else {
			// 选中年份的总供水量
			return supplyData
				.filter(item => item.year === selectedYear.value)
				.reduce((sum, item) => sum + item.supply, 0)
				.toFixed(1);
		}
	});

	// ECharts 实例引用
	const chartRef = ref<HTMLDivElement | null>(null);
	let chartInstance : echarts.ECharts | null = null;

	// 初始化折线图
	const initChart = () => {
		if (!chartRef.value) return;

		// 销毁已存在的实例
		if (chartInstance) {
			chartInstance.dispose();
		}

		// 创建新实例
		chartInstance = echarts.init(chartRef.value);

		// 处理图表数据:按区域分组,每个区域对应一条折线
		const seriesData = regions.map(region => {
			return {
				name: region,
				type: 'line',
				smooth: true, // 平滑折线
				data: years.map(year => {
					const item = supplyData.find(i => i.year === year && i.region === region);
					return item ? item.supply : 0;
				})
			};
		});

		// 图表配置项
		const option = {
			tooltip: {
				trigger: 'axis', // 触发方式:按轴触发
				backgroundColor: 'rgba(0, 30, 60, 0.8)',
				borderColor: 'rgba(34, 197, 255, 0.8)',
				textStyle: { color: '#fff', fontSize: 7 },
				formatter: '{b}年<br/>{a}:{c} 万m³' // 自定义提示框格式
			},
			legend: {
				data: regions, // 图例数据(区域名称)
				textStyle: { color: 'rgba(255, 255, 255, 0.8)', fontSize: 8 },
				top: 0,
				itemWidth: 12, // 图例标记的宽度
				itemHeight: 8, // 图例标记的高度
			},
			grid: {
				top: '25%',
				left: '3%',
				right: '0%',
				bottom: '2%',
				containLabel: true
			},
			xAxis: {
				type: 'category',
				data: years, // x轴数据(年份)
				axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
				splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
				axisLabel: {
					color: '#fff',
					fontSize: 8,
				}
			},
			yAxis: {
				type: 'value',
				name: '需水量(万m³)', // y轴名称
				nameTextStyle: { color: '#fff',fontSize: 8, },
				axisLabel: {
					color: '#fff',
					fontSize: 8,
				},
				axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
				splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } }
			},
			series: seriesData,
			color: ['#3498db', '#2ecc71', '#e74c3c', '#f39c12'] // 折线颜色(区分不同区域)
		};

		// 应用配置项
		chartInstance.setOption(option);
	};

	// 监听窗口大小变化,调整图表尺寸
	const handleResize = () => {
		chartInstance?.resize();
	};

	// 生命周期钩子
	onMounted(() => {
		initChart();
		window.addEventListener('resize', handleResize);
	});

	onUnmounted(() => {
		window.removeEventListener('resize', handleResize);
		chartInstance?.dispose(); // 销毁图表实例,释放资源
	});
</script>

<style scoped>
	.water-supply-stats {}

	.total-value {
		color: #fff;
		font-size: calc(0.7vw);
		font-weight: 600;
	}

	.chart-container {
		height: 12vh;
		position: relative;
	}

	.chart {
		width: 100%;
		height: 100%;
	}

	.chart-header {
		font-size: calc(0.8vw);
		height: 2vh;
		color: #30B5DE;
	}
</style>
// waterFlowChart
<template>
  <div class="custom-table-container">
    <table class="custom-table">
      <thead>
        <tr>
          <th>区域</th>
          <th>时间</th>
          <th>供水量</th>
        </tr>
      </thead>
      <!-- 表体 -->
      <tbody>
        <tr v-for="item in drillPlans" :key="item.id">
          <td>{{ item.name }}</td>
          <td>{{ item.time }}</td>
          <td>{{ item.completionRate }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';

// 表格数据
const drillPlans = ref([
  { id: 1, name: '省界调水量', time: '10-11', completionRate: 95 },
  { id: 2, name: '入下级湖水量', time: '10-13', completionRate: 100 },
  { id: 3, name: '入上级湖水量', time: '10-05', completionRate: 60 },
  { id: 4, name: '出上级湖水量', time: '10-10', completionRate: 0 },
  { id: 5, name: '入东平湖水量', time: '10-30', completionRate: 0 },
  { id: 6, name: '渠首闸水量', time: '10-30', completionRate: 0 },
  { id: 7, name: '出湖调水量', time: '10-30', completionRate: 0 },
  { id: 8, name: '南四湖进水量', time: '10-15', completionRate: 85 },
  { id: 9, name: '北四湖出水量', time: '10-20', completionRate: 70 },
  { id: 10, name: '中段调水量', time: '10-25', completionRate: 45 },
]);
</script>

<style lang="css" scoped>
/* 容器样式 */
.custom-table-container {
  width: 13vw; /* 缩小后的宽度 */
  height: 13vh; /* 缩小后的高度 */
  margin-left: -0.5vw;
  border-radius: 8px;
  border: 1px solid #004a99;
  overflow: hidden; /* 隐藏外部滚动 */
}

/* 表格整体样式 */
.custom-table {
  width: 100%;
  height: 100%;
  border-collapse: collapse;
  font-size: calc(0.5vw); /* 缩小后的字体大小 */
  color: #ffffff;
  background-color: transparent;
  display: block; /* 改为块级显示 */
}

/* 表头固定 */
.custom-table thead {
  display: block;
  width: 100%;
  background: linear-gradient(180deg, rgba(22, 117, 196, 0) 0%, rgba(22, 117, 196, 0.7) 100%);
}

/* 表头行 */
.custom-table thead tr {
  display: table;
  width: 100%;
  table-layout: fixed;
}

/* 表头单元格 */
.custom-table th {
  text-align: center;
  padding: 2px 4px; /* 缩小内边距 */
  border-right: 1px solid #004a99;
  border-bottom: 1px solid #004a99;
  line-height: 1.2; /* 缩小行高 */
}

/* 表体滚动 */
.custom-table tbody {
  display: block;
  height: calc(100% - 10px); /* 减去表头高度 */
  overflow-y: auto;
  width: 100%;
}

/* 表体行 */
.custom-table tbody tr {
  display: table;
  width: 100%;
  table-layout: fixed;
}

/* 表体单元格 */
.custom-table td {
  padding: 2px 4px; /* 缩小内边距 */
  text-align: center;
  border-right: 1px solid #004a99;
  border-bottom: 1px solid #004a99;
  line-height: 1.2; /* 缩小行高 */
  white-space: nowrap; /* 防止文字换行 */
  overflow: hidden; /* 隐藏超出内容 */
  text-overflow: ellipsis; /* 超出部分显示省略号 */
}

/* 列宽设置 */
.custom-table th:nth-child(1),
.custom-table td:nth-child(1) {
  width: 40%;
}

.custom-table th:nth-child(2),
.custom-table td:nth-child(2) {
  width: 30%;
}

.custom-table th:nth-child(3),
.custom-table td:nth-child(3) {
  width: 30%;
}

/* 移除最后一列的右边框 */
.custom-table th:last-child,
.custom-table td:last-child {
  border-right: none;
}

/* 行 hover 效果 */
.custom-table tbody tr:hover td {
  background-color: rgba(0, 74, 153, 0.3);
}

/* 自定义滚动条样式 */
.custom-table tbody::-webkit-scrollbar {
  width: 6px;
}

.custom-table tbody::-webkit-scrollbar-track {
  background: rgba(0, 74, 153, 0.2);
  border-radius: 3px;
}

.custom-table tbody::-webkit-scrollbar-thumb {
  background: rgba(0, 74, 153, 0.6);
  border-radius: 3px;
}

.custom-table tbody::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 74, 153, 0.8);
}
</style>
// waterSupplyChart
<template>
	<div class="water-supply-stats">
		<div class="chart-container">
			<div ref="chartRef" class="chart"></div>
		</div>
	</div>
</template>

<script setup lang="ts">
	import { ref, onMounted, onUnmounted, computed } from 'vue';
	import * as echarts from 'echarts';

	// 模拟数据:年份、区域、供水量(万m³)
	const supplyData = [
		{ year: 2020, region: '济南', supply: 205.3 },
		{ year: 2020, region: '青岛', supply: 160.8 },
		{ year: 2020, region: '淄博', supply: 115.2 },
		{ year: 2020, region: '枣庄', supply: 140.5 },
		{ year: 2021, region: '济南', supply: 210.5 },
		{ year: 2021, region: '青岛', supply: 165.2 },
		{ year: 2021, region: '淄博', supply: 120.8 },
		{ year: 2021, region: '枣庄', supply: 145.3 },
		{ year: 2022, region: '济南', supply: 230.8 },
		{ year: 2022, region: '青岛', supply: 178.5 },
		{ year: 2022, region: '淄博', supply: 132.4 },
		{ year: 2022, region: '枣庄', supply: 156.7 },
		{ year: 2023, region: '济南', supply: 256.3 },
		{ year: 2023, region: '青岛', supply: 195.8 },
		{ year: 2023, region: '淄博', supply: 145.2 },
		{ year: 2023, region: '枣庄', supply: 172.5 },
	];

	// 响应式状态:选中的年份(用于筛选总供水量)
	const selectedYear = ref<number | 'all'>('all');

	// 提取唯一的年份和区域
	const years = Array.from(new Set(supplyData.map(item => item.year))).sort((a, b) => a - b);
	const regions = Array.from(new Set(supplyData.map(item => item.region))).sort();

	// 计算总供水量(根据选中的年份筛选)
	const totalSupply = computed(() => {
		if (selectedYear.value === 'all') {
			// 所有年份的总供水量
			return supplyData.reduce((sum, item) => sum + item.supply, 0).toFixed(1);
		} else {
			// 选中年份的总供水量
			return supplyData
				.filter(item => item.year === selectedYear.value)
				.reduce((sum, item) => sum + item.supply, 0)
				.toFixed(1);
		}
	});

	// ECharts 实例引用
	const chartRef = ref<HTMLDivElement | null>(null);
	let chartInstance : echarts.ECharts | null = null;

	// 初始化折线图
	const initChart = () => {
		if (!chartRef.value) return;

		// 销毁已存在的实例
		if (chartInstance) {
			chartInstance.dispose();
		}

		// 创建新实例
		chartInstance = echarts.init(chartRef.value);

		// 处理图表数据:按区域分组,每个区域对应一条折线
		const seriesData = regions.map(region => {
			return {
				name: region,
				type: 'line',
				smooth: true, // 平滑折线
				data: years.map(year => {
					const item = supplyData.find(i => i.year === year && i.region === region);
					return item ? item.supply : 0;
				})
			};
		});

		// 图表配置项
		const option = {
			tooltip: {
				trigger: 'axis', // 触发方式:按轴触发
				backgroundColor: 'rgba(0, 30, 60, 0.8)',
				borderColor: 'rgba(34, 197, 255, 0.8)',
				textStyle: { color: '#fff', fontSize: 7 },
				formatter: '{b}年<br/>{a}:{c} 万m³' // 自定义提示框格式
			},
			legend: {
				data: regions, // 图例数据(区域名称)
				textStyle: { color: 'rgba(255, 255, 255, 0.8)', fontSize: 8 },
				top: 0,
				itemWidth: 12, // 图例标记的宽度
				itemHeight: 8, // 图例标记的高度
			},
			grid: {
				top: '25%',
				left: '5%',
				right: '0%',
				bottom: '2%',
				containLabel: true
			},
			xAxis: {
				type: 'category',
				data: years, // x轴数据(年份)
				axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
				splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
				axisLabel: {
					color: '#fff',
					fontSize: 8,
				}
			},
			yAxis: {
				type: 'value',
				name: '供水量(万m³)', // y轴名称
				nameTextStyle: { color: '#fff',fontSize: 8, },
				axisLabel: {
					color: '#fff',
					fontSize: 8,
				},
				axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } },
				splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } }
			},
			series: seriesData,
			color: ['#3498db', '#2ecc71', '#e74c3c', '#f39c12'] // 折线颜色(区分不同区域)
		};

		// 应用配置项
		chartInstance.setOption(option);
	};

	// 监听窗口大小变化,调整图表尺寸
	const handleResize = () => {
		chartInstance?.resize();
	};

	// 生命周期钩子
	onMounted(() => {
		initChart();
		window.addEventListener('resize', handleResize);
	});

	onUnmounted(() => {
		window.removeEventListener('resize', handleResize);
		chartInstance?.dispose(); // 销毁图表实例,释放资源
	});
</script>

<style scoped>
	.water-supply-stats {}

	.total-value {
		color: #fff;
		font-size: calc(0.7vw);
		font-weight: 600;
	}

	.chart-container {
		height: 12vh;
		position: relative;
	}

	.chart {
		width: 100%;
		height: 100%;
	}

	.chart-header {
		font-size: calc(0.8vw);
		height: 2vh;
		color: #30B5DE;
	}
</style>
// monitoringDataChart
<template>
  <div>
    <div ref="chartRef" class="chart-controls"></div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

export default {
  name: 'MonitoringDataChart',
  setup() {
    const chartRef = ref(null)
    const selectedSite = ref('site1')
    const selectedIndicators = ref(['temperature', 'pH', 'DO'])
    let chartInstance = null

    const sites = ref([
      { id: 'site1', name: '长江监测点' },
      { id: 'site2', name: '黄河监测点' },
      { id: 'site3', name: '珠江监测点' }
    ])

    const indicators = ref([
      { key: 'temperature', name: '水温(°C)', unit: '°C' },
      { key: 'pH', name: 'pH', unit: '' },
      { key: 'DO', name: '溶解氧(mg/L)', unit: 'mg/L' },
      { key: 'TP', name: '总磷(mg/L)', unit: 'mg/L' },
      { key: 'TN', name: '总氮(mg/L)', unit: 'mg/L' },
      { key: 'NH3N', name: '氨氮(mg/L)', unit: 'mg/L' }
    ])

    const timeData = ref(['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'])

    const monitoringData = ref({
      site1: {
        temperature: [12.5, 13.2, 15.8, 18.5, 22.3, 25.7, 28.9, 28.5, 25.2, 20.1, 16.3, 13.8],
        pH: [7.2, 7.3, 7.1, 7.4, 7.5, 7.3, 7.2, 7.4, 7.3, 7.2, 7.1, 7.3],
        DO: [8.5, 8.3, 8.7, 8.2, 7.9, 7.8, 7.5, 7.6, 7.9, 8.2, 8.4, 8.3],
        TP: [0.12, 0.15, 0.18, 0.22, 0.25, 0.28, 0.31, 0.29, 0.26, 0.21, 0.17, 0.14],
        TN: [1.2, 1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.2, 2.0, 1.8, 1.5, 1.3],
        NH3N: [0.35, 0.38, 0.42, 0.48, 0.52, 0.58, 0.62, 0.59, 0.53, 0.46, 0.40, 0.36]
      },
      site2: {
        temperature: [10.8, 11.5, 14.2, 17.0, 20.8, 24.2, 27.5, 27.0, 23.5, 18.2, 14.0, 11.2],
        pH: [7.5, 7.4, 7.6, 7.3, 7.2, 7.4, 7.5, 7.3, 7.4, 7.5, 7.6, 7.4],
        DO: [9.2, 9.0, 8.8, 8.5, 8.2, 8.0, 7.8, 7.9, 8.1, 8.4, 8.7, 9.0],
        TP: [0.08, 0.10, 0.13, 0.16, 0.19, 0.22, 0.25, 0.23, 0.20, 0.17, 0.13, 0.10],
        TN: [0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 1.9, 1.7, 1.5, 1.2, 1.0],
        NH3N: [0.25, 0.28, 0.32, 0.38, 0.42, 0.47, 0.51, 0.48, 0.43, 0.37, 0.31, 0.27]
      },
      site3: {
        temperature: [14.2, 15.0, 17.5, 20.2, 24.0, 27.3, 30.5, 30.0, 27.0, 22.5, 18.5, 15.5],
        pH: [7.0, 7.1, 6.9, 7.2, 7.3, 7.1, 7.0, 7.2, 7.1, 7.0, 6.9, 7.1],
        DO: [7.8, 7.6, 7.9, 7.5, 7.2, 7.0, 6.8, 6.9, 7.2, 7.5, 7.7, 7.6],
        TP: [0.15, 0.18, 0.22, 0.26, 0.30, 0.34, 0.38, 0.35, 0.31, 0.26, 0.21, 0.17],
        TN: [1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.5, 2.3, 2.1, 1.8, 1.6],
        NH3N: [0.42, 0.45, 0.50, 0.56, 0.61, 0.67, 0.72, 0.68, 0.62, 0.55, 0.48, 0.43]
      }
    })

    const initChart = () => {
      if (chartRef.value) {
        chartInstance = echarts.init(chartRef.value)
        updateChart()
      }
    }

    const updateChart = () => {
      if (!chartInstance) return
      
      const series = selectedIndicators.value.map(indicatorKey => {
        const indicator = indicators.value.find(i => i.key === indicatorKey)
        return {
          name: indicator.name,
          type: 'line',
          data: monitoringData.value[selectedSite.value][indicatorKey],
          smooth: true,
          symbol: 'circle',
          symbolSize: 6
        }
      })

      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross'
          }
        },
        legend: {
          data: selectedIndicators.value.map(key => {
            return indicators.value.find(i => i.key === key).name
          }),
          textStyle: { color: 'rgba(255, 255, 255, 0.8)', fontSize: 8 },
          top: 0,
          itemWidth: 12, // 图例标记的宽度
          itemHeight: 8, // 图例标记的高度
        },
        grid: {
		  top: '15%',
          left: '-2%',
          right: '10%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: timeData.value,
		  axisLabel: {
		  	color: '#fff',
		  	fontSize: 8,
		  },
		  splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
        },
        yAxis: {
          type: 'value',
		  axisLabel: {
		  	color: '#fff',
		  	fontSize: 8,
		  },
		  splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } },
        },
        series
      }
      
      chartInstance.setOption(option)
    }

    onMounted(() => {
      initChart()
      window.addEventListener('resize', () => {
        if (chartInstance) chartInstance.resize()
      })
    })

    onUnmounted(() => {
      if (chartInstance) {
        chartInstance.dispose()
      }
    })

    return {
      chartRef,
      sites,
      indicators,
      selectedSite,
      selectedIndicators,
      updateChart
    }
  }
}
</script>

<style scoped>
.chart-controls {
  width: 13vw;
  height: 13vh;
}
</style>
// siteComparisonRadar
<template>
  <div>
    <div ref="chartRef" class="chart-controls"></div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'

export default {
  name: 'SiteComparisonBar',
  setup() {
    const chartRef = ref(null)
    const selectedTime = ref('7月')
    const selectedSites = ref(['site1', 'site2', 'site3'])
    let chartInstance = null

    // 站点配置(保留原颜色)
    const sites = ref([
      { id: 'site1', name: '长江监测点', color: '#5470C6' },
      { id: 'site2', name: '黄河监测点', color: '#91CC75' },
      { id: 'site3', name: '珠江监测点', color: '#FAC858' }
    ])

    // 时间选项(不变)
    const timeOptions = ref(['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'])

    // 监测指标(x轴类目,保留单位)
    const indicators = ref([
      { name: '水温', max: 35, unit: '°C' },
      { name: 'pH', max: 9, unit: '' },
      { name: '溶解氧', max: 12, unit: 'mg/L' },
      { name: '总磷', max: 0.5, unit: 'mg/L' },
      { name: '总氮', max: 3, unit: 'mg/L' },
      { name: '氨氮', max: 1, unit: 'mg/L' }
    ])

    // 核心修正:radarData 是对象字面量 {},不是数组 []
    const radarData = ref({
      site1: {
        '1月': [12.5, 7.2, 8.5, 0.12, 1.2, 0.35],
        '2月': [13.2, 7.3, 8.3, 0.15, 1.3, 0.38],
        '3月': [15.8, 7.1, 8.7, 0.18, 1.5, 0.42],
        '4月': [18.5, 7.4, 8.2, 0.22, 1.7, 0.48],
        '5月': [22.3, 7.5, 7.9, 0.25, 1.9, 0.52],
        '6月': [25.7, 7.3, 7.8, 0.28, 2.1, 0.58],
        '7月': [28.9, 7.2, 7.5, 0.31, 2.3, 0.62],
        '8月': [28.5, 7.4, 7.6, 0.29, 2.2, 0.59],
        '9月': [25.2, 7.3, 7.9, 0.26, 2.0, 0.53],
        '10月': [20.1, 7.2, 8.2, 0.21, 1.8, 0.46],
        '11月': [16.3, 7.1, 8.4, 0.17, 1.5, 0.40],
        '12月': [13.8, 7.3, 8.3, 0.14, 1.3, 0.36]
      },
      site2: {
        '1月': [10.8, 7.5, 9.2, 0.08, 0.9, 0.25],
        '2月': [11.5, 7.4, 9.0, 0.10, 1.0, 0.28],
        '3月': [14.2, 7.6, 8.8, 0.13, 1.2, 0.32],
        '4月': [17.0, 7.3, 8.5, 0.16, 1.4, 0.38],
        '5月': [20.8, 7.2, 8.2, 0.19, 1.6, 0.42],
        '6月': [24.2, 7.4, 8.0, 0.22, 1.8, 0.47],
        '7月': [27.5, 7.5, 7.8, 0.25, 2.0, 0.51],
        '8月': [27.0, 7.3, 7.9, 0.23, 1.9, 0.48],
        '9月': [23.5, 7.4, 8.1, 0.20, 1.7, 0.43],
        '10月': [18.2, 7.5, 8.4, 0.17, 1.5, 0.37],
        '11月': [14.0, 7.6, 8.7, 0.13, 1.2, 0.31],
        '12月': [11.2, 7.4, 9.0, 0.10, 1.0, 0.27]
      },
      site3: {
        '1月': [14.2, 7.0, 7.8, 0.15, 1.5, 0.42],
        '2月': [15.0, 7.1, 7.6, 0.18, 1.6, 0.45],
        '3月': [17.5, 6.9, 7.9, 0.22, 1.8, 0.50],
        '4月': [20.2, 7.2, 7.5, 0.26, 2.0, 0.56],
        '5月': [24.0, 7.3, 7.2, 0.30, 2.2, 0.61],
        '6月': [27.3, 7.1, 7.0, 0.34, 2.4, 0.67],
        '7月': [30.5, 7.0, 6.8, 0.38, 2.6, 0.72],
        '8月': [30.0, 7.2, 6.9, 0.35, 2.5, 0.68],
        '9月': [27.0, 7.1, 7.2, 0.31, 2.3, 0.62],
        '10月': [22.5, 7.0, 7.5, 0.26, 2.1, 0.55],
        '11月': [18.5, 6.9, 7.7, 0.21, 1.8, 0.48],
        '12月': [15.5, 7.1, 7.6, 0.17, 1.6, 0.43]
      }
    })

    // 初始化图表
    const initChart = () => {
      if (chartRef.value) {
        chartInstance = echarts.init(chartRef.value)
        updateChart() // 初始加载数据
      }
    }

    // 更新图表(核心:生成柱形图系列数据)
    const updateChart = () => {
      if (!chartInstance) return

      // 1. 生成系列数据:每个选中的站点对应一个柱形系列
      const series = selectedSites.value.map(siteId => {
        const site = sites.value.find(s => s.id === siteId)
        return {
          name: site.name, // 系列名称(站点名,用于图例和tooltip)
          type: 'bar', // 图表类型:柱形图
          data: radarData.value[siteId][selectedTime.value], // 对应时间的指标数值数组
          itemStyle: { color: site.color }, // 沿用原站点颜色
          barWidth: 10, // 柱子宽度(可调整,避免过宽/过窄)
          groupPadding: 0.2 // 同指标下不同站点柱子的间距
        }
      })

      // 2. 柱形图配置项
      const option = {
        // 提示框:显示「指标名称+各站点数值(带单位)」
        tooltip: {
          trigger: 'axis', // 按x轴类目触发(同一指标的所有站点数据一起显示)
          axisPointer: { type: 'shadow' }, // 阴影指示器(增强交互体验)
          formatter: function(params) {
            // params:当前x轴类目下的所有系列数据([{seriesName, value}, ...])
            const indicator = indicators.value.find(ind => ind.name === params[0].axisValue)
            let tooltipHtml = `<div>${indicator.name}(单位:${indicator.unit || '无'})</div>`
            params.forEach(item => {
              tooltipHtml += `<div>${item.seriesName}:${item.value}${indicator.unit}</div>`
            })
            return tooltipHtml
          },
        },

        // 图例:显示选中的站点(支持点击切换显示/隐藏)
        legend: {
          data: selectedSites.value.map(id => sites.value.find(s => s.id === id).name),
          top: 0,
          left: 'center',
          textStyle: { color: '#fff', fontSize: 8 },
          itemWidth: 12,
          itemHeight: 8
        },

        // 网格布局:调整图表边距,避免标签重叠
        grid: {
          top: '20%',
          left: '0%',
          right: '10%',
          bottom: '20%', // 底部留足空间给x轴标签(防止指标名过长重叠)
          containLabel: true
        },

        // x轴:监测指标(类目轴)
        xAxis: {
          type: 'category',
          data: indicators.value.map(ind => ind.name), // x轴标签:指标名称
          axisLabel: {
          	color: '#fff',
          	fontSize: 8,
          },
          // axisLine: { lineStyle: { color: 'rgba(0,0,0,0.5)' } },
          axisTick: { alignWithLabel: true } // 刻度线与标签对齐
        },

        // y轴:数值轴(自适应数据范围)
        yAxis: {
          type: 'value',
          name: '数值',
          nameTextStyle: { color: '#fff', fontSize: 8 },
		  axisLabel: {
		  	color: '#fff',
		  	fontSize: 8,
			formatter: '{value}'
		  },
          axisLine: { lineStyle: { color: 'rgba(0,0,0,0.5)' } },
		  splitLine: { lineStyle: { color: 'rgba(0,0,0,0.1)' } } // 网格线样式
        },
        // 系列数据(前面生成的柱形系列)
        series
      }

      chartInstance.setOption(option)
    }

    // 挂载时初始化图表+监听窗口 resize
    onMounted(() => {
      initChart()
      window.addEventListener('resize', () => {
        chartInstance?.resize()
      })
    })

    // 销毁时释放图表实例
    onUnmounted(() => {
      chartInstance?.dispose()
    })

    return {
      chartRef,
      sites,
      timeOptions,
      selectedTime,
      selectedSites,
      updateChart // 暴露给父组件,用于切换时间/站点时更新图表
    }
  }
}
</script>

<style scoped>
/* 调整图表容器大小(原13vw/13vh过小,柱形图建议适当放大) */
.chart-controls {
  width: 13vw; /* 可根据页面布局调整 */
  height: 13vh; /* 可根据页面布局调整 */
}
</style>
// waterQualityDistribution
<template>
	<div ref="chartRef" class="chart"></div>
</template>

<script>
	import {
		ref,
		onMounted,
		onUnmounted
	} from 'vue'
	import * as echarts from 'echarts'

	export default {
		name: 'WaterQualityDistribution',
		setup() {
			const chartRef = ref(null)
			let chartInstance = null

			const initChart = () => {
				if (chartRef.value) {
					chartInstance = echarts.init(chartRef.value)

					const option = {
					  color: ['#67F9D8', '#FFE434', '#56A3F1', '#FF917C'],
					  // left: 20, // 这个配置对雷达图布局没有直接影响,可以删除
					  legend: {
					    textStyle: {
					      color: 'rgba(255, 255, 255, 0.8)',
					      fontSize: 10
					    },
					    top: 0,
					    left: 'left', // 可以将图例也居中,使其与图表整体协调
					    itemWidth: 12,
					    itemHeight: 8,
					  },
					  radar: [
					    {
					      indicator: [
					        { text: 'I类' },
					        { text: 'II类' },
					        { text: 'III类' },
					        { text: 'IV类' },
					        { text: 'V类' }
					      ],
					      // --- 核心改动:调整雷达图1的中心位置 ---
					      center: ['50%', '50%'], // 从 '25%' 调整到 '20%'
					      radius: 40,
					      startAngle: 10,
					      splitNumber: 4,
					      shape: 'circle',
					      axisName: {
					        formatter: '{value}',
					        color: '#428BD4'
					      },
					      splitArea: {
					        areaStyle: {
					          color: ['#77EADF', '#26C3BE', '#64AFE9', '#428BD4'],
					          shadowColor: 'rgba(0, 0, 0, 0.2)',
					          shadowBlur: 10
					        }
					      },
					      axisLine: {
					        lineStyle: {
					          color: 'rgba(211, 253, 250, 0.8)'
					        }
					      },
					      splitLine: {
					        lineStyle: {
					          color: 'rgba(211, 253, 250, 0.8)'
					        }
					      }
					    },
					    {
					      // --- 核心改动:调整雷达图2的中心位置 ---
					      center: ['60%', '50%'], // 从 '75%' 调整到 '70%'
					      radius: 120,
					      axisName: {
					        color: '#fff',
					        backgroundColor: '#666',
					        borderRadius: 3,
					        padding: [3, 5]
					      }
					    }
					  ],
					  series: [
					    {
					      type: 'radar',
					      emphasis: {
					        lineStyle: {
					          width: 4
					        }
					      },
					      data: [
					        {
					          value: [100, 8, 0.4, -80, 2000],
					          name: '站点总数'
					        }
					      ]
					    }
					  ]
					};

					chartInstance.setOption(option)
				}
			}

			onMounted(() => {
				initChart()
				window.addEventListener('resize', () => {
					if (chartInstance) chartInstance.resize()
				})
			})

			onUnmounted(() => {
				if (chartInstance) {
					chartInstance.dispose()
				}
			})

			return {
				chartRef
			}
		}
	}
</script>
<style scoped>
	.chart {
		width: 13vw;
		height: 13vh;
	}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值