<!-- 模型地图 -->
<template>
  <div>
    <div class="canvasMap" ref="container"></div>
  </div>
</template>

<script>
  import * as THREE from 'three';
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
  import TWEEN from 'tween.js';
  import { CSS2DRenderer, CSS2DObject, } from "three/examples/jsm/renderers/CSS2DRenderer";
  export default {
    data() {
      return {
        boxWidth:2594,
        // boxHeight:1320,
        boxHeight:1240,
        animationId: null,  // 存储动画帧ID
        model:null,
        rotateType:true,
        subModels: [],
        houses:[],
        textures: [], // 预加载的贴图选项
        scene: null,
        camera: null,
        renderer: null,
        controls: null,
        labelRenderer:null,
        // initialCameraPosition: new THREE.Vector3(0.265357272970017,1.0753467424793826,0.29965000584249346),//旋转-距离-俯角
        initialCameraPosition: new THREE.Vector3(0.5,0.60,0.5),//旋转-距离-俯角
        /*
        {"x":0.2509298883560245,"y":1.2088432847403026,"z":0.19985043873282318}
        {"x":0.265357272970017,"y":1.0753467424793826,"z":0.29965000584249346}
        */
        initialControlsTarget: new THREE.Vector3(0, 0, 0),
        /*本地管理数据*/
        modelBuildingList:[
          {school:'B',	name:'5号楼',	buildId:'101093843117592833',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'6B教学楼',	buildId:'101093843117592815',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'中心实验楼',	buildId:'101093843117592829',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'信息工程学院教学楼',	buildId:'101093843117592812',centerValue:'101093843117592812',valueList:['101093843117592812'],alarmType:false,},
          {school:'B',	name:'养虫楼',	buildId:'101093843117592805',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'农业机械实验室平房',	buildId:'101093843117592807',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'农药研究所',	buildId:'101093843117592804',centerValue:'101093843117592804',valueList:['101093843117592804'],alarmType:false,},
          {school:'B',	name:'动物中心B座',	buildId:'101093843117592785',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'动物中心实验楼A',	buildId:'101093843117592765',centerValue:'101093843117592765',valueList:['101093843117592765'],alarmType:false,},
          {school:'B',	name:'动物中心实验楼B',	buildId:'101093843117592764',centerValue:'101093843117592764',valueList:['101093843117592764'],alarmType:false,},
          {school:'B',	name:'动物科技学院楼',	buildId:'101093843117592831',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'动科楼',	buildId:'101093843117592832',centerValue:'101093843117592832',valueList:['101093843117592832'],alarmType:false,},
          {school:'B',	name:'北1号教学楼',	buildId:'101093843117592802',centerValue:'101093843117592802',valueList:['101093843117592802'],alarmType:false,},
          {school:'B',	name:'北2号教学楼',	buildId:'101093843117592813',centerValue:'101093843117592813',valueList:['101093843117592813'],alarmType:false,},
          {school:'B',	name:'北3号教学楼',	buildId:'101093843117592782',centerValue:'101093843117592782',valueList:['101093843117592782'],alarmType:false,},
          {school:'B',	name:'北4号教学楼',	buildId:'101093843117592781',centerValue:'101093843117592781',valueList:['101093843117592781'],alarmType:false,},
          {school:'B',	name:'北5号教学楼',	buildId:'101093843117592783',centerValue:'101093843117592833',valueList:['101093843117592833'],alarmType:false,},
          {school:'B',	name:'北6号教学楼',	buildId:'101093843117592814',centerValue:'101093843117592814',valueList:['101093843117592814'],alarmType:false,},
          {school:'B',	name:'北7号教学楼',	buildId:'101093843117592816',centerValue:'101093843117592816',valueList:['101093843117592816'],alarmType:false,},
          {school:'B',	name:'危化品服务中心',	buildId:'101093843117592826',centerValue:'101093843117592826-03',valueList:['101093843117592826-01','101093843117592826-02','101093843117592826-03','101093843117592826-04','101093843117592826-05'],alarmType:false,},
          {school:'B',	name:'原农一站办公楼',	buildId:'101093843117592811',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'工程训练中心楼',	buildId:'101093843117592808',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'教学办公楼',	buildId:'101093843117592806',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'未知实验室	',buildId:'1855791848753147906',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'机械及液压实验室平房',	buildId:'101093843117592810',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'机电锻铸车间',	buildId:'101093843117592809',centerValue:'101093843117592809',valueList:['101093843117592809'],alarmType:false,},
          {school:'B',	name:'水利与建筑工程学院',	buildId:'101093843117592788',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'水利与建筑工程学院A',	buildId:'101093843117592766',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'水利与建筑工程学院B',	buildId:'101093843117592768',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'水利与建筑工程学院C',	buildId:'101093843117592769',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'水利与建筑工程学院D',	buildId:'101093843117592767',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'水工厅', buildId:'1871736666456633346',centerValue:'1871736666456633346',valueList:['1871736666456633346'],alarmType:false,},
          {school:'B',	name:'水工水力学实验大厅', buildId:'1871729400227606529',centerValue:'1871729400227606529-01',valueList:['1871729400227606529-01','1871729400227606529-02'],alarmType:false,},
          {school:'B',	name:'理科综合实验楼A',	buildId:'101093843117592774',centerValue:'101093843117592774',valueList:['101093843117592774'],alarmType:false,},
          {school:'B',	name:'理科综合实验楼B',	buildId:'101093843117592779',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'理科综合实验楼C',	buildId:'101093843117592775',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'理科综合实验楼D',	buildId:'101093843117592776',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'理科综合实验楼E',	buildId:'101093843117592780',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'老昆虫博物馆',	buildId:'101093843117592786',centerValue:'101093843117592786',valueList:['101093843117592786'],alarmType:false,},
          {school:'B',	name:'食品楼',	buildId:'101093843117592789',centerValue:'101093843117592789',valueList:['101093843117592789'],alarmType:false,},
          {school:'B',	name:'食品楼A',	buildId:'101093843117592771',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'食品楼B',	buildId:'101093843117592770',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'食品楼C',	buildId:'101093843117592772',centerValue:'',valueList:[],alarmType:false,},
          {school:'B',	name:'食品楼D',	buildId:'101093843117592773',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'农科楼(农学院)',	buildId:'101084081109864872',centerValue:'101084081109864872',valueList:['101084081109864872'],alarmType:false,},
          {school:'N',	name:'农科楼(园艺学院)',	buildId:'101084081109864874',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'农科楼(植物保护学院)',	buildId:'101084081109864873',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'农科楼(资源环境学院)',	buildId:'101093843117592803',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'农科院子校教学楼（初中）',	buildId:'101093843117592823',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'南2号教学楼',	buildId:'101093843117592817',centerValue:'101093843117592817',valueList:['101093843117592817'],alarmType:false,},
          {school:'N',	name:'南3号教学楼',	buildId:'101093843117592820',centerValue:'101093843117592820',valueList:['101093843117592820'],alarmType:false,},
          {school:'N',	name:'南校子校楼',	buildId:'101093843117592800',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'南校水保楼',	buildId:'101093843117592801',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'家畜生物学重点实验室楼',	buildId:'101093843117592787',centerValue:'101093843117592787',valueList:['101093843117592787'],alarmType:false,},
          {school:'N',	name:'木艺坊',	buildId:'101093843117592819',centerValue:'101093843117592819',valueList:['101093843117592819'],alarmType:false,},
          {school:'N',	name:'林学院实验楼',	buildId:'101093843117592818',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'科研主楼',	buildId:'101093843117592784',centerValue:'101093843117592784',valueList:['101093843117592784','101093843117592822'],alarmType:false,},
          {school:'N',	name:'科研楼',	buildId:'101093843117592822',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'经管园林楼（文科楼）A',	buildId:'101093843117592777',centerValue:'101093843117592777',valueList:['101093843117592777'],alarmType:false,},
          {school:'N',	name:'经管园林楼（文科楼）C',	buildId:'101093843117592778',centerValue:'',valueList:[],alarmType:false,},
          {school:'N',	name:'草业与草原楼',	buildId:'101093843117592825',centerValue:'101093843117592825',valueList:['101093843117592825'],alarmType:false,},
          {school:'N',	name:'林学院实验楼',	buildId:'101093843117592821',centerValue:'101093843117592821',valueList:['101093843117592821'],alarmType:false,},
          {school:'旱研院校区',	name:'农一站科研楼',	buildId:'101093843117592827',centerValue:'',valueList:[],alarmType:false,},
          {school:'旱研院校区',	name:'农机一站平房',	buildId:'101093843117592828',centerValue:'',valueList:[],alarmType:false,},
          {school:'校外',	name:'农三站科研办公楼',	buildId:'101093843117592790',centerValue:'',valueList:[],alarmType:false,},
          {school:'校外',	name:'器材楼',	buildId:'101093843117592792',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'人工降雨实验大厅',	buildId:'101093843117592793',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'南校区农科楼',	buildId:'101093843117592799',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'国家重点实验室西楼',	buildId:'101093843117592798',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'水保所人工干旱环境气候室工程',	buildId:'101093843117592795',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'水保所国家重点实验室楼',	buildId:'101093843117592794',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'水保所科研大楼',	buildId:'101093843117592797',centerValue:'',valueList:[],alarmType:false,},
          {school:'水保所校区',	name:'水保所西区科研楼',	buildId:'101093843117592796',centerValue:'',valueList:[],alarmType:false,},
          {school:'老附中校区',	name:'附中实验楼',	buildId:'101093843117592830',centerValue:'',valueList:[],alarmType:false,},
          {school:'老附中校区',	name:'附中教学楼',	buildId:'101093843117592791',centerValue:'',valueList:[],alarmType:false,},
        ],
        // 本地
        // modelsUrlN:'/models/NxiaoQu.glb',
        // modelsUrlB:'/models/BxiaoQu.glb',
        // alarmUrl:'/png/alarm.png',
        // noAlarmUrl:'/png/noAlarm.png',
        // 本地部署
        // modelsUrlN:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/models/NxiaoQu.glb':'/labAppTest/largeScreen/models/NxiaoQu.glb',
        // modelsUrlB:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/models/BxiaoQu.glb':'/labAppTest/largeScreen/models/BxiaoQu.glb',
        // alarmUrl:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/png/alarm.png':'/labAppTest/largeScreen/png/alarm.png',
        // noAlarmUrl:this.judgmentNetworkReturnAddress()?'/v3/largeScreen/png/noAlarm.png':'/labAppTest/largeScreen/png/noAlarm.png',
        // 线上部署
        modelsUrlN:'/schoolData/models/NxiaoQu.glb',
        modelsUrlB:'/schoolData/models/BxiaoQu.glb',
        alarmUrl:'/schoolData/png/alarm.png',
        noAlarmUrl:'/schoolData/png/noAlarm.png',
        //当前校区状态
        schoolType:'',
      };
    },

    mounted() {
      this.initThree();
      this.loadModel('N');
      this.loadAssets();
      window.addEventListener('resize', this.onWindowResize);
    },

    beforeDestroy() {
      // 停止动画循环
      if (this.animationId) {
        cancelAnimationFrame(this.animationId);
      }
      // 销毁Three.js相关资源
      this.disposeThreeResources();
      // 停止所有补间动画
      TWEEN.removeAll();
      // 移除事件监听器
      window.removeEventListener('resize', this.onWindowResize);
      this.renderer.dispose();
    },

    methods: {
      //初始化
      initThree() {
        // 初始化场景
        this.scene = new THREE.Scene();
        //场景背景色
        this.scene.background = new THREE.Color(0x000F14);
        // 初始化渲染器
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        // 主渲染器尺寸设置
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setSize(this.boxWidth, this.boxHeight);  // 固定尺寸
        this.$refs.container.appendChild(this.renderer.domElement);
        // 初始化相机时使用存储的位置
        this.camera = new THREE.PerspectiveCamera(
          75,
          // window.innerWidth / window.innerHeight,
          this.boxWidth / this.boxHeight,
          0.1,
          1000
        );
        this.camera.position.copy(this.initialCameraPosition);

        // 初始化控制器时存储初始目标
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.target.copy(this.initialControlsTarget);
        this.labelRenderer = new CSS2DRenderer();
        this.labelRenderer.setSize(this.boxWidth, this.boxHeight);
        this.labelRenderer.domElement.style.position = 'absolute';
        this.labelRenderer.domElement.style.top = '0';
        this.labelRenderer.domElement.style.pointerEvents = 'none';//注释后 锁死鼠标视角
        this.$refs.container.appendChild(this.labelRenderer.domElement);

        // 初始化控制器
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enableDamping = true;
        this.controls.dampingFactor = 0.05;

        // 添加灯光
        const ambientLight = new THREE.AmbientLight(0xffffff, 1);
        this.scene.add(ambientLight);

        const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
        directionalLight.position.set(-1.5, 2, -1.5);
        this.scene.add(directionalLight);

        // 启动动画循环
        this.animate();
      },
      //模型加载
      loadModel(type) {
        let self = this;
        this.$set(this,'schoolType',type);
        if(type == 'N'){
          //南校区
          const loaderN = new GLTFLoader();
          loaderN.load(self.modelsUrlN, (gltf) => {
            const model = gltf.scene;
            this.model = gltf.scene;
            this.scene.add(model);
            // 递归查找所有Group
            const traverseGroups = (obj) => {
              if (obj.isGroup) {
                this.registerHouse(obj);
              }
              obj.children.forEach(child => traverseGroups(child));
            };
            traverseGroups(gltf.scene);
            // 获取所有子模型并计算中心点
            model.traverse((child) => {
              if (child.isGroup) {
                const center = this.getBoundingBoxCenter(child);
                this.subModels.push({
                  name: child.name,
                  center: center,
                  originalPosition: child.position.clone(),
                  mesh: child  // 直接引用mesh对象 用于材质修改
                });
              }
            });
          });
        }else if(type == 'B'){
          //北校区
          const loaderB = new GLTFLoader();
          loaderB.load(self.modelsUrlB, (gltf) => {
            const model = gltf.scene;
            this.model = gltf.scene;
            this.scene.add(model);
            // 递归查找所有Group
            const traverseGroups = (obj) => {
              if (obj.isGroup) {
                this.registerHouse(obj);
              }
              obj.children.forEach(child => traverseGroups(child));
            };
            traverseGroups(gltf.scene);
            // 获取所有子模型并计算中心点
            model.traverse((child) => {
              if (child.isGroup) {
                const center = this.getBoundingBoxCenter(child);
                this.subModels.push({
                  name: child.name,
                  center: center,
                  originalPosition: child.position.clone(),
                  mesh: child  // 直接引用mesh对象 用于材质修改
                });
              }
            });
          });
        }
        setTimeout(function(){
          self.buildingApplyTexture([]);
          // self.buildingColor([]);
        },500);
      },
      /****** 新模型相关方法-开始 *******/
      // 修改后的 registerHouse 方法
      registerHouse(group) {
        const bbox = new THREE.Box3().setFromObject(group);
        const center = bbox.getCenter(new THREE.Vector3());

        // 记录原始材质
        const originalMaterials = [];
        group.traverse(child => {
          if (child.isMesh) {
            originalMaterials.push({
              mesh: child,
              material: child.material.clone() // 重要：必须克隆原始材质
            });
          }
        });

        this.houses.push({
          name:group.name,
          uuid: group.uuid,
          group: group,
          screenPosition: new THREE.Vector2(),
          worldPosition: center,
          originalMaterials: originalMaterials // 新增原始材质存储
        });
      },
      // 预加载材质和贴图
      loadAssets() {
        const textureLoader = new THREE.TextureLoader();
        this.textures = [
          textureLoader.load(this.alarmUrl)
        ];
      },
      // 报警样式
      changeTexture(house) {
        const texture = this.textures[Math.floor(Math.random() * this.textures.length)];
        texture.needsUpdate = true;

        house.group.traverse(child => {
          if (child.isMesh) {
            child.material.map = texture;
            child.material.needsUpdate = true;
          }
        });
      },
      // 恢复正常显示
      restoreOriginalMaterial(house) {
        house.originalMaterials.forEach(entry => {
          entry.mesh.material.dispose(); // 清理当前材质
          entry.mesh.material = entry.material.clone();
          entry.mesh.material.needsUpdate = true;
        });
      },
      /****** 新模型相关方法-结束 *******/

      getBoundingBoxCenter(obj) {
        const box = new THREE.Box3().setFromObject(obj);
        return box.getCenter(new THREE.Vector3());
      },
      onWindowResize() {
        this.camera.aspect = this.boxWidth / this.boxHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(this.boxWidth, this.boxHeight);
        this.labelRenderer.setSize(this.boxWidth, this.boxHeight);

      },
      //动画刷新
      animate() {
        if (this.model&&this.rotateType) {
          this.model.rotation.y += 0.001; // 添加简单旋转动画
        }else if(this.model&&!this.rotateType){
          this.model.rotation.y = 0;
        }
        // requestAnimationFrame(this.animate);
        // TWEEN.update();
        // this.controls.update();
        // this.renderer.render(this.scene, this.camera);
        // this.labelRenderer.render(this.scene, this.camera);
        this.animationId = requestAnimationFrame(this.animate);
        TWEEN.update();
        this.controls.update();
        this.renderer.render(this.scene, this.camera);
        this.labelRenderer.render(this.scene, this.camera);
      },
      //预案关闭
      alarmOff(){
        let self = this;
        self.$set(this,'rotateType',true)
        if(this.schoolType == 'N'){
          self.resetCamera();
          self.del2dText();
          // self.buildingColor([]);
          self.buildingApplyTexture([]);
        }else{
          //删除所有文字
          for(let i=0;i<self.scene.children.length;i++){
            if(self.scene.children[i].isCSS2DObject){
              self.scene.remove(self.scene.children[i]);
              i--
            }
          }
          //删除模型数据
          this.$set(this,'subModels',[]);
          this.$set(this,'houses',[]);
          this.scene.children.forEach((item)=>{
            if(item.type == 'Group'){
              this.scene.remove(item);
            }
          })
          self.resetCamera();
          this.delLoadModel('N');
        }
      },
      // 新增恢复视角方法
      resetCamera() {
        this.controls.enabled = false;
        new TWEEN.Tween(this.camera.position)
          .to(this.initialCameraPosition, 1500)
          .easing(TWEEN.Easing.Quadratic.InOut)
          .start();

        new TWEEN.Tween(this.controls.target)
          .to(this.initialControlsTarget, 1500)
          .easing(TWEEN.Easing.Quadratic.InOut)
          .onComplete(() => {
            this.controls.enabled = true;
          })
          .start();
        this.del2dText();
      },
      //删除所有2d文字
      del2dText(){
        let self = this;
        for(let i=0;i<self.scene.children.length;i++){
          if(self.scene.children[i].isCSS2DObject){
            this.scene.remove(self.scene.children[i]);
            i--
          }
        }
      },
      //预案触发
      alarmTrigger(list,obj,type){
        let self = this;
        self.$set(this,'rotateType',false)
        //list=所有报警数据 obj=需要聚焦的报警数据 type=报警的校区
        if(this.schoolType == type){
          //当前校区
          self.buildingApplyTexture(list);
          // self.buildingColor(list);
          self.focusingLens(obj);
          //删除所有文字
          for(let i=0;i<self.scene.children.length;i++){
            if(self.scene.children[i].isCSS2DObject){
              self.scene.remove(self.scene.children[i]);
              i--
            }
          }
          //生成新文字
          this.alarmTextLabel(list);
        }else{
          //其他校区
          //删除所有文字
          for(let i=0;i<self.scene.children.length;i++){
            if(self.scene.children[i].isCSS2DObject){
              self.scene.remove(self.scene.children[i]);
              i--
            }
          }
          //删除模型数据
          this.$set(this,'subModels',[]);
          this.$set(this,'houses',[]);
          this.scene.children.forEach((item)=>{
            if(item.type == 'Group'){
              self.scene.remove(item);
            }
          })
          //加载新模型
          this.newLoadModel(list,obj,type);
        }
      },
      //新模型加载
      newLoadModel(list,obj,type) {
        let self = this;
        this.$set(this,'schoolType',type);
        if(type == 'N'){
          //南校区
          const loaderN = new GLTFLoader();
          loaderN.load(self.modelsUrlN, (gltf) => {
            const model = gltf.scene;
            this.model = gltf.scene;
            this.scene.add(model);
            // 递归查找所有Group
            const traverseGroups = (obj) => {
              if (obj.isGroup) {
                this.registerHouse(obj);
              }
              obj.children.forEach(child => traverseGroups(child));
            };
            traverseGroups(gltf.scene);
            // 获取所有子模型并计算中心点
            model.traverse((child) => {
              if (child.isGroup) {
                const center = this.getBoundingBoxCenter(child);
                this.subModels.push({
                  name: child.name,
                  center: center,
                  originalPosition: child.position.clone(),
                  mesh: child  // 直接引用mesh对象 用于材质修改
                });
              }
            });
          });
        }else if(type == 'B'){
          //北校区
          const loaderB = new GLTFLoader();
          loaderB.load(self.modelsUrlB, (gltf) => {
            const model = gltf.scene;
            this.model = gltf.scene;
            this.scene.add(model);
            // 递归查找所有Group
            const traverseGroups = (obj) => {
              if (obj.isGroup) {
                this.registerHouse(obj);
              }
              obj.children.forEach(child => traverseGroups(child));
            };
            traverseGroups(gltf.scene);
            // 获取所有子模型并计算中心点
            model.traverse((child) => {
              if (child.isGroup) {
                const center = this.getBoundingBoxCenter(child);
                this.subModels.push({
                  name: child.name,
                  center: center,
                  originalPosition: child.position.clone(),
                  mesh: child  // 直接引用mesh对象 用于材质修改
                });
              }
            });
          });
        }

        setTimeout(function(){
          self.focusingLens(obj);
          self.buildingApplyTexture(list);
          // self.buildingColor(list);
          self.alarmTextLabel(list);
        },1500);
      },
      //预案停止模型加载
      delLoadModel(type) {
        let self = this;
        this.$set(this,'schoolType',type);
        //南校区
        const loaderN = new GLTFLoader();
        loaderN.load(self.modelsUrlN, (gltf) => {
          const model = gltf.scene;
          this.model = gltf.scene;
          this.scene.add(model);
          // 递归查找所有Group
          const traverseGroups = (obj) => {
            if (obj.isGroup) {
              this.registerHouse(obj);
            }
            obj.children.forEach(child => traverseGroups(child));
          };
          traverseGroups(gltf.scene);
          // 获取所有子模型并计算中心点
          model.traverse((child) => {
            if (child.isGroup) {
              const center = this.getBoundingBoxCenter(child);
              this.subModels.push({
                name: child.name,
                center: center,
                originalPosition: child.position.clone(),
                mesh: child  // 直接引用mesh对象 用于材质修改
              });
            }
          });
        });
        setTimeout(function(){
          self.buildingApplyTexture([]);
          // self.buildingColor([]);
        },1500);
      },
      //初始化楼栋标记(报警时调用)
      buildingColor(list){
        let self = this;
        //处理基础数据
        self.modelBuildingList.forEach((maxItem)=>{
          let num = 0;
          list.forEach((item)=>{
            if(maxItem.buildId == item.buildId){
              num++
            }
          })
          maxItem.alarmType = num != 0;
        })
        //处理预警与正常贴图
        self.modelBuildingList.forEach((maxItem)=>{
          maxItem.valueList.forEach((bigItem)=>{
            this.scene.children.forEach((item)=>{
              if(item.type == 'Group'){
                item.children.forEach((minItem)=>{
                  if(minItem.name == bigItem){
                    if(maxItem.alarmType){
                      self.applyTexture(self.alarmUrl,minItem);
                    }else{
                      self.applyTexture(self.noAlarmUrl,minItem);
                    }
                  }
                })
              }
            })
          })
        })
      },
      buildingApplyTexture(list){
        let self = this;
        //处理基础数据
        self.modelBuildingList.forEach((maxItem)=>{
          let num = 0;
          list.forEach((item)=>{
            if(maxItem.buildId == item.buildId){
              num++
            }
          })
          maxItem.alarmType = num != 0;
        })
        this.$forceUpdate()
        //处理预警与正常贴图
        self.modelBuildingList.forEach((maxItem)=>{
          maxItem.valueList.forEach((bigItem)=>{
            self.houses.forEach((minItem)=>{
              if(minItem.name == bigItem){
                if(maxItem.alarmType){
                  self.changeTexture(minItem);
                }else{
                  self.restoreOriginalMaterial(minItem);
                }
              }
            })
          })
        })
      },
      //图片材质替换
      applyTexture(textureName,obj) {
        const textureLoader = new THREE.TextureLoader();
        textureLoader.load(
          textureName, // 图片放在public目录下
          (texture) => {
            texture.flipY = false; // 根据图片格式调整
            obj.traverse((child) => {
              if (child.isMesh) {
                // 释放旧纹理资源
                if (child.material.map) {
                  child.material.map.dispose();
                }
                child.material.map = texture;
                child.material.needsUpdate = true;
              }
            });
          },
          undefined,
          (error) => {
            console.error('材质加载失败:', error);
          }
        );
      },
      //材质方法调用
      changeMaterial(options) {
        const {
          modelSelector,    // 支持三种选择方式：名称(name)/索引(index)/'all'
          materialParams,   // 新材质参数对象
          keepOriginal = false // 是否保留原始材质
        } = options;

        const targetMeshes = this.getTargetMeshes(modelSelector);

        targetMeshes.forEach(mesh => {
          // 创建新材质
          const newMaterial = this.createMaterial(materialParams);

          // 处理旧材质
          if (!keepOriginal && mesh.userData.originalMaterial) {
            mesh.material.dispose();
          }

          // 应用新材质
          mesh.material = newMaterial;
        });
      },
      // 辅助方法：获取目标网格
      getTargetMeshes(selector) {
        if (selector === 'all') {
          return this.subModels.map(m => m.mesh);
        }

        if (typeof selector === 'number') {
          return [this.subModels[selector].mesh];
        }

        if (typeof selector === 'string') {
          return this.subModels
            .filter(m => m.name === selector)
            .map(m => m.mesh);
        }

        return [];
      },
      // 材质工厂方法
      createMaterial(params) {
        const type = params.type || 'Standard';
        const baseParams = {
          color: new THREE.Color(params.color || 0xffffff),
          map: params.texture ? this.loadTexture(params.texture) : null,
          transparent: params.transparent || false,
          opacity: params.opacity || 1.0,
          ...params
        };

        switch(type.toLowerCase()) {
          case 'basic':
            return new THREE.MeshBasicMaterial(baseParams);
          case 'phong':
            return new THREE.MeshPhongMaterial(baseParams);
          case 'standard':
            return new THREE.MeshStandardMaterial(baseParams);
          case 'physical':
            return new THREE.MeshPhysicalMaterial(baseParams);
          default:
            return new THREE.MeshStandardMaterial(baseParams);
        }
      },
      //聚焦镜头查询
      focusingLens(obj){
        let self = this;
        this.modelBuildingList.forEach((maxItem)=>{
          if(maxItem.buildId == obj.buildId){
            self.subModels.forEach((bigItem)=>{
              if(bigItem.name == maxItem.centerValue){
                self.moveCameraToModel(bigItem,bigItem.name);
              }
            })
          }
        })
      },
      //镜头聚焦方法
      moveCameraToModel(model,name) {
        let self = this;
        // 禁用控制器
        this.controls.enabled = false;

        // 计算目标位置（模型中心前上方）
        const targetPosition = new THREE.Vector3()
          .copy(model.center)
          .add(new THREE.Vector3(0.2,0.2, 0.2));

        // 动画参数
        const duration = 1500;
        const easing = TWEEN.Easing.Quadratic.InOut;

        // 相机位置动画
        new TWEEN.Tween(this.camera.position)
          .to(targetPosition, duration)
          .easing(easing)
          .start();

        // 控制器目标点动画
        new TWEEN.Tween(this.controls.target)
          .to(model.center, duration)
          .easing(easing)
          .onComplete(() => {
            this.controls.enabled = true; // 恢复控制
          })
          .start();
      },
      //预警文本生成
      alarmTextLabel(list){
        let self = this;
        let newList = [];
        self.modelBuildingList.forEach((maxItem)=>{
          list.forEach((item)=>{
            if(maxItem.buildId == item.buildId){
              let num = 0;
              newList.forEach((minItem)=>{
                if(minItem.buildId == item.buildId){
                  num++
                  if(!minItem.textList[1]){
                    minItem.textList.push(
                      "<div style='border-top:1px dashed #E90104;margin-top:15px;'>" +
                      "<p style='color:#FF8400;font-size:24px;line-height:30px;margin-top:19px;'>"+item.riskPlanName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
                      "</div>"
                    )
                  }else{
                    minItem.textList.splice(0,1)
                    minItem.textList.push(
                      "<div style='border-top:1px dashed #E90104;margin-top:35px;'>" +
                      "<p style='color:#FF8400;font-size:24px;line-height:30px;margin-top:19px;'>"+item.riskPlanName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
                      "</div>"
                    )
                  }
                }
              })
              if(num == 0){
                newList.push(
                  {
                    buildId:item.buildId,
                    centerValue:maxItem.centerValue,
                    text:"",
                    //校区-楼栋
                    titleName:item.schoolName+' - '+item.buildName,
                    textList:[
                      "<div>" +
                      "<p style='color:#FF8400;font-size:24px;line-height:30px;margin-top:19px;'>"+item.riskPlanName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+this.parseTime(item.eventStartTime,"{y}-{m}-{d} {h}:{i}:{s}")+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'>"+item.floorName+"-"+item.roomNum+"-"+item.deptName+"</p>"+
                      "<p style='font-size:24px;line-height:30px;margin-top:5px;'><span style='border:1px solid "+item.classLevelColor+";padding:0 10px;margin-right:20px;border-radius:20px;color:"+item.classLevelColor+";'>"+item.classLevelName+"</span>"+item.subName+"</p>"+
                      "</div>"
                    ]
                  }
                )
              }
            }
          })
        })
        //循环定位模型位置并生成textTable
        newList.forEach((maxItem,maxIndex)=>{
          maxItem.text = "<div style='z-index:"+maxIndex+";border-radius:20px;padding:20px;color:#ffffff;border:1px solid #E90104;box-shadow: inset 0 0 10px rgba(176, 0, 0, 1);background-color: rgba(187,0,0,0.5)'>"
          maxItem.text = maxItem.text+"<p style='border-bottom:1px solid #E90104;line-height:30px;padding-bottom:15px;'>"+maxItem.titleName+"</p>";
          maxItem.textList.forEach((html)=>{
            maxItem.text = maxItem.text + html
          })
          maxItem.text = maxItem.text+"</div>"
          self.subModels.forEach((bigItem)=>{
            if(bigItem.name == maxItem.centerValue){
              forScene(bigItem,maxItem.text);
            }
          })
        })
        function forScene(model,text){
          self.scene.children.forEach((item)=>{
            if(item.type == 'Group'){
              item.children.forEach((minItem)=>{
                if(minItem.name == model.name){
                  self.add2dFont(minItem,model.center,text);
                }
              })
            }
          })
        }
      },
      //生成css2d文字
      add2dFont(model,center,text){
        const labelDiv = document.createElement('div');
        labelDiv.style.fontSize = '24px';
        labelDiv.style.width = '560px';
        labelDiv.style.borderRadius = '10px';
        labelDiv.style.whiteSpace = "pre-line";
        labelDiv.innerHTML  = text;

        // 创建 CSS2D 对象并绑定到目标
        const label = new CSS2DObject(labelDiv);
        const targetPosition = new THREE.Vector3()
          .copy(center)
          .add(new THREE.Vector3(0.09,0.08,-0.1));
        label.position.set(targetPosition.x,targetPosition.y,targetPosition.z); // 在对象上方 1 单位位置
        this.scene.add(label);
      },
      //内外网地址判定
      judgmentNetworkReturnAddress() {
        /*判断是否是内网IP*/
        // 获取当前页面url
        var curPageUrl = window.location.href;

        var reg1 = /(http|ftp|https|www):\/\//g;//去掉前缀
        curPageUrl =curPageUrl.replace(reg1,'');

        var reg2 = /\:+/g;//替换冒号为一点
        curPageUrl =curPageUrl.replace(reg2,'.');

        curPageUrl = curPageUrl.split('.');//通过一点来划分数组


        var ipAddress = curPageUrl[0]+'.'+curPageUrl[1]+'.'+curPageUrl[2]+'.'+curPageUrl[3];

        var isInnerIp = false;//默认给定IP不是内网IP
        var ipNum = getIpNum(ipAddress);
        /**
         * 私有IP：A类  10.0.0.0    -10.255.255.255
         *       B类  172.16.0.0  -172.31.255.255
         *       C类  192.168.0.0 -192.168.255.255
         *       D类   127.0.0.0   -127.255.255.255(环回地址)
         **/
        var aBegin = getIpNum("10.0.0.0");
        var aEnd = getIpNum("10.255.255.255");
        var bBegin = getIpNum("172.16.0.0");
        var bEnd = getIpNum("172.31.255.255");
        var cBegin = getIpNum("192.168.0.0");
        var cEnd = getIpNum("192.168.255.255");
        var dBegin = getIpNum("127.0.0.0");
        var dEnd = getIpNum("127.255.255.255");
        isInnerIp = isInner(ipNum,aBegin,aEnd) || isInner(ipNum,bBegin,bEnd) || isInner(ipNum,cBegin,cEnd) || isInner(ipNum,dBegin,dEnd);
        return isInnerIp?true:false;
        /*获取IP数*/
        function getIpNum(ipAddress) {
          var ip = ipAddress.split(".");
          var a = parseInt(ip[0]);
          var b = parseInt(ip[1]);
          var c = parseInt(ip[2]);
          var d = parseInt(ip[3]);
          var ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
          return ipNum;
        }
        function isInner(userIp,begin,end){
          return (userIp>=begin) && (userIp<=end);
        }
      },
      // 资源销毁方法
      disposeThreeResources() {
        // 释放几何体和材质
        this.scene.traverse(child => {
          if (child.isMesh) {
            child.geometry.dispose();
            if (Array.isArray(child.material)) {
              child.material.forEach(m => m.dispose());
            } else {
              child.material.dispose();
            }
          }
        });

        // 释放渲染器
        this.renderer.dispose();
        this.labelRenderer.domElement.remove();
        this.labelRenderer = null;

        // 释放场景和相机
        this.scene = null;
        this.camera = null;

        // 释放控制器
        this.controls.dispose();
        this.controls = null;
      },
    }
  };
</script>
<style scoped lang="scss">
  .canvasMap{
    position: absolute;
    /*top: 122px;*/
    top: 202px;
    left: 64px;
    width: 100%;
    height: 100%;
  }
</style>
