<template>
  <el-container class="container">
    <!-- 侧边栏 --start -->
    <el-aside :class="asideWidth" id="el-aside">
      <el-menu
        ref="menu"
        :collapse="isCollapse"
        :uniqueOpened="true"
        default-active=""
        class="el-menu-vertical-demo"
        text-color="#303133"
        background-color="#f8f9fa"
        active-text-color="#409eff"
        :collapse-transition="false"
      >
        <!-- 展开收缩控制按钮 -->
        <el-menu-item index="0" @click="isCollapse=!isCollapse" id="fold">
          <i
            class="iconfont iconshouqi iconfont2"
            style="display:block;margin:0;text-align:center"
            :class="isCollapse?'rotate':'rotate2'"
          ></i>
        </el-menu-item>

        <!-- 加载数据 -->
        <el-sub-menu index="1">
          <template #title>
            <i class="iconfont iconshuju iconfont2"></i>
            <span>数据服务</span>
          </template>
          <el-sub-menu v-for="(data,index) in server_config" :key="data.id" :index="data.id" popper-append-to-body="false">
            <template #title>{{data.name}}</template>
            
            <!-- 基础地理实体使用文字列表方式 -->
            <template v-if="data.id === 'vectorData'">
              <el-menu-item 
                v-for="(data2, idx) in data.children" 
                :key="data2.name" 
                :index="data.id + '-' + idx"
                @dblclick="addDatas(data.id, data2)"
                class="vector-data-item"
              >
                <span class="vector-data-name" :class="{ 'vector-data-loaded': data2.state !== 0 }">{{ data2.name }}</span>
                <el-popconfirm
                  v-if="data2.state !== 0"
                  title="确定卸载吗？"
                  confirmButtonText="确认"
                  cancelButtonText="取消"
                  @confirm="DeleteDates(data.id, data2)"
                >
                  <template #reference>
                    <el-icon class="delete-vector-data-icon" title="卸载">
                      <CircleClose />
                    </el-icon>
                  </template>
                </el-popconfirm>
              </el-menu-item>
            </template>
            
            <!-- 其他数据服务使用图片方式 -->
            <div v-else class="box" :index="index">
              <div
                class="imgbox"
                v-for="data2 in data.children"
                :key="data2.name"
                @click="addDatas(data.id,data2)"
              >
                <img :src="data2.thumbnail" alt class="img" />
                <img src="/img/common/cross.png" alt class="cross" v-show="data2.state != 0" />
                <span>{{data2.name}}</span>
                <el-popconfirm
                  title="确定删除吗？"
                  confirmButtonText="确认"
                  cancelButtonText="取消"
                  @confirm="DeleteDates(data.id,data2)"
                >
                  <template #reference>
                    <CircleClose
                      v-show="data2.state != 0 && data.id !='baseLayer'"
                      title="删除"
                      class="circle-close-icon"
                    />
                  </template>
                </el-popconfirm>
              </div>
            </div>
          </el-sub-menu>
          
          <!-- 本地服务二级菜单 -->
          <el-sub-menu index="1-4">
            <template #title>自定义服务</template>
            <el-menu-item 
              v-for="(service, index) in addCustomServices" 
              :key="service.id || index" 
              :index="'1-4-' + (service.id || index)"
              @dblclick="loadCustomService(service)"
              class="custom-service-item"
            >
              <span class="service-name" :class="{ 'service-loaded': isServiceLoaded(service) }">{{ service.name }}</span>
              <el-popconfirm
                v-if="isServiceLoaded(service)"
                title="确定卸载吗？"
                confirmButtonText="确认"
                cancelButtonText="取消"
                @confirm="unloadCustomService(service)"
              >
                <template #reference>
                  <el-icon class="delete-service-icon" title="卸载">
                    <CircleClose />
                  </el-icon>
                </template>
              </el-popconfirm>
            </el-menu-item>
          </el-sub-menu>
          
          <el-menu-item index="1-5" @click="addCustomService" class="no-active-style">
            <el-icon @click.stop="addCustomService" class="square-plus-icon"><Plus /></el-icon>
          </el-menu-item>
        </el-sub-menu>

        <!-- 加载组件 -->
        <el-sub-menu v-for="(data,index) in config" :key="index" :index="data.id">
          <template #title>
            <i :class="data.icon" class="iconfont2"></i>
            <span>{{data.name}}</span>
          </template>
          <div class="box">
            <div
                class="imgbox"
                v-for="data2 in data.children"
                :key="data2.component"
                @click="data2.component && change(data2.component,data2.destroy)"
              >
                <img :src="data2.imgSrc" alt class="img" />
                <img
                  src="/img/common/cross.png"
                  alt
                  class="cross"
                  v-show="data2.component && (data2.component === view || data2.component === view2)"
                />
                <span>{{data2.name}}</span>
              </div>
          </div>
        </el-sub-menu>

        <!-- 模型菜单 -->
        <el-sub-menu index="model" v-if="modelConfig.id">
          <template #title>
            <i :class="modelConfig.icon" class="iconfont2"></i>
            <span>{{modelConfig.name}}</span>
          </template>
          <el-sub-menu 
            v-for="(secondLevel, firstLevelKey) in groupedModels" 
            :key="firstLevelKey" 
            :index="'model-' + firstLevelKey"
            popper-append-to-body="false"
          >
            <template #title>
              <span>{{getFirstLevelName(firstLevelKey)}}</span>
              <el-tag size="small" type="info" style="margin-left: 8px;">{{getTotalCount(secondLevel)}}</el-tag>
            </template>
            <el-sub-menu 
              v-for="(models, secondLevelKey) in secondLevel" 
              :key="secondLevelKey" 
              :index="'model-' + firstLevelKey + '-' + secondLevelKey"
              popper-append-to-body="false"
            >
              <template #title>
                <span>{{getSecondLevelName(secondLevelKey)}}</span>
              </template>
              <el-menu-item 
                v-for="model in models" 
                :key="model.id" 
                :index="'model-' + firstLevelKey + '-' + secondLevelKey + '-' + model.id"
                @dblclick="loadModel(model)"
                class="model-menu-item"
              >
                <span class="model-name" :class="{ 'model-loaded': isModelLoaded(model.id) }">{{ model.name }}</span>
                <el-popconfirm
                  v-if="isModelLoaded(model.id)"
                  title="确定删除吗？"
                  confirmButtonText="确认"
                  cancelButtonText="取消"
                  @confirm="removeModel(model)"
                >
                  <template #reference>
                    <el-icon 
                      class="delete-model-icon" 
                      title="删除模型"
                    >
                      <CircleClose />
                    </el-icon>
                  </template>
                </el-popconfirm>
              </el-menu-item>
            </el-sub-menu>
          </el-sub-menu>
        </el-sub-menu>

        <!-- 台风菜单 -->
        <el-sub-menu index="typhoon">
          <template #title>
            <img src="/img/typhoon.png" alt="" class="typhoon-icon" />
            <span>台风</span>
          </template>
          <!-- 实时台风菜单项 -->
          <el-menu-item index="typhoon-realtime" class="model-menu-item" @click="!showRealtimeTyphoon && loadRealtimeTyphoon()">
            <span class="typhoon-name" :class="{ 'typhoon-loaded': showRealtimeTyphoon }">实时台风</span>
            <el-popconfirm
              v-if="showRealtimeTyphoon"
              title="确定卸载吗？"
              confirmButtonText="确认"
              cancelButtonText="取消"
              @confirm="removeRealtimeTyphoon"
            >
              <template #reference>
                <el-icon
                  class="delete-typhoon-icon"
                  title="卸载台风"
                >
                  <CircleClose />
                </el-icon>
              </template>
            </el-popconfirm>
          </el-menu-item>
          <!-- 风场菜单项 -->
          <el-menu-item index="typhoon-windfield" class="model-menu-item" @click="!showWindField && loadWindField()">
            <span class="typhoon-name" :class="{ 'typhoon-loaded': showWindField }">风场</span>
            <el-popconfirm
              v-if="showWindField"
              title="确定卸载吗？"
              confirmButtonText="确认"
              cancelButtonText="取消"
              @confirm="removeWindField"
            >
              <template #reference>
                <el-icon
                  class="delete-typhoon-icon"
                  title="卸载风场"
                >
                  <CircleClose />
                </el-icon>
              </template>
            </el-popconfirm>
          </el-menu-item>
          <!-- 降雨菜单项 -->
          <el-menu-item index="typhoon-rainfall" class="model-menu-item" @click="!showRainfall && loadRainfall()">
            <span class="typhoon-name" :class="{ 'typhoon-loaded': showRainfall }">降雨</span>
            <el-popconfirm
              v-if="showRainfall"
              title="确定卸载吗？"
              confirmButtonText="确认"
              cancelButtonText="取消"
              @confirm="removeRainfall"
            >
              <template #reference>
                <el-icon
                  class="delete-typhoon-icon"
                  title="卸载降雨"
                >
                  <CircleClose />
                </el-icon>
              </template>
            </el-popconfirm>
          </el-menu-item>
          <!-- 云图菜单项 -->
          <el-menu-item index="typhoon-cloud" class="model-menu-item" @click="!showCloudMap && loadCloudMap()">
            <span class="typhoon-name" :class="{ 'typhoon-loaded': showCloudMap }">云图</span>
            <el-popconfirm
              v-if="showCloudMap"
              title="确定卸载吗？"
              confirmButtonText="确认"
              cancelButtonText="取消"
              @confirm="removeCloudMap"
            >
              <template #reference>
                <el-icon
                  class="delete-typhoon-icon"
                  title="卸载云图"
                >
                  <CircleClose />
                </el-icon>
              </template>
            </el-popconfirm>
          </el-menu-item>
          <!-- 雷达菜单项 -->
          <el-menu-item index="typhoon-radar" class="model-menu-item" @click="!showRadar && loadRadar()">
            <span class="typhoon-name" :class="{ 'typhoon-loaded': showRadar }">雷达</span>
            <el-popconfirm
              v-if="showRadar"
              title="确定卸载吗？"
              confirmButtonText="确认"
              cancelButtonText="取消"
              @confirm="removeRadar"
            >
              <template #reference>
                <el-icon
                  class="delete-typhoon-icon"
                  title="卸载雷达"
                >
                  <CircleClose />
                </el-icon>
              </template>
            </el-popconfirm>
          </el-menu-item>
        </el-sub-menu>

        <!-- 业务场景菜单 -->
        <el-sub-menu index="business-scenario">
          <template #title>
            <i class="iconfont iconxingcheng iconfont2"></i>
            <span>业务场景</span>
          </template>
          
          <!-- 固定场景 -->
          <template v-for="scene in fixedScenes" :key="scene.sceneId">
            <el-menu-item 
              :index="'business-scenario-' + scene.sceneId"
              class="model-menu-item"
              @click="loadFixedScene(scene)"
            >
              <span class="scene-name" :class="{ 'scene-loaded': isSceneLoaded(scene.sceneId) }">{{ scene.sceneName }}</span>
              <el-popconfirm
                v-if="isSceneLoaded(scene.sceneId)"
                title="确定卸载吗？"
                confirmButtonText="确认"
                cancelButtonText="取消"
                @confirm="unloadScene(scene)"
              >
                <template #reference>
                  <el-icon 
                    class="delete-scene-icon" 
                    title="卸载场景"
                  >
                    <CircleClose />
                  </el-icon>
                </template>
              </el-popconfirm>
            </el-menu-item>
          </template>
          
          <!-- 数据库场景 -->
          <template v-for="scene in businessScenes" :key="scene.sceneId">
            <el-menu-item
              :index="'business-scenario-' + scene.sceneId"
              class="model-menu-item"
              @click="loadBusinessScenarioFromDB(scene)"
            >
              <span class="scene-name" :class="{ 'scene-loaded': isSceneLoaded(scene.sceneId) }">{{ scene.sceneName }}</span>
              <el-popconfirm
                v-if="isSceneLoaded(scene.sceneId)"
                title="确定卸载吗？"
                confirmButtonText="确认"
                cancelButtonText="取消"
                @confirm="unloadScene(scene)"
              >
                <template #reference>
                  <el-icon 
                    class="delete-scene-icon" 
                    title="卸载场景"
                  >
                    <CircleClose />
                  </el-icon>
                </template>
              </el-popconfirm>
              <el-popconfirm
                v-else
                title="确定删除吗？"
                confirmButtonText="确认"
                cancelButtonText="取消"
                @confirm="handleDeleteBusinessScene(scene.sceneId)"
              >
                <template #reference>
                  <el-icon 
                    class="delete-scene-icon" 
                    title="删除场景"
                  >
                    <CircleClose />
                  </el-icon>
                </template>
              </el-popconfirm>
            </el-menu-item>
          </template>
          
          <el-menu-item index="business-scenario-create" @click="handleCreateScenario" class="no-active-style">
            <el-icon @click.stop="handleCreateScenario" class="square-plus-icon"><Plus /></el-icon>
            <span>创建场景</span>
          </el-menu-item>
        </el-sub-menu>


      </el-menu>
    </el-aside>
    <!-- 侧边栏部分--end -->

    <!-- 右侧内容展示部分 -->
    <el-main>
      <loading-bar></loading-bar>
      <!-- viewer组件 -->
      <component v-if="initViewer" :is="initViewer" :after-initviewer="removeLoad" :opening-animation="true"></component>
      <!-- 台风可视化组件 -->
      <TyphoonVisualization
        v-if="showTyphoon"
        :viewer="viewer"
        :visible="showTyphoon"
        :data-url="typhoonDataUrl"
      ></TyphoonVisualization>

      <!-- 实时台风可视化组件 -->
      <RealtimeTyphoon
        v-if="showRealtimeTyphoon"
        :viewer="viewer"
        :visible="showRealtimeTyphoon"
      ></RealtimeTyphoon>

      <!-- 风场可视化组件 -->
      <WindFieldVisualization
        v-if="showWindField"
        :viewer="viewer"
        :visible="showWindField"
        :data-url="`${PROXY_SLT}/Api/LastWind`"
      ></WindFieldVisualization>

      <!-- 降雨可视化组件 -->
      <RainfallVisualization
        v-if="showRainfall"
        :viewer="viewer"
        :visible="showRainfall"
        :default-hours="24"
      ></RainfallVisualization>

      <!-- 云图可视化组件 -->
      <CloudMapVisualization
        v-if="showCloudMap"
        :viewer="viewer"
        :visible="showCloudMap"
        :default-type="30"
      ></CloudMapVisualization>

      <!-- 雷达可视化组件 -->
      <RadarVisualization
        v-if="showRadar"
        :viewer="viewer"
        :visible="showRadar"
      ></RadarVisualization>

      <!-- 土地利用图例 -->
      <div class="land-use-legend" v-if="showLandUseLegend">
        <div class="land-use-legend-header">
          <span>土地利用图例</span>
          <span class="land-use-legend-toggle" @click="showLandUseLegend = false">−</span>
        </div>
        <div class="land-use-legend-body">
          <div class="land-use-legend-item" v-for="item in landUseLegendItems" :key="item.value">
            <span class="land-use-legend-color" :style="{ backgroundColor: item.color }"></span>
            <span class="land-use-legend-label">{{ item.label }}</span>
          </div>
        </div>
      </div>

      <!-- 添加自定义服务组件 -->
      <component
        v-if="addService"
        :is="addService"
        :clear-callback="addCustomService"
        :add-callback="addCallback"
        :get-layer-name="getName"
      ></component>
      <!-- 分析功能组件 -->
      <keep-alive>
        <component
          v-if="view"
          :is="view"
          :key="view"
          :delete-callback="deleteCallback"
          data-url="/data/netcdf/result_100_200_9_40.nc"  
          skylineSpatialAnalysisUrl="http://www.supermapol.com/realspace/services/spatialAnalysis-data_all/restjsr/spatialanalyst/geometry/3d/skylinesectorbody.json"
        ></component>
      </keep-alive>
      <!-- 地质体组件,单独展示，需要销毁 -->
      <component v-if="view2" :is="view2" :key="view2"></component>
      
      <!-- 模型拆解弹框 -->
      <ModelDisassemblyDialog
        ref="modelDisassemblyDialog"
        :visible="showModelDialog"
        :model-path="'/models/启闭机.glb'"
        :model-name="'启闭机模型'"
        :icon-config="modelIconConfig"
        @close="showModelDialog = false"
      />

      <!-- 鹰眼组件 -->
      <Sm3dOverviewMap 
        v-if="view === 'Sm3dOverviewMap'" 
        :visible="view === 'Sm3dOverviewMap'"
        @close="view = ''"
      ></Sm3dOverviewMap>
      
      <!-- MVT属性弹窗组件 -->
      <MvtAttributePopup
        :visible="mvtPopupVisible"
        :position="mvtPopupPosition"
        :title="mvtPopupTitle"
        :attributes="mvtPopupAttributes"
        @close="mvtPopupVisible = false"
      />

      <!-- 闸门启闭控制组件（非莒口水闸场景使用） -->
      <GateControl
        v-if="!showJuKouScenePage"
        ref="gateControl"
        :visible="showGateControl"
        :initial-position="gatePosition"
        @close="showGateControl = false"
        @position-change="handleGatePositionChange"
      />
      
      <!-- 莒口水闸业务场景独立页面组件 -->
      <JuKouShuiZhaPage
        v-if="showJuKouScenePage"
        :viewer="viewer"
        :visible="showJuKouScenePage"
        @scene-loaded="onJuKouSceneLoaded"
        @scene-unloaded="onJuKouSceneUnloaded"
      />
    </el-main>
  </el-container>
