
<template>
  <div
    class="modelBox"
    v-loading="modelLoading"
    element-loading-text="模型加载中...请稍等..."
    :element-loading-spinner="loadingSvg"
    element-loading-svg-view-box="-10, -10, 50, 50"
    element-loading-background="rgba(0, 0, 0, 0.5)"
  >
    <div class="l">
      <div
        ref="threeCanvas"
        class="threeCanvas"
        style="width: 100%; height: 100%"
      ></div>
    </div>
    <div class="r" :style="`opacity: ${splitOpacity / 100}`">
      <div
        ref="threeToushi"
        class="threeToushi"
        style="width: 100%; height: 100%"
      ></div>
    </div>
    <div class="bjSelectBox">
      <el-select
        v-model="bj"
        placeholder="完整展示"
        style="width: 300px"
        clearable
        filterable
        @change="bjChange"
      >
        <el-option
          v-for="item in bjList"
          :key="item.id"
          :label="item.name"
          :value="item.uuid"
        >
          <span
            :style="
              item.warnType ? `color: ${selectWarnType[item.warnType]}` : ''
            "
            >{{ item.name }}</span
          >
        </el-option>
      </el-select>
    </div>
    <div class="sliderBarBox">
      <el-slider
        v-model="splitOpacity"
        :min="0"
        :max="75"
        size="small"
        :format-tooltip="sliderFormatTooltip"
      />
    </div>
    <div class="alertBox" v-if="yujingList?.length || baojingList?.length">
      <el-alert title="" type="warning" v-if="yujingList.length">
        <template #default>
          <div class="alertItem">
            {{ yujingList.length }}条部件预警信息:
            <span v-for="item in yujingList" :key="item.uuid">
              <span
                class="name"
                @click="
                  () => {
                    bj = item.uuid;
                    bjChange(item.uuid);
                  }
                "
                >[{{ item.name }}]</span
              >
              :
              <span class="desc">{{ item.desc }}</span>
            </span>
          </div>
        </template>
      </el-alert>
      <el-alert title="" type="error" v-if="baojingList.length">
        <template #default>
          <div class="alertItem">
            {{ baojingList.length }}条部件预警信息:
            <span v-for="item in baojingList" :key="item.uuid">
              <span
                class="name"
                @click="
                  () => {
                    bj = item.uuid;
                    bjChange(item.uuid);
                  }
                "
                >[{{ item.name }}]</span
              >
              :
              <span class="desc">{{ item.desc }}</span>
            </span>
          </div>
        </template>
      </el-alert>
    </div>
  </div>
</template>
 
<script>
// import * as THREE from "../Three/Three.js";

// import { TEngine } from "../threeTool/ThreeEngine";
// import { allBaseObject } from "../threeTool/ThreeBaseObject";
// import { allLights } from "../threeTool/ThreeLights";
// import { getColor } from "../threeTool/ThreeColor";
// import { ThreeAxes } from "../threeTool/ThreeAxes";

// import { GLTFLoader } from "../Three/examples/jsm/loaders/GLTFLoader";
// import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

import * as THREE from "three";

import {
  WebGLRenderer,
  Scene,
  PerspectiveCamera,
  Vector3,
  MOUSE,
  Raycaster,
  Vector2,
} from "three";

import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { TransformControls } from "three/examples/jsm/controls/TransformControls";
import {
  CSS3DRenderer,
  CSS3DObject,
} from "three/examples/jsm/renderers/CSS3DRenderer";

import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
import { TDSLoader } from "three/examples/jsm/loaders/TDSLoader";
import { MTLLoader } from "three-obj-mtl-loader";

import { Color } from "../Three/Three.js";

import WebGL from "../threeTool/WebGL";

import axios from "axios";

THREE.Cache.enabled = true;

