Browse Source

解决左边菜单

hongrunxia 2 years ago
parent
commit
6c9ff82c46
30 changed files with 602 additions and 80 deletions
  1. BIN
      public/model/glft/fm/Fm-door-animation.glb
  2. BIN
      public/model/glft/fm/Fm-door.glb
  3. BIN
      public/model/glft/fm/Fm-onlylanewary.glb
  4. BIN
      public/model/glft/fm/Fm-overall.glb
  5. BIN
      public/model/glft/fm/Fm-wallonly.glb
  6. 6 0
      src/components/Container/src/Adaptive.vue
  7. 1 1
      src/components/Table/src/hooks/useColumnsCache.ts
  8. 2 1
      src/design/color.less
  9. 1 1
      src/design/vent/index.less
  10. 1 1
      src/design/vent/modal.less
  11. 1 0
      src/layouts/default/menu/index.vue
  12. 17 9
      src/layouts/default/sider/bottomSideder.vue
  13. 3 3
      src/layouts/default/sider/index.vue
  14. 3 1
      src/qiankun/state.ts
  15. 0 0
      src/utils/threejs/RafHelper.ts
  16. 68 0
      src/utils/threejs/loadModel.worker.js
  17. 4 0
      src/utils/threejs/main.worker.ts
  18. 105 0
      src/utils/threejs/modalParse.ts
  19. 240 0
      src/utils/threejs/parseModal.ts
  20. 73 0
      src/utils/threejs/type/types.ts
  21. 0 0
      src/utils/threejs/useThree.ts
  22. 1 1
      src/views/demo/threejs/damper.vue
  23. 1 1
      src/views/vent/monitorManager/fanLocalMonitor/fanLocal.three.ts
  24. 1 1
      src/views/vent/monitorManager/gateMonitor/detail.vue
  25. 65 57
      src/views/vent/monitorManager/gateMonitor/gate.threejs.ts
  26. 1 0
      src/views/vent/monitorManager/gateMonitor/index.vue
  27. 1 1
      src/views/vent/monitorManager/mainFanMonitor/main.threejs.ts
  28. 1 1
      src/views/vent/monitorManager/windowMonitor/window.threejs.ts
  29. 1 1
      src/views/vent/monitorManager/windrectMonitor/windrect.threejs.ts
  30. 5 0
      src/views/vent/monitorManager/windrectMonitor/zhedie.threejs.ts

BIN
public/model/glft/fm/Fm-door-animation.glb


BIN
public/model/glft/fm/Fm-door.glb


BIN
public/model/glft/fm/Fm-onlylanewary.glb


BIN
public/model/glft/fm/Fm-overall.glb


BIN
public/model/glft/fm/Fm-wallonly.glb


+ 6 - 0
src/components/Container/src/Adaptive.vue

@@ -10,6 +10,8 @@
   import { ref, onMounted, onUnmounted, nextTick, defineComponent } from 'vue';
   import { debounce, setRem } from '/@/utils/index';
   import { useAppStore } from '/@/store/modules/app';