</template>

<script>
import config from "../../config/views_config.js";  //组件配置
import server_config, { PROXY_TIANDITU_TILE, PROXY_SLT, TIANDITU_KEY } from "../../config/server_config.js";  //服务配置
import server_flyTo_config from "../../config/server_position_config.js";  //服务坐标配置
import layerManagement from "../../js/common/layerManagement.js";  //图层管理封装方法
import waterLayer from "../../js/common/waterLayer.js";  //水面图层
import camera from "../../js/common/camera.js";  //相机操作
import loadingBar from "../../components/loading.vue";  //加载动画
import TyphoonVisualization from "../../components/typhoon-visualization/typhoon-visualization.vue";  //台风可视化组件
import RealtimeTyphoon from "../../components/realtime-typhoon/realtime-typhoon.vue";  //实时台风可视化组件
import WindFieldVisualization from "../../components/wind-field-visualization/wind-field-visualization.vue";  //风场可视化组件
import RainfallVisualization from "../../components/rainfall-visualization/rainfall-visualization.vue";  //降雨可视化组件
import CloudMapVisualization from "../../components/cloud-map-visualization/cloud-map-visualization.vue";  //云图可视化组件
import RadarVisualization from "../../components/radar-visualization/radar-visualization.vue";  //雷达可视化组件
import CesiumThreeFusion from "@/components/ThreeCesiumIntegration/CesiumThreeFusion.vue";  //Three.js与Cesium融合组件
import ModelDisassemblyDialog from "../../components/ModelDisassemblyDialog.vue";  //模型拆解弹框组件
import { CircleClose, Plus, CirclePlus, Setting, Delete } from '@element-plus/icons-vue';  //删除图标
import { listModel, getModel } from '@/api/watershed/model';  //模型API
import { getDefaultMapConfig, saveMapConfig } from '@/api/cesium/mapConfig';  //地图配置API
import serviceApi from '@/api/watershed/service';  //自定义服务API
import { listBusinessScene, addBusinessScene, delBusinessScene } from '@/api/cesium/businessScene';  //业务场景API
import { ElMessage, ElMessageBox } from 'element-plus';
import { getToken } from '@/utils/auth'
import axios from 'axios'
import { onMounted, watch, computed } from 'vue'
import ModelEditor from "../../components/draw/draw-solid-figure/ModelEditor";  //模型编辑器
import MvtAttributePopup from "../../components/MvtAttributePopup.vue";  //MVT属性弹窗组件
import MvtClickHandler from "../../js/common/mvtClickHandler.js";  //MVT点击处理器
import GateControl from "../../components/gate-control/GateControl.vue";  //闸门启闭控制组件
import JuKouShuiZhaPage from "../../components/ju-kou-scene/JuKouShuiZhaPage.vue";  //莒口水闸业务场景页面组件
import { getFixedScenes, isFixedScene } from "../../config/fixedScenes.js";  //固定场景配置
import { loadJuKouScene, unloadJuKouScene, getController, JuKouSceneConfig } from "../../business-scenes/JuKouShuiZhaScene.js";  //莒口水闸业务场景模块

