BAI 1 tháng trước cách đây
mục cha
commit
2bbb812cff

+ 5 - 0
src/router/index.js

@@ -2,6 +2,7 @@ import { createRouter, createWebHashHistory } from "vue-router"
 
 const map = () => import("@/views/map/index.vue")
 const waterResource = () => import("@/views/waterResource/index.vue")
+const waterQuality = () => import("@/views/waterQuality/index.vue")
 
 const router = createRouter({
   history: createWebHashHistory(),
@@ -18,6 +19,10 @@ const router = createRouter({
       path: "/waterResource",
       component: waterResource,
     },
+    {
+      path: "/waterQuality",
+      component: waterQuality,
+    },
     {
       path: "/:pathMatch(.*)",
       redirect: "/",

+ 21 - 2
src/views/map/index.vue

@@ -114,6 +114,7 @@
 </template>
 <script setup>
 import { shallowRef, ref, reactive, onMounted, onBeforeUnmount, nextTick } from "vue"
+import { useRouter } from "vue-router"
 import mapScene from "./map.vue"
 import mHeader from "@/components/mHeader/index.vue"
 import mCountCard from "@/components/mCountCard/index.vue"
@@ -129,13 +130,14 @@ import PurposeSpecialFunds from "./components/PurposeSpecialFunds.vue"
 import ProportionPopulationConsumption from "./components/ProportionPopulationConsumption.vue"
 import ElectricityUsage from "./components/ElectricityUsage.vue"
 import QuarterlyGrowthSituation from "./components/QuarterlyGrowthSituation.vue"
-import WaterResourceContent from "@/views/waterResource/index.vue"
 
 import { Assets } from "./assets.js"
 import emitter from "@/utils/emitter"
 import gsap from "gsap"
 import autofit from "autofit.js"
 
+const router = useRouter()
+
 const assets = shallowRef(null)
 const mapSceneRef = ref(null)
 const state = reactive({
@@ -244,6 +246,23 @@ async function hideLoading() {
 
 function handleMenuSelect(index) {
   state.activeIndex = index
+  
+  // 根据索引跳转到不同页面
+  switch (index) {
+    case "1":
+      router.push("/map")
+      break
+    case "2":
+      router.push("/waterQuality")
+      break
+    case "3":
+      router.push("/waterResource")
+      break
+    default:
+      break
+  }
+  
+  // 添加页面内容的动画效果
   nextTick(() => {
     if (index === "1") {
       gsap.to(".left-card", { x: 0, opacity: 1, duration: 0.5, stagger: 0.1 })
@@ -251,7 +270,7 @@ function handleMenuSelect(index) {
       gsap.to(".count-card", { y: 0, opacity: 1, duration: 0.5, stagger: 0.1 })
       gsap.to(".bottom-tray", { y: 0, opacity: 1, duration: 0.5 })
       gsap.to(".bottom-radar", { y: 0, opacity: 1, duration: 0.5 })
-    } else if (index === "5") {
+    } else if (index === "2" || index === "3") {
       gsap.to(".water-resource-content .module-card", { x: 0, opacity: 1, duration: 0.5, stagger: 0.1 })
     }
   })

+ 320 - 0
src/views/waterQuality/components/CollaborativeProcess.vue

@@ -0,0 +1,320 @@
+<template>
+  <m-card title="协同业务流程" :width="398" :height="450">
+    <div class="collaborative-process">
+      <div class="process-tabs">
+        <div
+          class="tab-item"
+          v-for="(tab, index) in processTabs"
+          :key="index"
+          :class="{ active: activeTab === index }"
+          @click="activeTab = index"
+        >
+          {{ tab.name }}
+        </div>
+      </div>
+      <div class="process-flow">
+        <div class="flow-container">
+          <div
+            class="flow-node"
+            v-for="(node, index) in currentProcessData"
+            :key="index"
+            :class="{ active: index === currentStep }"
+          >
+            <div class="node-thumbnail">
+              <div class="thumbnail-icon">{{ node.icon }}</div>
+            </div>
+            <div class="node-content">
+              <div class="node-title">{{ node.title }}</div>
+              <div class="node-desc">{{ node.desc }}</div>
+            </div>
+            <div class="node-connector" v-if="index < currentProcessData.length - 1">
+              <div class="connector-line"></div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="process-actions">
+        <button class="action-btn primary" @click="showAnimation">
+          <span>🎬</span> 查看完整流程动画
+        </button>
+        <button class="action-btn secondary" @click="jumpToScene">
+          <span>🔗</span> 跳转会商场景
+        </button>
+      </div>
+    </div>
+  </m-card>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onUnmounted } from "vue"
+import mCard from "@/components/mCard/index.vue"
+
+const activeTab = ref(0)
+const currentStep = ref(0)
+
+const processTabs = ref([
+  { name: "水旱防御" },
+  { name: "水资源调度" },
+  { name: "水质管控" },
+])
+
+const processData = ref({
+  0: [
+    {
+      icon: "👁‍🗨",
+      title: "联合值班",
+      desc: "24小时值守监控水情变化",
+    },
+    {
+      icon: "🔍",
+      title: "联合巡查",
+      desc: "多部门协同巡检重点区域",
+    },
+    {
+      icon: "💬",
+      title: "联合会商",
+      desc: "多部门在线研判水情,形成调度方案",
+    },
+    {
+      icon: "📡",
+      title: "指令下达",
+      desc: "一键下达调度指令,实时跟踪执行",
+    },
+  ],
+  1: [
+    {
+      icon: "📊",
+      title: "联合值班",
+      desc: "实时监测水资源存量与用量",
+    },
+    {
+      icon: "🗺️",
+      title: "联合巡查",
+      desc: "管网全覆盖巡检,排查异常",
+    },
+    {
+      icon: "🤝",
+      title: "联合会商",
+      desc: "供需平衡分析,制定调度策略",
+    },
+    {
+      icon: "🎯",
+      title: "指令下达",
+      desc: "智能调度指令下达,优化配置",
+    },
+  ],
+  2: [
+    {
+      icon: "🌊",
+      title: "联合值班",
+      desc: "实时监测水质指标变化",
+    },
+    {
+      icon: "🧪",
+      title: "联合巡查",
+      desc: "采样检测重点断面水质状况",
+    },
+    {
+      icon: "📈",
+      title: "联合会商",
+      desc: "水质趋势分析,制定管控措施",
+    },
+    {
+      icon: "⚡",
+      title: "指令下达",
+      desc: "应急处置指令快速下达",
+    },
+  ],
+})
+
+const currentProcessData = computed(() => processData.value[activeTab.value] || [])
+
+let stepInterval = null
+
+onMounted(() => {
+  startStepAnimation()
+})
+
+onUnmounted(() => {
+  if (stepInterval) clearInterval(stepInterval)
+})
+
+function startStepAnimation() {
+  stepInterval = setInterval(() => {
+    currentStep.value = (currentStep.value + 1) % currentProcessData.value.length
+  }, 4000)
+}
+
+function showAnimation() {
+  console.log("查看完整流程动画")
+}
+
+function jumpToScene() {
+  console.log("跳转会商场景")
+}
+</script>
+
+<style lang="scss" scoped>
+.collaborative-process {
+  padding: 12px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  box-sizing: border-box;
+}
+
+.process-tabs {
+  display: flex;
+  gap: 8px;
+  background: rgba(0, 180, 255, 0.05);
+  padding: 6px;
+  border-radius: 6px;
+}
+
+.tab-item {
+  flex: 1;
+  padding: 8px 10px;
+  text-align: center;
+  border-radius: 4px;
+  font-size: 11px;
+  color: rgba(255, 255, 255, 0.6);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  &:hover {
+    color: rgba(255, 255, 255, 0.9);
+    background: rgba(48, 220, 255, 0.1);
+  }
+  &.active {
+    background: linear-gradient(135deg, #30DCFF, #00BFFF);
+    color: #fff;
+    box-shadow: 0 0 15px rgba(48, 220, 255, 0.4);
+  }
+}
+
+.process-flow {
+  flex: 1;
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 6px;
+  padding: 12px;
+  overflow-y: auto;
+}
+
+.flow-container {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.flow-node {
+  display: flex;
+  align-items: flex-start;
+  gap: 10px;
+  padding: 10px;
+  background: rgba(48, 220, 255, 0.05);
+  border-radius: 6px;
+  border: 1px solid rgba(48, 220, 255, 0.1);
+  transition: all 0.3s ease;
+  position: relative;
+  &.active {
+    background: rgba(48, 220, 255, 0.15);
+    border-color: rgba(48, 220, 255, 0.5);
+    box-shadow: 0 0 10px rgba(48, 220, 255, 0.2);
+    .node-thumbnail {
+      background: linear-gradient(135deg, #30DCFF, #00BFFF);
+      box-shadow: 0 0 15px rgba(48, 220, 255, 0.5);
+    }
+    .node-title {
+      color: #30DCFF;
+    }
+  }
+}
+
+.node-thumbnail {
+  width: 40px;
+  height: 40px;
+  border-radius: 8px;
+  background: rgba(255, 255, 255, 0.1);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+  transition: all 0.3s ease;
+  .thumbnail-icon {
+    font-size: 20px;
+  }
+}
+
+.node-content {
+  flex: 1;
+  min-width: 0;
+}
+
+.node-title {
+  font-size: 13px;
+  font-weight: 600;
+  color: rgba(255, 255, 255, 0.9);
+  margin-bottom: 4px;
+  transition: all 0.3s ease;
+}
+
+.node-desc {
+  font-size: 11px;
+  color: rgba(255, 255, 255, 0.5);
+  line-height: 1.4;
+}
+
+.node-connector {
+  position: absolute;
+  left: 21px;
+  bottom: -12px;
+  width: 2px;
+  height: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .connector-line {
+    width: 2px;
+    height: 100%;
+    background: linear-gradient(to bottom, rgba(48, 220, 255, 0.5), rgba(48, 220, 255, 0.1));
+  }
+}
+
+.process-actions {
+  display: flex;
+  gap: 10px;
+}
+
+.action-btn {
+  flex: 1;
+  padding: 10px 12px;
+  border-radius: 6px;
+  border: none;
+  cursor: pointer;
+  font-size: 11px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+  transition: all 0.3s ease;
+  &.primary {
+    background: linear-gradient(135deg, #30DCFF, #00BFFF);
+    color: #fff;
+    &:hover {
+      box-shadow: 0 0 15px rgba(48, 220, 255, 0.5);
+      transform: translateY(-2px);
+    }
+  }
+  &.secondary {
+    background: rgba(48, 220, 255, 0.15);
+    border: 1px solid rgba(48, 220, 255, 0.4);
+    color: #30DCFF;
+    &:hover {
+      background: rgba(48, 220, 255, 0.25);
+    }
+  }
+  span {
+    font-size: 14px;
+  }
+}
+</style>

+ 259 - 0
src/views/waterQuality/components/DemonstrationBase.vue

@@ -0,0 +1,259 @@
+<template>
+  <m-card title="示范基地全景" :width="398" :height="450">
+    <div class="demonstration-base">
+      <div class="base-tabs">
+        <div
+          class="tab-item"
+          v-for="(tab, index) in baseTabs"
+          :key="index"
+          :class="{ active: activeTab === index }"
+          @click="activeTab = index"
+        >
+          {{ tab.name }}
+        </div>
+      </div>
+      <div class="base-detail">
+        <div class="detail-thumbnail">
+          <div class="thumbnail-icon">{{ currentTabData.icon }}</div>
+          <div class="thumbnail-overlay"></div>
+        </div>
+        <div class="detail-info">
+          <div class="info-title">{{ currentTabData.title }}</div>
+          <div class="info-desc">{{ currentTabData.desc }}</div>
+          <div class="info-partner">
+            <span class="partner-label">合作单位:</span>
+            <span class="partner-value">{{ currentTabData.partner }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="base-cycle">
+        <div class="cycle-title">产学研闭环流程</div>
+        <div class="cycle-container">
+          <div class="cycle-item" v-for="(item, index) in cycleData" :key="index">
+            <div class="cycle-icon">{{ item.icon }}</div>
+            <div class="cycle-label">{{ item.label }}</div>
+            <div class="cycle-desc">{{ item.desc }}</div>
+            <div class="cycle-arrow" v-if="index < cycleData.length - 1">→</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </m-card>
+</template>
+
+<script setup>
+import { ref, computed } from "vue"
+import mCard from "@/components/mCard/index.vue"
+
+const activeTab = ref(0)
+
+const baseTabs = ref([
+  { name: "实习实训区" },
+  { name: "技术研发区" },
+  { name: "决策实验室" },
+])
+
+const tabData = ref([
+  {
+    icon: "🎓",
+    title: "实习实训区",
+    desc: "为高校师生提供实践实训平台,开展水文监测、水质分析等专业技能培训,培养专业技术人才。",
+    partner: "河海大学、南京大学",
+  },
+  {
+    icon: "🔬",
+    title: "技术研发区",
+    desc: "联合高校研发面源污染预警模型,开展水文监测新技术、新设备的研发与测试。",
+    partner: "河海大学、清华大学",
+  },
+  {
+    icon: "🧠",
+    title: "决策实验室",
+    desc: "基于数字孪生技术的仿真决策平台,开展水旱灾害防御、水资源调度模拟推演。",
+    partner: "中科院地理所",
+  },
+])
+
+const currentTabData = computed(() => tabData.value[activeTab.value] || tabData.value[0])
+
+const cycleData = ref([
+  { icon: "📚", label: "学", desc: "高校师生实训" },
+  { icon: "🔬", label: "研", desc: "联合攻关模型" },
+  { icon: "🏭", label: "产", desc: "技术产品化" },
+  { icon: "💡", label: "用", desc: "业务落地应用" },
+])
+</script>
+
+<style lang="scss" scoped>
+.demonstration-base {
+  padding: 12px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  box-sizing: border-box;
+}
+
+.base-tabs {
+  display: flex;
+  gap: 8px;
+  background: rgba(0, 180, 255, 0.05);
+  padding: 6px;
+  border-radius: 6px;
+}
+
+.tab-item {
+  flex: 1;
+  padding: 8px 10px;
+  text-align: center;
+  border-radius: 4px;
+  font-size: 10px;
+  color: rgba(255, 255, 255, 0.6);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  &:hover {
+    color: rgba(255, 255, 255, 0.9);
+    background: rgba(48, 220, 255, 0.1);
+  }
+  &.active {
+    background: linear-gradient(135deg, #30DCFF, #00BFFF);
+    color: #fff;
+    box-shadow: 0 0 15px rgba(48, 220, 255, 0.4);
+  }
+}
+
+.base-detail {
+  flex: 1;
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 6px;
+  padding: 12px;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.detail-thumbnail {
+  height: 65px;
+  border-radius: 6px;
+  background: linear-gradient(135deg, rgba(48, 220, 255, 0.1) 0%, rgba(67, 233, 123, 0.1) 100%);
+  border: 1px solid rgba(48, 220, 255, 0.2);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  overflow: hidden;
+  .thumbnail-icon {
+    font-size: 36px;
+    z-index: 1;
+  }
+  .thumbnail-overlay {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: 
+      radial-gradient(circle at 30% 30%, rgba(48, 220, 255, 0.2) 0%, transparent 50%),
+      radial-gradient(circle at 70% 70%, rgba(67, 233, 123, 0.15) 0%, transparent 50%);
+  }
+}
+
+.detail-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.info-title {
+  font-size: 12px;
+  font-weight: 600;
+  color: #30DCFF;
+}
+
+.info-desc {
+  font-size: 10px;
+  color: rgba(255, 255, 255, 0.7);
+  line-height: 1.4;
+  flex: 1;
+}
+
+.info-partner {
+  background: rgba(48, 220, 255, 0.1);
+  border-radius: 4px;
+  padding: 6px 8px;
+  font-size: 10px;
+  .partner-label {
+    color: rgba(255, 255, 255, 0.5);
+  }
+  .partner-value {
+    color: #43e97b;
+    font-weight: 500;
+  }
+}
+
+.base-cycle {
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 8px;
+  padding: 10px;
+}
+
+.cycle-title {
+  font-size: 11px;
+  color: #30DCFF;
+  font-weight: 600;
+  text-align: center;
+  margin-bottom: 10px;
+}
+
+.cycle-container {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  gap: 5px;
+}
+
+.cycle-item {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  text-align: center;
+  position: relative;
+}
+
+.cycle-icon {
+  width: 36px;
+  height: 36px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, rgba(48, 220, 255, 0.2), rgba(67, 233, 123, 0.2));
+  border: 2px solid rgba(48, 220, 255, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 16px;
+  margin-bottom: 4px;
+}
+
+.cycle-label {
+  font-size: 12px;
+  font-weight: 600;
+  color: #30DCFF;
+  margin-bottom: 2px;
+}
+
+.cycle-desc {
+  font-size: 9px;
+  color: rgba(255, 255, 255, 0.5);
+  line-height: 1.2;
+}
+
+.cycle-arrow {
+  position: absolute;
+  right: -10px;
+  top: 12px;
+  color: rgba(48, 220, 255, 0.6);
+  font-size: 12px;
+  font-weight: bold;
+}
+</style>

+ 401 - 0
src/views/waterQuality/components/IntegrationEffect.vue

@@ -0,0 +1,401 @@
+<template>
+  <m-card title="融合成效数据" :width="398" :height="450">
+    <div class="integration-effect">
+      <div class="effect-tabs">
+        <div
+          class="tab-item"
+          v-for="(tab, index) in effectTabs"
+          :key="index"
+          :class="{ active: activeTab === index }"
+          @click="activeTab = index"
+        >
+          {{ tab.name }}
+        </div>
+      </div>
+      <div class="effect-chart">
+        <div v-if="activeTab === 0" class="chart-line">
+          <div class="chart-title">应急响应速度提升 30%</div>
+          <div class="line-container">
+            <div class="line-item">
+              <div class="line-label">融合前</div>
+              <div class="line-bar-wrapper">
+                <div class="line-bar before" style="width: 60%"></div>
+              </div>
+              <div class="line-value">40分钟</div>
+            </div>
+            <div class="line-item">
+              <div class="line-label">融合后</div>
+              <div class="line-bar-wrapper">
+                <div class="line-bar after" style="width: 90%"></div>
+              </div>
+              <div class="line-value">26分钟</div>
+            </div>
+          </div>
+        </div>
+        <div v-if="activeTab === 1" class="chart-bar">
+          <div class="chart-title">成本降低成效</div>
+          <div class="bar-container">
+            <div class="bar-item">
+              <div class="bar-value">-50%</div>
+              <div class="bar-fill" style="height: 70%"></div>
+              <div class="bar-label">数据共享耗时</div>
+            </div>
+            <div class="bar-item">
+              <div class="bar-value">-20%</div>
+              <div class="bar-fill" style="height: 45%"></div>
+              <div class="bar-label">运维成本</div>
+            </div>
+          </div>
+        </div>
+        <div v-if="activeTab === 2" class="chart-ring">
+          <div class="chart-title">决策精准度提升</div>
+          <div class="ring-container">
+            <div class="ring-item">
+              <div class="ring-wrapper">
+                <svg width="80" height="80" viewBox="0 0 80 80">
+                  <circle cx="40" cy="40" r="35" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="6" />
+                  <circle cx="40" cy="40" r="35" fill="none" stroke="url(#gradient1)" stroke-width="6" stroke-linecap="round"
+                    stroke-dasharray="220" stroke-dashoffset="66" transform="rotate(-90 40 40)" />
+                  <defs>
+                    <linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
+                      <stop offset="0%" stop-color="#30DCFF" />
+                      <stop offset="100%" stop-color="#43e97b" />
+                    </linearGradient>
+                  </defs>
+                </svg>
+                <div class="ring-text">
+                  <span class="percent">+40%</span>
+                  <span class="label">调度命中率</span>
+                </div>
+              </div>
+            </div>
+            <div class="ring-item">
+              <div class="ring-wrapper">
+                <svg width="80" height="80" viewBox="0 0 80 80">
+                  <circle cx="40" cy="40" r="35" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="6" />
+                  <circle cx="40" cy="40" r="35" fill="none" stroke="url(#gradient2)" stroke-width="6" stroke-linecap="round"
+                    stroke-dasharray="220" stroke-dashoffset="110" transform="rotate(-90 40 40)" />
+                  <defs>
+                    <linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="0%">
+                      <stop offset="0%" stop-color="#ff6b6b" />
+                      <stop offset="100%" stop-color="#ffd93d" />
+                    </linearGradient>
+                  </defs>
+                </svg>
+                <div class="ring-text">
+                  <span class="percent down">-25%</span>
+                  <span class="label">内涝误报率</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="effect-compare">
+        <div class="compare-title">融合前后对比</div>
+        <div class="compare-container">
+          <div class="compare-card before">
+            <div class="card-label">融合前</div>
+            <div class="card-items">
+              <div class="card-item">
+                <span class="item-label">应急响应</span>
+                <span class="item-value">40分钟</span>
+              </div>
+              <div class="card-item">
+                <span class="item-label">数据共享</span>
+                <span class="item-value">2小时</span>
+              </div>
+              <div class="card-item">
+                <span class="item-label">调度精准</span>
+                <span class="item-value">60%</span>
+              </div>
+            </div>
+          </div>
+          <div class="compare-arrow">→</div>
+          <div class="compare-card after">
+            <div class="card-label">融合后</div>
+            <div class="card-items">
+              <div class="card-item">
+                <span class="item-label">应急响应</span>
+                <span class="item-value highlight">26分钟</span>
+              </div>
+              <div class="card-item">
+                <span class="item-label">数据共享</span>
+                <span class="item-value highlight">1小时</span>
+              </div>
+              <div class="card-item">
+                <span class="item-label">调度精准</span>
+                <span class="item-value highlight">100%</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </m-card>
+</template>
+
+<script setup>
+import { ref } from "vue"
+import mCard from "@/components/mCard/index.vue"
+
+const activeTab = ref(0)
+
+const effectTabs = ref([
+  { name: "效率提升" },
+  { name: "成本降低" },
+  { name: "决策精准" },
+])
+</script>
+
+<style lang="scss" scoped>
+.integration-effect {
+  padding: 12px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  box-sizing: border-box;
+}
+
+.effect-tabs {
+  display: flex;
+  gap: 8px;
+  background: rgba(0, 180, 255, 0.05);
+  padding: 6px;
+  border-radius: 6px;
+}
+
+.tab-item {
+  flex: 1;
+  padding: 8px 10px;
+  text-align: center;
+  border-radius: 4px;
+  font-size: 10px;
+  color: rgba(255, 255, 255, 0.6);
+  cursor: pointer;
+  transition: all 0.3s ease;
+  &:hover {
+    color: rgba(255, 255, 255, 0.9);
+    background: rgba(48, 220, 255, 0.1);
+  }
+  &.active {
+    background: linear-gradient(135deg, #30DCFF, #00BFFF);
+    color: #fff;
+    box-shadow: 0 0 15px rgba(48, 220, 255, 0.4);
+  }
+}
+
+.effect-chart {
+  flex: 1;
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 6px;
+  padding: 12px;
+  display: flex;
+  flex-direction: column;
+}
+
+.chart-title {
+  font-size: 11px;
+  color: #30DCFF;
+  font-weight: 600;
+  text-align: center;
+  margin-bottom: 12px;
+}
+
+.chart-line .line-container {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.line-item {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.line-label {
+  width: 50px;
+  font-size: 10px;
+  color: rgba(255, 255, 255, 0.7);
+}
+
+.line-bar-wrapper {
+  flex: 1;
+  height: 18px;
+  background: rgba(255, 255, 255, 0.1);
+  border-radius: 9px;
+  overflow: hidden;
+}
+
+.line-bar {
+  height: 100%;
+  border-radius: 10px;
+  transition: width 1.5s ease;
+  &.before {
+    background: linear-gradient(90deg, rgba(255, 107, 107, 0.8), rgba(255, 217, 61, 0.8));
+  }
+  &.after {
+    background: linear-gradient(90deg, #30DCFF, #43e97b);
+    box-shadow: 0 0 10px rgba(48, 220, 255, 0.4);
+  }
+}
+
+.line-value {
+  width: 50px;
+  font-size: 12px;
+  font-weight: 600;
+  color: #30DCFF;
+  text-align: right;
+}
+
+.chart-bar .bar-container {
+  display: flex;
+  justify-content: space-around;
+  align-items: flex-end;
+  flex: 1;
+  padding: 0 20px;
+}
+
+.bar-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 8px;
+}
+
+.bar-value {
+  font-size: 14px;
+  font-weight: 700;
+  color: #ff6b6b;
+}
+
+.bar-fill {
+  width: 40px;
+  background: linear-gradient(to top, #30DCFF, #43e97b);
+  border-radius: 5px 5px 0 0;
+  transition: height 1.5s ease;
+}
+
+.bar-label {
+  font-size: 10px;
+  color: rgba(255, 255, 255, 0.6);
+}
+
+.chart-ring .ring-container {
+  display: flex;
+  justify-content: space-around;
+  flex: 1;
+}
+
+.ring-item {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.ring-wrapper {
+  position: relative;
+  width: 80px;
+  height: 80px;
+}
+
+.ring-text {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  text-align: center;
+  .percent {
+    display: block;
+    font-size: 14px;
+    font-weight: 700;
+    color: #30DCFF;
+    &.down {
+      color: #ff6b6b;
+    }
+  }
+  .label {
+    display: block;
+    font-size: 9px;
+    color: rgba(255, 255, 255, 0.6);
+    margin-top: 2px;
+  }
+}
+
+.effect-compare {
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 8px;
+  padding: 10px;
+}
+
+.compare-title {
+  font-size: 11px;
+  color: #30DCFF;
+  font-weight: 600;
+  text-align: center;
+  margin-bottom: 10px;
+}
+
+.compare-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+}
+
+.compare-card {
+  flex: 1;
+  background: rgba(255, 255, 255, 0.05);
+  border-radius: 6px;
+  padding: 8px;
+  &.before {
+    border: 1px solid rgba(255, 107, 107, 0.3);
+    .card-label {
+      color: #ff6b6b;
+    }
+  }
+  &.after {
+    border: 1px solid rgba(48, 220, 255, 0.3);
+    .card-label {
+      color: #30DCFF;
+    }
+  }
+}
+
+.card-label {
+  font-size: 10px;
+  font-weight: 600;
+  text-align: center;
+  margin-bottom: 6px;
+}
+
+.card-items {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.card-item {
+  display: flex;
+  justify-content: space-between;
+  font-size: 10px;
+  .item-label {
+    color: rgba(255, 255, 255, 0.5);
+  }
+  .item-value {
+    color: rgba(255, 255, 255, 0.8);
+    &.highlight {
+      color: #30DCFF;
+      font-weight: 600;
+    }
+  }
+}
+
+.compare-arrow {
+  font-size: 18px;
+  color: #30DCFF;
+  font-weight: bold;
+}
+</style>

+ 304 - 0
src/views/waterQuality/components/SystemIntegration.vue

@@ -0,0 +1,304 @@
+<template>
+  <m-card title="机制体制融合" :width="398" :height="450">
+    <div class="system-integration">
+      <div class="core-indicator">
+        <div class="indicator-main">
+          <span class="main-value">92</span>
+          <span class="main-unit">%</span>
+        </div>
+        <div class="indicator-label">协同工作考核优秀率</div>
+      </div>
+      <div class="stats-grid">
+        <div class="stat-item" v-for="(item, index) in statsData" :key="index">
+          <div class="stat-value">{{ item.value }}</div>
+          <div class="stat-label">{{ item.label }}</div>
+        </div>
+      </div>
+      <div class="architecture-flow">
+        <div class="architecture-title">架构体系</div>
+        <div class="architecture-chain">
+          <div class="arch-node">
+            <div class="node-icon">🎯</div>
+            <div class="node-label">融合发展委员会</div>
+          </div>
+          <div class="arch-arrow">→</div>
+          <div class="arch-node">
+            <div class="node-icon">👥</div>
+            <div class="node-label">项目混编团队</div>
+          </div>
+          <div class="arch-arrow">→</div>
+          <div class="arch-node">
+            <div class="node-icon">🏢</div>
+            <div class="node-label">基层工作组</div>
+          </div>
+        </div>
+        <div class="flow-title">运作流程</div>
+        <div class="flow-chain">
+          <div class="flow-item" v-for="(item, index) in flowData" :key="index" :class="{ active: index === currentStep }">
+            <div class="flow-number">{{ index + 1 }}</div>
+            <div class="flow-label">{{ item.label }}</div>
+          </div>
+        </div>
+      </div>
+      <div class="action-buttons">
+        <button class="action-btn primary" @click="showTeamDetail">
+          <span>📋</span> 查看混编团队详情
+        </button>
+        <button class="action-btn secondary" @click="showOfficeScene">
+          <span>🏢</span> 查看办公场景
+        </button>
+      </div>
+    </div>
+  </m-card>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue"
+import mCard from "@/components/mCard/index.vue"
+
+const currentStep = ref(1)
+
+const statsData = ref([
+  { value: "132", label: "共同决策事项" },
+  { value: "4", label: "混编共建团队" },
+  { value: "365", label: "联合值班(天)" },
+  { value: "156", label: "共派监测人员" },
+])
+
+const flowData = ref([
+  { label: "统一派遣" },
+  { label: "统一管理" },
+  { label: "统一考核" },
+])
+
+let stepInterval = null
+
+onMounted(() => {
+  startStepAnimation()
+})
+
+onUnmounted(() => {
+  if (stepInterval) clearInterval(stepInterval)
+})
+
+function startStepAnimation() {
+  stepInterval = setInterval(() => {
+    currentStep.value = (currentStep.value + 1) % flowData.value.length
+  }, 3000)
+}
+
+function showTeamDetail() {
+  console.log("查看混编团队详情")
+}
+
+function showOfficeScene() {
+  console.log("查看办公场景")
+}
+</script>
+
+<style lang="scss" scoped>
+.system-integration {
+  padding: 12px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  box-sizing: border-box;
+}
+
+.core-indicator {
+  text-align: center;
+  padding: 12px;
+  background: linear-gradient(135deg, rgba(48, 220, 255, 0.1) 0%, rgba(0, 191, 255, 0.05) 100%);
+  border-radius: 8px;
+  border: 1px solid rgba(48, 220, 255, 0.3);
+}
+
+.indicator-main {
+  margin-bottom: 5px;
+}
+
+.main-value {
+  font-size: 40px;
+  font-weight: 700;
+  color: #30DCFF;
+  font-family: "D-DIN";
+  text-shadow: 0 0 20px rgba(48, 220, 255, 0.5);
+}
+
+.main-unit {
+  font-size: 20px;
+  color: rgba(48, 220, 255, 0.8);
+  font-weight: 600;
+}
+
+.indicator-label {
+  color: rgba(255, 255, 255, 0.8);
+  font-size: 12px;
+  letter-spacing: 1px;
+}
+
+.stats-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 6px;
+}
+
+.stat-item {
+  background: rgba(0, 180, 255, 0.08);
+  border: 1px solid rgba(0, 180, 255, 0.2);
+  border-radius: 4px;
+  padding: 6px 4px;
+  text-align: center;
+  transition: all 0.3s ease;
+  &:hover {
+    background: rgba(0, 180, 255, 0.15);
+    border-color: rgba(48, 220, 255, 0.5);
+  }
+}
+
+.stat-value {
+  font-size: 16px;
+  font-weight: 700;
+  color: #30DCFF;
+  font-family: "D-DIN";
+  margin-bottom: 2px;
+}
+
+.stat-label {
+  font-size: 9px;
+  color: rgba(255, 255, 255, 0.6);
+  line-height: 1.2;
+}
+
+.architecture-flow {
+  flex: 1;
+  background: rgba(0, 180, 255, 0.05);
+  border-radius: 6px;
+  padding: 8px;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.architecture-title,
+.flow-title {
+  font-size: 11px;
+  color: #30DCFF;
+  font-weight: 600;
+  padding-bottom: 4px;
+  border-bottom: 1px solid rgba(48, 220, 255, 0.2);
+}
+
+.architecture-chain {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 4px;
+  padding: 6px 0;
+}
+
+.arch-node {
+  background: rgba(48, 220, 255, 0.15);
+  border: 1px solid rgba(48, 220, 255, 0.4);
+  border-radius: 6px;
+  padding: 6px 8px;
+  text-align: center;
+  min-width: 80px;
+}
+
+.node-icon {
+  font-size: 16px;
+  margin-bottom: 2px;
+}
+
+.node-label {
+  font-size: 9px;
+  color: rgba(255, 255, 255, 0.9);
+}
+
+.arch-arrow {
+  color: #30DCFF;
+  font-size: 14px;
+  font-weight: bold;
+}
+
+.flow-chain {
+  display: flex;
+  justify-content: space-around;
+  padding: 6px 0;
+}
+
+.flow-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+  opacity: 0.5;
+  transition: all 0.3s ease;
+  &.active {
+    opacity: 1;
+    .flow-number {
+      background: linear-gradient(135deg, #30DCFF, #00BFFF);
+      box-shadow: 0 0 15px rgba(48, 220, 255, 0.6);
+    }
+  }
+}
+
+.flow-number {
+  width: 20px;
+  height: 20px;
+  border-radius: 50%;
+  background: rgba(255, 255, 255, 0.2);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 11px;
+  font-weight: 700;
+  color: #fff;
+  transition: all 0.3s ease;
+}
+
+.flow-label {
+  font-size: 11px;
+  color: rgba(255, 255, 255, 0.8);
+}
+
+.action-buttons {
+  display: flex;
+  gap: 10px;
+}
+
+.action-btn {
+  flex: 1;
+  padding: 10px 12px;
+  border-radius: 6px;
+  border: none;
+  cursor: pointer;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+  transition: all 0.3s ease;
+  &.primary {
+    background: linear-gradient(135deg, #30DCFF, #00BFFF);
+    color: #fff;
+    &:hover {
+      box-shadow: 0 0 15px rgba(48, 220, 255, 0.5);
+      transform: translateY(-2px);
+    }
+  }
+  &.secondary {
+    background: rgba(48, 220, 255, 0.15);
+    border: 1px solid rgba(48, 220, 255, 0.4);
+    color: #30DCFF;
+    &:hover {
+      background: rgba(48, 220, 255, 0.25);
+    }
+  }
+  span {
+    font-size: 14px;
+  }
+}
+</style>

+ 114 - 0
src/views/waterQuality/index.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="water-resource-content">
+    <div class="left-column">
+      <div class="left-column-3d">
+        <div class="module-card">
+          <SystemIntegration />
+        </div>
+        <div class="module-card">
+          <IntegrationEffect />
+        </div>
+      </div>
+    </div>
+    <div class="right-column">
+      <div class="right-column-3d">
+        <div class="module-card">
+          <CollaborativeProcess />
+        </div>
+        <div class="module-card">
+          <DemonstrationBase />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import SystemIntegration from "./components/SystemIntegration.vue"
+import CollaborativeProcess from "./components/CollaborativeProcess.vue"
+import IntegrationEffect from "./components/IntegrationEffect.vue"
+import DemonstrationBase from "./components/DemonstrationBase.vue"
+</script>
+
+<style lang="scss">
+.water-resource-content {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 2;
+  pointer-events: none;
+}
+
+.left-column {
+  position: absolute;
+  z-index: 4;
+  width: 398px;
+  left: 32px;
+  top: 150px;
+  bottom: 50px;
+  perspective: 500px;
+  perspective-origin: 50% 50%;
+  .left-column-3d {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    gap: 20px;
+    transform: translate3d(0px, 0px, 0px) scaleX(1) scaleY(1) rotateX(0deg) rotateY(6deg) rotateZ(0deg) skewX(0deg) skewY(0deg);
+    z-index: 4;
+  }
+  .module-card {
+    height: 450px;
+    background: rgba(0, 20, 40, 0.8);
+    border: 1px solid rgba(48, 220, 255, 0.3);
+    border-radius: 10px;
+    box-shadow: 0 0 20px rgba(48, 220, 255, 0.1);
+    transform: translateX(-150%);
+    opacity: 0;
+    &:first-child {
+      margin-top: -50px;
+    }
+  }
+}
+
+.right-column {
+  position: absolute;
+  z-index: 4;
+  width: 398px;
+  right: 32px;
+  top: 150px;
+  bottom: 50px;
+  perspective: 800px;
+  perspective-origin: 50% 50%;
+  .right-column-3d {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    gap: 20px;
+    transform: translate3d(0px, 0px, 0px) scaleX(1) scaleY(1) rotateX(0deg) rotateY(-6deg) rotateZ(0deg) skewX(0deg) skewY(0deg);
+  }
+  .module-card {
+    height: 450px;
+    background: rgba(0, 20, 40, 0.8);
+    border: 1px solid rgba(48, 220, 255, 0.3);
+    border-radius: 10px;
+    box-shadow: 0 0 20px rgba(48, 220, 255, 0.1);
+    transform: translateX(150%);
+    opacity: 0;
+    &:first-child {
+      margin-top: -30px;
+    }
+  }
+}
+</style>