+  import { getActions } from '/@/qiankun/state';
+
   export default defineComponent({
     name: 'AdaptiveContainer',
     props: {
@@ -34,6 +36,8 @@
        * observer: window.MutationObserver(Bom实例)监听dom改变
        */
       let dom, observer;
+      const actions = getActions();
+      
 
       //设置初始值
       const initSize = () => {
@@ -86,6 +90,8 @@
         const heightScale = currentHeight / realHeight;
         appStore.setWidthScale(widthScale);
         appStore.setHeightScale(heightScale);
+        actions.setGlobalState({ widthScale, heightScale });
+
         //如果dom存在,就按照比例缩放
         dom && (dom.style.transform = `scale(${widthScale}, ${heightScale})`);
       };

+ 1 - 1
src/components/Table/src/hooks/useColumnsCache.ts

@@ -14,7 +14,7 @@ export function useColumnsCache(opt, setColumns, handleColumnFixed) {
   const { createMessage: $message } = useMessage();
   // 列表配置缓存key
   const cacheKey = computed(() => {
-    const { fullPath } = router.currentRoute.value;
+    const { fullPath } = router.currentRoute.value || router.currentRoute;
     let key = fullPath.replace(/[\/\\]/g, '_');
     const cacheKey = table.getBindValues.value.tableSetting?.cacheKey;
     if (cacheKey) {

+ 2 - 1
src/design/color.less

@@ -13,7 +13,8 @@ html {
 @white: #fff;
 
 // @content-bg: #f4f7f9;
-@content-bg: #03114c;
+// @content-bg: #03114c;
+@content-bg: #09172c;
 
 // :export {
 //   name: "less";

+ 1 - 1
src/design/vent/index.less

@@ -4,7 +4,7 @@
 @import './comment.less';
 
 .vent {
-  background-color: #03114c !important;
+  background-color: #09172c !important;
   // background-color: #031d4c !important; //031d4c
 }
 /* 按钮 */

+ 1 - 1
src/design/vent/modal.less

@@ -14,7 +14,7 @@
     position: absolute;
     left: 0;
     top: 0;
-    background: #03114ccc;
+    background: #09172c;
   }
   .threejs-Object-CSS {
     pointer-events: none;

+ 1 - 0
src/layouts/default/menu/index.vue

@@ -109,6 +109,7 @@
       //update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
       const localeStore = useLocaleStore();
       function handleMenuClick(path: string, item) {
+        debugger
         if (item) {
           localeStore.setPathTitle(path, item.title || '');
         }

+ 17 - 9
src/layouts/default/sider/bottomSideder.vue

@@ -26,11 +26,11 @@
           </template>
         </div>
         <div class="setting-group">
-          <SvgIcon class="icon-style" size="18" name="home" @click="go('/dashboard/analysis')" />
+          <SvgIcon class="icon-style" size="18" name="home" @click="go('/micro-vent-3dModal/dashboard/analysis')" />
           <SvgIcon class="icon-style" size="18" name="fixed" />
           <SvgIcon class="icon-style" size="18" name="enter" />
           <!-- <SvgIcon class="icon-style" size="18" name="setting" />
-        <SvgIcon class="icon-style" size="18" name="hidden" /> -->
+          <SvgIcon class="icon-style" size="18" name="hidden" /> -->
         </div>
       </div>
     </div>
@@ -67,8 +67,13 @@
       }
 
       function handleMenuClick(path: Menu) {
-        if (route.path.startsWith('/micro-')) {
-          history.pushState({}, '', path.path);
+        if (route.path.startsWith('/micro-')) { 
+          if (route.path.startsWith('/micro-vent-3dModal')){
+            const { href } = router.resolve(path.path)
+            window.open(href, '_blank')
+          }else {
+            history.pushState({}, '', path.path);
+          }    
         } else {
           go(path.path);
         }
@@ -102,7 +107,7 @@
 </script>
 
 <style lang="less" scoped>
-  @keyframes menuShow {
+@keyframes menuShow {
     0% {
       width: 0;
       height: 0;
@@ -124,15 +129,18 @@
       width: 480px;
       position: absolute;
       bottom: 0;
+      // border: 1px solid #0099e6;
+      // background-color: #06115a;
       border: 1px solid #0099e6;
-      background-color: #06115a;
-      backdrop-filter: blur(8px);
+      background-color: #0c1e2b;
+      // backdrop-filter: blur(8px);
       .four-border-bg {
         margin: 5px;
         background-color: #ffffff00;
         .main-container {
           background-color: #ffffff00 !important;
           box-shadow: 0 0 3px #ffffff33 inset;
+          backdrop-filter: blur(0px)
         }
 
         .parent-menu {
@@ -144,7 +152,7 @@
         }
         .child-menu-item {
           width: 100px;
-          background-color: #0c3898;
+          background-color: #07476b;
           border-radius: 2px;
           display: flex;
           align-items: center;
@@ -153,7 +161,7 @@
           cursor: pointer;
           box-shadow: 0 0 3px #ffffff22 inset;
           &:hover {
-            background-color: #0069ed;
+            background-color: #0676b5;
           }
         }
       }

+ 3 - 3
src/layouts/default/sider/index.vue

@@ -1,6 +1,6 @@
 <template>
   <Drawer
-    v-if="getIsMobile"
+    v-if="getIsMobile && getShowSidebar"
     placement="left"
     :class="prefixCls"
     :width="getMenuWidth"
@@ -31,14 +31,14 @@
     setup() {
       const { prefixCls } = useDesign('layout-sider-wrapper');
       const { getIsMobile } = useAppInject();
-      const { setMenuSetting, getCollapsed, getMenuWidth, getIsMixSidebar, getIsBottomMenu, getIsBottomMenuH } = useMenuSetting();
+      const { setMenuSetting, getCollapsed, getMenuWidth, getIsMixSidebar, getIsBottomMenu, getIsBottomMenuH, getShowSidebar } = useMenuSetting();
       function handleClose() {
         setMenuSetting({
           collapsed: true,
         });
       }
 
-      return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth, getIsMixSidebar, getIsBottomMenu, getIsBottomMenuH };
+      return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth, getIsMixSidebar, getIsBottomMenu, getIsBottomMenuH, getShowSidebar, };
     },
   });
 </script>

+ 3 - 1
src/qiankun/state.ts

@@ -24,7 +24,7 @@ export function getProps() {
  * 定义全局状态,并返回通信方法,在主应用使用,微应用通过 props 获取通信方法。
  * @param state 主应用穿的公共数据
  */
-export function initGlState(info: any = { token: '', userInfo: {}, isMounted: false }) {
+export function initGlState(info: any = { token: '', userInfo: {}, isMounted: false , widthScale: 1, heightScale: 1}) {
   if (actions) return;
   // 初始化state
   actions = initGlobalState(info);
@@ -32,6 +32,8 @@ export function initGlState(info: any = { token: '', userInfo: {}, isMounted: fa
   actions.setGlobalState({
     token: getToken(),
     isMounted: false,
+    widthScale: 1,
+    heightScale: 1
   });
   // 注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
   actions.onGlobalStateChange((newState, prev) => {

+ 0 - 0
src/hooks/core/threejs/RafHelper.ts → src/utils/threejs/RafHelper.ts


+ 68 - 0
src/utils/threejs/loadModel.worker.js

@@ -0,0 +1,68 @@
+import * as THREE from 'three';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
+import { genGroupStruct, genAnimations } from './modalParse'
+
+
+const gltfLoader = new GLTFLoader();
+const dracoLoader = new DRACOLoader();
+dracoLoader.setDecoderPath('/model/draco/gltf/');
+dracoLoader.setDecoderConfig({ type: 'js' }); //使用兼容性强的draco_decoder.js解码器
+dracoLoader.preload();
+gltfLoader.setDRACOLoader(dracoLoader);
+gltfLoader.setPath('/model/glft/');
+
+
+async function loadModel(data) {
+    const { modalValue, modalName, message } = data
+    if (!modalValue) {
+        const db = window['CustomDB'];
+        const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
+        modalValue = modalArr[0].modalVal;
+    }
+    gltfLoader.parse(
+        modalValue,
+        '/model/glft/',
+        (gltf) => {
+            const object = gltf.scene;
+            object.traverse((obj) => {
+                if (obj instanceof THREE.Mesh) {
+                    obj.material.emissiveIntensity = 1;
+                    // obj.material.emissiveMap = obj.material.map;
+                    obj.material.blending = THREE.CustomBlending;
+                    if (obj.material.opacity < 1) {
+                        obj.material.transparent = true;
+                    }
+                    obj.renderOrder = 1;
+                    // if (obj.name !== 'buxiugangse') {
+                    //   obj.receiveShadow = true;
+                    // }
+                    // obj.castShadow = true;
+                }
+            });
+            object.name = modalName;
+            dracoLoader.dispose();
+            self.postMessage({
+                modalName,
+                work_type: "parseModel", 
+                ...genGroupStruct(object),
+                sceneAnimations: genAnimations(gltf.animations),
+            });
+            self.close();
+        },
+        (err) => {
+            console.log(err);
+            self.postMessage({ message: 'end', data: null });
+            self.close();
+        }
+    )
+}
+
+self.addEventListener(
+    'message',
+    async function (e) {
+        const data = e.data
+        await loadModel(data)
+    },
+    false
+);

+ 4 - 0
src/utils/threejs/main.worker.ts

@@ -10,6 +10,10 @@ export function initModalWorker() {
 
   const modalUrlArr = [
     'fm/fm.glb',
+    'fm/Fm-door.glb',
+    'fm/Fm-door-animation.glb',
+    'fm/Fm-overall.glb',
+    'fm/Fm-wallonly.glb',
     'fc/sdFc.glb',
     'fc/ddFc.glb',
     'cf/lmcf.glb',

+ 105 - 0
src/utils/threejs/modalParse.ts

@@ -0,0 +1,105 @@
+import { IBaseProps, IGroupParams, IPointLight } from "./type/types";
+
+/**
+ * 生成基本参数 旋转 位移 缩放等属性
+ */
+const genBaseStruct = (obj: THREE.Object3D): IBaseProps => {
+    const { type, name, quaternion: q, position: p, rotation: r, scale: s, up: u, userData, visible, matrix } = obj;
+    const quaternion: IBaseProps["quaternion"] = [q.x, q.y, q.z, q.w];
+    const position: IBaseProps["position"] = [p.x, p.y, p.z];
+    const rotation: IBaseProps["rotation"] = [r.x, r.y, r.z, r.order];
+    const scale: IBaseProps["scale"] = [s.x, s.y, s.z];
+    const up: IBaseProps["up"] = [u.x, u.y, u.z];
+
+    return {
+        type,
+        name,
+        quaternion,
+        position,
+        rotation,
+        scale,
+        up,
+        matrix,
+        userData,
+        visible,
+        children: genObject3DChildren(obj.children),
+        animations: genAnimations(obj.animations),
+    };
+};
+
+/**
+ * 生成动画结构
+ */
+export const genAnimations = (animations: THREE.AnimationClip[]) =>{
+    return animations.map((animation) => {
+        animation["tracks"].forEach((t) => {
+            //删除这个方法就可以传递过去了
+            //@ts-ignore
+            delete t["createInterpolant"]
+            t['type'] = t['ValueTypeName'] //vector quaternion
+        });
+        return animation;
+    });
+}
+    
+
+/**
+ * 生成物体参数
+ */
+const genMeshStruct = (mesh: THREE.Mesh) => {
+    const { geometry, material } = mesh;
+
+    return {
+        geometry,
+        material,
+        ...genBaseStruct(mesh),
+    };
+};
+
+const genPointLightStruct = (pointLight: THREE.PointLight): IPointLight => {
+    return {
+        power: pointLight.power,
+        color: pointLight.color,
+        decay: pointLight.decay,
+        castShadow: pointLight.castShadow,
+        distance: pointLight.distance,
+        frustumCulled: pointLight.frustumCulled,
+        intensity: pointLight.intensity,
+        layers: pointLight.layers,
+        ...genBaseStruct(pointLight),
+    };
+};
+
+const genObject3DStruct = (object: THREE.Object3D) => {
+    return {
+        ...genBaseStruct(object),
+    };
+};
+
+/**
+ * 生成子元素结构
+ */
+const genObject3DChildren = (children: THREE.Object3D[]) => {
+    const childStruct: IGroupParams["children"] = [];
+    for (const child of children) {
+        const { type } = child;
+        if (type === "Mesh") {
+            childStruct.push(genMeshStruct(child as THREE.Mesh));
+        } else if (type === "Group") {
+            childStruct.push(genGroupStruct(child as THREE.Group));
+        } else if (type === "PointLight") {
+            childStruct.push(genPointLightStruct(child as THREE.PointLight));
+        } else if (type === "Object3D") {
+            childStruct.push(genObject3DStruct(child));
+        }
+    }
+    return childStruct;
+};
+
+/**
+ * 生成物体组结构
+ */
+export const genGroupStruct = (group: THREE.Group) => {
+    const struct: IGroupParams = { ...genBaseStruct(group) };
+    return struct;
+};

+ 240 - 0
src/utils/threejs/parseModal.ts

@@ -0,0 +1,240 @@
+import * as THREE from "three";
+import { THREEMaterialType, IBaseProps, IGroupParams, IMeshParams, IPointLight } from "./type/types";
+
+/**
+ * glb模型并发加载工作原理
+ * web worker 可以解析glb模型 但是postMessage发送的数据类型只是普通的对象,
+ * 并且不能存在方法 方法无法传递,带方法会导致发送失败,并且不会触发onerror
+ * THREE构建物体所需的bufferGeometry,还是BufferAttribute 或者Material等原型对象无法被传递
+ * 传递到主线程的只是一个普通对象和上面的属性(对象中不能有函数)
+ * 可以通过生成一个THREE所需的类型 把传递过来的对象上的参数复制给THREE需要的对象上
+ * 这样在主线程生成一个同样的模型,但是省去了解析模型时间(模型解析在web worker中与js主线程并发执行)
+ * 实现并发加载
+ */
+
+/**
+ * 通过设置attributes index来复刻一个集合体
+ */
+const genGeometry = (geometry: IMeshParams["geometry"], IMeshParams) => {
+    // console.log('目标------>',geometry);
+    const geom = new THREE.BufferGeometry();
+    const { 
+        attributes: { position, uv, normal },
+        index,
+    } = geometry;
+
+    
+    //处理几何坐标
+    if(uv){
+        const attributes = {
+            position: new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
+            uv: new THREE.BufferAttribute(uv.array, uv.itemSize, uv.normalized),
+            normal: new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized),
+        };
+        geom.attributes = attributes;
+    }else {
+        const attributes = {
+            position: new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
+            normal: new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized),
+        };
+        // geom.attributes['position'] = new THREE.BufferAttribute(position.array, position.itemSize, position.normalized),
+        // geom.attributes['normal'] = new THREE.BufferAttribute(normal.array, normal.itemSize, normal.normalized)
+        geom.attributes = attributes;
+    }
+    
+    
+    geom.index = index ? new THREE.BufferAttribute(index.array, index.itemSize, index.normalized) : null;
+    return geom;
+};
+
+
+
+/**
+ * 根据传入纹理的参数生成真正有效的Material类型数据
+ */
+const genMaterial = (mate: IMeshParams["material"]) => {
+    if (!mate) return undefined;
+    const multipleMaterial = Array.isArray(mate);
+    const material = multipleMaterial ? ([] as THREE.Material[]) : new THREE[mate.type as THREEMaterialType]();
+    //处理材质
+    //多个材质
+    if (multipleMaterial && Array.isArray(material)) {
+        console.log('有多个材质');
+        
+        for (const m of mate) {
+            const im = new THREE[m.type as THREEMaterialType]();
+
+            material.push(im);
+        }
+    } else if (mate) {
+        //单个材质
+        Object.assign(material, mate);
+        for (const key in mate) {
+            if (Object.prototype.hasOwnProperty.call(mate, key)) {
+                const element = mate[key];
+                if(element && Object.prototype.toString.call(element === '[object Object]')) {
+                    if( element.isTexture){
+                        const texture = new THREE.Texture()
+                        for (const k in texture) {
+                            if(Object.prototype.toString.call(texture[k]) === '[object Object]'){
+                                if(k==='center' || k==='offset' || k==='repeat'){
+                                    texture[k] = new THREE.Vector2()
+                                }
+                                if(k === 'matrix'){
+                                    texture[k] = new THREE.Matrix3()
+                                }
+                                if(k === 'source'){
+                                    texture[k] = new THREE.Source(null)
+                                }
+                                Object.assign(texture[k] , element[k]);
+                            }else {
+                                texture[k] =  element[k]
+                            }
+                        }
+                        material[key] = texture
+                    }
+                    if(element.isColor){
+                        const color = new THREE.Color()
+                        Object.assign(color, element);
+                        material[key] = color
+                    }
+                    if(key === 'normalScale'){
+                        const vector2 = new THREE.Vector2()
+                        Object.assign(vector2, element);
+                        material[key] = vector2
+                    }
+                }   
+            }   
+        }   
+    }    
+    return material;
+};
+
+/**
+ * 处理基本属性转换(Object3D基类上的属性) matrix scale rotate translate position children  animations
+ */
+const parseBaseParams = (params: IBaseProps, object: THREE.Object3D) => {
+    const matrix = new THREE.Matrix4();
+    matrix.elements = params.matrix.elements;
+    object.name = params.name;
+    object.matrix = matrix;
+    object.rotation.set(...params.rotation);
+    object.position.set(...params.position);
+    object.scale.set(...params.scale);
+    object.quaternion.set(...params.quaternion);
+    object.up.set(...params.up);
+    object.userData = params.userData;
+    object.visible = params.visible;
+
+    parseChildren(object, params.children);
+    genAnimations(object, params.animations);
+    // deleteObjectKeys(params, [
+    //     "name",
+    //     "matrix",
+    //     "rotation",
+    //     "position",
+    //     "scale",
+    //     "quaternion",
+    //     "up",
+    //     "userData",
+    //     "visible",
+    // ]);
+    // object.scale.x += 0.3;
+    // object.scale.y += 0.3;
+    // object.scale.z += 0.3;
+};
+
+const parseMesh = (IMeshParams: IMeshParams) => {
+    const geometry = genGeometry(IMeshParams.geometry, IMeshParams);
+    const material = genMaterial(IMeshParams.material);
+
+    const mesh = new THREE.Mesh(geometry, material);
+    parseBaseParams(IMeshParams, mesh);
+    return mesh;
+};
+
+const parseGroup = (params: IGroupParams) => {
+    const group = new THREE.Group();
+    parseBaseParams(params, group);
+    return group;
+};
+
+const parsePointLight = (params: IPointLight) => {
+    const color = new THREE.Color();
+    // 色彩空间
+    // export type ColorSpace = NoColorSpace | SRGBColorSpace | LinearSRGBColorSpace;
+    // export type NoColorSpace = '';
+    // export type SRGBColorSpace = 'srgb';
+    // export type LinearSRGBColorSpace = 'srgb-linear';
+    //glb模型为了亮度恢复 使用srgb格式 所以颜色也使用同样格式 使其颜色模式一致
+    color.setRGB(params.color.r, params.color.g, params.color.b, "srgb-linear");
+
+    const pointLight = new THREE.PointLight(color, params.intensity, params.distance, params.decay);
+    parseBaseParams(params, pointLight);
+    return pointLight;
+};
+
+const parseObject3D = (params: IBaseProps) => {
+    const object = new THREE.Object3D();
+    parseBaseParams(params, object);
+    return object;
+};
+
+const parseChildren = (object3D: THREE.Object3D, children: IBaseProps[]) => {
+    if (!children.length) return;
+    const objectList: THREE.Object3D[] = [];
+    for (const child of children) {
+        const { type } = child;
+        if (type === "Mesh") {
+            objectList.push(parseMesh(child as IMeshParams));
+        } else if (type === "Group") {
+            objectList.push(parseGroup(child));
+        } else if (type === "PointLight") {
+            objectList.push(parsePointLight(child as IPointLight));
+        } else if (type === "Object3D") {
+            objectList.push(parseObject3D(child));
+        } else {
+            throw new Error("出现了未处理的类型:" + type);
+        }
+    }
+
+    object3D.add(...objectList);
+};
+/**
+ * 生成动画
+ */
+const genAnimations = (object3D: THREE.Object3D, sceneAnimations: IGroupParams["sceneAnimations"]) => {
+    if (!sceneAnimations) return;
+    const animations: THREE.AnimationClip[] = [];
+
+    for (const animation of sceneAnimations!) {
+        const clip = new THREE.AnimationClip(animation.name, animation.duration, [], animation.blendMode);
+        //@ts-ignore
+        for (const { name, times, values, type } of animation.tracks) {
+            let nreTrack;
+            if(type){
+                if(type === 'vector') nreTrack = new THREE.VectorKeyframeTrack(name, times as any, values as any);
+                if(type === 'quaternion') nreTrack = new THREE.QuaternionKeyframeTrack(name, times as any, values as any);
+            }else {
+                nreTrack = new THREE.QuaternionKeyframeTrack(name, times as any, values as any);
+            }
+
+            clip.tracks.push(nreTrack);
+        }
+
+        animations.push(clip);
+    }
+
+    object3D.animations = animations;
+};
+
+/**
+ * 解析传入的模型参数生成有效的three.js物体
+ */
+export const parseModel = (params: IGroupParams) => {
+    const model = parseGroup(params);
+    // model.position.x += 10;
+    genAnimations(model, params.sceneAnimations);
+    // console.log("解析完:", model);
+    return model;
+};

+ 73 - 0
src/utils/threejs/type/types.ts

@@ -0,0 +1,73 @@
+
+export interface MonitorArray<T> extends Array<T> {
+    monitoringChanges?: (...items: T[]) => void;
+    constructor: {
+        prototype: {
+            monitoringChanges?: (...items: T[]) => void;
+        };
+    };
+}
+
+/**
+ * Three.js 支持的所有材料类型
+ */
+export type THREEMaterialType =
+    | "ShadowMaterial"
+    | "SpriteMaterial"
+    | "RawShaderMaterial"
+    | "ShaderMaterial"
+    | "PointsMaterial"
+    | "MeshPhysicalMaterial"
+    | "MeshStandardMaterial"
+    | "MeshPhongMaterial"
+    | "MeshToonMaterial"
+    | "MeshNormalMaterial"
+    | "MeshLambertMaterial"
+    | "MeshDepthMaterial"
+    | "MeshDistanceMaterial"
+    | "MeshBasicMaterial"
+    | "MeshMatcapMaterial"
+    | "LineDashedMaterial"
+    | "LineBasicMaterial"
+    | "Material";
+
+export type Vector3Arr = [x: number, y: number, z: number];
+
+export interface IBaseProps {
+    name: string;
+    type: string;
+    matrix: THREE.Mesh["matrix"];
+    position: Vector3Arr;
+    quaternion: [...Vector3Arr, number];
+    rotation: [...Vector3Arr, THREE.Mesh["rotation"]["order"]];
+    scale: Vector3Arr;
+    up: Vector3Arr;
+    userData: THREE.Mesh["userData"];
+    visible: THREE.Mesh["visible"];
+    children: Array<IMeshParams | IGroupParams | IPointLight>;
+    animations: THREE.AnimationClip[];
+    /**
+     * blender 制作的模型动画添载在scene上的animations上 这个参数导出scene上的动画
+     */
+    sceneAnimations?: THREE.AnimationClip[];
+}
+
+/**
+ * 将整个模型解析完发送过去的数据结构 根据这个结构生成模型
+ */
+export interface IMeshParams extends IBaseProps {
+    geometry: THREE.Mesh["geometry"];
+    material: THREE.Mesh["material"];
+}
+export interface IGroupParams extends IBaseProps {}
+
+export interface IPointLight extends IBaseProps {
+    power: number;
+    color: THREE.Color;
+    decay: number;
+    castShadow: boolean;
+    distance: number;
+    frustumCulled: boolean;
+    intensity: number;
+    layers?: any;
+}

+ 0 - 0
src/hooks/core/threejs/useThree.ts → src/utils/threejs/useThree.ts


+ 1 - 1
src/views/demo/threejs/damper.vue

@@ -7,7 +7,7 @@
 
 <script setup lang="ts">
   import { ref, onMounted, onUnmounted } from 'vue';
-  import UseThree from '../../../hooks/core/threejs/useThree';
+  import UseThree from '../../../utils/threejs/useThree';
   import * as THREE from 'three';
   import gsap from 'gsap';
 

+ 1 - 1
src/views/vent/monitorManager/fanLocalMonitor/fanLocal.three.ts

@@ -1,7 +1,7 @@
 import * as THREE from 'three';
 import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 import { animateCamera, getTextCanvas, renderVideo } from '/@/utils/threejs/util';
-import UseThree from '../../../../hooks/core/threejs/useThree';
+import UseThree from '../../../../utils/threejs/useThree';
 import fcFan from './fcfanLocal.three';
 import fmFan from './fmfanLocal.three';
 import Smoke from '/@/views/vent/comment/threejs/Smoke';

+ 1 - 1
src/views/vent/monitorManager/gateMonitor/detail.vue

@@ -7,7 +7,7 @@
 
 <script setup lang="ts">
   import { ref, onMounted, onUnmounted } from 'vue';
-  import UseThree from '../../../../hooks/core/threejs/useThree';
+  import UseThree from '../../../../utils/threejs/useThree';
 
   const loading = ref(false);
   let model;

+ 65 - 57
src/views/vent/monitorManager/gateMonitor/gate.threejs.ts

@@ -3,7 +3,7 @@ import * as THREE from 'three';
 import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
 import { getTextCanvas, renderVideo, transScreenCoord } from '/@/utils/threejs/util';
-import UseThree from '../../../../hooks/core/threejs/useThree';
+import UseThree from '../../../../utils/threejs/useThree';
 import { animateCamera } from '/@/utils/threejs/util';
 import { flyLine } from '/@/views/vent/comment/threejs/FlyLine';
 import { createComposer } from '/@/views/vent/comment/threejs/bloomPass';
@@ -614,63 +614,71 @@ export const mountedThree = (playerVal1, playerVal2) => {
     model.setEnvMap('test1');
     model.renderer.toneMappingExposure = 0.8;
     model.setModel(modelName).then((gltf) => {
-      group = gltf.scene;
-      group.layers.enableAll();
-      if (gltf.animations && gltf.animations.length > 0) {
-        model.mixers = [];
-        model.animations = [];
-        gltf.animations.forEach((animation) => {
-          const mixer = new THREE.AnimationMixer(group);
-          model.mixers.push(mixer);
-          model.animations.push(animation);
-        });
-      }
-      model.scene?.add(group);
-      // model.camera.position.set(-1000, 100, 500);
-      addLight(model.scene);
-      // resetCamera();
-      setModalPosition();
-      startAnimation();
-      // 初始化左右摇摆动画;
-      // startAnimation();
-      initAnimation();
-      model.animate();
-      drawHots();
-      deviceDetailCard();
-      if (model.camera.layers.mask == -1) model.camera.layers.toggle(1);
-      // renderBloomPass = createComposer(model).renderBloomPass;
-
-      // const flyLineMesh = flyLine(
-      //   [
-      //     new THREE.Vector3(-110, 0, 0),
-      //     // new THREE.Vector3(5, 4, 0),
-      //     new THREE.Vector3(120, 0, 0),
-      //   ],
-      //   '/model/hdr/y1.png'
-      // );
-      // group.add(flyLineMesh);
-
-      setTimeout(async () => {
-        player1 = playerVal1;
-        player2 = playerVal2;
-        const videoPlayer1 = document.getElementById('fm-player1')?.getElementsByClassName('vjs-tech')[0];
-        const videoPlayer2 = document.getElementById('fm-player2')?.getElementsByClassName('vjs-tech')[0];
-        if (videoPlayer1) {
-          const mesh = renderVideo(group, videoPlayer1, 'player1');
-          mesh.scale.set(-0.028, 0.0285, 1);
-          mesh.position.set(4.298, 0.02, -0.4);
-          mesh.rotation.y = -Math.PI;
-          group.add(mesh);
-        }
-        if (videoPlayer2) {
-          const mesh = renderVideo(group, videoPlayer2, 'player2');
-          mesh.scale.set(-0.028, 0.0285, 1);
-          mesh.position.set(-4.262, 0.02, -0.4);
-          mesh.rotation.y = -Math.PI;
-          group.add(mesh);
+      if(gltf){
+        console.log('返回的模型内容', gltf);
+        // group = gltf
+        group = gltf.scene;
+        group.layers.enableAll();
+        if (gltf.animations && gltf.animations.length > 0) {
+          model.mixers = [];
+          model.animations = [];
+          gltf.animations.forEach((animation) => {
+            const mixer = new THREE.AnimationMixer(group);
+            model.mixers.push(mixer);
+            model.animations.push(animation);
+          });
         }
-        resolve(model);
-      }, 0);
+        model.scene?.add(group);
+        
+        // resetCamera();
+        setModalPosition();
+        // 初始化左右摇摆动画;
+        // startAnimation();
+        initAnimation();
+        drawHots();
+
+        console.log('加载到渲染完所用时间--->', window['startTime']-new Date().getTime());
+        
+        // deviceDetailCard();
+        // model.camera.position.set(-1000, 100, 500);
+        addLight(model.scene);
+        startAnimation();
+        model.animate();
+        if (model.camera.layers.mask == -1) model.camera.layers.toggle(1);
+        // renderBloomPass = createComposer(model).renderBloomPass;
+
+        // const flyLineMesh = flyLine(
+        //   [
+        //     new THREE.Vector3(-110, 0, 0),
+        //     // new THREE.Vector3(5, 4, 0),
+        //     new THREE.Vector3(120, 0, 0),
+        //   ],
+        //   '/model/hdr/y1.png'
+        // );
+        // group.add(flyLineMesh);
+
+        setTimeout(async () => {
+          player1 = playerVal1;
+          player2 = playerVal2;
+          const videoPlayer1 = document.getElementById('fm-player1')?.getElementsByClassName('vjs-tech')[0];
+          const videoPlayer2 = document.getElementById('fm-player2')?.getElementsByClassName('vjs-tech')[0];
+          if (videoPlayer1) {
+            const mesh = renderVideo(group, videoPlayer1, 'player1');
+            mesh?.scale.set(-0.028, 0.0285, 1);
+            mesh?.position.set(4.298, 0.02, -0.4);
+            mesh.rotation.y = -Math.PI;
+            group.add(mesh);
+          }
+          if (videoPlayer2) {
+            const mesh = renderVideo(group, videoPlayer2, 'player2');
+            mesh?.scale.set(-0.028, 0.0285, 1);
+            mesh?.position.set(-4.262, 0.02, -0.4);
+            mesh.rotation.y = -Math.PI;
+            group.add(mesh);
+          }
+          resolve(model);
+        }, 0);
+      }
     });
   });
 };

+ 1 - 0
src/views/vent/monitorManager/gateMonitor/index.vue

@@ -420,6 +420,7 @@
   const handleOk = (e: MouseEvent) => {
     //
   };
+  
 
   onBeforeMount(() => {
     const sendVal = JSON.stringify({ pagetype: 'normal', devicetype: 'gate', orgcode: '', ids: '', systemID: '' });

+ 1 - 1
src/views/vent/monitorManager/mainFanMonitor/main.threejs.ts

@@ -1,6 +1,6 @@
 import * as THREE from 'three';
 import { animateCamera, getTextCanvas, renderVideo } from '/@/utils/threejs/util';
-import UseThree from '../../../../hooks/core/threejs/useThree';
+import UseThree from '../../../../utils/threejs/useThree';
 import mainWindRect from './mainWind.threejs';
 import gsap from 'gsap';
 // import * as dat from 'dat.gui';

+ 1 - 1
src/views/vent/monitorManager/windowMonitor/window.threejs.ts

@@ -1,5 +1,5 @@
 import * as THREE from 'three';
-import UseThree from '../../../../hooks/core/threejs/useThree';
+import UseThree from '../../../../utils/threejs/useThree';
 import singleWindow from './dandaoFc.threejs';
 import doubleWindow from './shuangdaoFc.threejs';
 import { animateCamera } from '/@/utils/threejs/util';

+ 1 - 1
src/views/vent/monitorManager/windrectMonitor/windrect.threejs.ts

@@ -1,6 +1,6 @@
 import * as THREE from 'three';
 import { animateCamera, getTextCanvas, renderVideo } from '/@/utils/threejs/util';
-import UseThree from '../../../../hooks/core/threejs/useThree';
+import UseThree from '../../../../utils/threejs/useThree';
 import lmWindRect from './longmen.threejs';
 import zdWindRect from './zhedie.threejs';
 import dsWindRect from './duishe.threejs';

+ 5 - 0
src/views/vent/monitorManager/windrectMonitor/zhedie.threejs.ts

@@ -2,6 +2,7 @@ import * as THREE from 'three';
 
 import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
 import gsap from 'gsap';
+import { log } from 'console';
 
 class zdWindRect {
   model;
@@ -230,6 +231,9 @@ class zdWindRect {
     this.animationAction = this.mixers[0].clipAction(this.animations[0]);
     this.animationAction.clampWhenFinished = true;
     this.animationAction.loop = THREE.LoopOnce;
+    debugger
+    console.log('模型动画-------------->', this.animationAction);
+    
   }
 
   /* 点击风窗,风窗全屏 */
@@ -308,6 +312,7 @@ class zdWindRect {
       this.model.setModel(this.modelName).then((gltf) => {
         this.group = gltf.scene;
         if (gltf.animations && gltf.animations.length > 0) {
+          console.log('[ 解析后的模型动画 ] >', gltf.animations)
           gltf.animations.forEach((animation) => {
             const mixer = new THREE.AnimationMixer(gltf.scene);
             this.mixers.push(mixer);