export default {
  name: "layout-aside",
  components: {
    loadingBar,
    CircleClose,
    TyphoonVisualization,
    RealtimeTyphoon,
    WindFieldVisualization,
    RainfallVisualization,
    CloudMapVisualization,
    RadarVisualization,
    CesiumThreeFusion,
    ModelDisassemblyDialog,
    MvtAttributePopup,
    GateControl,
    JuKouShuiZhaPage
  },
  setup() {
    // 使用 computed 来获取 window.viewer
    const viewer = computed(() => window.viewer);
    
    return {
      viewer
    }
  },
  data() {
    return {
      isCollapse: false,
      asideWidth: "asideWidth1",
      view: "",
      view2: "",
      initViewer: "",
      addService: "",
      sceneURL:
        "http://www.supermapol.com/realspace/services/3D-ZF_normal/rest/realspace",
      config: config,
      server_config: server_config,
      // 暴露给模板使用：Options API 模板只能访问 data/computed/methods，
      // 不能直接访问 import 的变量，否则模板里 PROXY_SLT 为 undefined，
      // 导致 :data-url="`${PROXY_SLT}/Api/LastWind`" 变成 "undefined/Api/LastWind"
      PROXY_SLT: PROXY_SLT,
      baseLayerObj: server_config[1].children[0],
      terrainLayerObj: null,
      addCustomServices: [],
      getNames: [],  //记录自定义服务名称
      varName: null, //临时保存当前自定义名称
      loadedCustomServiceIds: [],  //已加载的自定义服务ID
      modelConfig: {},  //模型菜单配置
      modelData: [],  //模型数据
      loadedModelEntities: [],  //已加载的模型实体
      currentModelEditor: null,  //当前模型编辑器
      lastHighlightedModel: null,  //最后高亮的模型
      saveConfigTimer: null,  //保存配置的防抖定时器
      lastSaveTime: 0,  //上次保存时间戳
      showTyphoon: false,  //是否显示台风可视化
      typhoonDataUrl: '',  //台风数据URL
      loadedTyphoonId: null,  //已加载的台风ID
      showRealtimeTyphoon: false,  //是否显示实时台风可视化
      showWindField: false,  //是否显示风场可视化
      showRainfall: false,  //是否显示降雨可视化
      showCloudMap: false,  //是否显示云图可视化
      showRadar: false,  //是否显示雷达可视化
      showLandUseLegend: false,  //是否显示土地利用图例
      landUseLegendItems: [
        { value: 0, color: '#000000', label: '背景/海域无效像元' },
        { value: 1, color: '#FFF8DC', label: '耕地' },
        { value: 2, color: '#006400', label: '有林地' },
        { value: 3, color: '#2E8B57', label: '灌木林地' },
        { value: 4, color: '#98FB98', label: '草地' },
        { value: 5, color: '#00008B', label: '河流水面、湖泊、水库坑塘' },
        { value: 6, color: '#87CEEB', label: '冰川及永久积雪' },
        { value: 7, color: '#D2B48C', label: '裸地、戈壁、盐碱地' },
        { value: 8, color: '#FF00FF', label: '城乡建设用地' },
        { value: 9, color: '#00BFFF', label: '沼泽、滩涂湿地' },
        { value: 10, color: '#000000', label: '园地（果园、茶园等）' },
        { value: 11, color: '#000000', label: '荒漠、重度盐碱地' },
        { value: 12, color: '#000000', label: '独立工矿、公路铁路用地' },
        { value: 13, color: '#000000', label: '其他未利用土地' },
      ],
      showModelDialog: false,  //是否显示模型弹框
      businessScenes: [],  //业务场景列表（从数据库读取）
      fixedScenes: [],  //固定场景列表
      currentSceneId: null,  //当前加载的场景ID
      // 闸门控制相关
      showGateControl: false,  //是否显示闸门控制面板
      gatePosition: 0,  //闸门当前开度(0-100)
      // 莒口水闸场景页面显示
      showJuKouScenePage: false,  //是否显示莒口水闸场景页面组件
      // MVT属性弹窗相关
      mvtPopupVisible: false,  //弹窗是否可见
      mvtPopupPosition: { x: 0, y: 0 },  //弹窗位置
      mvtPopupTitle: '属性信息',  //弹窗标题
      mvtPopupAttributes: {},  //属性数据
      // MVT点击处理器
      mvtClickHandler: null,  //MVT点击处理器实例
      _hasMvtLayer: false,  //是否有MVT图层加载
      // 模型拆解图标配置
      modelIconConfig: null,  //场景模型拆解图标配置
    };
  },

  computed: {
    groupedModels() {
      const grouped = {}
      this.modelData.forEach(model => {
        const type = model.type || '5/5-1'
        const parts = type.split('/')
        const firstLevel = parts[0] || '5'
        const secondLevel = parts[1] || '5-1'
        
        if (!grouped[firstLevel]) {
          grouped[firstLevel] = {}
        }
        if (!grouped[firstLevel][secondLevel]) {
          grouped[firstLevel][secondLevel] = []
        }
        grouped[firstLevel][secondLevel].push(model)
      })
      return grouped
    }
  },

  methods: {
    async fetchCustomServices() {
      try {
        console.log('开始从数据库加载自定义服务...');
        const response = await serviceApi.getServiceList({});
        console.log('获取到的自定义服务:', response);
        
        if (response && response.rows) {
          this.addCustomServices = response.rows.map(service => ({
            id: service.id || service.serviceId,
            name: service.name || service.serviceName,
            type: service.type || service.serviceType,
            url: service.url || service.serviceUrl,
            tokenRequired: service.tokenRequired || service.token_required,
            token: service.token || service.serviceToken,
            status: service.status,
            layers: [{
              type: service.type || service.serviceType,
              layerName: service.name || service.serviceName
            }]
          }));
          console.log('处理后的自定义服务列表:', this.addCustomServices);
        }
      } catch (error) {
        console.error('加载自定义服务失败:', error);
      }
    },

    getModelTypeOptions() {
      return [
        {
          value: '1',
          label: '水利工程实体',
          children: [
            { value: '1-1', label: '水库工程' },
            { value: '1-2', label: '水闸工程' },
            { value: '1-3', label: '泵站工程' },
            { value: '1-4', label: '灌区工程' },
            { value: '1-5', label: '堤防与护岸工程' }
          ]
        },
        {
          value: '2',
          label: '水系水利设施',
          children: [
            { value: '2-1', label: '河流' },
            { value: '2-2', label: '湖泊与水库水面' },
            { value: '2-3', label: '渠道与输水管道' },
            { value: '2-4', label: '河口与海岸带' }
          ]
        },
        {
          value: '3',
          label: '地理环境要素',
          children: [
            { value: '3-1', label: '地形地貌' },
            { value: '3-2', label: '行政区划' },
            { value: '3-3', label: '重要地物' }
          ]
        },
        {
          value: '4',
          label: '自然生态景观',
          children: [
            { value: '4-1', label: '湖泊湿地' },
            { value: '4-2', label: '森林公园' },
            { value: '4-3', label: '地质公园' },
            { value: '4-4', label: '海岸带景观' }
          ]
        },
        {
          value: '5',
          label: '模型集与项目',
          children: [
            { value: '5-1', label: '待分类模型' },
            { value: '5-2', label: 'XX市防洪排涝工程' }
          ]
        }
      ]
    },

    getFirstLevelName(firstLevelKey) {
      const options = this.getModelTypeOptions()
      const option = options.find(opt => opt.value === firstLevelKey)
      return option ? option.label : firstLevelKey
    },

    getSecondLevelName(secondLevelKey) {
      const options = this.getModelTypeOptions()
      for (const opt of options) {
        if (opt.children) {
          const child = opt.children.find(c => c.value === secondLevelKey)
          if (child) return child.label
        }
      }
      return secondLevelKey
    },

    getTotalCount(secondLevel) {
      let count = 0
      for (const key in secondLevel) {
        count += secondLevel[key].length
      }
      return count
    },

    getTypeDisplayName(type) {
      const typeMapping = {
        'RESERVOIR': '水库',
        'HYDROPOWER': '水电站',
        'IRRIGATION': '灌溉',
        'FLOOD_CONTROL': '防洪',
        'CANAL': '渠道',
        'PUMPING_STATION': '泵站',
        'OTHER': '其他'
      }
      
      if (typeMapping[type]) {
        return typeMapping[type]
      }
      
      return type
    },

    async fetchModels() {
      try {
        const data = await listModel({})
        const modelList = data.rows || []
        console.log('获取到的模型数据:', modelList)
        
        this.modelData = modelList.map(model => {
          console.log('模型详情:', model)
          return {
            id: model.id,
            name: model.name,
            type: model.type,
            format: model.format,
            uploadUnit: model.uploadUnit || '未知单位',
            filePath: model.filePath || model.file_path || model.url || '',
            coordinates: model.modelCoordinates || model.coordinates || '',
            rotationX: model.rotationX != null ? model.rotationX : (model.rotation_x != null ? model.rotation_x : 0),
            rotationY: model.rotationY != null ? model.rotationY : (model.rotation_y != null ? model.rotation_y : 0),
            rotationZ: model.rotationZ != null ? model.rotationZ : (model.rotation_z != null ? model.rotation_z : 0),
            scaleX: model.scaleX != null ? model.scaleX : (model.scale_x != null ? model.scale_x : 1),
            scaleY: model.scaleY != null ? model.scaleY : (model.scale_y != null ? model.scale_y : 1),
            scaleZ: model.scaleZ != null ? model.scaleZ : (model.scale_z != null ? model.scale_z : 1),
            originalData: model
          }
        })
        
        console.log('处理后的模型数据:', this.modelData)
        
        if (this.modelData.length > 0) {
          this.modelConfig = {
            id: 'model',
            icon: 'iconfont iconmoxing',
            name: '模型'
          }
        }
      } catch (error) {
        console.error('获取模型数据错误:', error)
      }
    },

    async loadModel(model, shouldFlyTo = true) {
      if (!viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }

      if (!viewer.scene) {
        ElMessage.error('Cesium scene 未初始化')
        return
      }

      try {
        const latestData = await getModel(model.id)
        if (latestData && latestData.data) {
          const latest = latestData.data
          model.coordinates = latest.modelCoordinates || latest.coordinates || model.coordinates
          model.rotationX = latest.rotationX != null ? latest.rotationX : (model.rotationX || 0)
          model.rotationY = latest.rotationY != null ? latest.rotationY : (model.rotationY || 0)
          model.rotationZ = latest.rotationZ != null ? latest.rotationZ : (model.rotationZ || 0)
          model.scaleX = latest.scaleX != null ? latest.scaleX : (model.scaleX || 1)
          model.scaleY = latest.scaleY != null ? latest.scaleY : (model.scaleY || 1)
          model.scaleZ = latest.scaleZ != null ? latest.scaleZ : (model.scaleZ || 1)
        }
      } catch (fetchError) {
        console.warn('获取最新模型数据失败，使用缓存数据:', fetchError.message)
      }

      if (this.currentModelEditor) {
        if (this.currentModelEditor.destroy) {
          this.currentModelEditor.destroy();
        }
        this.currentModelEditor = null;
      }
      if (this.lastHighlightedModel) {
        this.restoreModelStyle(this.lastHighlightedModel);
        this.lastHighlightedModel = null;
      }

      if (this.isModelLoaded(model.id)) {
        this.removeModel(model)
      }

      try {
        if (!model.filePath) {
          ElMessage.warning('模型文件路径不存在，请检查数据库中的filePath字段')
          return
        }

        let modelUrl = model.filePath.replace(/\/+/g, '/')
        
        const isLocalPath = modelUrl.startsWith('/') || modelUrl.startsWith('./') || modelUrl.startsWith('../') || !modelUrl.startsWith('http')
        
        if (isLocalPath) {
          const baseURL = import.meta.env.VITE_APP_BASE_API || ''
          const apiUrl = baseURL + "/common/download/resource?resource=" + encodeURIComponent(modelUrl)
          
          try {
            const response = await axios({
              method: 'get',
              url: apiUrl,
              responseType: 'blob',
              headers: { 'Authorization': 'Bearer ' + getToken() }
            })
            
            if (response.data.size === 0) {
              ElMessage.error('模型文件为空，请检查文件是否有效')
              return
            }
            
            let mimeType = 'model/gltf-binary'
            const format = model.format ? model.format.toUpperCase() : 'GLB'
            const fileExtension = model.filePath ? model.filePath.split('.').pop().toLowerCase() : ''
            
            if (format === 'GLTF' || fileExtension === 'gltf') {
              mimeType = 'model/gltf+json'
            } else if (format === 'GLB' || fileExtension === 'glb') {
              mimeType = 'model/gltf-binary'
            } else if (format === 'OBJ' || fileExtension === 'obj') {
              mimeType = 'model/obj'
            }
            
            const blob = new Blob([response.data], { type: mimeType })
            modelUrl = URL.createObjectURL(blob)
          } catch (apiError) {
            console.error('通过API获取模型文件失败:', apiError)
            ElMessage.error('无法获取模型文件，请检查文件路径或权限')
            return
          }
        }

        let position
        if (model.coordinates && model.coordinates.trim() !== '' && model.coordinates !== '未设置') {
          try {
            const coords = model.coordinates.split(',').map(c => parseFloat(c.trim()))
            
            if (coords.length >= 2 && !isNaN(coords[0]) && !isNaN(coords[1])) {
              const lon = coords[0]
              const lat = coords[1]
              const modelHeight = coords[2] !== undefined && !isNaN(coords[2]) ? coords[2] : 0
              
              const cartographic = Cesium.Cartographic.fromDegrees(lon, lat)
              
              if (viewer.scene.globe && viewer.terrainProvider) {
                try {
                  const updatedPositions = await Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [cartographic])
                  const terrainHeight = updatedPositions[0].height || 0
                  position = Cesium.Cartesian3.fromDegrees(lon, lat, modelHeight)
                } catch (terrainError) {
                  position = Cesium.Cartesian3.fromDegrees(lon, lat, modelHeight)
                }
              } else {
                position = Cesium.Cartesian3.fromDegrees(lon, lat, modelHeight)
              }
            } else {
              position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
            }
          } catch (e) {
            console.error('解析经纬度失败:', e)
            position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
          }
        } else {
          position = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0)
        }

        if (!position) {
          ElMessage.error('位置计算失败')
          return
        }

        const entity = viewer.entities.add({
          name: model.id,
          position: position,
          model: {
            uri: modelUrl,
            minimumPixelSize: 0,
            maximumScale: 1,
            scale: 1.0,
            show: true,
            distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 50000000.0),
            heightReference: Cesium.HeightReference.NONE
          },
          _rotation: { x: model.rotationX || 0, y: model.rotationY || 0, z: model.rotationZ || 0 },
          _scale: { x: model.scaleX || 1, y: model.scaleY || 1, z: model.scaleZ || 1 }
        })

        if (model.rotationX !== undefined || model.rotationY !== undefined || model.rotationZ !== undefined) {
          const heading = Cesium.Math.toRadians(model.rotationZ || 0);
          const pitch = Cesium.Math.toRadians(model.rotationX || 0);
          const roll = Cesium.Math.toRadians(model.rotationY || 0);
          const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
          entity.orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
        }

        if (model.scaleX !== undefined || model.scaleY !== undefined || model.scaleZ !== undefined) {
          const scaleX = model.scaleX || 1;
          const scaleY = model.scaleY || 1;
          const scaleZ = model.scaleZ || 1;
          entity.model.scale = (scaleX + scaleY + scaleZ) / 3;
          entity._modelScale = { x: scaleX, y: scaleY, z: scaleZ };
        }

        this.loadedModelEntities.push(entity)
        // requestRenderMode 下，模型资源(glb)异步加载，注册短时渲染需求确保加载完成后立即显示
        if (window.__requestContinuousRender) {
          window.__requestContinuousRender('model-loading');
          clearTimeout(window.__modelLoadTimer);
          window.__modelLoadTimer = setTimeout(() => {
            window.__cancelContinuousRender && window.__cancelContinuousRender('model-loading');
          }, 5000);
        }

        if (shouldFlyTo) {
          setTimeout(() => {
            const calculateFlyDistance = () => {
              const tryGetModelSize = (attempts = 0) => {
                if (attempts > 15) {
                  viewer.flyTo(entity, {
                    duration: 2,
                    offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), 100)
                  });
                  return;
                }
                
                const entityPosition = entity.position.getValue(viewer.clock.currentTime);
                if (!entityPosition) {
                  setTimeout(() => tryGetModelSize(attempts + 1), 300);
                  return;
                }
                
                let bestModel = null;
                let bestDistance = Infinity;
                
                for (let i = 0; i < viewer.scene.primitives.length; i++) {
                  const primitive = viewer.scene.primitives.get(i);
                  if (primitive instanceof Cesium.Model) {
                    const modelPosition = primitive.modelMatrix ? primitive.modelMatrix.clone() : null;
                    if (modelPosition) {
                      const modelCartesian = Cesium.Matrix4.getTranslation(modelPosition, new Cesium.Cartesian3());
                      const distance = Cesium.Cartesian3.distance(entityPosition, modelCartesian);
                      if (distance < bestDistance) {
                        bestDistance = distance;
                        bestModel = primitive;
                      }
                    }
                  }
                }
                
                if (bestModel && bestModel.boundingSphere && bestModel.boundingSphere.radius > 0) {
                  const modelRadius = bestModel.boundingSphere.radius;
                  const flyDistance = modelRadius * 3;
                  
                  viewer.flyTo(entity, {
                    duration: 2,
                    offset: new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-30), flyDistance)
                  });
                  return;
                }
                
                setTimeout(() => tryGetModelSize(attempts + 1), 300);
              };
              
              tryGetModelSize();
            };

            calculateFlyDistance();
          }, 1500)
        }

        this.saveLoadedServices()
        this.addModelClickHandler(entity)
      } catch (error) {
        console.error('加载模型失败:', error)
        ElMessage.error('加载模型失败: ' + (error.message || '未知错误'))
      }
    },

    isModelLoaded(modelId) {
      return this.loadedModelEntities.some(entity => entity.name === modelId)
    },

    isSceneLoaded(sceneId) {
      return this.currentSceneId === sceneId
    },

    // 加载场景配置的模型
    async loadSceneModel(model) {
      const viewer = window.viewer
      if (!viewer || !viewer.scene) {
        console.error('Cesium viewer 未初始化')
        return
      }

      if (this.isModelLoaded(model.id)) {
        return
      }

      try {
        const Cesium = window.Cesium
        
        const coords = model.coordinates.split(',').map(c => parseFloat(c.trim()))
        if (coords.length < 2) {
          console.error('坐标格式不正确:', model.coordinates)
          return
        }
        
        const lon = coords[0]
        const lat = coords[1]
        const height = coords.length > 2 ? coords[2] : 0
        
        const position = Cesium.Cartesian3.fromDegrees(lon, lat, height)
        
        const rotation = model.rotation ? model.rotation.split(',').map(r => parseFloat(r.trim())) : [0, 0, 0]
        const heading = Cesium.Math.toRadians(rotation[2] || 0)
        const pitch = Cesium.Math.toRadians(rotation[0] || 0)
        const roll = Cesium.Math.toRadians(rotation[1] || 0)
        const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
        
        const entity = viewer.entities.add({
          id: model.id,
          name: model.name,
          position: position,
          orientation: Cesium.Transforms.headingPitchRollQuaternion(position, hpr),
          model: {
            uri: model.filePath,
            minimumPixelSize: 128,
            maximumScale: 20000,
            distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 50000000.0),
            heightReference: Cesium.HeightReference.NONE,
            clampAnimations: true
          },
          disableTransform: model.disableTransform || false,
          gateNodePrefix: model.gateNodePrefix || '',
          gateNodeNames: model.gateNodeNames || []
        })

        this.loadedModelEntities.push(entity)
        
        const modelFileName = model.filePath.split('/').pop()
        
        const isModel = (obj) => {
          if (!obj) return false
          const hasModelProps = obj._gltf !== undefined || 
                               obj.readyPromise !== undefined || 
                               obj._url !== undefined ||
                               obj.constructor?.name === 'Model' ||
                               (obj._runtimeNodes && Array.isArray(obj._runtimeNodes)) ||
                               obj._uri !== undefined
          return hasModelProps
        }
        
        const findModelRecursively = (collection) => {
          if (!collection || typeof collection.get !== 'function') {
            return null
          }
          
          let foundModels = []
          
          for (let i = 0; i < collection.length; i++) {
            const primitive = collection.get(i)
            
            if (isModel(primitive)) {
              const modelUrl = primitive._url || primitive._uri
              if (modelUrl && modelUrl.includes(modelFileName)) {
                return primitive
              }
              foundModels.push(primitive)
            }
            
            if (primitive && primitive.length !== undefined && typeof primitive.get === 'function') {
              const found = findModelRecursively(primitive)
              if (found) return found
            }
          }
          
          if (foundModels.length > 0) {
            return foundModels[0]
          }
          
          return null
        }
        
        const processModel = (modelPrimitive) => {
          // 查找 GLTF 数据的辅助函数
          const findGltfData = (obj) => {
            if (!obj || typeof obj !== 'object') return null;
            
            // 尝试从多个位置获取 GLTF 数据
            if (obj._gltf && obj._gltf.nodes) {
              return obj._gltf;
            }
            if (obj._model && obj._model._gltf && obj._model._gltf.nodes) {
              return obj._model._gltf;
            }
            if (obj._sourceChapters) {
              for (const chapter of obj._sourceChapters) {
                if (chapter._gltf && chapter._gltf.nodes) {
                  return chapter._gltf;
                }
              }
            }
            // 深度搜索
            const searchDeep = (currentObj, depth = 0) => {
              if (depth > 3) return null;
              for (const key of Object.keys(currentObj)) {
                if (key.startsWith('_') && typeof currentObj[key] === 'object') {
                  if (currentObj[key]._gltf && currentObj[key]._gltf.nodes) {
                    return currentObj[key]._gltf;
                  }
                  const found = searchDeep(currentObj[key], depth + 1);
                  if (found) return found;
                }
              }
              return null;
            };
            return searchDeep(obj);
          };
          
          const waitForGltf = (callback) => {
            let waitCount = 0
            const maxWaitCount = 30  // 减少等待次数，从50次降到30次
            
            const checkGltf = () => {
              waitCount++
              
              // 使用增强的查找函数
              const gltfData = findGltfData(modelPrimitive);
              const hasGltfData = gltfData !== null;
              
              console.log(`⏳ 等待 GLTF [${waitCount}/${maxWaitCount}]: ready=${modelPrimitive.ready}, 找到GLTF=${hasGltfData}`)
              
              if (hasGltfData) {
                console.log('✅ GLTF 数据完整，开始提取节点...')
                // 将找到的 GLTF 数据挂载到 modelPrimitive 上
                if (!modelPrimitive._gltf) {
                  modelPrimitive._gltf = gltfData;
                }
                callback()
              } else if (waitCount < maxWaitCount) {
                setTimeout(checkGltf, 100)  // 减少等待间隔，从200ms降到100ms
              } else {
                console.warn('⚠️ 等待超时，尝试提取现有数据...')
                callback()
              }
            }
            
            checkGltf()
          }
          
          const handleReadyModel = () => {
            console.log('🔧 handleReadyModel 被调用');
            
            this.applyTransparentMaterial(modelPrimitive, model.transparency)
            this.enableAntialiasing(modelPrimitive)
            
            const gateNames = entity.gateNodeNames
            const hasGateNames = gateNames && (Array.isArray(gateNames) || Array.isArray(gateNames.valueOf?.())) && gateNames.length > 0
            
            console.log('========== 莒口水闸场景处理 ==========');
            console.log('模型URL:', model.url);
            console.log('gateNodeNames:', gateNames);
            console.log('hasGateNames:', hasGateNames);
            console.log('entity:', entity);
            console.log('model:', model);
            
            // 对于莒口水闸场景，使用控制器初始化闸门
            const modelPath = model.url || model.filePath;
            const isJuKouScene = modelPath && modelPath.includes('莒口水闸')
            if (hasGateNames || isJuKouScene) {
              console.log('🔄 莒口水闸场景，使用控制器初始化闸门...');
              const that = this
              
              // 使用控制器初始化场景
              createController(entity, {
                initialPosition: that.gatePosition,
                onGateNodesReady: (gateNames) => {
                  console.log(`✅ 闸门节点就绪，共 ${gateNames.length} 个闸门`);
                  
                  // 显示闸门控制面板
                  that.showGateControl = true;
                  
                  // 更新 GateControl 组件的闸门列表
                  that.$nextTick(() => {
                    if (that.$refs.gateControl) {
                      that.$refs.gateControl.updateGateList(gateNames);
                      console.log('✅ 闸门列表已更新');
                    } else if (window.gateControlInstance) {
                      window.gateControlInstance.updateGateList(gateNames);
                      console.log('✅ 通过全局实例更新闸门列表');
                    }
                  });
                }
              });
            }
          }
          
          if (modelPrimitive.ready === true) {
            handleReadyModel()
          } else if (modelPrimitive.readyPromise && typeof modelPrimitive.readyPromise.then === 'function') {
            try {
              const promise = modelPrimitive.readyPromise.then(handleReadyModel)
              if (promise && typeof promise.catch === 'function') {
                promise.catch((error) => {
                  console.error('模型加载失败:', error)
                })
              }
            } catch (e) {
              console.error('处理 readyPromise 失败:', e)
              handleReadyModel()
            }
          } else {
            setTimeout(() => {
              handleReadyModel()
            }, 500)
          }
        }
        
        const findModelInPrimitives = () => {
          if (!window.viewer || !window.viewer.scene) {
            return null
          }
          
          const primitives = window.viewer.scene.primitives
          return findModelRecursively(primitives)
        }
        
        let targetModel = findModelInPrimitives()
        
        if (targetModel) {
          processModel(targetModel)
        } else {
          let pollCount = 0
          const maxPollCount = 50
          const pollForModel = () => {
            pollCount++
            targetModel = findModelInPrimitives()
            
            if (targetModel) {
              processModel(targetModel)
            } else if (pollCount < maxPollCount) {
              setTimeout(pollForModel, 200)
            }
          }
          pollForModel()
          return
        }
        
      } catch (error) {
        console.error('加载场景模型失败:', error)
      }
    },

    // 应用半透明材质
    applyTransparentMaterial(modelPrimitive, transparency) {
      try {
        const Cesium = window.Cesium
        const alpha = transparency !== undefined ? transparency : 0.7
        
        if (modelPrimitive._sourceChapter && modelPrimitive._sourceChapter._programs) {
          const chapters = modelPrimitive._sourceChapter
          if (chapters) {
            modelPrimitive.style = new Cesium.ModelStyle({
              color: Cesium.Color.fromAlpha(Cesium.Color.WHITE, alpha)
            })
          }
        }
        
        modelPrimitive.color = Cesium.Color.fromAlpha(Cesium.Color.WHITE, alpha)
        
      } catch (error) {
        console.error('应用半透明材质失败:', error)
      }
    },

    // 启用抗锯齿
    enableAntialiasing(modelPrimitive) {
      try {
        const Cesium = window.Cesium
        
        modelPrimitive.enableAntiAliasing = true
        
        if (modelPrimitive.silhouette) {
          modelPrimitive.silhouetteEnabled = true
        }
        
      } catch (error) {
        console.error('启用抗锯齿失败:', error)
      }
    },

    removeTyphoon() {
      console.log('卸载台风')
      this.showTyphoon = false
      this.typhoonDataUrl = ''
      this.loadedTyphoonId = null
      ElMessage.success('台风路径已卸载')
    },

    // 加载风场
    loadWindField() {
      console.log('加载风场')

      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }

      this.showWindField = true
      ElMessage.success('风场加载成功')
    },

    // 卸载风场
    removeWindField() {
      console.log('卸载风场')
      this.showWindField = false
      ElMessage.success('风场已卸载')
    },

    // 加载降雨
    loadRainfall() {
      console.log('加载降雨')
      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }
      this.showRainfall = true
      ElMessage.success('降雨加载成功')
    },

    // 卸载降雨
    removeRainfall() {
      console.log('卸载降雨')
      this.showRainfall = false
      ElMessage.success('降雨已卸载')
    },

    // 加载云图
    loadCloudMap() {
      console.log('加载云图')
      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }
      this.showCloudMap = true
      ElMessage.success('云图加载成功')
    },

    // 卸载云图
    removeCloudMap() {
      console.log('卸载云图')
      this.showCloudMap = false
      ElMessage.success('云图已卸载')
    },

    // 加载雷达
    loadRadar() {
      console.log('加载雷达')
      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }
      this.showRadar = true
      ElMessage.success('雷达加载成功')
    },

    // 卸载雷达
    removeRadar() {
      console.log('卸载雷达')
      this.showRadar = false
      ElMessage.success('雷达已卸载')
    },

    removeModel(model) {
      const index = this.loadedModelEntities.findIndex(entity => entity.name === model.id)
      if (index !== -1) {
        const entity = this.loadedModelEntities[index]
        
        // 恢复模型样式
        this.restoreModelStyle(entity);
        
        // 清理点击事件 handler
        if (entity._clickHandler) {
          entity._clickHandler.destroy();
          entity._clickHandler = null;
        }
        
        viewer.entities.remove(entity)
        this.loadedModelEntities.splice(index, 1)
        this.saveLoadedServices()
        
        // 如果当前编辑器是这个模型，清理编辑器
        if (this.currentModelEditor && this.currentModelEditor.entity === entity) {
          if (this.currentModelEditor.destroy) {
            this.currentModelEditor.destroy();
          }
          this.currentModelEditor = null;
        }
        
        // 如果最后高亮的模型是这个模型，清除引用
        if (this.lastHighlightedModel === entity) {
          this.lastHighlightedModel = null;
        }
        
        // ElMessage.success(`模型 "${model.name}" 已删除`)
      }
    },

    // 加载实时台风（API获取逻辑由 RealtimeTyphoon 组件内部处理）
    loadRealtimeTyphoon() {
      console.log('加载实时台风')

      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }

      // 如果已加载历史台风，先卸载避免冲突
      if (this.showTyphoon) {
        this.removeTyphoon()
      }

      this.showRealtimeTyphoon = true
    },

    // 卸载实时台风
    removeRealtimeTyphoon() {
      console.log('卸载实时台风')
      this.showRealtimeTyphoon = false
      ElMessage.success('实时台风已卸载')
    },

    // 加载业务场景
    loadBusinessScenario(scenarioType) {
      console.log('加载业务场景:', scenarioType)

      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }

      const scenarioConfig = {
        'flood-control': {
          title: '防洪排涝',
          description: '加载防洪排涝业务场景相关数据'
        },
        'water-resource': {
          title: '水资源管理',
          description: '加载水资源管理业务场景相关数据'
        },
        'project-supervision': {
          title: '水利工程监管',
          description: '加载水利工程监管业务场景相关数据'
        },
        'eco-environment': {
          title: '生态环境保护',
          description: '加载生态环境保护业务场景相关数据'
        }
      }

      const config = scenarioConfig[scenarioType]
      if (config) {
        ElMessage.info(`${config.title}：${config.description}`)
        console.log(config.description)
      } else {
        ElMessage.warning('未知的业务场景类型')
      }
    },

    // 创建业务场景 - 通知父组件打开弹窗
    handleCreateScenario() {
      console.log('触发创建业务场景事件')
      this.$emit('open-create-scenario')
    },

    // 从数据库刷新业务场景列表
    refreshBusinessScenes() {
      console.log('从数据库加载业务场景列表...')
      listBusinessScene({}).then(response => {
        if (response.code === 200) {
          this.businessScenes = response.rows || []
          console.log('业务场景列表加载成功:', this.businessScenes)
        } else {
          console.error('加载业务场景列表失败:', response.msg)
          ElMessage.error('加载业务场景列表失败')
        }
      }).catch(error => {
        console.error('加载业务场景列表出错:', error)
        ElMessage.error('加载业务场景列表出错')
      })
    },

    // 加载固定场景列表
    loadFixedScenes() {
      this.fixedScenes = getFixedScenes()
      console.log('固定场景列表加载成功:', this.fixedScenes)
    },

    // 加载固定场景
    async loadFixedScene(scene) {
      console.log('加载固定场景:', scene)
      
      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }
      
      if (!window.Cesium) {
        ElMessage.error('Cesium 库未加载')
        return
      }

      try {
        // 设置当前场景ID
        this.currentSceneId = scene.sceneId
        
        // 加载第三人称相机位置（视口跳转）
        if (scene.thirdPersonCameraPos) {
          const cameraPos = JSON.parse(scene.thirdPersonCameraPos)
          
          if (!Array.isArray(cameraPos) || cameraPos.length !== 3) {
            console.error('相机位置格式不正确:', cameraPos)
            ElMessage.error('相机位置数据格式错误')
            return
          }
          
          const Cesium = window.Cesium
          const posCartesian = Cesium.Cartesian3.fromArray(cameraPos)
          
          if (!posCartesian) {
            console.error('Cartesian3.fromArray 返回 undefined')
            ElMessage.error('坐标转换失败')
            return
          }
          
          // 优先使用方向向量和上向量设置相机
          if (scene.cameraDirection && scene.cameraUp) {
            const direction = Cesium.Cartesian3.fromArray(JSON.parse(scene.cameraDirection))
            const up = Cesium.Cartesian3.fromArray(JSON.parse(scene.cameraUp))
            
            if (direction && up) {
              console.log('使用方向向量和上向量设置相机')
              window.viewer.camera.setView({
                destination: posCartesian,
                orientation: {
                  direction: direction,
                  up: up
                }
              })
            } else {
              // 回退到HPR角度设置
              const heading = scene.thirdPersonCameraHeading || 0
              const pitch = scene.thirdPersonCameraPitch || Cesium.Math.toRadians(-90)
              const roll = scene.thirdPersonCameraRoll || 0
              
              window.viewer.camera.setView({
                destination: posCartesian,
                orientation: {
                  heading: heading,
                  pitch: pitch,
                  roll: roll
                }
              })
            }
          } else {
            // 使用目标位置计算方向向量
            if (scene.thirdPersonCameraTarget) {
              const cameraTarget = JSON.parse(scene.thirdPersonCameraTarget)
              if (Array.isArray(cameraTarget) && cameraTarget.length === 3) {
                const targetCartesian = Cesium.Cartesian3.fromArray(cameraTarget)
                if (targetCartesian) {
                  const direction = Cesium.Cartesian3.subtract(posCartesian, targetCartesian, new Cesium.Cartesian3())
                  Cesium.Cartesian3.normalize(direction, direction)
                  
                  window.viewer.camera.setView({
                    destination: posCartesian,
                    orientation: {
                      direction: direction,
                      up: new Cesium.Cartesian3(0, 0, 1)
                    }
                  })
                }
              }
            } else {
              // 使用HPR角度设置
              const heading = scene.thirdPersonCameraHeading || 0
              const pitch = scene.thirdPersonCameraPitch || Cesium.Math.toRadians(-90)
              const roll = scene.thirdPersonCameraRoll || 0
              
              window.viewer.camera.setView({
                destination: posCartesian,
                orientation: {
                  heading: heading,
                  pitch: pitch,
                  roll: roll
                }
              })
            }
          }
        }

        // 莒口水闸场景使用独立页面组件
        if (scene.sceneId === JuKouSceneConfig.sceneId) {
          console.log('📌 使用莒口水闸独立页面组件')
          
          // 显示莒口水闸页面组件
          this.showJuKouScenePage = true
          this.gatePosition = scene.initialGatePosition || 0
          
          // 等待组件加载场景
          // 场景加载逻辑由 JuKouShuiZhaPage 组件内部处理
        } else {
          // 其他场景使用通用加载方法
          if (scene.loadedModels) {
            try {
              const models = JSON.parse(scene.loadedModels)
              console.log('需要加载的模型:', models)
              for (const model of models) {
                const modelObj = {
                  id: model.name,
                  name: model.name,
                  filePath: model.filePath,
                  coordinates: model.coordinates,
                  rotation: model.rotation || '0,0,0',
                  transparency: model.transparency,
                  disableTransform: scene.disableModelTransform || false,
                  gateNodePrefix: scene.gateNodePrefix || '',
                  gateNodeNames: scene.gateNodeNames || []
                }
                this.loadSceneModel(modelObj)
              }
            } catch (e) {
              console.error('解析模型配置失败:', e)
            }
          }
          
          if (scene.enableGateControl) {
            this.gatePosition = scene.initialGatePosition || 0
            console.log('📌 场景启用闸门控制，等待模型加载...')
          }
        }

        // 加载模型拆解图标
        if (scene.modelDisassemblyIcon) {
          this.modelIconConfig = scene.modelDisassemblyIcon
        }
        
        ElMessage.success(`已加载固定场景：${scene.sceneName}`)
      } catch (error) {
        console.error('加载固定场景失败:', error)
        ElMessage.error('加载固定场景失败')
      }
    },

    // 卸载场景
    unloadScene(scene) {
      console.log('卸载场景:', scene)
      
      // 莒口水闸场景使用独立卸载方法
      if (scene.sceneId === JuKouSceneConfig.sceneId) {
        console.log('📌 使用莒口水闸独立场景卸载方法')
        unloadJuKouScene()
      } else {
        // 其他场景使用通用卸载方法
        if (scene.loadedModels) {
          try {
            const models = JSON.parse(scene.loadedModels)
            console.log('需要卸载的模型:', models)
            for (const model of models) {
              this.unloadSceneModel(model.name)
            }
          } catch (e) {
            console.error('解析模型配置失败:', e)
          }
        }
      }
      
      // 关闭闸门控制面板
      this.showGateControl = false
      this.gatePosition = 0
      
      // 关闭莒口水闸页面组件
      this.showJuKouScenePage = false

      // 清除模型拆解图标配置
      this.modelIconConfig = null

      // 清除当前场景ID
      this.currentSceneId = null
      
      ElMessage.success(`已卸载场景：${scene.sceneName}`)
    },

    // 卸载场景模型
    unloadSceneModel(modelId) {
      const viewer = window.viewer
      if (!viewer) {
        console.error('Cesium viewer 未初始化')
        return
      }
      
      // 查找并移除模型实体
      const entityIndex = this.loadedModelEntities.findIndex(entity => entity.name === modelId)
      if (entityIndex !== -1) {
        const entity = this.loadedModelEntities[entityIndex]
        viewer.entities.remove(entity)
        this.loadedModelEntities.splice(entityIndex, 1)
        console.log('场景模型卸载完成:', modelId)
      } else {
        console.log('未找到需要卸载的模型:', modelId)
      }
    },

    // 显示删除确认对话框
    showDeleteConfirm(scene) {
      console.log('显示删除确认:', scene)
      
      // 检查是否为固定场景
      if (isFixedScene(scene.sceneId)) {
        ElMessage.warning('固定场景不能删除，只能卸载')
        return
      }
      
      ElMessageBox.confirm(
        '确定要删除场景 "' + scene.sceneName + '" 吗?',
        '提示',
        {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }
      ).then(() => {
        this.handleDeleteBusinessScene(scene.sceneId)
      }).catch(() => {
        ElMessage.info('已取消删除')
      })
    },

    // 删除业务场景
    handleDeleteBusinessScene(sceneId) {
      console.log('删除业务场景:', sceneId)
      delBusinessScene(sceneId).then(response => {
        if (response.code === 200) {
          ElMessage.success('业务场景删除成功')
          // 刷新场景列表
          this.refreshBusinessScenes()
        } else {
          ElMessage.error('删除业务场景失败: ' + response.msg)
        }
      }).catch(error => {
        console.error('删除业务场景出错:', error)
        ElMessage.error('删除业务场景出错')
      })
    },

    // 从数据库加载业务场景
    loadBusinessScenarioFromDB(scene) {
      console.log('从数据库加载业务场景:', scene)
      
      if (!window.viewer) {
        ElMessage.error('Cesium viewer 未初始化')
        return
      }
      
      if (!window.Cesium) {
        ElMessage.error('Cesium 库未加载')
        return
      }

      try {
        // 设置当前场景ID
        this.currentSceneId = scene.sceneId
        // 加载第三人称相机位置（视口跳转）
        if (scene.thirdPersonCameraPos && scene.thirdPersonCameraTarget) {
          console.log('thirdPersonCameraPos:', scene.thirdPersonCameraPos)
          console.log('thirdPersonCameraTarget:', scene.thirdPersonCameraTarget)
          
          const cameraPos = JSON.parse(scene.thirdPersonCameraPos)
          const cameraTarget = JSON.parse(scene.thirdPersonCameraTarget)
          console.log('解析后的相机位置:', cameraPos, '类型:', Array.isArray(cameraPos))
          console.log('解析后的目标位置:', cameraTarget, '类型:', Array.isArray(cameraTarget))
          
          // 检查数组长度是否正确
          if (!Array.isArray(cameraPos) || cameraPos.length !== 3) {
            console.error('相机位置格式不正确:', cameraPos)
            ElMessage.error('相机位置数据格式错误')
            return
          }
          
          if (!Array.isArray(cameraTarget) || cameraTarget.length !== 3) {
            console.error('目标位置格式不正确:', cameraTarget)
            ElMessage.error('目标位置数据格式错误')
            return
          }
          
          // 使用 Cesium 的 flyTo 方法跳转到指定视角
          const Cesium = window.Cesium
          console.log('Cesium.Cartesian3:', Cesium.Cartesian3)
          
          const posCartesian = Cesium.Cartesian3.fromArray(cameraPos)
          console.log('posCartesian:', posCartesian)
          
          const targetCartesian = Cesium.Cartesian3.fromArray(cameraTarget)
          console.log('targetCartesian:', targetCartesian)
          
          if (!posCartesian || !targetCartesian) {
            console.error('Cartesian3.fromArray 返回 undefined')
            ElMessage.error('坐标转换失败')
            return
          }
          
          const direction = Cesium.Cartesian3.subtract(
            posCartesian,
            targetCartesian,
            new Cesium.Cartesian3()
          )
          console.log('direction before normalize:', direction)
          
          Cesium.Cartesian3.normalize(direction, direction)
          console.log('direction after normalize:', direction)
          
          // 优先使用方向向量和上向量精确恢复相机状态
          if (scene.thirdPersonCameraDirection && scene.thirdPersonCameraUp) {
            try {
              const directionArray = JSON.parse(scene.thirdPersonCameraDirection)
              const upArray = JSON.parse(scene.thirdPersonCameraUp)
              
              if (directionArray && directionArray.length === 3 && upArray && upArray.length === 3) {
                const direction = new Cesium.Cartesian3(
                  directionArray[0],
                  directionArray[1],
                  directionArray[2]
                )
                const up = new Cesium.Cartesian3(
                  upArray[0],
                  upArray[1],
                  upArray[2]
                )
                
                console.log('使用方向向量和上向量恢复相机状态')
                console.log('方向向量:', direction)
                console.log('上向量:', up)
                
                // 使用 setView 方法精确恢复相机状态
                window.viewer.camera.setView({
                  destination: posCartesian,
                  orientation: {
                    direction: direction,
                    up: up
                  }
                })
                console.log('使用方向向量和上向量恢复相机完成')
                return  // 使用方向向量恢复后继续后续逻辑
              }
            } catch (e) {
              console.error('解析方向向量或上向量失败:', e)
            }
          }
          
          // 如果没有方向向量，使用 HPR 角度（数据库中已存储为弧度值）
          const heading = scene.thirdPersonCameraHeading || 0
          const pitch = scene.thirdPersonCameraPitch || Cesium.Math.toRadians(-90)  // 默认俯视90度
          const roll = scene.thirdPersonCameraRoll || 0
          console.log('保存的相机朝向(弧度):', heading, pitch, roll)
          console.log('保存的相机朝向(角度):', Cesium.Math.toDegrees(heading), Cesium.Math.toDegrees(pitch), Cesium.Math.toDegrees(roll))
          
          // 使用 setView 方法设置相机位置和朝向
          // 注意：数据库中保存的已经是弧度值，不需要再转换
          window.viewer.camera.setView({
            destination: posCartesian,
            orientation: {
              heading: heading,
              pitch: pitch,
              roll: roll
            }
          })
          console.log('使用HPR角度恢复相机完成')
        }

        // 加载第一人称相机设置
        if (scene.firstPersonCameraPos && scene.firstPersonCameraTarget) {
          const firstPersonPos = JSON.parse(scene.firstPersonCameraPos)
          const firstPersonTarget = JSON.parse(scene.firstPersonCameraTarget)
          // 可以根据需要应用第一人称相机设置
          console.log('第一人称相机位置:', firstPersonPos, firstPersonTarget)
        }

        // 加载模型
        if (scene.loadedModels) {
          const models = JSON.parse(scene.loadedModels)
          console.log('需要加载的模型:', models)
          // 可以根据需要加载模型
        }

        // 加载数据服务
        if (scene.dataServices) {
          const services = JSON.parse(scene.dataServices)
          console.log('需要加载的数据服务:', services)
          // 可以根据需要加载数据服务
        }

        // 加载POI点
        if (scene.poiPoints) {
          const poiPoints = JSON.parse(scene.poiPoints)
          console.log('需要加载的POI点:', poiPoints)
          // 可以根据需要加载POI点
        }
        
        // 数据库场景不显示闸门控制框
        this.showGateControl = false
        
        ElMessage.success(`已加载业务场景：${scene.sceneName}`)
      } catch (error) {
        console.error('加载业务场景失败:', error)
        ElMessage.error('加载业务场景失败')
      }
    },

    // 处理闸门位置变化（支持单独控制每个闸门）
    handleGatePositionChange(data) {
      console.log('🔧 handleGatePositionChange 被调用:', data)
      
      // 获取控制器实例
      const controller = getController()
      if (!controller || !controller.isInitialized) {
        console.warn('⚠️ 莒口水闸场景控制器未初始化')
        return
      }
      
      // 使用控制器设置闸门位置
      if (typeof data === 'number') {
        // 设置所有闸门位置
        this.gatePosition = data
        controller.setAllGatesPosition(data)
      } else if (data && data.gateName && typeof data.position === 'number') {
        // 设置单个闸门位置
        controller.setSingleGatePosition(data.gateName, data.position)
      }
    },
    
    // 莒口水闸场景加载完成回调
    onJuKouSceneLoaded() {
      console.log('✅ 莒口水闸场景页面组件加载完成')
    },
    
    // 莒口水闸场景卸载完成回调
    onJuKouSceneUnloaded() {
      console.log('✅ 莒口水闸场景页面组件已卸载')
      this.showJuKouScenePage = false
    },


    // 区分地质体组件（需销毁）和其他组件
    change(val, val2) {
      if (val2) {
        this.view = "";
        if (val === this.view2) {
          this.view2 = "";
          return;
        }
        this.view2 = val;
      } else {
        this.view2 = "";
        if (val === this.view) {
          this.view = "";
          return;
        }
        this.view = val;
      }
    },

    //添加公共服务
    addWebServe(obj, shouldFlyTo = true) {
      console.log('===== addWebServe 开始 =====');
      console.log('服务名称:', obj.name);
      console.log('服务类型:', obj.type);
      console.log('服务状态:', obj.state);
      console.log('是否有transparentColor:', obj.transparentColor);
      
      if (obj.state === 0) {
        // add mvt
        if (obj.type === "MVT") {
          console.log('MVT类型，直接返回');
          layerManagement.addMvtLayer(obj.proxiedUrl, obj.name, () => {
            obj.state = 1;
            window.store.actions.setChangeLayers();
            this.saveLoadedServices();
          });
          return;
        }
        // add scene
        console.log('REALSPACE类型，准备加载场景');
        // useServerView 为 true 时使用超图服务端默认视口，否则使用本地配置的视口
        const useServerView = obj.useServerView || false;
        // 构建场景选项，包含透明色配置
        const sceneOptions = { 
          autoSetView: useServerView 
        };
        if (obj.transparentColor) {
          sceneOptions.transparentColor = obj.transparentColor;
          sceneOptions.transparentColorTolerance = obj.transparentColorTolerance || 0.01;
          console.log('透明色配置:', sceneOptions);
        } else {
          console.log('没有配置透明色');
        }
        
        layerManagement.addScene(
          obj.proxiedUrl,
          sceneOptions,
          layers => {
            console.log('场景加载完成回调');
            obj.state = 1;
            window.store.actions.setChangeLayers();
            // 白膜添加线框
            if (obj.style && obj.style.fillStyle) {
              layers[0].style3D.lineColor = Cesium.Color.fromCssColorString(
                "rgb(67,67,67)"
              );
              layers[0].style3D.fillStyle =
                Cesium.FillStyle[obj.style.fillStyle];
            }
            
            this.saveLoadedServices();
            // 如果使用服务端默认视口，则不需要调用本地flyTo
            if (shouldFlyTo && !useServerView) {
              this.flyTo(obj.name, server_flyTo_config);
            }
          }
        );
      } else {
        console.log('服务已加载，仅执行飞行');
        if (shouldFlyTo) {
          this.flyTo(obj.name, server_flyTo_config);
        }
      }
    },
    // 添加底图
    addBaseLayer(obj) {
      if (this.baseLayerObj.type === obj.type) return;
      this.baseLayerObj.state = 0;
      obj.state = 1;
      this.baseLayerObj = obj;
      let type = obj.type;
      let url = obj.proxiedUrl;
      let imageryLayerCollection = viewer.scene.globe._imageryLayerCollection;
      let layer = imageryLayerCollection.get(0);
      let imageryProvider;
      
      // 保存注记图层（如果存在）
      let labelLayer = null;
      for (let i = 0; i < imageryLayerCollection.length; i++) {
        const currentLayer = imageryLayerCollection.get(i);
        if (currentLayer && currentLayer.imageryProvider && currentLayer.imageryProvider.url && currentLayer.imageryProvider.url.includes('cia_c')) {
          labelLayer = currentLayer;
          imageryLayerCollection.remove(currentLayer);
          break;
        }
      }
      
      if (imageryLayerCollection.get(2)) {
        imageryLayerCollection.remove(imageryLayerCollection.get(2));
      }
      if (imageryLayerCollection.get(1)) {
        imageryLayerCollection.remove(imageryLayerCollection.get(1));
      }
      switch (type) {
        case "BINGMAP":
          imageryProvider = new Cesium.BingMapsImageryProvider({
            url: url,
            key:
              "Aq0D7MCY5ErORA9vrwFtfE9aancUq5J6uNjw0GieF0ostaIrVuJZ8ScXxNHHvEwS"
          });
          break;
        case "TIANDITU":
          // 使用天地图影像底图（使用HTTPS）
          imageryProvider = new Cesium.WebMapTileServiceImageryProvider({
            url: `${PROXY_TIANDITU_TILE}/img_c/wmts?service=WMTS&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${TIANDITU_KEY}`,
            layer: 'img',
            style: 'default',
            format: 'image/jpeg',
            tileMatrixSetID: 'c',
            subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
            tilingScheme: new Cesium.GeographicTilingScheme(),
            tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
            maximumLevel: 15,
            show: true
          });
          break;
        case "IMAGE":
          imageryProvider = new Cesium.SingleTileImageryProvider({
            url: url
          });
          break;
        case "OSM":
          imageryProvider = new Cesium.createOpenStreetMapImageryProvider({
            url: url
          });
          break;
        case "MAPBOX":
          imageryProvider = new Cesium.MapboxImageryProvider({
            mapId: "mapbox.dark"
          });
          break;
        case "SUPERMAPDARK":
          imageryProvider = new Cesium.SuperMapImageryProvider({
            url: url
          });
          break;
        case "SUPERMAPLIGHT":
          imageryProvider = new Cesium.SuperMapImageryProvider({
            url: url
          });
          break;
        case "GRIDIMAGERY":
          imageryProvider = imageryProvider;
          break;
        default:
          imageryProvider = new Cesium.SingleTileImageryProvider({
            url: url
          });
          break;
      }
      if (type != "GRIDIMAGERY") {
        imageryLayerCollection.addImageryProvider(imageryProvider, 0);
        imageryLayerCollection.remove(layer);
        
        // 重新添加注记图层
        if (type === "TIANDITU") {
          // 当天地图作为底图时，总是添加影像注记图层
          // tk 来自外部配置 supermap.config.js
          labelLayer = imageryLayerCollection.addImageryProvider(
            new Cesium.WebMapTileServiceImageryProvider({
              url: `${PROXY_TIANDITU_TILE}/cia_c/wmts?service=WMTS&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${TIANDITU_KEY}`,
              layer: 'cia',
              style: 'default',
              format: 'image/png',
              tileMatrixSetID: 'c',
              subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
              tilingScheme: new Cesium.GeographicTilingScheme(),
              tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
              maximumLevel: 15,
              show: true
            })
          );
          console.log('为天地图添加影像注记图层');
        } else if (!labelLayer) {
          // 其他底图时，如果没有注记图层，则添加
          // tk 来自外部配置 supermap.config.js
          labelLayer = imageryLayerCollection.addImageryProvider(
            new Cesium.WebMapTileServiceImageryProvider({
              url: `${PROXY_TIANDITU_TILE}/cia_c/wmts?service=WMTS&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=c&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${TIANDITU_KEY}`,
              layer: 'cia',
              style: 'default',
              format: 'image/png',
              tileMatrixSetID: 'c',
              subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
              tilingScheme: new Cesium.GeographicTilingScheme(),
              tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19'],
              maximumLevel: 15,
              show: true
            })
          );
          console.log('添加天地图影像注记图层');
        } else {
          imageryLayerCollection.addImageryProvider(labelLayer);
          console.log('恢复天地图影像注记图层');
        }
      }
      if (type == "GRIDIMAGERY") {
        imageryLayerCollection.addImageryProvider(
          new Cesium.TileCoordinatesImageryProvider(),
          1
        );
        imageryLayerCollection.addImageryProvider(
          new Cesium.GridImageryProvider(),
          2
        );
      }
      this.saveLoadedServices();
    },
    // 添加在线地形
    addOnlineTerrain(obj) {
      if (this.terrainLayerObj) this.terrainLayerObj.state = 0;
      obj.state = 1;
      this.terrainLayerObj = obj;
      let type = obj.type;
      let url = obj.proxiedUrl;
      switch (type) {
        case "STKTerrain":
          viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
            url: url,
            isSct: false
          });
          break;
        case "tianDiTuTerrain":
          console.log('加载 Cesium 官方地形...');
          const cesiumIonToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiZGYwNWZmMi0wYTJjLTQ3MDgtODk3Mi1mNTg4YjljZTMzYWUiLCJpZCI6MjA0NTU5LCJpYXQiOjE3MTE0NDc4NDh9.xU-eWEQxv-FeADvsH_uM35EKRN6brqmXl1AQm6phnEM";
          Cesium.Ion.defaultAccessToken = cesiumIonToken;
          // 低性能机器关闭水面掩码和顶点法线，减少地形数据量和光照计算负担
          const perfLow = window.__perfTier === 'low';
          try {
            viewer.terrainProvider = Cesium.createWorldTerrain({
              requestWaterMask: !perfLow,
              requestVertexNormals: !perfLow
            });
            console.log('Cesium 地形加载成功', perfLow ? '(低性能模式：已关闭水面掩码/顶点法线)' : '');
          } catch (e) {
            console.error('Cesium 地形加载失败:', e);
            viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
          }
          break;
        case "supermapOnlineTerrain":
          viewer.terrainProvider = new Cesium.SCTTerrainProvider({
            urls: [url]
          });
          break;
        default:
          break;
      }
      this.saveLoadedServices();
    },

    // 移除地形
    removeTerrainLayer() {
      viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider({
        ellipsoid: viewer.scene.globe.ellipsoid
      });
      this.terrainLayerObj = null;
    },

    addDatas(id, obj, shouldFlyTo = true) {
      switch (id) {
        case "webServe":
          this.addWebServe(obj, shouldFlyTo);
          break;
        case "baseLayer":
          this.addBaseLayer(obj);
          break;
        case "onlineTerrain":
          this.addOnlineTerrain(obj);
          break;
        case "vectorData":
          this.addVectorData(obj, shouldFlyTo);
          break;
      }
    },
    
    // 添加矢量数据服务 - 根据类型选择加载方式
    addVectorData(obj, shouldFlyTo = true) {
      console.log('===== addVectorData 开始 =====');
      console.log('服务名称:', obj.name);
      console.log('服务URL:', obj.proxiedUrl);
      console.log('服务类型:', obj.type);
      console.log('服务状态:', obj.state);

      if (!viewer) {
        ElMessage.error('Cesium viewer 未初始化');
        return;
      }

      // 检查URL是否为空
      if (!obj.proxiedUrl || obj.proxiedUrl.trim() === '') {
        ElMessage.warning(`${obj.name} 暂无数据`);
        return;
      }

      if (obj.state === 0) {
        // 根据数据类型选择加载方式
        if (obj.type === 'MVT') {
          // MVT 类型使用矢量瓦片加载
          console.log('使用MVT方式加载');
          layerManagement.addMvtLayer(obj.proxiedUrl, obj.name, obj.style, (mvtLayer) => {
            if (mvtLayer) {
              obj.state = 1;
              window.store.actions.setChangeLayers();
              this.saveLoadedServices();
              console.log(`${obj.name} MVT加载成功`);
              ElMessage.success(`${obj.name} 加载成功`);
              
              // 添加MVT图层点击事件处理，传入当前图层的瓦片URL
              this.addMvtClickHandler(obj.proxiedUrl);
            } else {
              ElMessage.error(`加载 ${obj.name} 失败`);
            }
          });
        } else if (obj.type === 'RESTFEATURE') {
          // RESTFEATURE 类型使用GeoJsonDataSource加载REST数据服务
          console.log('使用RESTFEATURE方式加载');
          this.addRestFeatureLayer(obj, shouldFlyTo);
        } else if (obj.type === 'RESTIMAGE') {
          // RESTIMAGE 类型使用 SuperMapImageryProvider 加载栅格地图服务
          console.log('使用RESTIMAGE方式加载');
          const imgLayer = layerManagement.addImageLayer(obj.proxiedUrl, obj.name, { skipOutputPrj: obj.skipOutputPrj });
          if (imgLayer) {
            obj.state = 1;
            window.store.actions.setChangeLayers();
            this.saveLoadedServices();
            console.log(`${obj.name} 栅格图加载成功`);
            ElMessage.success(`${obj.name} 加载成功`);
            // 土地利用加载时显示图例
            if (obj.name === '土地利用') {
              this.showLandUseLegend = true;
            }
          } else {
            ElMessage.error(`加载 ${obj.name} 失败`);
          }
        } else {
          ElMessage.warning(`不支持的数据类型: ${obj.type}`);
        }
      } else {
        console.log('服务已加载');
        if (shouldFlyTo) {
          this.flyToVectorData(obj.name);
        }
      }
    },
    
    // 使用GeoJsonDataSource加载REST要素服务（参考SHP转GeoJson的加载方式）
    addRestFeatureLayer(obj, shouldFlyTo = true) {
      console.log('===== 使用GeoJsonDataSource加载矢量数据 =====');
      console.log('服务名称:', obj.name);
      console.log('服务URL:', obj.proxiedUrl);
      
      // 获取样式配置
      const style = obj.style || {
        fillColor: '#00FF00',
        fillOpacity: 0.4,
        strokeColor: '#0055FF',
        strokeWidth: 2
      };
      
      // 参考SHP转GeoJson的加载方式，使用Cesium.GeoJsonDataSource.load
      layerManagement.addGeoJsonLayerWithStyle(obj.proxiedUrl, obj.name, style, (geoJsonLayer) => {
        if (geoJsonLayer) {
          obj.state = 1;
          window.store.actions.setChangeLayers();
          this.saveLoadedServices();
          
          console.log(`${obj.name} 矢量数据加载成功`);
          
          if (shouldFlyTo) {
            viewer.flyTo(geoJsonLayer, { duration: 2 });
          }
          
          // 为矢量数据添加点击弹窗事件
          this.addVectorDataClickHandler(obj.name);
          
          ElMessage.success(`${obj.name} 加载成功`);
        } else {
          ElMessage.error(`加载 ${obj.name} 失败`);
        }
      });
    },
    
    
    
    // 定位到矢量数据
    flyToVectorData(name) {
      if (!viewer || !viewer.dataSources) return;
      
      const dataSources = viewer.dataSources._dataSources;
      for (const ds of dataSources) {
        if (ds.name === name || (ds.name && ds.name.includes(name))) {
          viewer.flyTo(ds, { duration: 2 });
          break;
        }
      }
    },
    
    // 为矢量数据添加点击弹窗事件
    addVectorDataClickHandler(layerName) {
      if (!viewer) return;
      
      // 确保只添加一次点击事件
      if (viewer._vectorDataClickHandler) {
        viewer._vectorDataClickHandler.destroy();
      }
      
      viewer._vectorDataClickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      viewer._vectorDataClickHandler.setInputAction((movement) => {
        const picked = viewer.scene.pick(movement.position);
        
        if (Cesium.defined(picked) && picked.id && picked.id._dataSource) {
          const dataSource = picked.id._dataSource;
          const entity = picked.id;
          
          // 检查是否是当前加载的矢量数据层
          if (dataSource.name === layerName || (dataSource.name && dataSource.name.includes(layerName))) {
            this.showVectorDataPopup(entity, movement.position);
          }
        } else if (Cesium.defined(picked) && picked.id && picked.id._properties) {
          // 处理通过GeoJsonDataSource加载的实体
          const entity = picked.id;
          this.showVectorDataPopup(entity, movement.position);
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    },
    
    // 显示矢量数据弹窗
    showVectorDataPopup(entity, position) {
      console.log('点击矢量数据:', entity);
      
      // 获取实体属性
      let properties = {};
      if (entity._properties) {
        properties = entity._properties.getValue(Cesium.JulianDate.now());
      } else if (entity.properties) {
        properties = entity.properties.getValue(Cesium.JulianDate.now()) || entity.properties;
      }
      
      // 构建弹窗内容
      let content = '<div style="padding: 10px; max-width: 300px;">';
      content += `<h3 style="margin: 0 0 10px 0; font-size: 14px; color: #333;">${entity.name || '属性信息'}</h3>`;
      
      if (properties && typeof properties === 'object') {
        for (const key in properties) {
          if (properties.hasOwnProperty(key)) {
            let value = properties[key];
            if (value === null || value === undefined) value = '';
            if (typeof value === 'object') value = JSON.stringify(value);
            content += `<div style="margin: 5px 0; font-size: 12px;">
              <span style="color: #666;">${key}:</span>
              <span style="margin-left: 5px; color: #333;">${value}</span>
            </div>`;
          }
        }
      } else {
        content += '<div style="color: #999; font-size: 12px;">暂无属性信息</div>';
      }
      content += '</div>';
      
      // 使用tooltip显示弹窗（tooltip2.js的API是 showAt(message, top)）
      if (window.tooltip) {
        const topPosition = position.y + 'px';
        window.tooltip.showAt(content, topPosition);
      }
    },
    
    // 添加MVT图层点击事件处理
    addMvtClickHandler(tileBaseUrl) {
      console.log('aside.vue addMvtClickHandler 被调用', tileBaseUrl);
      
      // 初始化 MVT 图层列表（支持多图层）
      if (!this._mvtTileUrls) {
        this._mvtTileUrls = [];
      }
      
      if (tileBaseUrl) {
        const tileUrl = tileBaseUrl.endsWith('/') 
          ? tileBaseUrl + 'tiles/{z}/{x}/{y}.mvt'
          : tileBaseUrl + '/tiles/{z}/{x}/{y}.mvt';
        
        // 避免重复添加相同的瓦片URL
        if (!this._mvtTileUrls.includes(tileUrl)) {
          this._mvtTileUrls.push(tileUrl);
        }
      }
      
      // 初始化点击处理器（单例）
      if (!this.mvtClickHandler) {
        this.mvtClickHandler = new MvtClickHandler(this);
      }
      
      // 标记已加载 MVT 图层，全局点击处理器会进行检测
      this._hasMvtLayer = true;
    },
    
    // 初始化viewer后的传入回调：添加底图和清除加载动画
    async removeLoad() {
      this.isRestoring = true; // 标记正在恢复，禁止保存
      
      setTimeout(() => {
        let loading = document.getElementById("loadingbar");
        if (loading) loading.remove(); //移除加载动画
        document.getElementById("el-aside").classList.remove("disable"); //解除禁止点击
      }, 1000);
      
      // 等待自定义服务加载完成后再恢复
      setTimeout(async () => {
        await this.fetchCustomServices();
        await this.restoreLoadedServices();
        this.isRestoring = false; // 恢复完成，允许保存
        
        // 加载水面图层
        this.loadWaterLayers();
      }, 1500);
    },
    
    // 加载水面图层
    loadWaterLayers() {
      console.log('========== loadWaterLayers 开始执行 ==========');
      console.log('window.viewer:', window.viewer);
      
      if (!window.viewer) {
        console.error('window.viewer 不存在，无法加载水面图层');
        return;
      }
      
      console.log('开始加载莒口水闸河道范围水面...');
      
      // 加载莒口水闸河道范围水面
      waterLayer.loadWaterLayer(
        '/js/莒口水闸河道范围.geojson',
        '莒口水闸河道',
        (dataSource) => {
          console.log('水面图层加载完成', dataSource);
        }
      );
    },

    // 删除场景公共服务
    DeleteDates(datatype, obj) {
      switch (datatype) {
        case "webServe":
          obj.layers.forEach(layer => {
            layerManagement.layersDelete(layer.type, layer.layerName, () => {
              obj.state = 0;
              window.store.actions.setChangeLayers();
              this.saveLoadedServices();
            });
          });
          break;
        case "baseLayer":
          break;
        case "onlineTerrain":
          this.removeTerrainLayer();
          obj.state = 0;
          this.saveLoadedServices();
          break;
        case "vectorData":
          // 根据矢量数据类型选择卸载方式：MVT用removeVectorTilesMap，RESTIMAGE用imageryLayers，RESTFEATURE用dataSources
          let vectorDeleteType;
          if (obj.type === 'MVT') {
            vectorDeleteType = 'MVT';
          } else if (obj.type === 'RESTIMAGE') {
            vectorDeleteType = 'IMG';
          } else {
            vectorDeleteType = 'GEOJSON';
          }
          layerManagement.layersDelete(vectorDeleteType, obj.name, () => {
            obj.state = 0;
            window.store.actions.setChangeLayers();
            this.saveLoadedServices();
            // 土地利用卸载时隐藏图例
            if (obj.name === '土地利用') {
              this.showLandUseLegend = false;
            }
          });
          // MVT 卸载后从图层列表中移除，并更新标记
          if (obj.type === 'MVT') {
            // 从 _mvtTileUrls 中移除当前图层的 URL
            if (this._mvtTileUrls && obj.proxiedUrl) {
              const tileUrl = obj.proxiedUrl.endsWith('/') 
                ? obj.proxiedUrl + 'tiles/{z}/{x}/{y}.mvt'
                : obj.proxiedUrl + '/tiles/{z}/{x}/{y}.mvt';
              const index = this._mvtTileUrls.indexOf(tileUrl);
              if (index > -1) {
                this._mvtTileUrls.splice(index, 1);
              }
            }
            // 只有当所有 MVT 图层都卸载时，才清除标记
            this._hasMvtLayer = this._mvtTileUrls && this._mvtTileUrls.length > 0;
            this.mvtPopupVisible = false;
          }
          break;
      }
    },

    isServiceLoaded(service) {
      if (!service) {
        return false;
      }
      const serviceId = service.id || service.serviceId;
      return this.loadedCustomServiceIds.includes(serviceId);
    },

    async loadCustomService(service, flyTo = true, shouldSave = true) {
      console.log('========== 开始加载自定义服务 ==========');
      console.log('服务名称:', service.name);
      console.log('服务类型:', service.type);
      console.log('服务URL:', service.url);
      console.log('是否跳转视角:', flyTo);
      console.log('viewer状态:', viewer);

      if (!viewer) {
        ElMessage.error('Cesium viewer 未初始化');
        return;
      }

      if (this.isServiceLoaded(service)) {
        console.log('服务已加载，执行定位');
        if (flyTo) {
          const type = service.type || (service.layers && service.layers[0] ? service.layers[0].type : null);
          if (type === 'SCENE' || type === 'S3M') {
            const layerNames = service.loadedLayerNames || [service.name];
            let foundLayer = null;
            for (const name of layerNames) {
              foundLayer = viewer.scene.layers.find(name);
              if (foundLayer) break;
            }
            if (foundLayer) {
              console.log('跳转到图层:', foundLayer.name);
              viewer.flyTo(foundLayer, { duration: 2 });
            } else if (viewer.scene.layers.layerQueue && viewer.scene.layers.layerQueue.length > 0) {
              viewer.flyTo(viewer.scene.layers.layerQueue[0], { duration: 2 });
            }
          } else {
            const layerName = service.name;
            camera.flyByLayerName(type, layerName);
          }
        }
        return;
      }

      try {
        const type = service.type || (service.layers && service.layers[0] ? service.layers[0].type : null);
        const url = service.url;
        const layerName = service.name;
        const token = service.token;
        const tokenRequired = service.tokenRequired;
        const serviceId = service.id || service.serviceId;

        const markAsLoaded = () => {
          if (!this.loadedCustomServiceIds.includes(serviceId)) {
            this.loadedCustomServiceIds.push(serviceId);
          }
        };

        switch (type) {
          case 'SCENE':
            let sceneOptions = {
              autoSetView: flyTo
            };
            if (tokenRequired && token) {
              sceneOptions.SceneToken = token;
            }
            const beforeLayersCount = viewer.scene.layers ? viewer.scene.layers.layerQueue.length : 0;
            console.log('加载前图层数量:', beforeLayersCount);
            console.log('是否自动跳转视角:', flyTo);
            layerManagement.addScene(url, sceneOptions, (layers) => {
              console.log('SCENE加载成功:', layers);
              markAsLoaded();
              const afterLayers = viewer.scene.layers ? viewer.scene.layers.layerQueue : [];
              const newLayers = afterLayers.slice(beforeLayersCount);
              console.log('新添加的图层:', newLayers);
              const newLayerNames = newLayers.map(l => l.name);
              console.log('新图层名称:', newLayerNames);
              service.loadedLayerNames = newLayerNames;
              const currentPosition = viewer.camera.positionCartographic;
              service.defaultCameraPosition = {
                longitude: currentPosition.longitude,
                latitude: currentPosition.latitude,
                height: currentPosition.height,
                heading: viewer.camera.heading,
                pitch: viewer.camera.pitch,
                roll: viewer.camera.roll
              };
              console.log('记录默认视口位置:', service.defaultCameraPosition);
              if (flyTo && newLayers.length > 0) {
                camera.flyByLayerName('SCENE', newLayers[0].name);
              }
            });
            break;
          case 'S3M':
            let scps = [{ url: url, options: { name: layerName } }];
            layerManagement.addS3mLayers(scps, (layers) => {
              console.log('S3M加载成功:', layers);
              markAsLoaded();
              if (flyTo) {
                camera.flyByLayerName('S3M', layerName);
              }
            });
            break;
          case 'IMG':
          case 'IMAGE':
            let imgLayer = layerManagement.addImageLayer(url, layerName);
            console.log('IMG/IMAGE加载成功:', imgLayer);
            markAsLoaded();
            if (flyTo) {
              camera.flyByLayerName('IMG', layerName);
            }
            break;
          case 'TERRAIN':
            let terrainProvider = layerManagement.addTerrainLayer(url, false);
            console.log('TERRAIN加载成功:', terrainProvider);
            markAsLoaded();
            break;
          case 'MVT':
            layerManagement.addMvtLayer(url, layerName, (mvtlayer) => {
              console.log('MVT加载成功:', mvtlayer);
              markAsLoaded();
              if (flyTo) {
                viewer.flyTo(mvtlayer, { duration: 2 });
              }
              // 添加MVT图层点击事件处理，传入当前图层的瓦片URL
              this.addMvtClickHandler(url);
            });
            break;
          case 'SHP':
          case 'GEOJSON':
          case 'VECTOR_TILE':
            layerManagement.addGeoJsonLayer(url, layerName, (geoJsonLayer) => {
              console.log('SHP/GEOJSON/VECTOR_TILE加载成功:', geoJsonLayer);
              markAsLoaded();
              if (flyTo) {
                viewer.flyTo(geoJsonLayer, { duration: 2 });
              }
            });
            break;
          default:
            ElMessage.warning('不支持的服务类型: ' + type);
            return;
        }

        if (shouldSave) {
          ElMessage.success(`服务 "${service.name}" 加载成功`);
        }
        console.log('========== 自定义服务加载完成 ==========');
        
        if (shouldSave) {
          this.saveLoadedServices();
        }
      } catch (error) {
        console.error('加载自定义服务失败:', error);
        ElMessage.error('加载服务失败: ' + (error.message || '未知错误'));
      }
    },

    unloadCustomService(service) {
      console.log('开始卸载服务:', service);
      if (!service) {
        console.warn('服务对象为空');
        return;
      }
      
      const serviceId = service.id || service.serviceId;
      const serviceType = service.type || 'SCENE';
      
      const finishUnload = () => {
        const idx = this.loadedCustomServiceIds.indexOf(serviceId);
        if (idx > -1) {
          this.loadedCustomServiceIds.splice(idx, 1);
          console.log('已从loadedCustomServiceIds中移除服务:', serviceId);
        }
        window.store.actions.setChangeLayers();
        this.saveLoadedServices();
        this.$forceUpdate();
      };
      
      if (serviceType === 'SCENE' && service.loadedLayerNames && service.loadedLayerNames.length > 0) {
        console.log('使用loadedLayerNames卸载SCENE图层:', service.loadedLayerNames);
        let unloadedCount = 0;
        service.loadedLayerNames.forEach((layerName, index) => {
          console.log('卸载图层:', layerName);
          layerManagement.layersDelete('S3M', layerName, () => {
            console.log('图层已卸载:', layerName);
            unloadedCount++;
            if (unloadedCount === service.loadedLayerNames.length) {
              finishUnload();
            }
          });
        });
      } else if (service.layers && service.layers.length > 0) {
        let unloadedCount = 0;
        service.layers.forEach((layer, index) => {
          console.log('卸载图层:', layer.type, layer.layerName);
          layerManagement.layersDelete(layer.type, layer.layerName, () => {
            console.log('图层已卸载:', layer.layerName);
            unloadedCount++;
            if (unloadedCount === service.layers.length) {
              finishUnload();
            }
          });
        });
      } else {
        const type = service.type || 'SCENE';
        const layerName = service.name;
        console.log('卸载参数 - serviceId:', serviceId, 'type:', type, 'layerName:', layerName);
        layerManagement.layersDelete(type, layerName, () => {
          console.log('服务已卸载:', service.name);
          finishUnload();
        });
      }
    },

    // 控制自定义服务组件显隐
    addCustomService() {
      if (this.addService === "") {
        this.addService = "Sm3dCustomService";
        // 重置面板位置到默认值，使用setTimeout确保组件完全渲染
        setTimeout(() => {
          const panel = document.getElementById('CustomService-panel');
          if (panel) {
            panel.style.left = '35%';
            panel.style.top = '25%';
          }
        }, 100);
      } else {
        this.addService = "";
      }
      // 移除自定义服务菜单项的选中状态
      this.$nextTick(() => {
        const menu = this.$refs.menu;
        if (menu) {
          menu.activeIndex = "";
        }
      });
    },
    // 自定义服务添加回调函数
    addCallback(layers, add_type, serviceInfo) {
      console.log('接收到addCallback调用:', layers, add_type, serviceInfo);
      
      if (!Array.isArray(layers)) {
        layers = [layers];
      }
      let serviceOption = [];
      let serviceUrl = serviceInfo ? serviceInfo.url : "";
      let serviceToken = serviceInfo ? serviceInfo.token : "";
      let isAddToken = serviceInfo ? serviceInfo.tokenRequired : false;
      
      try {
        for (let i = 0; i < layers.length; i++) {
          if (!layers[i]) {
            console.log('layers[' + i + '] 是 undefined 或 null，跳过');
            continue;
          }
          if (layers[i].hasOwnProperty("_isS3MB")) {
            let option = {
              type: "S3M",
              layerName: layers[i].name
                ? layers[i].name
                : "s3m_" + new Date().getTime().toFixed(6)
            };
            serviceOption.push(option);
            if (!layers[i].maxVisibleAltitude)
              layers[i].maxVisibleAltitude = 8000;
          } else if (viewer.imageryLayers.contains(layers[i])) {
            let option = {
              type: "IMG",
              layerName: layers[i].imageryProvider.tablename
                ? layers[i].imageryProvider.tablename
                : "image" + new Date().getTime().toFixed(6)
            };
            serviceOption.push(option);
          } else if (
            layers[i].imageryLayer &&
            layers[i].imageryLayer._imageryProvider instanceof
              Cesium.MvtProviderGL
          ) {
            let option = {
              type: "MVT",
              layerName: layers[i].name
                ? layers[i].name
                : "mvt" + new Date().getTime().toFixed(6)
            };
            serviceOption.push(option);
            let index = this.addCustomServices.length;
            let obj = {
              name: layers[i].name ? layers[i].name : "自定义服务" + index,
              layers: serviceOption
            };
            this.addCustomServices.push(obj);
            this.addService = "";
            
            // 存储服务到数据库
            console.log('准备调用saveServiceToDatabase (MVT):', serviceInfo, add_type, obj.name);
            if (serviceInfo && serviceUrl) {
              console.log('调用saveServiceToDatabase (MVT)');
              this.saveServiceToDatabase(serviceInfo, add_type, obj.name);
            } else {
              console.log('serviceInfo或serviceUrl为空 (MVT):', serviceInfo, serviceUrl);
            }
            
            return;
          } else {
            let option = {
              type: "TERRAIN",
              layerName: layers[i].tablename ? layers[i].tablename : "terrain"
            };
            serviceOption.push(option);
          }
        }
      } catch (error) {
        console.error('处理layers时发生错误:', error);
      }
      
      if (this.varName) this.getNames.push(this.varName);
      let index = this.addCustomServices.length;
      let len = this.getNames.length - 1;
      let obj = {
        name: this.varName ? this.varName : "自定义服务" + index,
        layers: serviceOption
      };
      this.addCustomServices.push(obj);
      this.addService = "";
      this.varName = undefined;
      
      // 存储服务到数据库
      // SHP上传后端已自动保存，跳过前端重复保存
      const isShpUpload = serviceUrl && serviceUrl.startsWith('/profile/geojson/');
      if (!isShpUpload) {
        console.log('准备调用saveServiceToDatabase (非MVT):', serviceInfo, add_type, obj.name);
        if (serviceInfo && serviceUrl) {
          console.log('调用saveServiceToDatabase (非MVT)');
          this.saveServiceToDatabase(serviceInfo, add_type, obj.name);
        } else {
          console.log('serviceInfo或serviceUrl为空 (非MVT):', serviceInfo, serviceUrl);
        }
      } else {
        console.log('SHP上传后端已自动保存，直接标记为已加载');
        this.addService = "";
        this.varName = undefined;
        // 使用上传响应中返回的id更新服务对象并标记为已加载
        const serviceId = serviceInfo && serviceInfo.id;
        if (serviceId) {
          // 更新已在列表中的obj，使isServiceLoaded能匹配
          obj.id = serviceId;
          obj.url = serviceUrl;
          obj.type = add_type || 'GEOJSON';
          obj.layers = [{ type: 'GEOJSON', layerName: serviceInfo.layerName || obj.name }];
          if (!this.loadedCustomServiceIds.includes(serviceId)) {
            this.loadedCustomServiceIds.push(serviceId);
          }
        }
        return;
      }
      
      if ((add_type && add_type === "SCENE") || add_type === "TERRAIN") return;
      camera.flyByLayerName(add_type, obj.layers[0].layerName); //定位
    },
    
    // 保存服务到数据库
    saveServiceToDatabase(serviceInfo, serviceType, serviceName) {
      console.log('开始保存服务到数据库:', serviceInfo, serviceType, serviceName);
      
      const serviceData = {
        name: serviceName,
        type: serviceType,
        url: serviceInfo.url,
        tokenRequired: serviceInfo.tokenRequired ? 1 : 0,
        token: serviceInfo.token,
        status: 'NORMAL'
      };
      
      console.log('准备保存服务:', serviceData);
      
      serviceApi.addService(serviceData)
        .then(response => {
          console.log('服务保存成功:', response);
          const newServiceId = response.data || response.serviceId;
          if (newServiceId) {
            const service = this.addCustomServices.find(s => s.name === serviceName);
            if (service) {
              service.id = newServiceId;
              service.url = serviceInfo.url;
              service.token = serviceInfo.token;
              service.tokenRequired = serviceInfo.tokenRequired;
              service.type = serviceType;
            }
            if (!this.loadedCustomServiceIds.includes(newServiceId)) {
              this.loadedCustomServiceIds.push(newServiceId);
            }
          }
        })
        .catch(error => {
          console.error('服务保存失败:', error);
          if (error.response) {
            console.error('错误响应状态:', error.response.status);
            console.error('错误响应数据:', error.response.data);
          } else if (error.request) {
            console.error('错误请求:', error.request);
          } else {
            console.error('错误信息:', error.message);
          }
        });
    },

    // 自定义服务获取名称回调
    getName(name) {
      this.varName = name;
    },

    // 添加服务飞行函数(公共服务配置好的定位)
    flyTo(webName, obj) {
      let cameraParam = obj[webName];
      if (cameraParam) {
        let destination;
        // 支持两种格式：经纬度格式和笛卡尔坐标格式
        if (cameraParam.longitude !== undefined && cameraParam.latitude !== undefined) {
          destination = Cesium.Cartesian3.fromDegrees(
            cameraParam.longitude,
            cameraParam.latitude,
            cameraParam.height || 50000
          );
        } else if (cameraParam.Cartesian3) {
          destination = new Cesium.Cartesian3(
            cameraParam.Cartesian3.x,
            cameraParam.Cartesian3.y,
            cameraParam.Cartesian3.z
          );
        }
        
        viewer.scene.camera.flyTo({
          destination: destination,
          orientation: {
            heading: cameraParam.heading,
            pitch: cameraParam.pitch,
            roll: cameraParam.roll
          },
          duration: 2
        });
        return;
      } else {
      }
    },

    //图层管理右键删除回调
    deleteCallback(node_rightClick) {
      this.server_config[0].children.forEach(sceneObj => {
        sceneObj.layers.forEach(layerObj => {
          if (layerObj.layerName === node_rightClick.label) {
            sceneObj.state = 0;
            return;
          }
        });
      });
    },

    // 保存已加载的服务到数据库 (带防抖)
    saveLoadedServices() {
      if (this.isRestoring) {
        console.log('正在恢复配置，跳过保存');
        return;
      }
      
      if (this.saveConfigTimer) {
        clearTimeout(this.saveConfigTimer);
      }
      
      this.saveConfigTimer = setTimeout(async () => {
        const now = Date.now();
        if (now - this.lastSaveTime < 2000) {
          console.log('距离上次保存不足2秒，跳过保存');
          return;
        }
        
        try {
          const loadedServices = {
            baseLayer: this.baseLayerObj ? {
              type: this.baseLayerObj.type,
              name: this.baseLayerObj.name,
              proxiedUrl: this.baseLayerObj.proxiedUrl
            } : null,
            terrainLayer: this.terrainLayerObj ? {
              type: this.terrainLayerObj.type,
              name: this.terrainLayerObj.name,
              proxiedUrl: this.terrainLayerObj.proxiedUrl
            } : null,
            webServices: this.server_config[0].children
              .filter(service => service.state === 1)
              .map(service => ({
                type: service.type,
                name: service.name,
                proxiedUrl: service.proxiedUrl,
                layers: service.layers,
                style: service.style
              }))
          };
          
          const loadedModels = this.loadedModelEntities.map(entity => {
            const model = this.modelData.find(m => m.id === entity.name);
            return model ? {
              id: model.id,
              name: model.name,
              type: model.type,
              filePath: model.filePath,
              coordinates: model.coordinates,
              format: model.format,
              rotationX: model.rotationX,
              rotationY: model.rotationY,
              rotationZ: model.rotationZ,
              scaleX: model.scaleX,
              scaleY: model.scaleY,
              scaleZ: model.scaleZ
            } : null;
          }).filter(m => m !== null);

          const loadedCustomServices = this.addCustomServices
            .filter(service => this.loadedCustomServiceIds.includes(service.id || service.serviceId))
            .map(service => ({
              id: service.id || service.serviceId,
              name: service.name,
              type: service.type,
              url: service.url,
              tokenRequired: service.tokenRequired,
              token: service.token,
              loadedLayerNames: service.loadedLayerNames || []
            }));

          const configData = {
            configName: '默认配置',
            baseLayerType: loadedServices.baseLayer ? loadedServices.baseLayer.type : '',
            baseLayerName: loadedServices.baseLayer ? loadedServices.baseLayer.name : '',
            baseLayerUrl: loadedServices.baseLayer ? loadedServices.baseLayer.proxiedUrl : '',
            terrainLayerType: loadedServices.terrainLayer ? loadedServices.terrainLayer.type : '',
            terrainLayerName: loadedServices.terrainLayer ? loadedServices.terrainLayer.name : '',
            terrainLayerUrl: loadedServices.terrainLayer ? loadedServices.terrainLayer.proxiedUrl : '',
            webServices: JSON.stringify(loadedServices.webServices),
            loadedModels: JSON.stringify(loadedModels),
            loadedCustomServices: JSON.stringify(loadedCustomServices)
          };

          console.log('========== 保存地图配置 ==========');
          await saveMapConfig(configData);
          this.lastSaveTime = Date.now();
          console.log('地图配置已保存到数据库');
        } catch (error) {
          console.error('保存地图配置失败:', error);
          console.error('错误详情:', error.message);
          if (error.response) {
            console.error('响应状态:', error.response.status);
            console.error('响应数据:', error.response.data);
          }
        }
      }, 500);
    },

    // 从数据库恢复已加载的服务和模型
    async restoreLoadedServices() {
      try {
        console.log('========== 开始恢复地图配置 ==========');
        const response = await getDefaultMapConfig();
        console.log('API响应:', response);
        
        if (!response || !response.data) {
          console.log('没有找到保存的地图配置，使用默认配置');
          return;
        }

        const config = response.data;
        console.log('保存的配置:', config);
        
        let loadedServices = null;
        let loadedModels = null;

        // 保存需要恢复的自定义服务信息（不立即设置loadedCustomServiceIds，避免跳过加载步骤）
        let customServicesToLoad = [];
        if (config.loadedCustomServices) {
          try {
            const savedCustomServices = JSON.parse(config.loadedCustomServices);
            console.log('解析的自定义服务配置:', savedCustomServices);
            if (savedCustomServices && savedCustomServices.length > 0) {
              customServicesToLoad = savedCustomServices.map(s => ({
                id: s.id,
                loadedLayerNames: s.loadedLayerNames || []
              }));
              console.log('需要恢复的自定义服务:', customServicesToLoad);
            }
          } catch (e) {
            console.error('解析自定义服务配置失败:', e);
          }
        }

        // 解析服务配置
        if (config.webServices) {
          try {
            loadedServices = JSON.parse(config.webServices);
            console.log('解析的服务配置:', loadedServices);
          } catch (e) {
            console.error('解析服务配置失败:', e);
          }
        }

        // 解析模型配置
        if (config.loadedModels) {
          try {
            loadedModels = JSON.parse(config.loadedModels);
            console.log('解析的模型配置:', loadedModels);
          } catch (e) {
            console.error('解析模型配置失败:', e);
          }
        }

        // 恢复底图 (server_config[2] 是在线底图)
        if (config.baseLayerType) {
          const baseLayer = this.server_config[2].children.find(
            layer => layer.type === config.baseLayerType
          );
          if (baseLayer) {
            console.log('恢复底图:', baseLayer.name);
            await new Promise(resolve => {
              this.addBaseLayer(baseLayer);
              setTimeout(resolve, 500);
            });
          }
        }

        // 恢复地形 (server_config[3] 是在线地形)
        if (config.terrainLayerType) {
          const terrainLayer = this.server_config[3].children.find(
            layer => layer.type === config.terrainLayerType
          );
          if (terrainLayer) {
            console.log('恢复地形:', terrainLayer.name);
            await new Promise(resolve => {
              this.addOnlineTerrain(terrainLayer);
              setTimeout(resolve, 500);
            });
          }
        }

        // 恢复公共服务 (server_config[1] 是公共服务)
        if (loadedServices && loadedServices.length > 0) {
          for (const webService of loadedServices) {
            const service = this.server_config[1].children.find(
              s => s.name === webService.name
            );
            if (service) {
              console.log('恢复公共服务:', service.name);
              await new Promise(resolve => {
                service.state = 0;
                this.addWebServe(service, false);
                setTimeout(resolve, 500);
              });
            }
          }
        }

        // 恢复模型 —— 默认初始化时不加载模型，由用户手动加载
        // if (loadedModels && loadedModels.length > 0) {
        //   for (const modelData of loadedModels) {
        //     const model = this.modelData.find(m => m.id === modelData.id);
        //     if (model && !this.isModelLoaded(model.id)) {
        //       await new Promise(resolve => {
        //         this.loadModel(model, false);
        //         setTimeout(resolve, 1000);
        //       });
        //     }
        //   }
        // }

        // 恢复自定义服务 (初始化时不跳转视角，不保存)
        if (customServicesToLoad && customServicesToLoad.length > 0) {
          for (const serviceInfo of customServicesToLoad) {
            const customService = this.addCustomServices.find(
              s => (s.id || s.serviceId) === serviceInfo.id
            );
            if (customService && !this.isServiceLoaded(customService)) {
              // 恢复 loadedLayerNames
              if (serviceInfo.loadedLayerNames && serviceInfo.loadedLayerNames.length > 0) {
                customService.loadedLayerNames = serviceInfo.loadedLayerNames;
                console.log('恢复服务loadedLayerNames:', customService.name, serviceInfo.loadedLayerNames);
              }
              console.log('恢复自定义服务:', customService.name);
              await new Promise(resolve => {
                this.loadCustomService(customService, false, false);
                setTimeout(resolve, 1000);
              });
            }
          }
        }

        console.log('地图配置恢复完成');
      } catch (error) {
        console.error('恢复地图配置失败:', error);
        console.error('错误详情:', error.message);
        console.error('错误堆栈:', error.stack);
        if (error.response) {
          console.error('响应状态:', error.response.status);
          console.error('响应数据:', error.response.data);
        }
      }
    },

    // 清除所有持久化数据
    clearPersistence() {
      localStorage.removeItem('cesium_loaded_services');
      localStorage.removeItem('cesium_loaded_models');
      console.log('持久化数据已清除');
    },
    
    // 为模型添加点击事件，激活编辑器
    addModelClickHandler(entity) {
      if (!entity || !window.viewer) return;
      
      const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.canvas);
      
      handler.setInputAction((movement) => {
        const picked = window.viewer.scene.pick(movement.position);
        
        console.log('pick结果:', picked);
        console.log('picked.id:', picked ? picked.id : null);
        console.log('picked.id === entity:', picked ? (picked.id === entity) : false);
        
        if (Cesium.defined(picked) && picked.id === entity) {
          console.log('点击了模型:', entity);
          console.log('entity.model:', entity.model);
          console.log('entity类型:', entity.constructor.name);
          
          // 先恢复上一个模型的样式
          if (this.lastHighlightedModel && this.lastHighlightedModel !== entity) {
            this.restoreModelStyle(this.lastHighlightedModel);
          }
          
          // 高亮显示模型
          this.highlightModel(entity);
          this.lastHighlightedModel = entity;
          
          // 激活编辑器 - 已禁用，不再显示模型控制器
          // this.activateModelEditor(entity);
          
          // 标记已选中模型
          this._isModelSelected = true;
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      
      // 保存 handler，以便后续清理
      if (!entity._clickHandler) {
        entity._clickHandler = handler;
      }
    },
    
    // 全局点击处理器 - 检测空白区域点击取消选中
    initGlobalClickHandler() {
      if (!window.viewer || this._globalClickHandlerInitialized) return;
      
      const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.canvas);
      
      handler.setInputAction(async (movement) => {
        const picked = window.viewer.scene.pick(movement.position);
        
        console.log('全局点击 - picked:', picked);
        
        // ========== MVT 矢量瓦片点击检测 ==========
        // MVT 无法被 scene.pick() 拾取，只在未拾取到任何实体时才尝试 MVT 查询，
        // 避免与 GeoJSON 要素、模型等其他实体的点击事件冲突
        if (this._hasMvtLayer && this.mvtClickHandler && !Cesium.defined(picked)) {
          // 获取点击位置经纬度
          const ray = window.viewer.camera.getPickRay(movement.position);
          const cartesian = window.viewer.scene.globe.pick(ray, window.viewer.scene);
          
          if (cartesian) {
            const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
            const lon = Cesium.Math.toDegrees(cartographic.longitude);
            const lat = Cesium.Math.toDegrees(cartographic.latitude);
            
            // 计算当前相机 zoom：画面能渲染说明该级别有瓦片，优先查该级别命中最快
            // (兼容只在特定比例尺发布的服务，如水资源分区仅 z=6/7 有瓦片)
            const camHeight = window.viewer.camera.positionCartographic.height;
            const currentZoom = Math.round(Math.log2((2 * Math.PI * 6378137) / camHeight));
            // 遍历所有已加载的 MVT 图层进行查询
            await this.mvtClickHandler.handleClick(lon, lat, {
              x: movement.position.x,
              y: movement.position.y
            }, this._mvtTileUrls || [], currentZoom);
          }
        }
        
        // 如果点击的是GeoJSON点，不处理（让model-transform-panel.vue处理）
        if (picked && picked.id && picked.id.isGeoJsonPoint) {
          console.log('点击了GeoJSON点，不处理');
          return;
        }
        
        // 检查是否点击了空白区域（不是模型）
        // 如果没有 pick 到任何东西，或者 pick 到的东西不是当前选中的模型
        let isClickingOnSelectedModel = false;
        
        if (picked && picked.id && this.lastHighlightedModel) {
          // 检查是否点击的是当前选中的模型
          if (picked.id === this.lastHighlightedModel || 
              (typeof picked.id === 'string' && picked.id === this.lastHighlightedModel._id)) {
            isClickingOnSelectedModel = true;
          }
        }
        
        // 如果点击的不是当前选中的模型，且有选中的模型，则取消选中
        if (!isClickingOnSelectedModel && this.lastHighlightedModel) {
          console.log('取消选中模型');
          this.restoreModelStyle(this.lastHighlightedModel);
          
          // 销毁编辑器
          if (this.currentModelEditor) {
            this.currentModelEditor.destroy();
            this.currentModelEditor = null;
          }
          
          this.lastHighlightedModel = null;
          this._isModelSelected = false;
        }
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
      
      this._globalClickHandlerInitialized = true;
      console.log('全局点击处理器已初始化');
    },
    
    // 恢复模型样式
    restoreModelStyle(entity) {
      if (!entity) return;
      
      if (entity._originalShow !== undefined) {
        entity.show = entity._originalShow;
      }
      if (entity.model) {
        entity.model.color = entity._originalColor;
        entity.model.colorBlendMode = entity._originalColorBlendMode;
        entity.model.colorBlendAmount = entity._originalColorBlendAmount;
      }
    },
    
    // 高亮显示模型
    highlightModel(entity) {
      if (!entity) return;
      
      // 保存原始状态
      if (!entity._originalShow) {
        entity._originalShow = entity.show;
      }
      if (!entity._originalColor) {
        entity._originalColor = entity.model ? entity.model.color : undefined;
      }
      if (!entity._originalColorBlendMode) {
        entity._originalColorBlendMode = entity.model ? entity.model.colorBlendMode : undefined;
      }
      if (!entity._originalColorBlendAmount) {
        entity._originalColorBlendAmount = entity.model ? entity.model.colorBlendAmount : undefined;
      }
      
      // 高亮显示模型 - 使用透明模式
      if (entity.model) {
        console.log('模型存在,设置透明');
        // 尝试直接设置颜色
        entity.model.color = Cesium.Color.YELLOW;
      } else {
        console.log('模型不存在,实体结构:', entity);
      }
    },
    
    // 激活模型编辑器
    activateModelEditor(entity) {
      if (!entity || !window.viewer) return;
      
      // 清除之前的编辑器
      if (this.currentModelEditor) {
        if (this.currentModelEditor.destroy) {
          this.currentModelEditor.destroy();
        }
        this.currentModelEditor = null;
      }
      
      // 创建新的编辑器
      try {
        console.log('创建 ModelEditor for entity:', entity);
        console.log('window.viewer:', window.viewer);
        console.log('window.viewer.primitives:', window.viewer ? window.viewer.primitives : undefined);
        this.currentModelEditor = new ModelEditor(window.viewer, entity);
        
        // 等待模型加载完成
        if (this.currentModelEditor.waitForModelLoad) {
          this.currentModelEditor.waitForModelLoad().then(() => {
            console.log('模型加载完成，编辑器初始化成功');
          }).catch((error) => {
            console.error('模型加载或编辑器初始化失败:', error);
          });
        }
        
        console.log('ModelEditor 创建成功');
      } catch (error) {
        console.error('创建 ModelEditor 失败:', error);
        this.currentModelEditor = null;
      }
    }
  },
  
  watch: {
    isCollapse(val) {
      if (val) {
        this.asideWidth = "asideWidth2";
      } else {
        this.asideWidth = "asideWidth1";
      }
    }
  },
  mounted() {
    document.getElementById("el-aside").classList.add("disable"); //禁止点击
    // 直接初始化 viewer，不使用 window.onload
    this.$nextTick(() => {
      this.initViewer = "Sm3dViewer";
      // 获取模型数据
      this.fetchModels();
      // 从数据库加载自定义服务
      this.fetchCustomServices();
      // 从数据库加载业务场景列表
      this.refreshBusinessScenes();
      // 加载固定场景列表
      this.loadFixedScenes();
    });
    
    // 延迟初始化全局点击处理器，等待 viewer 准备好
    setTimeout(() => {
      console.log('延迟初始化全局点击处理器');
      if (window.viewer) {
        this.viewer = window.viewer;
        this.initGlobalClickHandler();
      }
    }, 3000);

    // 监听创建场景成功事件，刷新业务场景列表
    this.refreshScenesHandler = () => {
      this.refreshBusinessScenes()
    }
    document.addEventListener('refreshBusinessScenes', this.refreshScenesHandler)
  },
  beforeUnmount() {
    // 移除事件监听
    document.removeEventListener('refreshBusinessScenes', this.refreshScenesHandler)
  }
};
</script>

<style lang="scss" scoped>
.container {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  pointer-events: auto;
}

/* 侧边栏宽度控制 */
.asideWidth1 {
  width: 280px !important;
  transition-duration: 0.3s;
  height: 100% !important;
}
.asideWidth2 {
  transition-duration: 0.5s;
  width: 40px !important;
  height: 100% !important;
  display: flex !important;
  justify-content: center !important;
}

/* 确保el-container能够铺满整个容器 */
.el-container {
  height: 100% !important;
  width: 100% !important;
  pointer-events: auto;
}

/* 确保el-aside能够正确接收点击事件 */
.el-aside {
  position: relative;
  z-index: 1000;
  pointer-events: auto;
}

/* 确保el-main能够铺满整个容器 */
.el-main {
  position: relative;
  z-index: 1;
  height: 100% !important;
  width: 100% !important;
  padding: 0 !important;
  margin: 0 !important;
  pointer-events: auto;
}

/* 确保cesiumContainer能够铺满整个el-main */
#cesiumContainer {
  position: relative;
  z-index: 10;
  height: 100% !important;
  width: 100% !important;
  pointer-events: auto;
}

/* 确保cesium-viewer能够铺满整个cesiumContainer */
.cesium-viewer {
  height: 100% !important;
  width: 100% !important;
  pointer-events: auto;
}

/* 确保cesium-widget能够铺满整个cesium-viewer */
.cesium-widget {
  height: 100% !important;
  width: 100% !important;
  pointer-events: auto;
}

/* 确保canvas能够铺满整个cesium-widget */
.cesium-widget canvas {
  position: relative;
  z-index: 5;
  height: 100% !important;
  width: 100% !important;
  pointer-events: auto;
}

/* 确保侧边栏菜单能够正确接收点击事件 */
.el-menu-vertical-demo {
  position: relative;
  z-index: 1001;
  pointer-events: auto;
}

/* 确保加载动画不影响点击事件 */
#loadingbar {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 9999;
  pointer-events: none;
}

/* 侧边栏样式增强 */
.el-aside {
  background-color: #f8f9fa;
  overflow-y: scroll;
  overflow-x: hidden;
  scrollbar-width: none;
  /* firefox */
  -ms-overflow-style: none;
  height: 100% !important;
  box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);

  .el-menu--collapse {
    width: 40px;
    height: 100% !important;
  }
  
  :deep(.el-menu--collapse.el-menu) {
    width: 40px !important;
    margin: 0 auto !important;
  }
  
  :deep(.el-menu--collapse .el-menu-item),
  :deep(.el-menu--collapse .el-submenu__title),
  :deep(.el-menu--collapse .el-sub-menu__title) {
    padding: 0 !important;
    text-align: center !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    position: relative !important;
    left: 0 !important;
    right: 0 !important;
    width: 40px !important;
    height: 45px !important;
    margin: 0 auto !important;
    /* 强制开启hover触发，解决折叠后无法识别子项问题 */
    pointer-events: auto !important;
    cursor: pointer !important;
  }
  
  :deep(.el-menu--collapse .el-sub-menu) {
    width: 40px !important;
    /* 二级弹框容器相对定位，让三级能基于它定位 */
    position: relative !important;
  }
  
  :deep(.el-menu--collapse .el-menu-item > *),
  :deep(.el-menu--collapse .el-submenu__title > *),
  :deep(.el-menu--collapse .el-sub-menu__title > *) {
    margin: 0 !important;
    padding: 0 !important;
    position: static !important;
    left: auto !important;
    right: auto !important;
    top: auto !important;
    bottom: auto !important;
    transform: none !important;
    float: none !important;
    display: inline-block !important;
    width: auto !important;
    height: auto !important;
  }
  
  :deep(.el-menu--collapse .el-menu-item i.rotate),
  :deep(.el-menu--collapse .el-submenu__title i.rotate),
  :deep(.el-menu--collapse .el-sub-menu__title i.rotate) {
    margin: 0 !important;
    padding: 0 !important;
    display: inline-block !important;
    text-align: center !important;
    width: auto !important;
    height: auto !important;
    position: static !important;
    left: auto !important;
    right: auto !important;
    transform: rotate(-90deg) !important;
    line-height: normal !important;
    float: none !important;
  }
  
  :deep(.el-menu--collapse .el-submenu__title .iconfont2),
  :deep(.el-menu--collapse .el-menu-item .iconfont2),
  :deep(.el-menu--collapse .el-sub-menu__title .iconfont2) {
    margin: 0 !important;
    padding: 0 !important;
    display: inline-block !important;
    text-align: center !important;
    width: auto !important;
    height: auto !important;
    position: static !important;
    left: auto !important;
    right: auto !important;
    transform: none !important;
    float: none !important;
  }
  
  :deep(.el-menu--collapse .el-sub-menu__title .typhoon-icon) {
    margin: 0 !important;
    padding: 0 !important;
    display: inline-block !important;
    text-align: center !important;
    width: 20px !important;
    height: 20px !important;
    position: static !important;
    left: auto !important;
    right: auto !important;
    transform: none !important;
    float: none !important;
  }
  
  :deep(.el-menu--collapse .el-submenu__title span),
  :deep(.el-menu--collapse .el-menu-item span),
  :deep(.el-menu--collapse .el-sub-menu__title span) {
    display: none !important;
  }
  
  :deep(.el-menu--collapse .el-submenu__icon-arrow),
  :deep(.el-menu--collapse .el-sub-menu__icon-arrow) {
    display: none !important;
  }

  .el-menu {
    border-right: none;
    height: 100% !important;
    .el-menu-item,
    .el-submenu__title {
      height: 45px;
      line-height: 45px;
    }
    
    :deep(.el-menu--collapse .el-menu-item),
    :deep(.el-menu--collapse .el-submenu__title),
    :deep(.el-menu--collapse .el-sub-menu__title) {
      padding: 0 !important;
      text-align: center !important;
      display: flex !important;
      align-items: center !important;
      justify-content: center !important;
      position: relative !important;
      left: 0 !important;
      right: 0 !important;
      width: 40px !important;
      height: 45px !important;
      line-height: normal !important;
    }

    #fold {
      height: 35px;
      transition-duration: 1s;
      color: #303133 !important;
      padding: 0 !important;
      text-align: center !important;
      line-height: 35px !important;
      display: flex !important;
      align-items: center !important;
      justify-content: center !important;
      background-color: transparent !important;
    }
    
    #fold.is-active {
      background-color: transparent !important;
      color: #303133 !important;
    }
    
    #fold:hover {
      background-color: rgba(64, 158, 255, 0.1) !important;
    }
  }
}

