Bläddra i källkod

新增苏州地图

BAI 1 månad sedan
förälder
incheckning
4d3a4d73ee

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/assets/json/苏州区县点数据.json


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
public/assets/json/苏州市.json


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/assets/苏州区县点数据.json


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
src/assets/苏州市.json


+ 10 - 0
src/views/map/assets.js

@@ -56,6 +56,16 @@ export class Assets {
         name: "mapStroke",
         path: base_url + "assets/json/昆山-轮廓.json",
       },
+      {
+        type: "File",
+        name: "suzhouJson",
+        path: base_url + "assets/json/苏州市.json",
+      },
+      {
+        type: "File",
+        name: "suzhouDistricts",
+        path: base_url + "assets/json/苏州区县点数据.json",
+      },
 
       { type: "Texture", name: "huiguang", path: huiguang },
       { type: "Texture", name: "rotationBorder1", path: rotationBorder1 },

+ 90 - 4
src/views/map/index.vue

@@ -21,10 +21,10 @@
     </div>
     <!-- 视频背景 -->
     <div class="video-background" v-if="state.showVideoPlayer">
-      <video 
+      <video
         ref="videoPlayerRef"
         class="video-bg-player"
-        src="@/assets/昆山水文站.mp4" 
+        src="@/assets/昆山水文站.mp4"
         autoplay
         loop
         muted
@@ -34,6 +34,15 @@
         <span>返回</span>
       </div>
     </div>
+    <!-- 地图切换按钮 -->
+    <div class="map-switch-btns" v-show="state.activeIndex === '1' || state.activeIndex === '3'">
+      <div class="map-switch-btn" :class="{ active: state.currentMapMode === 'suzhou' }" @click="switchToSuzhouMap">
+        <span>苏州地图</span>
+      </div>
+      <div class="map-switch-btn" :class="{ active: state.currentMapMode === 'kunshan' }" @click="switchToKunshanMap">
+        <span>昆山地图</span>
+      </div>
+    </div>
     <div class="vignette-overlay"></div>
     <div class="large-screen-wrap" id="large-screen">
       <m-header title="昆山水务水文系统" sub-text="Hydrological Visualization System">
@@ -46,8 +55,8 @@
           <div class="m-header-date"><span>2025-12-11</span><span>17:53:16</span></div>
         </template>
       </m-header>
-      <!-- 顶部菜单 -->
-      <div class="top-menu">
+      <!-- 顶部菜单 - 仅在昆山地图模式下显示 -->
+      <div class="top-menu" v-show="state.currentMapMode === 'kunshan'">
         <mMenu :default-active="state.activeIndex" @select="handleMenuSelect">
           <mMenuItem index="1">区域总览</mMenuItem>
           <mMenuItem index="2">融合体系</mMenuItem>