export default {
  name: "ThreeScene",

  props: {
    modelUnpackType: {
      type: String,
      default: () => {
        return "";
      },
    },
    showModelMsg: {
      type: Boolean,
      default: true,
    },
  },

  data() {
    return {
      selectWarnType: {
        warn: "#e6a23c",
        error: "#f56c6c",
      },
      yujingList: [],
      baopjingList: [],
      splitOpacity: 25,
      modelLoading: false,
      loadingSvg: `<path class="path" d="
          M 30 15
          L 28 17
          M 25.61 25.61
          A 15 15, 0, 0, 1, 15 30
          A 15 15, 0, 1, 1, 27.99 7.5
          L 15 15
        " style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>`,
      modelUnpackMap: {
        fengji: "fengji",
        guangfu: "gfSolo",
        dixing: "dixing",
      },
      ThreeEngine: null,
      scene: null,
      camera: null,
      renderer: null,
      scene_Toushi: null,
      camera_Toushi: null,
      renderer_Toushi: null,
      composer: null,
      outlinePass: null,
      loader: null,
      baseWidth: 0,
      baseHeight: 0,
      orbitControls: null,
      css3DRenderer: null,
      orbitControls_Toushi: null,
      css3DRenderer_Toushi: null,
      animationId: null,
      basicModel: null,
      bj: "",
      bjList: [],
      modleMap: {
        fengji: {
          dizuo13: {
            name: "底座",
          },
          tadiyiceng14: {
            name: "塔底一层",
          },
          zhuzhou15: {
            name: "主轴",
          },
          zhengliuzhao16: {
            name: "整流罩",
          },
          jicangzhao0117: {
            name: "机舱罩01",
          },
          pati18: {
            name: "爬梯",
          },
          tadierceng19: {
            name: "塔底二层",
          },
          tadisanceng20: {
            name: "塔底三层",
          },
          miehuoqi21: {
            name: "灭火器",
          },
          chilunxiang22: {
            name: "齿轮箱",
          },
          diaoji23: {
            name: "吊机",
          },
          qita24: {
            name: "其他",
          },
          jicanggui25: {
            name: "机舱柜",
          },
          dianji26: {
            name: "电机",
          },
          lianzhouqi27: {
            name: "联轴器",
          },
          men28: {
            name: "门",
          },
          xiangbian29: {
            name: "箱变",
          },
          tatong0230: {
            name: "塔筒02",
          },
          tatong0331: {
            name: "塔筒03",
          },
          pianhangxitong32: {
            name: "偏航系统",
          },
          tajia33: {
            name: "塔架",
          },
          tatong0136: {
            name: "塔筒01",
          },
          dianlanxian37: {
            name: "电缆线",
          },
          yeyazhan38: {
            name: "液压站",
          },
          bianjiangxitong39: {
            name: "变桨系统",
          },
          shanye0240: {
            name: "扇叶02",
          },
          shanye0141: {
            name: "扇叶01",
          },
          shanye0342: {
            name: "扇叶03",
          },
          jicangzhao0243: {
            name: "机舱罩02",
          },
        },
      },
    };
  },

  created() {
    // this.test();
  },
  mounted() {},
  methods: {
    init() {
      this.$nextTick(async () => {
        await this.initLoader();
        await this.initThree({
          dom: this.$refs.threeCanvas,
          dom_Toushi: this.$refs.threeToushi,
          showGridHelper: true,
        });
        await this.addModel({
          fileType: "glb",
          fileName: this.modelUnpackMap[this.modelUnpackType],
        });
      });
    },

    sliderFormatTooltip(value) {
      return `透视透明度: ${value}% `;
    },

    async test() {
      const res = await axios.get(
        `https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p50.pl`,
        {
          file: "gfs.t18z.pgrb2full.0p50.f000",
          leftlon: 0,
          rightlon: 360,
          toplat: 90,
          bottomlat: -90,
          dir: `/gfs.20250827/18/atmos`,
          "lev_PV=1e-06_(Km^2/kg/s)_surface": "on",
          var_TMP: "on",
        }
      );
      console.log(1111111111, res);
      return;
      // 获取 gfs 文件列表
      const rootRes = await axios.get("/nomads");
      const gfsList = this.extractDOM(rootRes.data, "body p");
      const firstGfsName = gfsList[1].querySelector("a").innerHTML;

      // 获取首个文件内的日期列表
      const dateRes = await axios.get(`/nomads?dir=/${firstGfsName}`);
      const gfsFileDateList = this.extractDOM(dateRes.data, "body td a");
      const firstDate = gfsFileDateList[0].innerHTML;

      // 获取 gfs 文件详情
      const gfsRes = await axios.get(
        `/nomads?dir=/${firstGfsName}/${firstDate}/atmos`
      );
      const gfsDetailPage = this.extractDOM(gfsRes.data, "body form p");

      // 获取 gfs 模型列表
      const gfsModelList = gfsDetailPage[1].querySelectorAll("option");
      let model = [];
      for (let i = 0; i < gfsModelList.length; i++) {
        model.push(gfsModelList[i].innerHTML.replace(/\n/g, ""));
      }

      // 获取级别
      const levelsList = gfsDetailPage[4];
      const levelInputList = levelsList.querySelectorAll("input");
      let lvl = [];
      for (let i = 0; i < levelInputList.length; i++) {
        const ipt = levelInputList[i];
        const lvlStr = ipt
          .getAttribute("name")
          .replace(/^lev_/, "")
          .replace(/\\/g, "");
        lvl.push(lvlStr);
      }

      lvl.shift();

      // 获取变量
      const variablesList = gfsDetailPage[5];
      const varInputList = variablesList.querySelectorAll("input");
      let variables = [];
      for (let i = 0; i < varInputList.length; i++) {
        const ipt = varInputList[i];
        const varStr = ipt
          .getAttribute("name")
          .replace(/^var_/, "")
          .replace(/\\/g, "");
        variables.push(varStr);
      }

      variables.shift();
    },

    // 提取DOM元素函数
    extractDOM(htmlString, selector) {
      if (!htmlString.trim()) {
        showMessage("请输入HTML字符串", "error");
        return;
      }

      if (!selector.trim()) {
        showMessage("请输入CSS选择器", "error");
        return;
      }

      try {
        // 使用DOMParser解析HTML字符串
        const parser = new DOMParser();
        const doc = parser.parseFromString(htmlString, "text/html");

        // 使用选择器查找元素
        const element = doc.querySelectorAll(selector);

        if (element) {
          // 输出元素的outerHTML
          return element;
        } else {
          console.log("未匹配到元素:", selector);
        }
      } catch (error) {
        console.log("解析HTML时出错: ", error.message);
      }
    },

    enableFallbackOutline() {
      console.log("启用备用描边方案 (EdgesGeometry)");

      let yujingList = [];
      let baojingList = [];

      this.basicModel.children.forEach((bj) => {
        if (bj.name === "变桨系统" || bj.name === "齿轮箱") {
          bj.name === "变桨系统"
            ? yujingList.push({ name: bj.name, uuid: bj.uuid, desc: "三相电流不平衡度超过10%且持续120秒" })
            : bj.name === "齿轮箱"
            ? baojingList.push({
                name: bj.name,
                uuid: bj.uuid,
                desc: "齿箱油温功率超限",
              })
            : "";

          bj.traverse((child) => {
            if (child.isMesh) {
              // 移除可能存在的旧描边
              if (child.userData.outline) {
                child.remove(child.userData.outline);
              }
              // 创建边缘几何体
              const edges = new THREE.EdgesGeometry(child.geometry, 15); // 角度阈值
              const lineMaterial = new THREE.LineBasicMaterial({
                color: this.getColor(
                  this.selectWarnType[bj.name === "齿轮箱" ? "error" : "warn"]
                ),
                linewidth: 2,
              });
              const outline = new THREE.LineSegments(edges, lineMaterial);
              // 添加到网格
              child.add(outline);
              child.userData.outline = outline;
              child.userData.outline.visible = false;
              // 确保描边随父级变换
              outline.matrixAutoUpdate = false;
            }
          });
        }
      });

      this.yujingList = yujingList;
      this.baojingList = baojingList;

      // this.basicModel.traverse((child) => {
      //   if (child.isMesh) {
      //     // 移除可能存在的旧描边
      //     if (child.userData.outline) {
      //       child.remove(child.userData.outline);
      //     }

      //     // 创建边缘几何体
      //     const edges = new THREE.EdgesGeometry(child.geometry, 15); // 角度阈值
      //     const lineMaterial = new THREE.LineBasicMaterial({
      //       color: this.getColor("red"),
      //       linewidth: 2,
      //     });
      //     const outline = new THREE.LineSegments(edges, lineMaterial);

      //     // 添加到网格
      //     child.add(outline);
      //     child.userData.outline = outline;
      //     child.userData.outline.visible = false;

      //     // 确保描边随父级变换
      //     outline.matrixAutoUpdate = false;
      //   }
      // });
    },

    showOutlineForSelected(selectedPart) {
      // 隐藏所有描边
      this.basicModel.traverse((child) => {
        if (child.userData.outline) {
          child.userData.outline.visible = false;
        }
      });

      // 显示选中部件的描边
      selectedPart.traverse((child) => {
        if (child.userData.outline) {
          child.userData.outline.visible = true;
        }
      });
    },

    hideOutline(part) {
      part.traverse((child) => {
        if (child.userData.outline) {
          child.userData.outline.visible = false;
        }
      });
    },

    showOutline(part) {
      part.traverse((child) => {
        if (child.userData.outline) {
          child.userData.outline.visible = true;
        }
      });
    },

    bjChange(uuid) {
      let modelBjList = this.scene.children.find((modelEle) => {
        return !modelEle.isLight && !modelEle.isLineSegments;
      });
      if (uuid) {
        modelBjList.children.forEach((ele) => {
          if (ele.uuid === uuid) {
            this.focusOnPart(ele);
            ele.visible = true;
            // 显示选中部件的描边，隐藏其他的
            this.showOutlineForSelected(ele);
          } else {
            ele.visible = false;
            this.hideOutline(ele);
          }
        });
      } else {
        this.focusOnPart(this.basicModel);
        modelBjList.children.forEach((ele) => {
          ele.visible = true;
        });
      }
    },

    calcFixBeiLv(value) {
      return 4 * value;
      switch (value) {
        case 1:
          return 96;
        case 2:
          return 92.4;
        case 3:
          return 88.75;
        case 4:
          return 85.25;
        case 5:
          return 81.75;
        case 6:
          return 78.4;
        case 7:
          return 75.5;
        default:
          return 4 * value;
      }
    },

    initThree(options) {
      this.$nextTick(() => {
        const parentDom = getComputedStyle(
          document.querySelector(".el-drawer__body"),
          null
        );

        this.baseWidth = parentDom.width ? parseInt(parentDom.width) : 0;
        this.baseHeight = parentDom.height ? parseInt(parentDom.height) : 0;

        // 创建3D场景对象Scene
        const scene = new THREE.Scene();
        scene.background = this.getColor("#93bcd3");
        const scene_Toushi = new THREE.Scene();
        scene_Toushi.background = this.getColor("#93bcd3");

        // 相机
        const camera = new PerspectiveCamera(
          // 视野角度（fov）
          options?.cameraOptions?.fov || 75,
          // 长宽比（aspect ratio）
          options?.cameraOptions?.aspectRatio ||
            this.baseWidth / this.baseHeight,
          // 近截面（near）
          options?.cameraOptions?.near || 0.1,
          // 远截面（far）
          options?.cameraOptions?.far || 1000
        );
        const camera_Toushi = new PerspectiveCamera(
          // 视野角度（fov）
          options?.cameraOptions?.fov || 75,
          // 长宽比（aspect ratio）
          options?.cameraOptions?.aspectRatio ||
            this.baseWidth / this.baseHeight,
          // 近截面（near）
          options?.cameraOptions?.near || 0.1,
          // 远截面（far）
          options?.cameraOptions?.far || 1000
        );

        // 设置摄像机位置和朝向
        camera.position.set(80, 100, 200); // 调整摄像机位置
        camera.lookAt(new THREE.Vector3(0, 0, 0)); // 设置摄像机朝向场景中心
        camera_Toushi.position.set(80, 100, 200); // 调整摄像机位置
        camera_Toushi.lookAt(new THREE.Vector3(0, 0, 0)); // 设置摄像机朝向场景中心

        // 渲染器
        const renderer = new WebGLRenderer({
          antialias: true, // 开启抗锯齿
        });
        const renderer_Toushi = new WebGLRenderer({
          antialias: true, // 开启抗锯齿
        });

        // 添加灯光
        const ambientLight = new THREE.AmbientLight(0xffffff, 5);
        scene.add(ambientLight);
        const directionalLight1 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight1.position.set(0, 0, 1);
        scene.add(directionalLight1);
        const directionalLight2 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight2.position.set(0, 0, -1);
        scene.add(directionalLight2);
        const directionalLight3 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight3.position.set(1, 0, 0);
        scene.add(directionalLight3);
        const directionalLight4 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight4.position.set(-1, 0, 0);
        scene.add(directionalLight4);
        const directionalLight5 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight5.position.set(0, 1, 0);
        scene.add(directionalLight5);
        const directionalLight6 = new THREE.DirectionalLight(0xffffff, 3);
        directionalLight6.position.set(0, -1, 0);
        scene.add(directionalLight6);

        const ambientLight_Toushi = new THREE.AmbientLight(0xffffff, 5);
        scene_Toushi.add(ambientLight_Toushi);
        const directionalLight1_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight1_Toushi.position.set(0, 0, 1);
        scene_Toushi.add(directionalLight1_Toushi);
        const directionalLight2_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight2_Toushi.position.set(0, 0, -1);
        scene_Toushi.add(directionalLight2_Toushi);
        const directionalLight3_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight3_Toushi.position.set(1, 0, 0);
        scene_Toushi.add(directionalLight3_Toushi);
        const directionalLight4_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight4_Toushi.position.set(-1, 0, 0);
        scene_Toushi.add(directionalLight4_Toushi);
        const directionalLight5_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight5_Toushi.position.set(0, 1, 0);
        scene_Toushi.add(directionalLight5_Toushi);
        const directionalLight6_Toushi = new THREE.DirectionalLight(
          0xffffff,
          3
        );
        directionalLight6_Toushi.position.set(0, -1, 0);
        scene_Toushi.add(directionalLight6_Toushi);

        // renderer.setSize(window.innerWidth, window.innerHeight);
        const rendererBox = getComputedStyle(options.dom, null);
        const renderSizeWidth = parseFloat(rendererBox.width);
        const renderSizeHeight = parseFloat(rendererBox.height);
        renderer.setSize(renderSizeWidth, renderSizeHeight);

        const rendererBox_Toushi = getComputedStyle(options.dom_Toushi, null);
        const renderSizeWidth_Toushi = parseFloat(rendererBox_Toushi.width);
        const renderSizeHeight_Toushi = parseFloat(rendererBox_Toushi.height);
        renderer_Toushi.setSize(
          renderSizeWidth_Toushi,
          renderSizeHeight_Toushi
        );

        options?.dom?.appendChild(renderer.domElement);
        options?.dom_Toushi?.appendChild(renderer_Toushi.domElement);

        const css3DRenderer = new CSS3DRenderer();
        css3DRenderer.setSize(renderSizeWidth, renderSizeHeight);
        css3DRenderer.render(scene, camera);
        css3DRenderer.domElement.style.position = "absolute";
        css3DRenderer.domElement.style.top = 0;
        css3DRenderer.domElement.style.pointerEvents = "none";
        options.dom.appendChild(css3DRenderer.domElement);

        const css3DRenderer_Toushi = new CSS3DRenderer();
        css3DRenderer_Toushi.setSize(
          renderSizeWidth_Toushi,
          renderSizeHeight_Toushi
        );
        css3DRenderer_Toushi.render(scene_Toushi, camera_Toushi);
        css3DRenderer_Toushi.domElement.style.position = "absolute";
        css3DRenderer_Toushi.domElement.style.top = 0;
        css3DRenderer_Toushi.domElement.style.pointerEvents = "none";
        options.dom_Toushi.appendChild(css3DRenderer_Toushi.domElement);

        // 设置鼠标功能键（轨道控制器）
        const orbitControls = new OrbitControls(camera, renderer.domElement);
        // 移动带阻尼
        orbitControls.enableDamping = options.enableDamping || false;
        // 设置阻尼系数
        orbitControls.dampingFactor = options.dampingFactor || 0;
        // 自动旋转
        orbitControls.autoRotate = options.autoRotate || false;

        orbitControls.mouseButtons = {
          LEFT: MOUSE.ROTATE, // 左键无功能
          MIDDLE: MOUSE.PAN, // 中键移动
          RIGHT: null, // 右键旋转
        };

        orbitControls.addEventListener("change", () => {
          // 复制位置
          camera_Toushi.position.copy(camera.position);

          // 复制旋转
          camera_Toushi.rotation.copy(camera.rotation);

          // 复制up向量
          camera_Toushi.up.copy(camera.up);

          camera_Toushi.updateMatrixWorld();

          orbitControls_Toushi.target.copy(orbitControls.target);
          // orbitControls.update();
          // orbitControls_Toushi.update();
        });

        const orbitControls_Toushi = new OrbitControls(
          camera_Toushi,
          renderer_Toushi.domElement
        );
        // 移动带阻尼
        orbitControls_Toushi.enableDamping = options.enableDamping || false;
        // 设置阻尼系数
        orbitControls_Toushi.dampingFactor = options.dampingFactor || 0;
        // 自动旋转
        orbitControls_Toushi.autoRotate = options.autoRotate || false;

        orbitControls_Toushi.mouseButtons = {
          LEFT: MOUSE.ROTATE, // 左键无功能
          MIDDLE: MOUSE.PAN, // 中键移动
          RIGHT: null, // 右键旋转
        };

        orbitControls_Toushi.addEventListener("change", () => {
          // 复制位置
          camera.position.copy(camera_Toushi.position);

          // 复制旋转
          camera.rotation.copy(camera_Toushi.rotation);

          // 复制up向量
          camera.up.copy(camera_Toushi.up);

          camera.updateMatrixWorld();

          orbitControls.target.copy(orbitControls_Toushi.target);
          // orbitControls_Toushi.update();
          // orbitControls.update();
        });

        if (options.showGridHelper) {
          // 底图网格 (总长宽，分多少个网格，颜色，轴线颜色，和网格颜色 #e6e8ed)
          const gridHelper = new THREE.GridHelper(
            2000,
            100,
            this.getColor("#dedede"),
            this.getColor("#dedede")
          );
          gridHelper.name = "网格地板";
          gridHelper.disalbedDelete = true;
          scene.add(gridHelper);

          const gridHelper_Toushi = new THREE.GridHelper(
            2000,
            100,
            this.getColor("#dedede"),
            this.getColor("#dedede")
          );
          gridHelper_Toushi.name = "网格地板透视";
          gridHelper_Toushi.disalbedDelete = true;
          scene_Toushi.add(gridHelper_Toushi);
        }

        // 渲染循环
        const animate = () => {
          requestAnimationFrame(animate);
          orbitControls.update();
          css3DRenderer.render(scene, camera);
          renderer.render(scene, camera);
        };

        const animate_Toushi = () => {
          requestAnimationFrame(animate_Toushi);
          orbitControls_Toushi.update();
          css3DRenderer_Toushi.render(scene_Toushi, camera_Toushi);
          renderer_Toushi.render(scene_Toushi, camera_Toushi);
        };

        this.scene = scene;
        this.camera = camera;
        this.renderer = renderer;
        this.orbitControls = orbitControls;
        this.css3DRenderer = css3DRenderer;

        this.scene_Toushi = scene_Toushi;
        this.camera_Toushi = camera_Toushi;
        this.renderer_Toushi = renderer_Toushi;
        this.orbitControls_Toushi = orbitControls_Toushi;
        this.css3DRenderer_Toushi = css3DRenderer_Toushi;

        if (WebGL.isWebGLAvailable()) {
          animate();
          animate_Toushi();
          // 窗口调整大小时更新渲染器
          window.addEventListener("resize", () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer_Toushi.setSize(window.innerWidth, window.innerHeight);
            camera_Toushi.aspect = window.innerWidth / window.innerHeight;
            camera_Toushi.updateProjectionMatrix();

            // 修复：更新 composer 尺寸
            composer.setSize(window.innerWidth, window.innerHeight);
          });
        } else {
          const warning = WebGL.getWebGLErrorMessage();
          options?.dom?.appendChild(warning);
          dom_Toushi?.appendChild(warning);
        }
      });
    },

    // 初始化模型加载器
    initLoader() {
      this.loader = {
        glb: new GLTFLoader(),
        gltf: new GLTFLoader(),
        obj: new OBJLoader(),
        fbx: new FBXLoader(),
        "3ds": new TDSLoader(),
      };
    },

    // 获取颜色
    getColor(colorHex) {
      return new Color(colorHex);
    },

    // 添加模型
    addModel({ fileType = "glb", fileName }) {
      this.modelLoading = true;
      const loader = this.loader[fileType];
      loader.setResourcePath(`./static/model/${fileName}/`);
      loader.load(`./static/model/${fileName}/model.${fileType}`, (result) => {
        if (fileType === "fbx") {
          this.basicModel = this.formatModel(result);
        } else if (fileType === "glb") {
          this.basicModel = this.formatModel(result.scene);
        }
        this.basicModel.isCurrentAddModel = true;

        let bjList = [];

        this.basicModel.children.forEach((bj) => {
          if (
            bj.name.indexOf("Particle_View") === -1 &&
            bj.name !== "34" &&
            bj.name !== "_35" &&
            this.modleMap?.[fileName]?.[bj.name]
          ) {
            bj.name = this.modleMap?.[fileName]?.[bj.name]?.name || bj.name;
            const warnType =
              bj.name === "齿轮箱"
                ? "error"
                : bj.name === "变桨系统"
                ? "warn"
                : "";
            bjList.push({
              id: bj.id,
              uuid: bj.uuid,
              name: bj.name,
              warnType,
            });
          }
        });

        this.bjList = bjList;

        this.collectMaterialInfo(this.basicModel);
        this.scene.add(this.basicModel);
        this.scene_Toushi.add(this.basicModel.clone());
        this.modelLoading = false;

        this.enableFallbackOutline();
      });
    },

    // 调整视角到选中部件
    focusOnPart(part) {
      const box = new THREE.Box3().setFromObject(part);
      const size = box.getSize(new THREE.Vector3());
      const center = box.getCenter(new THREE.Vector3());
      const speed = 0.5;

      // 计算最佳相机距离
      const maxDim = Math.max(size.x, size.y, size.z);
      const cameraDistance = maxDim * parseFloat(speed);

      // 计算相机位置
      const direction = this.orbitControls.target
        .clone()
        .sub(this.camera.position)
        .normalize();
      const cameraPosition = center
        .clone()
        .sub(direction.multiplyScalar(cameraDistance));

      // 是否启用平滑过渡
      const smooth = true;

      if (smooth) {
        // 平滑过渡到新位置
        this.animateCamera(
          this.camera.position,
          cameraPosition,
          this.orbitControls.target,
          center,
          speed
        );

        setTimeout(() => {
          // 复制位置
          this.camera_Toushi.position.copy(this.camera.position);

          // 复制旋转
          this.camera_Toushi.rotation.copy(this.camera.rotation);

          // 复制up向量
          this.camera_Toushi.up.copy(this.camera.up);

          this.camera_Toushi.updateMatrixWorld();

          this.orbitControls_Toushi.target.copy(this.orbitControls.target);
          this.orbitControls.update();
          this.orbitControls_Toushi.update();
        }, 1000);
      } else {
        // 直接设置新位置
        this.camera.position.copy(cameraPosition);
        this.orbitControls.target.copy(center);
        this.orbitControls.update();
      }
    },

    // 平滑移动相机
    animateCamera(fromPosition, toPosition1, fromTarget, toTarget, speed) {
      const that = this;
      const duration = 1000; // 动画持续时间（ms）
      const startTime = Date.now();

      const toPosition = toPosition1;
      toPosition.x *= 1.03;
      toPosition.y *= 1.01;
      toPosition.z *= 1.03;

      // 缓动函数
      let easeInOutCubic = (t) => {
        return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
      };

      function update() {
        const elapsed = Date.now() - startTime;
        const progress = Math.min(elapsed / duration, 1);

        // 使用缓动函数
        const easeProgress = easeInOutCubic(progress);

        // 插值计算相机位置和目标位置
        that.camera.position.lerpVectors(
          fromPosition,
          toPosition,
          easeProgress
        );
        that.orbitControls.target.lerpVectors(
          fromTarget,
          toTarget,
          easeProgress
        );
        that.orbitControls.update();

        if (progress < 1) {
          requestAnimationFrame(update);
        }
      }

      update();
    },

    formatModel(model) {
      console.log("model", model);
      this.renderer.outputColorSpace = THREE.SRGBColorSpace;
      const camera = this.camera;

      // model.traverse((child) => {
      //   if (child.material) {
      //     child.material.emissive = child.material.color;
      //     child.material.emissiveMap = child.material.map;
      //   }
      // });

      // model.children.forEach((item, index) => {
      //   item.castShadow = true;
      //   item.receiveShadow = true;
      // });

      const baseBoxSize = new THREE.Vector3(100, 100, 100);
      // 计算模型的包围盒（bounding box）
      const boundingBox = new THREE.Box3().setFromObject(model);
      const modelSize = new THREE.Vector3();
      boundingBox.getSize(modelSize);

      // 计算模型的最大尺寸
      const maxModelSize = Math.max(modelSize.x, modelSize.y, modelSize.z);

      // 计算缩放比例，使模型尺寸不超过基准盒子的尺寸
      const scaleFactor = baseBoxSize.clone().divideScalar(maxModelSize);

      // 将模型等比例缩放到适应基准盒子大小
      model.scale.set(scaleFactor.x, scaleFactor.y, scaleFactor.z);

      camera.position.z = 200;
      return model;
    },

    collectMaterialInfo(model) {
      if (!model) return;

      const info = [];
      const originalMaterials = new Map();

      let materialCount = 0;

      model.traverse((node) => {
        if (node.isMesh && node.material) {
          materialCount++;
          const materials = Array.isArray(node.material)
            ? node.material
            : [node.material];

          materials.forEach((material, index) => {
            if (material.name === "Mesh_225") {
            }
            info.push(`材质 ${materialCount}-${index + 1}: ${material.type}`);
            info.push(`  名称: ${material.name || "未命名"}`);
            info.push(
              `  颜色: ${material.color ? material.color.getHexString() : "无"}`
            );
            info.push(`  贴图: ${material.map ? "有" : "无"}`);
            info.push(`  自发光贴图: ${material.emissiveMap ? "有" : "无"}`);
            info.push(
              `  金属粗糙度贴图: ${material.metalnessMap ? "有" : "无"}`
            );
            info.push(`  法线贴图: ${material.normalMap ? "有" : "无"}`);
            info.push("---");

            // 保存原始材质引用
            originalMaterials.set(node.uuid + "-" + index, material);
          });
        }
      });
      // console.log(333, info);
    },

    overrideMaterials() {
      if (!model) return;

      model.traverse((node) => {
        if (node.isMesh) {
          // 创建新的基本材质
          const newMaterial = new THREE.MeshStandardMaterial({
            color: 0x4488ff,
            metalness: metalness.value,
            roughness: roughness.value,
            emissive: new THREE.Color(0x888888).multiplyScalar(
              emissiveIntensity.value
            ),
            wireframe: wireframe.value,
          });

          node.material = newMaterial;
        }
      });
    },
  },
  watch: {
    showModelMsg: {
      handler(val) {
        if (val) {
          this.init();
        }
      },
      immediate: true,
    },
  },
};
</script>

<style lang="less" scoped>
.modelBox {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  justify-content: flex-start;
  align-items: center;

  .l,
  .r {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    overflow: hidden;
  }

  .r {
    pointer-events: none;
  }

  .threeToushi {
    z-index: 10;
    position: absolute;
    left: 0;
    top: 0;
  }

  .bjSelectBox {
    position: absolute;
    left: 5px;
    top: 25px;
  }

  .sliderBarBox {
    width: 100%;
    position: absolute;
    left: 5px;
    top: 5px;
    width: calc(100% - 10px);
  }

  .alertBox {
    width: calc(100% - 24px);
    margin: 0 12px;
    position: absolute;
    left: 0;
    bottom: 12px;

    .el-alert {
      margin-bottom: 4px;

      &:last-child {
        margin-bottom: 0;
      }
    }

    .alertItem {
      .name {
        color: #1890ff;
        cursor: pointer;

        &:hover {
          color: #409eff;
          text-decoration: underline;
        }
      }

      .desc {
        font-weight: 700;
      }
    }
  }
}
</style>