/* 修复Element Plus 2.x 子菜单标题样式 */
.el-submenu__title {
  padding: 0 12px !important;
}

.el-menu--vertical > .el-menu-item {
  padding: 0 12px !important;
}

/* 确保标题框尺寸正确 */
.el-submenu__title .el-submenu__icon-arrow {
  margin-top: -4px !important;
}

/* 修复图标和文字对齐 */
.iconfont2 {
  font-size: 20px !important;
  margin-right: 10px;
  vertical-align: middle;
  color: #303133;
}

/* 台风图标样式 */
.typhoon-icon {
  width: 20px;
  height: 20px;
  margin-right: 10px;
  vertical-align: middle;
}

/* 子菜单项文字颜色 */
.el-menu--vertical .el-sub-menu .el-menu-item {
  color: #303133 !important;
  font-size: 0.875rem !important;
  line-height: 1.5;
}

/* 本地服务菜单项文字向左移动 */
.custom-service-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-left: 50px !important;
}

.custom-service-item.is-active {
  background-color: transparent !important;
}

.custom-service-item .service-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.custom-service-item .service-name.service-loaded {
  color: #409eff;
  font-weight: 500;
}

.custom-service-item .delete-service-icon {
  margin-left: 8px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.custom-service-item .delete-service-icon:hover {
  color: #f56c6c;
}

/* 自定义服务菜单项移除选中样式 */
.no-active-style {
  background-color: transparent !important;
}
.no-active-style.is-active {
  background-color: transparent !important;
}

/* 基础地理实体菜单项样式 */
.vector-data-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-left: 50px !important;
}