@@ -198,6 +207,8 @@ const state = reactive({
   historyImageSrc: new URL("@/assets/video/昆山水务水文水质监测.png", import.meta.url).href,
   // 是否显示历史沿革图片
   showHistoryImage: false,
+  // 当前地图模式: 'kunshan' | 'suzhou',默认苏州地图
+  currentMapMode: 'suzhou',
   // 卡片统计数据
   totalView: [
     {
@@ -231,6 +242,10 @@ onMounted(() => {
   emitter.$on("mapPlayComplete", handleMapPlayComplete)
   // 监听水文站图标点击事件
   emitter.$on("waterStationClick", handleWaterStationClick)
+  // 监听地图模式变化
+  emitter.$on("mapModeChanged", handleMapModeChanged)
+  // 监听切换到区域总览页面事件
+  emitter.$on("switchToRegionOverview", handleSwitchToRegionOverview)
   // 自动适配
   assets.value = autofit.init({
     dh: 1080,
@@ -251,8 +266,37 @@ onMounted(() => {
 onBeforeUnmount(() => {
   emitter.$off("mapPlayComplete", handleMapPlayComplete)
   emitter.$off("waterStationClick", handleWaterStationClick)
+  emitter.$off("mapModeChanged", handleMapModeChanged)
+  emitter.$off("switchToRegionOverview", handleSwitchToRegionOverview)
 })
 
+// 处理地图模式变化
+function handleMapModeChanged(mode) {
+  state.currentMapMode = mode
+}
+
+// 处理切换到区域总览页面
+function handleSwitchToRegionOverview() {
+  // 切换到区域总览页面(activeIndex 为 1)
+  state.activeIndex = '1'
+}
+
+// 切换到苏州地图
+function switchToSuzhouMap() {
+  if (state.currentMapMode === 'suzhou') return
+  if (mapSceneRef.value) {
+    mapSceneRef.value.switchToSuzhou()
+  }
+}
+
+// 切换到昆山地图
+function switchToKunshanMap() {
+  if (state.currentMapMode === 'kunshan') return
+  if (mapSceneRef.value) {
+    mapSceneRef.value.switchToKunshan()
+  }
+}
+
 // 处理水文站图标点击事件
 function handleWaterStationClick(stationInfo) {
   console.log('播放水文站视频:', stationInfo.name)
@@ -532,6 +576,48 @@ function handleMapPlayComplete() {
   opacity: 0;
 }
 
+// 地图切换按钮样式
+.map-switch-btns {
+  position: absolute;
+  left: 50%;
+  bottom: 100px;
+  transform: translateX(-50%);
+  z-index: 10;
+  display: flex;
+  gap: 10px;
+  pointer-events: auto;
+
+  .map-switch-btn {
+    padding: 8px 20px;
+    background: rgba(0, 20, 40, 0.85);
+    border: 1px solid rgba(48, 220, 255, 0.3);
+    border-radius: 4px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+
+    span {
+      color: #a3dcde;
+      font-size: 14px;
+    }
+
+    &:hover {
+      background: rgba(48, 220, 255, 0.2);
+      border-color: rgba(48, 220, 255, 0.6);
+    }
+
+    &.active {
+      background: rgba(48, 220, 255, 0.3);
+      border-color: rgba(48, 220, 255, 0.8);
+      box-shadow: 0 0 15px rgba(48, 220, 255, 0.4);
+
+      span {
+        color: #30dcff;
+        font-weight: bold;
+      }
+    }
+  }
+}
+
 .fusion-bg, .history-bg, .culture-bg, .science-bg {
   position: absolute;
   z-index: 1;

+ 564 - 13
src/views/map/map.js

@@ -97,17 +97,32 @@ export class World extends Mini3d {
   constructor(canvas, assets) {
     super(canvas)
     
-    // 地图配置参数
-    this.geoProjectionCenter = [120.98422, 31.39244] // 地图中心点坐标(昆山市)
-    this.geoProjectionScale = 1200 // 地图缩放比例
-    this.flyLineCenter = [120.98422, 31.39244] // 飞线中心点
+    // 地图配置参数 - 默认苏州地图
+    this.geoProjectionCenter = [120.62, 31.32] // 地图中心点坐标(苏州市)
+    this.geoProjectionScale = 800 // 地图缩放比例
+    this.flyLineCenter = [120.62, 31.32] // 飞线中心点
     this.depth = 0.5 // 地图拉伸高度
-    
+
     // 地图焦点标签信息
     this.mapFocusLabelInfo = {
-      name: "昆山市",
-      enName: "KUNSHAN CITY",
-      center: [120.98422, 31.39244],
+      name: "苏州市",
+      enName: "SUZHOU CITY",
+      center: [120.62, 31.32],
+    }
+
+    // 当前地图模式: 'kunshan' | 'suzhou'
+    this.currentMapMode = 'suzhou'
+
+    // 昆山地图配置
+    this.kunshanConfig = {
+      geoProjectionCenter: [120.98422, 31.39244],
+      geoProjectionScale: 1200,
+      flyLineCenter: [120.98422, 31.39244],
+      mapFocusLabelInfo: {
+        name: "昆山市",
+        enName: "KUNSHAN CITY",
+        center: [120.98422, 31.39244],
+      }
     }
     
     // 是否显示水文站图标
@@ -145,6 +160,7 @@ export class World extends Mini3d {
     // 初始化标签组
     this.labelGroup = new Group() // 创建标签组
     this.label3d = new Label3d(this) // 创建3D标签实例
+    this.label3d.setRenderLevel(10) // 设置标签层级,确保显示在其他元素之上
     this.labelGroup.rotation.x = -Math.PI / 2 // 旋转标签组使其水平
     this.scene.add(this.labelGroup) // 添加到场景
     
@@ -512,9 +528,17 @@ export class World extends Mini3d {
     
     // 添加到场景
     this.scene.add(mapGroup)
-    
+
     // 创建柱状图
     this.createBar()
+
+    // 苏州地图模式下创建区县标注,并控制柱状图显示/隐藏
+    if (this.currentMapMode === 'suzhou') {
+      this.createSuzhouDistrictLabels()
+      this.setBarVisibility(false)
+    } else {
+      this.setBarVisibility(true)
+    }
   }
   /**
    * 创建中国地图
@@ -573,8 +597,10 @@ export class World extends Mini3d {
    * @returns {Object} 包含省份地图、顶部地图和地图线条的对象
    */
   createProvince() {
-    // 获取省份地图数据
-    let mapJsonData = this.assets.instance.getResource("mapJson")
+    // 根据当前模式获取地图数据,默认苏州地图
+    let mapJsonData = this.currentMapMode === 'kunshan'
+      ? this.assets.instance.getResource("mapJson")
+      : this.assets.instance.getResource("suzhouJson")
     
     // 创建省份地图材质
     let [topMaterial, sideMaterial] = this.createProvinceMaterial()
@@ -803,8 +829,19 @@ export class World extends Mini3d {
   /**
    * 创建柱状图
    * 功能:根据省份数据创建3D柱状图,包括柱子、光圈、辉光效果和标签
+   * 苏州地图模式下不显示柱状图
    */
   createBar() {
+    // 苏州地图模式下不创建柱状图
+    if (this.currentMapMode === 'suzhou') {
+      this.barGroup = null
+      this.allBar = []
+      this.allBarMaterial = []
+      this.allGuangquan = []
+      this.allProvinceLabel = []
+      return
+    }
+
     let self = this
     let data = sortByValue(provincesData).filter((item, index) => index < 10)
     const barGroup = new Group()
@@ -895,7 +932,14 @@ export class World extends Mini3d {
     this.eventElement.map((mesh) => {
       this.interactionManager.add(mesh)
       mesh.addEventListener("mousedown", (ev) => {
-        console.log(ev.target.userData.name)
+        const areaName = ev.target.userData.name
+        console.log(areaName)
+        // 苏州地图模式下,点击昆山区域切换到昆山地图
+        if (this.currentMapMode === 'suzhou' && areaName === '昆山市') {
+          this.switchToKunshanMap()
+          // 通知父组件切换到水文测站页面
+          emitter.$emit('switchToRegionOverview')
+        }
       })
       mesh.addEventListener("mouseover", (event) => {
         if (!objectsHover.includes(event.target.parent)) {
@@ -914,6 +958,185 @@ export class World extends Mini3d {
       })
     })
   }
+
+  /**
+   * 创建苏州区县标注
+   * 功能:在苏州地图上标注各个区县名称
+   */
+  createSuzhouDistrictLabels() {
+    // 获取苏州区县点数据
+    let districtData = this.assets.instance.getResource("suzhouDistricts")
+    console.log('suzhouDistricts data:', districtData)
+    console.log('districtData type:', typeof districtData)
+
+    // 如果数据是字符串,解析为 JSON
+    if (typeof districtData === 'string') {
+      try {
+        districtData = JSON.parse(districtData)
+        console.log('解析后的 districtData:', districtData)
+      } catch (e) {
+        console.error('解析苏州区县数据失败:', e)
+        return
+      }
+    }
+
+    if (districtData) {
+      console.log('districtData.features:', districtData.features)
+      console.log('districtData.features length:', districtData.features ? districtData.features.length : 0)
+    }
+    if (!districtData || !districtData.features) {
+      console.warn('苏州区县数据未加载或格式不正确,尝试延迟加载...')
+      // 如果资源未加载,延迟 1 秒后重试
+      setTimeout(() => {
+        this.createSuzhouDistrictLabels()
+      }, 1000)
+      return
+    }
+
+    // 清除已有标注
+    this.clearSuzhouDistrictLabels()
+    this.suzhouDistrictLabels = []
+
+    districtData.features.forEach((feature, index) => {
+      const { geometry, properties } = feature
+      if (!geometry || !geometry.coordinates) {
+        console.warn(`区县数据格式不正确,索引: ${index}`)
+        return
+      }
+
+      const [lng, lat] = geometry.coordinates
+      const name = properties.地名 || properties.NAME_3 || properties.name
+
+      if (!name) {
+        console.warn(`区县名称不存在,索引: ${index}`, properties)
+        return
+      }
+
+      // 使用地理投影转换坐标
+      const [x, y] = this.geoProjection([lng, lat])
+      console.log(`创建区县标签: ${name}, 坐标: [${x}, ${y}]`)
+
+      // 创建区县标签 - 增加高度确保标签显示在地图上方
+      const label = this.createDistrictLabel(name, new Vector3(x, -y, this.depth + 0.5), name === '昆山市')
+      this.suzhouDistrictLabels.push(label)
+    })
+
+    console.log(`共创建 ${this.suzhouDistrictLabels.length} 个区县标签`)
+  }
+
+  /**
+   * 创建单个区县标签
+   * @param {string} name - 区县名称
+   * @param {Vector3} position - 标签位置
+   * @param {boolean} isClickable - 是否可点击(用于昆山市下钻)
+   * @returns {Object} 标签对象
+   */
+  createDistrictLabel(name, position, isClickable = false) {
+    console.log(`createDistrictLabel: ${name}, position:`, position)
+    const labelClass = isClickable ? 'district-label district-label-clickable' : 'district-label'
+    const label = this.label3d.create("", labelClass, false)
+    console.log(`label created, element:`, label.element)
+    label.init(
+      `<div class="district-label-wrap">
+        <span class="name">${name}</span>
+      </div>`,
+      position
+    )
+    console.log(`label initialized, element innerHTML:`, label.element.innerHTML)
+    this.label3d.setLabelStyle(label, 0.02, "x", Math.PI / 2, "auto")
+    label.setParent(this.labelGroup)
+    console.log(`label added to labelGroup, labelGroup children count:`, this.labelGroup.children.length)
+
+    // 如果是昆山市标签,添加点击事件
+    if (isClickable) {
+      const labelElement = label.element.querySelector('.district-label-wrap')
+      if (labelElement) {
+        labelElement.style.cursor = 'pointer'
+        labelElement.addEventListener('click', (e) => {
+          e.stopPropagation()
+          e.preventDefault()
+          console.log('昆山市标签被点击,切换到昆山地图')
+          this.switchToKunshanMap()
+          // 通知父组件切换到区域总览页面
+          emitter.$emit('switchToRegionOverview')
+        })
+
+        // 添加鼠标悬停效果
+        labelElement.addEventListener('mouseenter', () => {
+          labelElement.style.transform = 'scale(1.1)'
+          labelElement.style.transition = 'transform 0.2s ease'
+        })
+
+        labelElement.addEventListener('mouseleave', () => {
+          labelElement.style.transform = 'scale(1)'
+        })
+      }
+    }
+
+    return label
+  }
+
+  /**
+   * 清除苏州区县标注
+   */
+  clearSuzhouDistrictLabels() {
+    console.log('clearSuzhouDistrictLabels - 清除前标签数量:', this.suzhouDistrictLabels ? this.suzhouDistrictLabels.length : 0)
+    if (this.suzhouDistrictLabels) {
+      this.suzhouDistrictLabels.forEach((label, index) => {
+        if (label) {
+          console.log(`清除标签 ${index}`)
+          // 使用 label.remove() 从父级 Group 中移除
+          if (label.remove) {
+            label.remove()
+          }
+          // 同时隐藏元素
+          if (label.element) {
+            label.element.style.visibility = 'hidden'
+            label.element.style.display = 'none'
+          }
+        }
+      })
+      this.suzhouDistrictLabels = []
+    }
+    console.log('clearSuzhouDistrictLabels - 清除完成')
+  }
+
+  /**
+   * 隐藏柱状图标签
+   */
+  hideBarLabels() {
+    // 隐藏柱状图
+    if (this.barGroup) {
+      this.barGroup.visible = false
+    }
+    // 隐藏柱状图标签
+    if (this.allProvinceLabel) {
+      this.allProvinceLabel.forEach(label => {
+        if (label && label.hide) {
+          label.hide()
+        }
+      })
+    }
+  }
+
+  /**
+   * 显示柱状图标签
+   */
+  showBarLabels() {
+    // 显示柱状图
+    if (this.barGroup) {
+      this.barGroup.visible = true
+    }
+    // 显示柱状图标签
+    if (this.allProvinceLabel) {
+      this.allProvinceLabel.forEach(label => {
+        if (label && label.show) {
+          label.show()
+        }
+      })
+    }
+  }
+
   /**
    * 创建辉光效果
    * 功能:为柱状图创建辉光效果,增强视觉效果
@@ -1683,7 +1906,10 @@ export class World extends Mini3d {
    * 功能:在地图上创建轮廓线,增强地图的边界感和细节
    */
   createStorke() {
-    const mapStroke = this.assets.instance.getResource("mapStroke") // 获取轮廓数据
+    // 根据当前模式获取轮廓数据,默认苏州地图
+    const mapStroke = this.currentMapMode === 'kunshan'
+      ? this.assets.instance.getResource("mapStroke")
+      : this.assets.instance.getResource("suzhouJson")
     const texture = this.assets.instance.getResource("pathLine3") // 获取轮廓纹理
     texture.wrapS = texture.wrapT = RepeatWrapping // 设置纹理包装方式
     texture.repeat.set(2, 1) // 设置纹理重复
@@ -1734,6 +1960,331 @@ export class World extends Mini3d {
     this.interactionManager && this.interactionManager.update() // 更新交互管理器
   }
   
+  /**
+   * 切换到苏州地图
+   * 功能:从昆山地图切换到苏州地图
+   */
+  switchToSuzhouMap() {
+    if (this.currentMapMode === 'suzhou') return
+
+    this.currentMapMode = 'suzhou'
+
+    // 应用苏州配置
+    this.geoProjectionCenter = [120.62, 31.32]
+    this.geoProjectionScale = 800
+    this.flyLineCenter = [120.62, 31.32]
+    this.mapFocusLabelInfo = {
+      name: "苏州市",
+      enName: "SUZHOU CITY",
+      center: [120.62, 31.32],
+    }
+
+    // 重新加载地图数据
+    this.reloadMapData('suzhouJson')
+
+    // 触发事件通知父组件
+    emitter.$emit('mapModeChanged', 'suzhou')
+  }
+
+  /**
+   * 切换回昆山地图
+   * 功能:从苏州地图切换回昆山地图
+   */
+  switchToKunshanMap() {
+    if (this.currentMapMode === 'kunshan') return
+
+    this.currentMapMode = 'kunshan'
+
+    // 应用昆山配置
+    this.geoProjectionCenter = [120.98422, 31.39244]
+    this.geoProjectionScale = 1200
+    this.flyLineCenter = [120.98422, 31.39244]
+    this.mapFocusLabelInfo = {
+      name: "昆山市",
+      enName: "KUNSHAN CITY",
+      center: [120.98422, 31.39244],
+    }
+
+    // 重新加载地图数据
+    this.reloadMapData('mapJson')
+
+    // 触发事件通知父组件
+    emitter.$emit('mapModeChanged', 'kunshan')
+  }
+
+  /**
+   * 重新加载地图数据
+   * @param {string} resourceName - 资源名称
+   */
+  reloadMapData(resourceName) {
+    // 清除现有的焦点地图组
+    if (this.focusMapGroup) {
+      // 移除旧的地图元素
+      while (this.focusMapGroup.children.length > 0) {
+        const child = this.focusMapGroup.children[0]
+        this.focusMapGroup.remove(child)
+        if (child.geometry) child.geometry.dispose()
+        if (child.material) {
+          if (Array.isArray(child.material)) {
+            child.material.forEach(m => m.dispose())
+          } else {
+            child.material.dispose()
+          }
+        }
+      }
+    }
+
+    // 获取新的地图数据
+    const mapJsonData = this.assets.instance.getResource(resourceName)
+
+    // 重新创建省份地图
+    let [topMaterial, sideMaterial] = this.createProvinceMaterial()
+    this.focusMapTopMaterial = topMaterial
+    this.focusMapSideMaterial = sideMaterial
+
+    // 创建拉伸地图
+    let map = new ExtrudeMap(this, {
+      geoProjectionCenter: this.geoProjectionCenter,
+      geoProjectionScale: this.geoProjectionScale,
+      position: new Vector3(0, 0, 0.11),
+      data: mapJsonData,
+      depth: this.depth,
+      topFaceMaterial: topMaterial,
+      sideMaterial: sideMaterial,
+      renderOrder: 9,
+    })
+
+    // 创建顶部材质
+    let faceMaterial = new MeshStandardMaterial({
+      color: 0xffffff,
+      transparent: true,
+      opacity: 0.5,
+    })
+
+    // 添加渐变着色器
+    let faceGradientShader = new GradientShader(faceMaterial, {
+      uColor1: 0x12bbe0,
+      uColor2: 0x0094b5,
+    })
+
+    // 存储默认材质和高亮材质
+    this.defaultMaterial = faceMaterial
+    this.defaultLightMaterial = this.defaultMaterial.clone()
+    this.defaultLightMaterial.color = new Color("rgba(115,208,255,1)")
+    this.defaultLightMaterial.opacity = 0.8
+
+    // 创建顶部地图
+    let mapTop = new BaseMap(this, {
+      geoProjectionCenter: this.geoProjectionCenter,
+      geoProjectionScale: this.geoProjectionScale,
+      position: new Vector3(0, 0, this.depth + 0.22),
+      data: mapJsonData,
+      material: faceMaterial,
+      renderOrder: 2,
+    })
+
+    // 收集可交互的网格元素
+    this.eventElement = []
+    mapTop.mapGroup.children.map((group) => {
+      group.children.map((mesh) => {
+        if (mesh.type === "Mesh") {
+          this.eventElement.push(mesh)
+        }
+      })
+    })
+
+    // 创建地图线条
+    this.mapLineMaterial = new LineBasicMaterial({
+      color: 0xffffff,
+      opacity: 0,
+      transparent: true,
+      fog: false,
+    })
+
+    let mapLine = new Line(this, {
+      geoProjectionCenter: this.geoProjectionCenter,
+      geoProjectionScale: this.geoProjectionScale,
+      data: mapJsonData,
+      material: this.mapLineMaterial,
+      renderOrder: 3,
+    })
+    mapLine.lineGroup.position.z += this.depth + 0.23
+
+    // 添加到焦点地图组
+    map.setParent(this.focusMapGroup)
+    mapTop.setParent(this.focusMapGroup)
+    mapLine.setParent(this.focusMapGroup)
+
+    // 更新标签
+    this.updateLabels()
+
+    // 更新飞线
+    this.updateFlyLines()
+
+    // 更新轮廓
+    this.updateStroke()
+
+    // 重新创建柱状图
+    this.recreateBar()
+
+    // 处理苏州区县标注和柱状图标签的显示/隐藏
+    console.log('reloadMapData - currentMapMode:', this.currentMapMode)
+    if (this.currentMapMode === 'suzhou') {
+      // 苏州模式:显示区县标签,隐藏柱状图
+      console.log('切换到苏州模式:显示区县标签,隐藏柱状图')
+      this.createSuzhouDistrictLabels()
+      this.setBarVisibility(false)
+    } else {
+      // 昆山模式:隐藏区县标签,显示柱状图
+      console.log('切换到昆山模式:隐藏区县标签,显示柱状图')
+      this.clearSuzhouDistrictLabels()
+      this.setBarVisibility(true)
+    }
+  }
+
+  /**
+   * 更新标签
+   */
+  updateLabels() {
+    console.log('updateLabels - mapFocusLabelInfo:', this.mapFocusLabelInfo)
+    // 更新地图焦点标签
+    if (this.otherLabel) {
+      console.log('updateLabels - otherLabel 数量:', this.otherLabel.length)
+      this.otherLabel.forEach((label, index) => {
+        console.log(`标签 ${index}:`, label.element ? label.element.className : '无 element')
+        // 检查标签是否是地图焦点标签(通过 className 判断)
+        if (label.element && label.element.classList.contains('map-label')) {
+          // 更新标签内容
+          const [x, y] = this.geoProjection(this.mapFocusLabelInfo.center)
+          console.log(`更新标签位置: [${x}, ${y}]`)
+          label.position.set(x, -y, 0.4)
+
+          // 更新文字
+          const nameEl = label.element.querySelector('span:first-child')
+          const enNameEl = label.element.querySelector('span:last-child')
+          console.log('更新文字:', this.mapFocusLabelInfo.name, this.mapFocusLabelInfo.enName)
+          if (nameEl) nameEl.textContent = this.mapFocusLabelInfo.name
+          if (enNameEl) enNameEl.textContent = this.mapFocusLabelInfo.enName
+        }
+      })
+    } else {
+      console.log('updateLabels - otherLabel 不存在')
+    }
+  }
+
+  /**
+   * 更新飞线
+   */
+  updateFlyLines() {
+    if (this.flyLineGroup) {
+      // 清除旧飞线
+      while (this.flyLineGroup.children.length > 0) {
+        this.flyLineGroup.remove(this.flyLineGroup.children[0])
+      }
+
+      // 重新创建飞线
+      this.createFlyLine()
+    }
+  }
+
+  /**
+   * 重新创建柱状图
+   * 功能:切换地图模式时重新创建或清除柱状图
+   */
+  recreateBar() {
+    console.log('recreateBar - currentMapMode:', this.currentMapMode)
+    // 清除旧柱状图
+    if (this.barGroup) {
+      console.log('清除旧柱状图')
+      this.scene.remove(this.barGroup)
+      this.barGroup = null
+    }
+    // 清除旧标签
+    if (this.allProvinceLabel) {
+      console.log('清除旧柱状图标签,数量:', this.allProvinceLabel.length)
+      this.allProvinceLabel.forEach(label => {
+        if (label && label.element) {
+          label.element.remove()
+        }
+      })
+      this.allProvinceLabel = []
+    }
+    // 清除光圈
+    if (this.allGuangquan) {
+      this.allGuangquan.forEach(quan => {
+        if (quan && quan.parent) {
+          quan.parent.remove(quan)
+        }
+      })
+      this.allGuangquan = []
+    }
+
+    // 重置数组
+    this.allBar = []
+    this.allBarMaterial = []
+
+    // 重新创建柱状图(昆山模式下)
+    if (this.currentMapMode === 'kunshan') {
+      console.log('昆山模式,重新创建柱状图')
+      this.createBar()
+      console.log('createBar 完成后 - barGroup:', this.barGroup ? '存在' : '不存在')
+      console.log('createBar 完成后 - allProvinceLabel 数量:', this.allProvinceLabel ? this.allProvinceLabel.length : 0)
+    } else {
+      console.log('苏州模式,不创建柱状图')
+    }
+  }
+
+  /**
+   * 设置柱状图和标签的可见性
+   * @param {boolean} visible - 是否可见
+   */
+  setBarVisibility(visible) {
+    console.log('setBarVisibility:', visible)
+    console.log('barGroup:', this.barGroup ? '存在' : '不存在')
+    console.log('allProvinceLabel:', this.allProvinceLabel ? `数量 ${this.allProvinceLabel.length}` : '不存在')
+
+    // 设置柱状图可见性
+    if (this.barGroup) {
+      this.barGroup.visible = visible
+      console.log('设置柱状图可见性:', visible)
+    } else {
+      console.log('barGroup 不存在,无法设置可见性')
+    }
+
+    // 设置柱状图标签可见性
+    if (this.allProvinceLabel && this.allProvinceLabel.length > 0) {
+      console.log('设置柱状图标签可见性,标签数量:', this.allProvinceLabel.length)
+      this.allProvinceLabel.forEach((label, index) => {
+        if (label && label.element) {
+          console.log(`标签 ${index} - element:`, label.element.style.visibility)
+          if (visible) {
+            label.element.style.visibility = 'visible'
+            label.element.style.display = ''
+            label.element.style.opacity = '1'
+            console.log(`标签 ${index} 设置为可见`)
+          } else {
+            label.element.style.visibility = 'hidden'
+            label.element.style.display = 'none'
+            label.element.style.opacity = '0'
+            console.log(`标签 ${index} 设置为隐藏`)
+          }
+        } else {
+          console.log(`标签 ${index} 不存在或没有 element`)
+        }
+      })
+    } else {
+      console.log('allProvinceLabel 为空或不存在')
+    }
+  }
+
+  /**
+   * 更新轮廓
+   */
+  updateStroke() {
+    // 轮廓会在下一次reload时更新
+    // 这里可以添加轮廓更新逻辑
+  }
+
   /**
    * 销毁
    * 功能:销毁场景和资源,释放内存

+ 71 - 0
src/views/map/map.vue

@@ -32,10 +32,24 @@ async function play() {
   canvasMap.value.animateTl.timeScale(1); // 设置播放速度正常
   canvasMap.value.animateTl.play();
 }
+// 切换到苏州地图
+function switchToSuzhou() {
+  if (canvasMap.value) {
+    canvasMap.value.switchToSuzhouMap();
+  }
+}
+// 切换回昆山地图
+function switchToKunshan() {
+  if (canvasMap.value) {
+    canvasMap.value.switchToKunshanMap();
+  }
+}
 defineExpose({
   loadMap,
   play,
   canvasMap,
+  switchToSuzhou,
+  switchToKunshan,
 });
 </script>
 
@@ -295,5 +309,62 @@ defineExpose({
       }
     }
   }
+
+  // 苏州区县标签样式
+  .district-label {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 6px 14px;
+    background: rgba(0, 30, 60, 0.85);
+    border: 1px solid rgba(48, 220, 255, 0.6);
+    border-radius: 4px;
+    box-shadow: 0 0 15px rgba(48, 220, 255, 0.4), inset 0 0 10px rgba(48, 220, 255, 0.1);
+    backdrop-filter: blur(4px);
+    transition: all 0.3s ease;
+    pointer-events: auto;
+
+    &:hover {
+      background: rgba(48, 220, 255, 0.25);
+      border-color: rgba(48, 220, 255, 0.9);
+      box-shadow: 0 0 20px rgba(48, 220, 255, 0.6), inset 0 0 15px rgba(48, 220, 255, 0.2);
+      transform: scale(1.05);
+    }
+
+    .district-label-wrap {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .name {
+      color: #ffffff;
+      font-size: 16px;
+      font-weight: 700;
+      text-shadow: 0 0 8px rgba(48, 220, 255, 0.8), 0 0 15px rgba(48, 220, 255, 0.4);
+      white-space: nowrap;
+      letter-spacing: 2px;
+    }
+
+    // 可点击的昆山市标签样式
+    &.district-label-clickable {
+      background: rgba(0, 60, 120, 0.9);
+      border: 2px solid rgba(255, 200, 50, 0.8);
+      box-shadow: 0 0 20px rgba(255, 200, 50, 0.5), inset 0 0 10px rgba(255, 200, 50, 0.2);
+      cursor: pointer;
+
+      &:hover {
+        background: rgba(255, 200, 50, 0.3);
+        border-color: rgba(255, 220, 100, 1);
+        box-shadow: 0 0 30px rgba(255, 200, 50, 0.8), inset 0 0 15px rgba(255, 200, 50, 0.3);
+        transform: scale(1.1);
+      }
+
+      .name {
+        color: #ffd700;
+        text-shadow: 0 0 10px rgba(255, 200, 50, 1), 0 0 20px rgba(255, 200, 50, 0.6);
+      }
+    }
+  }
 }
 </style>

Vissa filer visades inte eftersom för många filer har ändrats