.vector-data-item.is-active {
  background-color: transparent !important;
}

.vector-data-item .vector-data-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vector-data-item .vector-data-name.vector-data-loaded {
  color: #409eff;
  font-weight: 500;
}

.vector-data-item .delete-vector-data-icon {
  margin-left: 8px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.vector-data-item .delete-vector-data-icon:hover {
  color: #f56c6c;
}

/* 模型菜单项样式 */
.model-menu-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-left: 50px !important;
}

.model-menu-item.is-active {
  background-color: transparent !important;
}

.model-menu-item .model-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-left: 10px;
}

.model-menu-item .model-name.model-loaded {
  color: #409eff;
  font-weight: 500;
}

.model-menu-item .delete-model-icon {
  margin-left: 8px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.model-menu-item .delete-model-icon:hover {
  color: #f56c6c;
}

/* 台风加载状态 */
.el-menu-item .typhoon-name.typhoon-loaded {
  color: #409eff;
  font-weight: 500;
}

.el-menu-item .delete-typhoon-icon {
  margin-left: 8px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

/* 场景加载状态 */
.model-menu-item .scene-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.model-menu-item .scene-name.scene-loaded {
  color: #409eff;
  font-weight: 500;
}

.model-menu-item .delete-scene-icon {
  margin-left: 8px;
  cursor: pointer;
  color: #909399;
  transition: color 0.3s;
}

.model-menu-item .delete-scene-icon:hover {
  color: #f56c6c;
}

.el-menu-item .delete-typhoon-icon:hover {
  color: #f56c6c;
}

/* 滚动条隐藏 */
.el-aside::-webkit-scrollbar {
  display: none;
}

/* 功能模块网格布局 */
.box {
  font-family: "Microsoft Yahei";
  display: flex;
  width: 100%;
  padding: 0 10px 0 28px;
  justify-content: space-between;
  box-sizing: border-box;
  flex-wrap: wrap;

  .imgbox {
    position: relative;
    width: 100px;
    height: 100px;
    display: flex;
    box-sizing: border-box;
    margin-bottom: 5px;
    flex-wrap: wrap;
    justify-content: center;
    span {
      font-size: 12px;
      color: #303133;
    }
    .img {
      width: 100%;
      height: 72px;
      border-radius: 4px;
      background-color: #585858;
      border: 1px solid #e4e7ed;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      transition: all 0.3s ease;
    }
    .img:hover {
      border: 2px solid #409eff;
      box-shadow: 0 4px 8px rgba(64, 158, 255, 0.2);
      transform: translateY(-2px);
    }
    .cross {
      width: 25px;
      height: 25px;
      position: absolute;
      top: 0;
      left: 0;
    }
  }
}

/* 删除图标样式 */
.circle-close-icon {
  position: absolute;
  right: 0px;
  top: 3px;
  color: #ffffff;
  cursor: pointer;
  width: 16px;
  height: 16px;
  font-size: 16px;
  outline: none;
}
.circle-close-icon:focus,
.circle-close-icon:active {
  outline: none;
  box-shadow: none;
}

/* el-menu-item 内的删除图标样式 */
.el-menu-item .circle-close-icon {
  top: 50%;
  transform: translateY(-50%);
  color: #000000;
  right: 25px;
}

/* 展开/收缩按钮动画 */
.rotate2 {
  transform: rotate(0deg);
  -ms-transform: rotate(0); /* IE 9 */
  -moz-transform: rotate(0); /* Firefox */
  -webkit-transform: rotate(0); /* Safari 和 Chrome */
  -o-transform: rotate(0); /* Opera */
  transition-duration: 1s;
}

.rotate {
  margin-left: 8px !important;
  line-height: 52px !important;
  transform: rotate(-90deg);
  -ms-transform: rotate(-90deg); /* IE 9 */
  -moz-transform: rotate(-90deg); /* Firefox */
  -webkit-transform: rotate(-90deg); /* Safari 和 Chrome */
  -o-transform: rotate(-90deg); /* Opera */
  transition-duration: 1s;
}

/* 禁用状态 */
.disable {
  pointer-events: none;
}

/* 图标样式 */
.iconfont2 {
  font-size: 20px !important;
  margin-right: 10px;
}

/* 方形加号图标 */
.square-plus-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 1.5px solid #909399;
  border-radius: 4px;
  font-size: 16px;
  margin-left: 10px;
}

/* 场景名称 */
.scene-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 删除图标 */
.delete-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: 16px;
  margin-left: 8px;
  cursor: pointer;
  opacity: 0.6;
  transition: opacity 0.2s;
  flex-shrink: 0;
}

.delete-icon:hover {
  opacity: 1;
  color: #f56c6c;
}

// ########### 关键修复：折叠状态下 二级弹框保留原位置 + 三级强制弹出 ###########
// 1. 折叠状态二级弹框：强制保留原有正确位置，不做任何改动
:deep(.el-menu--collapse .el-sub-menu > .el-sub-menu__popper) {
  position: fixed !important;
  margin-left: 0 !important;
  transform: none !important;
  z-index: 9998 !important;
}

// 2. 折叠状态二级弹框容器：hover时强制显示三级内容，核心触发逻辑
:deep(.el-menu--collapse .el-sub-menu__popper) {
  pointer-events: auto !important;
  &:hover .box {
    display: flex !important;
  }
}

// 3. 折叠状态三级内容（box）：强制定位在二级弹框右侧，顶对齐
:deep(.el-menu--collapse .el-sub-menu__popper .box) {
  position: absolute !important;
  left: 100% !important; // 紧贴二级弹框右侧
  top: 0 !important;     // 和二级弹框顶对齐
  z-index: 9999 !important;
  width: 300px !important; // 适配你的imgbox布局
  background: #f8f9fa !important;
  border: 1px solid #e4e7ed !important;
  border-left: none !important;
  box-shadow: 2px 2px 10px rgba(0,0,0,0.05) !important;
  padding: 10px !important;
  margin: 0 !important;
  // 强制显示，解决折叠后被隐藏问题
  display: flex !important;
  pointer-events: auto !important;
}

// 4. 折叠状态三级imgbox布局适配：清除多余间距，正常显示
:deep(.el-menu--collapse .el-sub-menu__popper .box .imgbox) {
  margin: 0 10px 10px 0 !important;
}

// 5. 展开状态：恢复原有布局，不影响任何效果
:deep(.el-menu--not-collapse .el-sub-menu__popper .box) {
  position: static !important;
  width: 100% !important;
  box-shadow: none !important;
  border: none !important;
}

// 土地利用图例样式
.land-use-legend {
  position: absolute;
  bottom: 20px;
  right: 20px;
  z-index: 1000;
  background: rgba(20, 25, 40, 0.85);
  border: 1px solid rgba(100, 180, 255, 0.4);
  border-radius: 6px;
  padding: 10px 12px;
  max-height: 60vh;
  overflow-y: auto;
  user-select: none;
  backdrop-filter: blur(4px);

  .land-use-legend-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
    font-size: 14px;
    font-weight: bold;
    color: #e0e0e0;
    border-bottom: 1px solid rgba(100, 180, 255, 0.2);
    padding-bottom: 6px;

    .land-use-legend-toggle {
      cursor: pointer;
      color: #aaa;
      font-size: 16px;
      line-height: 1;
      padding: 0 4px;
      &:hover { color: #fff; }
    }
  }

  .land-use-legend-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .land-use-legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: #ccc;
  }

  .land-use-legend-color {
    display: inline-block;
    width: 16px;
    height: 12px;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 2px;
    flex-shrink: 0;
  }

  .land-use-legend-label {
    white-space: nowrap;
  }
}
</style>