Procházet zdrojové kódy

[Wip 0000] 看板页面对接

houzekong před 10 měsíci
rodič
revize
05a8ff0320

+ 571 - 0
src/hooks/vent/useThree.ts

@@ -0,0 +1,571 @@
+import * as THREE from 'three';
+// 导入轨道控制器
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
+import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+// import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
+import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
+import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
+import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
+import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
+import Stats from 'three/examples/jsm/libs/stats.module.js';
+import { useModelStore } from '/@/store/modules/threejs';
+import TWEEN from 'three/examples/jsm/libs/tween.module.js';
+import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
+import { useGlobSetting } from '/@/hooks/setting';
+import { getList } from '@/views/vent/sys/resources/file.api';
+import { saveModel } from '/@/utils/threejs/util';
+
+const globSetting = useGlobSetting();
+const baseApiUrl = globSetting.domainUrl;
+
+export function useThree(containerID: string, css3dContainerID: string, css2dContainerID: string) {
+
+}
+
+class UseThree {
+
+  constructor(canvasSelector, css3Canvas?, css2Canvas?) {
+    this.canvasContainer = document.querySelector(canvasSelector);
+    //初始化
+    this.init(css3Canvas, css2Canvas);
+    // this.animate();
+    window.addEventListener('resize', this.resizeRenderer.bind(this));
+    // 添加滚动事件,鼠标滚动模型执行动画
+    // window.addEventListener('wheel', this.wheelRenderer.bind(this));
+
+    // this.canvasContainer?.appendChild(gui.domElement);
+  }
+  init(css3Canvas?, css2Canvas?) {
+    // 初始化场景
+    this.initScene();
+    // 初始化环境光
+    // this.initLight();
+    // 初始化相机
+    this.initCamera();
+    //初始化渲染器
+    this.initRenderer();
+    // 初始化控制器
+    this.initControles();
+    if (css3Canvas) {
+      this.initCSS3Renderer(css3Canvas);
+    }
+    if (css2Canvas) {
+      this.initCSS2Renderer(css2Canvas);
+    }
+    // this.setTestPlane();
+    this.rayCaster = new THREE.Raycaster();
+    // this.createStats();
+    // this.removeSawtooth();
+  }
+
+  createStats() {
+    this.stats = Stats();
+    this.stats?.setMode(0);
+    this.stats.domElement.style = 'position: absolute; top: 300px';
+    this.canvasContainer?.appendChild(this.stats.domElement);
+  }
+
+  initScene() {
+    this.scene = new THREE.Scene();
+    // const axesHelper = new THREE.AxesHelper(100);
+    // this.scene?.add(axesHelper);
+    // const size = 1000;
+    // const divisions = 10;
+    // const gridHelper = new THREE.GridHelper(size, divisions);
+    // this.scene?.add(gridHelper);
+  }
+
+  initLight() {
+    // const light = new THREE.AmbientLight(0xffffff, 1);
+    // light.position.set(0, 1000, 1000);
+    // (this.scene as THREE.Scene).add(light);
+  }
+
+  initCamera() {
+    // this.camera = new THREE.PerspectiveCamera(50, this.canvasContainer.clientWidth / this.canvasContainer.clientHeight, 0.0000001, 1000);
+    if (!window['$camera']) {
+      throw new Error('threejs摄像头初始化异常!');
+    } else {
+      this.camera = window['$camera'] as THREE.PerspectiveCamera;
+      this.camera.layers.enableAll();
+      if (this.canvasContainer) this.camera.aspect = this.canvasContainer.clientWidth / this.canvasContainer.clientHeight;
+      this.camera.near = 0.0000001;
+      this.camera.far = 1000;
+    }
+
+    //
+    // const helper = new THREE.CameraHelper(this.camera);
+    // this.scene?.add(helper);
+    // gui.add(this.camera.position, 'x', 0.00001, 10000);
+    // gui.add(this.camera.position, 'y', 0.00001, 10000);
+    // gui.add(this.camera.position, 'z', 0.00001, 10000);
+    // gui.add(this.camera, 'near', 0.01, 1).step(0.01);
+    // gui.add(this.camera, 'far', 10, 100000);
+    // gui.add(this.camera, 'fov', 0, 180);
+  }
+
+  initRenderer() {
+    if (!window['$renderer']) {
+      throw new Error('threejs渲染器初始化异常!');
+    } else {
+      this.renderer = window['$renderer'];
+      if (this.canvasContainer) {
+        this.renderer.toneMappingExposure = 1.0;
+        this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
+        // const gl = this.renderer?.getContext('webgl');
+        // gl && gl.getExtension('WEBGL_lose_context')?.restoreContext();
+        // this.renderer?.forceContextRestore()
+        this.renderer?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+        this.canvasContainer.appendChild(this.renderer.domElement);
+      }
+    }
+  }
+
+  initCSS3Renderer(cssCanvas) {
+    this.CSSCanvasContainer = document.querySelector(cssCanvas);
+    if (this.CSSCanvasContainer) {
+      this.css3dRender = new CSS3DRenderer() as CSS3DRenderer;
+      this.css3dRender.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+      this.CSSCanvasContainer?.appendChild(this.css3dRender.domElement);
+      this.css3dRender.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera);
+      // this.css3dRender.domElement.style.pointerEvents = 'none';
+      // this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.css3dRender?.domElement) as OrbitControls;
+      // this.orbitControls.update();
+    }
+  }
+
+  initCSS2Renderer(cssCanvas) {
+    this.CSSCanvasContainer = document.querySelector(cssCanvas);
+    if (this.CSSCanvasContainer) {
+      this.css2dRender = new CSS2DRenderer();
+      this.css2dRender.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+      this.CSSCanvasContainer?.appendChild(this.css2dRender.domElement);
+      this.css2dRender.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera);
+
+      // this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.css2dRender?.domElement) as OrbitControls;
+      // this.orbitControls.update();
+      // this.css2dRender.domElement.style.pointerEvents = 'none';
+    }
+  }
+
+  initControles() {
+    if (!window['$orbitControls']) {
+      throw new Error('threejs控制器初始化异常!');
+    } else {
+      this.orbitControls = window['$orbitControls'];
+      this.orbitControls.panSpeed = 1;
+      this.orbitControls.rotateSpeed = 1;
+      this.orbitControls.maxPolarAngle = Math.PI;
+      this.orbitControls.minPolarAngle = 0;
+    }
+    // this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement) as OrbitControls;
+    // this.orbitControls.update();
+    // this.orbitControls.minDistance = 1;
+    // this.orbitControls.maxDistance = 100;
+    // this.orbitControls.maxDistance = true;
+  }
+
+  setGLTFModel(modalNames, group = null) {
+    window['startTime'] = new Date().getTime();
+    const modelStore = useModelStore();
+    return new Promise(async (resolve, reject) => {
+      try {
+        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);
+
+        const db = window['CustomDB'];
+        const resolvePromise: Promise<any>[] = [];
+
+        const modalNameArr = Object.prototype.toString.call(modalNames) === '[object Array]' ? modalNames : [modalNames];
+        const len = modalNameArr.length;
+
+        for (let i = 0; i < len; i++) {
+          resolvePromise[i] = new Promise(async (childResolve, reject) => {
+            try {
+              // 解析模型
+              const modalNameStr = modalNameArr[i];
+              const data = modelStore.modelArr.get(modalNameStr) || null;
+              let modalValue;
+              if (!data) {
+                const modalArr = await db.modal.where('modalName').equals(modalNameStr).toArray();
+                if (modalArr.length > 0) {
+                  modalValue = modalArr[0].modalVal;
+                }
+              } else {
+                modalValue = data.modalVal;
+              }
+              if (modalValue) {
+                gltfLoader.parse(
+                  modalValue,
+                  '/model/glft/',
+                  (gltf) => {
+                    debugger;
+                    const object = gltf.scene;
+                    // setModalCenter(object);
+                    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;
+                        }
+                        if (obj.material.map) {
+                          obj.material.map.colorSpace = THREE.SRGBColorSpace;
+                          obj.material.map.flipY = false;
+                          obj.material.map.anisotropy = 1;
+                        }
+                        if (obj.material.emissiveMap) {
+                          obj.material.emissiveMap.colorSpace = THREE.SRGBColorSpace;
+                          obj.material.emissiveMap.flipY = false;
+                        }
+
+                        if (obj.material.map || obj.material.emissiveMap) {
+                          obj.material.needsUpdate = true;
+                        }
+
+                        // if (envMap) {
+                        //   obj.material.envMap = envMap;
+                        //   obj.material.envMapIntensity = 1;
+                        // }
+                        // obj.renderOrder = 1;
+                      }
+                    });
+                    object.animations = gltf.animations;
+                    object.name = modalNameStr;
+                    group?.add(object);
+                    childResolve(object);
+                  },
+                  (err) => {
+                    console.log(err);
+                  }
+                );
+              } else {
+                // 开启线程下载
+                console.log('需要开启线程下载模型资源。。。。。');
+                const result = (await getList({ fileName: modalNameStr })) || [];
+                const file = result['records'][0];
+                if (file && file.path) {
+                  gltfLoader.load(`${baseApiUrl}/sys/common/static/${file.path}`, async (glft) => {
+                    if (glft) {
+                      const object = glft.scene;
+                      object.name = modalNameStr;
+                      if (glft.animations.length > 0) {
+                        object.animations = glft.animations;
+                      }
+                      group?.add(object);
+                      childResolve(object);
+                      const modalArr = await db.modal.where('modalName').equals(modalNameStr).toArray();
+                      if (modalArr.length < 1) {
+                        saveModel(modalNameStr, file.path, file.version);
+                      }
+                    }
+                  });
+                } else {
+                  childResolve(null);
+                }
+              }
+            } catch (error) {
+              console.log(error);
+              reject();
+            }
+          });
+        }
+        Promise.all(resolvePromise).then((objects) => {
+          dracoLoader.dispose();
+          resolve(objects);
+        });
+      } catch (error) {
+        reject('加载模型出错');
+      }
+    });
+  }
+
+  // setFBXModel(modalNames, group = null) {
+  //   window['startTime'] = new Date().getTime();
+  //   const modelStore = useModelStore();
+  //   return new Promise(async (resolve, reject) => {
+  //     try {
+  //       const fbxLoader = new FBXLoader();
+
+  //       fbxLoader.setPath('/model/fbx/');
+
+  //       const db = window['CustomDB'];
+  //       const resolvePromise: Promise<any>[] = [];
+
+  //       let modalNameArr = Object.prototype.toString.call(modalNames) === '[object Array]' ? modalNames : [modalNames];
+  //       let len = modalNameArr.length;
+
+  //       for (let i = 0; i < len; i++) {
+  //         resolvePromise[i] = new Promise(async (childResolve, reject) => {
+  //           try {
+  //             // 解析模型
+  //             let modalValue;
+  //             const modalNameStr = modalNameArr[i];
+  //             let data = modelStore.modelArr.get(modalNameStr) || null;
+  //             if (!data) {
+  //               const modalArr = await db.modal.where('modalName').equals(modalNameStr).toArray();
+  //               if (modalArr.length > 0) modalValue = modalArr[0].modalVal;
+  //             } else {
+  //               modalValue = data.modalVal;
+  //             }
+  //             if (modalValue) {
+  //               const object = fbxLoader.parse(modalValue, '/model/fbx/');
+  //               // const object = fbx.scene;
+  //               // setModalCenter(object);
+  //               if (object) {
+  //                 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;
+  //                     }
+  //                     if (obj.material.map) {
+  //                       obj.material.map.encoding = THREE.sRGBEncoding;
+  //                       obj.material.map.flipY = false;
+  //                       obj.material.map.anisotropy = 1;
+  //                     }
+  //                     if (obj.material.emissiveMap) {
+  //                       obj.material.emissiveMap.encoding = THREE.sRGBEncoding;
+  //                       obj.material.emissiveMap.flipY = false;
+  //                     }
+
+  //                     if (obj.material.map || obj.material.emissiveMap) {
+  //                       obj.material.needsUpdate = true;
+  //                     }
+
+  //                     // if (envMap) {
+  //                     //   obj.material.envMap = envMap;
+  //                     //   obj.material.envMapIntensity = 1;
+  //                     // }
+  //                     // obj.renderOrder = 1;
+  //                   }
+  //                 });
+  //                 object.animations = object.animations;
+  //                 object.name = modalNameStr;
+  //                 group?.add(object);
+  //                 childResolve(object);
+  //               }
+  //             }
+  //           } catch (error) {
+  //             console.log(error);
+  //             reject();
+  //           }
+  //         });
+  //       }
+
+  //       Promise.all(resolvePromise).then((objects) => {
+  //         resolve(objects);
+  //       });
+  //     } catch (error) {
+  //       reject('加载模型出错');
+  //     }
+  //   });
+  // }
+
+  setTestPlane() {
+    const plane = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshPhongMaterial({ color: 0x999999, specular: 0x101010 }));
+    plane.rotation.x = -Math.PI / 2;
+    plane.position.y = 0.03;
+    plane.receiveShadow = true;
+    this.scene?.add(plane);
+  }
+
+  removeSawtooth() {
+    this.composer = new EffectComposer(this.renderer as THREE.WebGLRenderer);
+    const FXAAShaderPass = new ShaderPass(FXAAShader);
+    FXAAShaderPass.uniforms['resolution'].value.set(1 / this.canvasContainer.clientWidth, 1 / this.canvasContainer.clientHeight);
+    FXAAShaderPass.renderToScreen = true;
+    this.composer.addPass(FXAAShaderPass);
+  }
+
+  /* 场景环境背景 */
+  setEnvMap(hdr) {
+    if (!this.scene) return;
+    // (this.scene as THREE.Scene).environment
+    new RGBELoader().setPath('/model/hdr/').load(hdr + '.hdr', (texture) => {
+      texture.colorSpace = THREE.SRGBColorSpace;
+      texture.mapping = THREE.EquirectangularReflectionMapping;
+      if (this.scene) (this.scene as THREE.Scene).environment = texture;
+      texture.dispose();
+    });
+  }
+
+  render() {
+    this.startAnimation();
+    this.orbitControls?.update();
+    this.camera?.updateMatrixWorld();
+    // this.composer?.render();
+    this.css3dRender?.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera);
+    this.css2dRender?.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera);
+    this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
+  }
+
+  timeRender() {
+    //设置为可渲染状态
+    this.renderEnabled = true;
+    //清除上次的延迟器
+    if (this.timeOut) {
+      clearTimeout(this.timeOut);
+    }
+    this.timeOut = setTimeout(() => {
+      this.renderEnabled = false;
+    }, 3000);
+  }
+
+  /* 漫游 */
+  startMY() {}
+  /* 初始动画 */
+  startAnimation() {}
+
+  renderAnimationScene() {}
+
+  animate() {
+    if (this.isRender) {
+      this.animationId = requestAnimationFrame(this.animate.bind(this));
+      const T = this.clock?.getDelta() || 0;
+      this.timeS = this.timeS + T;
+      if (this.timeS > this.renderT) {
+        this.renderAnimationScene();
+
+        if (this.renderEnabled) {
+          this.render();
+        }
+        this.stats?.update();
+        this.timeS = 0;
+      }
+      // this.stats?.update();
+      // // TWEEN.update();
+      // // this.renderAnimationScene();
+      // if (this.renderEnabled) {
+      //   this.render();
+      // }
+    }
+  }
+
+  resizeRenderer() {
+    // 更新相机比例
+    if (this.camera) {
+      (this.camera as THREE.PerspectiveCamera).aspect = this.canvasContainer.clientWidth / this.canvasContainer.clientHeight;
+      // 刷新相机矩阵
+      this.camera?.updateProjectionMatrix();
+    }
+    // 设置场景尺寸
+    this.renderer?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+    this.css3dRender?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+    this.css2dRender?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+  }
+
+  wheelRenderer(e: WheelEvent) {
+    const timeScale = e.deltaY > 0 ? 1 : -1;
+    this.animationAction?.setEffectiveTimeScale(timeScale);
+    (this.animationAction as THREE.AnimationAction).paused = false;
+    this.animationAction?.play();
+    if (this.timeoutId) {
+      clearTimeout(this.timeoutId);
+    }
+    this.timeoutId = setTimeout(() => {
+      this.animationAction?.halt(0.5);
+    }, 500);
+  }
+
+  clearMesh(item) {
+    item.geometry?.dispose();
+    if (item.material) {
+      const material = item.material;
+
+      for (const key of Object.keys(material)) {
+        const value = material[key];
+        // if (value && typeof value === 'object' && 'minFilter' in value) {
+        //   // this.textureMap.set(value.uuid, value);
+        //   value.dispose();
+        // }
+        if (value && typeof value === 'object' && value['dispose'] && typeof value['dispose'] === 'function') {
+          value.dispose();
+        }
+      }
+      material.dispose();
+    }
+    if (item.texture) {
+      item.texture.dispose();
+    }
+  }
+  clearGroup(group) {
+    const removeObj = (obj) => {
+      if (obj && obj?.children && obj?.children.length > 0) {
+        for (let i = obj?.children.length - 1; i >= 0; i--) {
+          const item = obj?.children[i];
+          if (item && item.children && item.children.length > 0) {
+            removeObj(item);
+            item?.clear();
+          } else {
+            if (item) {
+              if (item.parent) item.parent.remove(item);
+              this.clearMesh(item);
+              item.clear();
+            }
+          }
+        }
+      }
+    };
+    removeObj(group);
+  }
+
+  clearScene() {
+    this.clearGroup(this.scene);
+    // console.log('场景纹理数量----------->', this.textureMap.size);
+    this.textureMap.forEach((texture) => {
+      texture?.dispose();
+    });
+    this.textureMap.clear();
+  }
+
+  destroy() {
+    TWEEN.getAll().forEach((item) => {
+      item.stop();
+    });
+    TWEEN.removeAll();
+    this.isRender = false;
+    cancelAnimationFrame(this.animationId);
+    this.clearScene();
+    window.removeEventListener('resize', this.resizeRenderer);
+    // this.orbitControls?.dispose();
+    // this.scene?.environment?.dispose();
+    this.scene?.clear();
+    this.renderer?.dispose();
+    this.renderer?.getRenderTarget()?.dispose();
+
+    if (this.renderer && this.canvasContainer) this.canvasContainer.innerHTML = '';
+    if (this.CSSCanvasContainer && this.css3dRender) this.CSSCanvasContainer.innerHTML = '';
+
+    // if (this.renderer) this.renderer.domElement = null;
+    this.renderer?.clear();
+
+    if (this.css3dRender) this.css3dRender.domElement = null;
+    if (this.css2dRender) this.css2dRender.domElement = null;
+
+    if (this.canvasContainer) this.canvasContainer.innerHTML = '';
+    if (this.CSSCanvasContainer) this.CSSCanvasContainer.innerHTML = '';
+
+    this.camera = null;
+    this.orbitControls = null;
+    this.renderer = null;
+    this.stats = null;
+    this.scene = null;
+    this.css3dRender = null;
+    this.css2dRender = null;
+    THREE.Cache.clear();
+    console.log('场景销毁后信息----------->', window['$renderer']?.info, this.scene);
+  }
+}
+
+export default UseThree;

+ 117 - 0
src/views/vent/home/billboard/billboard.api.ts

@@ -0,0 +1,117 @@
+import { useUserStore } from '/@/store/modules/user';
+import { defHttp } from '/@/utils/http/axios';
+
+const store = useUserStore();
+
+enum Api {
+  getSummary = '/ventanaly-device/monitor/groupCompany/getEachMineInfo',
+}
+
+/**
+ * 获取看板的详细数据
+ * @param params
+ */
+export const getSummary = () =>
+  Promise.resolve({
+    dustInfo: {
+      //矿井粉尘风险信息
+      totalNum: 8,
+      dustTypeList: [
+        {
+          deviceType: 'dusting_auto', //设备类型编码
+          warnLevel: 0, //状态(0或者101:低风险、102:一般风险、103:较大风险、104:重大风险、201:报警、1001:网络断开)
+          num: 8, //监测数量
+          typeName: '粉尘传感器', //设备类型名称
+        },
+      ],
+      dustWarnLevel: 0, //矿井粉尘风险性等级
+    },
+    fileServerInfo: {
+      //文件共享中心
+      totalNum: 29, //文档总数
+      approvalNum: 0, //待审批文档
+    },
+    fireInfo: {
+      //矿井火灾风险数据
+      tempMax: 0, //矿井温度传感器最高温度
+      fireWarnLevel: 0, //矿井火灾风险等级
+      coSensorInfo: 0, //矿井CO传感器报警等级
+      bundletubeInfo: 0, //矿井束管监测报警等级
+      smokeSensorInfo: 0, //矿井烟雾传感器报警等级
+      fiberInfo: 0, //矿井光纤测温系统报警等级
+      tempSensorInfo: 0, //矿井温度传感器报警等级
+    },
+    gasInfo: {
+      //瓦斯风险信息
+      gasWarnLevel: 0, //瓦斯风险等级
+      gasTypeList: [
+        {
+          warnLevel: 0, //当前状态
+          num: 2, //监测数量
+          typeName: '瓦斯抽采泵站', //设备类型名称
+        },
+        {
+          warnLevel: 0,
+          num: 1,
+          typeName: '瓦斯巡检',
+        },
+        {
+          warnLevel: 0,
+          num: 2,
+          typeName: '瓦斯巡检',
+        },
+      ],
+    },
+    ventInfo: {
+      //通风系统信息
+      totallength: 1562.35, //矿井巷道总长度
+      zonghuifeng: '25881.74', //总回风
+      fanMainList: [
+        {
+          sqzlfb: '31.0%-29.0%-40.0%', //三区阻力分布
+          strname: '2号回风立井', //风机名称
+          strinstallpos: '2号回风立井', //安装位置
+          Fan1m3: '12465.84', //1号风机风量
+          Fan2m3: '12493.72', //2号风机风量
+          FanFreqHz: '225', //风机频率
+        },
+        {
+          sqzlfb: '42.0%-37.0%-21.0%',
+          strname: '1号回风斜井',
+          strinstallpos: '1号回风斜井',
+          Fan1m3: '13419.83',
+          Fan2m3: '13415.9',
+          FanFreqHz: '225',
+        },
+      ],
+      xufengliang: 15065, //总需风量
+      zongjinfeng: '24987.25', //总进风
+    },
+  });
+// defHttp.post({
+//   url: Api.getSummary,
+//   params: {
+//     userName: store.userInfo?.username,
+//   },
+// });
+
+/**
+ * 获取矿区列表
+ * @param params
+ */
+export const getList = () =>
+  Promise.resolve([
+    { title: '布尔台', address: 'http://10.246.95.4:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '上湾', address: 'http://10.246.167.205:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '大柳塔', address: 'http://10.248.135.10:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '活鸡兔', address: 'http://10.248.135.121:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '锦界', address: 'http://10.248.151.42:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '哈拉沟', address: 'http://10.248.223.12:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '石圪台', address: 'http://10.246.191.13:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '柳塔', address: 'http://10.246.87.121:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '补连塔', address: 'http://10.246.175.16:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '寸草塔矿', address: 'http://10.246.23.171:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '寸草塔二矿', address: 'http://10.246.63.5:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '乌兰木伦', address: 'http://10.246.183.35:8092/micro-vent-3dModal/dashboard/analysis' },
+    { title: '榆家梁', address: 'http://10.248.143.211:8092/micro-vent-3dModal/dashboard/analysis' },
+  ]);

+ 61 - 55
src/views/vent/home/billboard/billboard.data.ts

@@ -6,63 +6,35 @@ import GasStatus from './components/GasStatus.vue';
 import leftImg from '/@/assets/images/files/homes/file.svg';
 import rightImg from '/@/assets/images/files/homes/sp.svg';
 
-// 各个煤矿看板的配置
-export const BILLBOARDS = [
-  {
-    title: 'A矿',
-    type: 'dust',
-  },
-  {
-    title: 'B矿',
-    type: 'fire',
-  },
-  {
-    title: 'C矿',
-    type: 'file',
-  },
-  {
-    title: 'D矿',
-    type: 'ventilate',
-  },
-  {
-    title: 'E矿',
-    type: 'gas',
-  },
-  {
-    title: 'F矿',
-    type: 'dust',
-  },
-  {
-    title: 'G矿',
-    type: 'fire',
-  },
+// 基础的表格列配置,针对普通设备
+export const GAS_STATUS_COLUMN = [
   {
-    title: 'H矿',
-    type: 'file',
+    name: '设备类型',
+    prop: 'typeName',
   },
   {
-    title: 'I矿',
-    type: 'ventilate',
+    name: '监测数量',
+    prop: 'num',
   },
   {
-    title: 'J矿',
-    type: 'gas',
+    name: '当前状态',
+    prop: 'warnLevel',
   },
 ];
 
-// 基础的表格列配置,针对普通设备
-export const BASIC_COLUMN = [
+// 粉尘状态模块表格列配置
+export const DUST_STATUS_COLUMN = [
   {
     name: '设备类型',
-    prop: 'a',
+    prop: 'deviceType',
   },
   {
     name: '监测数量',
-    prop: 'b',
+    prop: 'num',
   },
   {
     name: '当前状态',
-    prop: 'c',
+    prop: 'warnLevelStr',
   },
 ];
 
@@ -71,75 +43,109 @@ export const FIRE_STATUS_LIST = [
   {
     icon: 'warning-optical-fiber',
     label: '矿井光纤测温系统报警',
-    prop: 'a',
+    prop: 'fiberInfo',
   },
   {
     icon: 'warning-tubes',
     label: '矿井束管监测系统报警',
-    prop: 'b',
+    prop: 'bundletubeInfo',
   },
   {
     icon: 'warning-smoke-2',
     label: '矿井烟雾传感器报警',
-    prop: 'c',
+    prop: 'smokeSensorInfo',
   },
   {
     icon: 'warning-CO-2',
     label: '矿井CO传感器报警',
-    prop: 'd',
+    prop: 'coSensorInfo',
   },
   {
     icon: 'warning-temp',
     label: '矿井温度传感器报警',
-    prop: 'e',
+    prop: 'tempSensorInfo',
   },
   {
     icon: 'warning-max-temp',
     label: '矿井温度传感器最高值',
-    prop: 'f',
+    prop: 'tempMax',
   },
 ];
 
 // 文件总览相关的内容配置项
 export const FILE_OVERVIEW_CONFIG = [
-  { src: leftImg, text: '文档总数', num: 233, id: 'file_cfg_001' },
-  { src: rightImg, text: '待审批数', num: 50, id: 'file_cfg_002' },
+  { src: leftImg, text: '文档总数', prop: 'totalNum', id: 'file_cfg_001' },
+  { src: rightImg, text: '待审批数', prop: 'approvalNum', id: 'file_cfg_002' },
 ];
 
 // 通风状态监测相关的内容配置项
 export const VENTILATION_STATUS_HEADER_CONFIG = [
   {
     label: '总进风量(m³/min)',
-    prop: 'a',
+    prop: 'zongjinfeng',
     type: 'blue-to-left',
   },
   {
     label: '总回风量(m³/min)',
-    prop: 'b',
+    prop: 'zonghuifeng',
     type: 'green-to-right',
   },
   {
     label: '总需风量(m³/min)',
-    prop: 'c',
+    prop: 'xufengliang',
     type: 'green-to-left',
   },
   {
     label: '通风巷道总长度',
-    prop: 'd',
+    prop: 'totallength',
     type: 'blue-to-right',
   },
 ];
 
+// 通风状态监测(树形节点详情)相关的内容配置项
+export const VENTILATION_STATUS_TREE_CONFIG = {
+  prefix: '',
+  prop: 'strname',
+  suffix: '',
+  children: [
+    {
+      prefix: '名称:',
+      prop: 'strinstallpos',
+      suffix: '',
+    },
+    {
+      prefix: '1号风机风量:',
+      prop: 'Fan1m3',
+      suffix: '(m³/min)',
+    },
+    {
+      prefix: '2号风机风量:',
+      prop: 'Fan2m3',
+      suffix: '(m³/min)',
+    },
+    {
+      prefix: '频率:',
+      prop: 'FanFreqHz',
+      suffix: 'Hz',
+    },
+    {
+      prefix: '三区阻力分布:',
+      prop: 'sqzlfb',
+      suffix: '',
+    },
+  ],
+};
+
 // 瓦斯状态监测相关的内容配置项
 export const GAS_STATUS_HEADER_CONFIG = [
   {
     label: '瓦斯风险等级',
-    prop: 'a',
+    prop: 'gasWarnLevel',
     type: 'to-bottom-right',
   },
   {
     label: '瓦斯鉴定等级',
-    prop: 'b',
+    prop: 'gasJudgeLevel',
     type: 'to-top-right',
   },
 ];

+ 7 - 3
src/views/vent/home/billboard/components/CommonTable.vue

@@ -2,11 +2,11 @@
   <div class="table">
     <div class="table__content">
       <div class="table__content_label">
-        <div class="label-t" v-for="(item, index) in columns" :key="`svvhbcth-${index}`">{{ item.name }}</div>
+        <div class="label-t" v-for="(item, index) in columns" :key="`svvhbcth-${index}`" :style="{ flexBasis }">{{ item.name }}</div>
       </div>
       <div class="table__content_list">
         <div class="table__content_list_row" v-for="(item, index) in data" :key="`svvhbct-${index}`">
-          <div v-for="(t, i) in columns" :key="`svvhbctr-${i}`">
+          <div v-for="(t, i) in columns" :key="`svvhbctr-${i}`" :style="{ flexBasis }">
             <slot :name="t.prop" :scope="item">
               <span>{{ get(item, t.prop) }}</span>
             </slot>
@@ -17,7 +17,7 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { defineProps } from 'vue';
+  import { computed, defineProps } from 'vue';
   import _ from 'lodash-es';
 
   let props = withDefaults(
@@ -34,6 +34,10 @@
     }
   );
 
+  const flexBasis = computed(() => {
+    return Math.fround(100 / props.columns.length) + '%';
+  });
+
   function get(o, p) {
     return _.get(o, p, props.defaultValue);
   }

+ 30 - 11
src/views/vent/home/billboard/components/DustStatus.vue

@@ -1,22 +1,41 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
   <CommonTitle class="mb-10px" label="矿井粉尘风险性等级" :value="risk" />
-  <CommonTable :columns="BASIC_COLUMN" :data="data" />
+  <CommonTable :columns="DUST_STATUS_COLUMN" :data="data" />
 </template>
 <script lang="ts" setup>
   import CommonTable from './CommonTable.vue';
-  // import CommonTitle from './CommonTitle.vue';
-  import { BASIC_COLUMN } from '../billboard.data';
-  import { ref } from 'vue';
+  import CommonTitle from './CommonTitle.vue';
+  import { DUST_STATUS_COLUMN } from '../billboard.data';
+  import { onMounted, ref, shallowRef } from 'vue';
+  import { getSummary } from '../billboard.api';
   // import mapComponent from './3Dmap/index.vue';
 
   const risk = ref('低风险');
-  const data = ref([
-    { a: 'XXXXX', b: 2, c: '正常' },
-    { a: 'XXXXX', b: 21, c: '正常' },
-    { a: 'XXXXX', b: 22, c: '正常' },
-    { a: 'XXXXX', b: 12, c: '正常' },
-    { a: 'XXXXX', b: 42, c: '异常' },
-  ]);
+  const data = shallowRef<any[]>([]);
+
+  function fetchData() {
+    const trans = {
+      0: '低风险',
+      101: '低风险',
+      102: '一般风险',
+      103: '较大风险',
+      104: '重大风险',
+      201: '报警',
+      1001: '网络断开',
+    };
+    getSummary().then((r) => {
+      data.value = r.dustInfo.dustTypeList.map((e) => {
+        return {
+          ...e,
+          warnLevelStr: trans[e.warnLevel],
+        };
+      });
+    });
+  }
+
+  onMounted(() => {
+    fetchData();
+  });
 </script>
 <style lang="less" scoped></style>

+ 16 - 2
src/views/vent/home/billboard/components/FileOverview.vue

@@ -3,16 +3,29 @@
     <div class="box" v-for="item in tabs" :key="item.id" @click="$emit('click', item)">
       <div class="img"> <img :src="item.src" alt="" /> </div>
       <div class="text">{{ item.text }}</div>
-      <div class="num">{{ item.num }}</div>
+      <div class="num">{{ data[item.prop] }}</div>
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { onMounted, ref, shallowRef } from 'vue';
   import { FILE_OVERVIEW_CONFIG } from '../billboard.data';
+  import { getSummary } from '../billboard.api';
 
   defineEmits(['click']);
 
+  const data = shallowRef<any>({});
+
+  function fetchData() {
+    getSummary().then((r) => {
+      data.value = r.fileServerInfo;
+    });
+  }
+
+  onMounted(() => {
+    fetchData();
+  });
+
   let tabs = ref(FILE_OVERVIEW_CONFIG);
 </script>
 
@@ -96,3 +109,4 @@
     }
   }
 </style>
+onMounted, import { getSummary } from '../billboard.api';onMounted, import { getSummary } from '../billboard.api';

+ 12 - 8
src/views/vent/home/billboard/components/FireStatus.vue

@@ -12,20 +12,24 @@
   />
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { onMounted, ref, shallowRef } from 'vue';
   import CommonTitle from './CommonTitle.vue';
   import ListItem from './ListItem.vue';
   import { FIRE_STATUS_LIST } from '../billboard.data';
+  import { getSummary } from '../billboard.api';
 
   const risk = ref('低风险');
 
-  const data = ref({
-    a: '正常',
-    b: '正常',
-    c: '正常',
-    d: '正常',
-    e: '正常',
-    f: '22℃',
+  const data = shallowRef<any>({});
+
+  function fetchData() {
+    getSummary().then((r) => {
+      data.value = r.fireInfo;
+    });
+  }
+
+  onMounted(() => {
+    fetchData();
   });
 </script>
 <style lang="less" scoped></style>

+ 19 - 11
src/views/vent/home/billboard/components/GasStatus.vue

@@ -5,24 +5,32 @@
       <LargeBoard :label="item.label" :value="headerData[item.prop]" :type="item.type" />
     </Col>
   </Row>
-  <CommonTable class="mt-10px" :columns="BASIC_COLUMN" :data="data" />
+  <CommonTable class="mt-10px" :columns="GAS_STATUS_COLUMN" :data="data" />
 </template>
 <script lang="ts" setup>
   import { Row, Col } from 'ant-design-vue';
-  import { GAS_STATUS_HEADER_CONFIG, BASIC_COLUMN } from '../billboard.data';
+  import { GAS_STATUS_HEADER_CONFIG, GAS_STATUS_COLUMN } from '../billboard.data';
   import LargeBoard from './LargeBoard.vue';
-  import { ref } from 'vue';
+  import { onMounted, shallowRef } from 'vue';
   import CommonTable from './CommonTable.vue';
+  import { getSummary } from '../billboard.api';
   // import mapComponent from './components/3Dmap/index.vue';
 
-  const headerData = ref({
-    a: '低风险',
-    b: '低风险',
+  const headerData = shallowRef({});
+  const data = shallowRef<any[]>([]);
+
+  function fetchData() {
+    getSummary().then((r) => {
+      headerData.value = {
+        gasWarnLevel: r.gasInfo.gasWarnLevel,
+        gasJudgeLevel: 0,
+      };
+      data.value = r.gasInfo.gasTypeList;
+    });
+  }
+
+  onMounted(() => {
+    fetchData();
   });
-  const data = ref([
-    { a: '瓦斯抽采泵站', b: 2, c: '正常' },
-    { a: '瓦斯监测传感器', b: 12, c: '正常' },
-    { a: '瓦斯巡检机器人', b: 23, c: '异常' },
-  ]);
 </script>
 <style lang="less" scoped></style>

+ 38 - 55
src/views/vent/home/billboard/components/VentilationStatus.vue

@@ -14,65 +14,48 @@
   import { Row, Col } from 'ant-design-vue';
   import { BasicTree } from '/@/components/Tree';
   import type { TreeProps } from 'ant-design-vue';
-  import { VENTILATION_STATUS_HEADER_CONFIG } from '../billboard.data';
+  import { VENTILATION_STATUS_HEADER_CONFIG, VENTILATION_STATUS_TREE_CONFIG } from '../billboard.data';
   import MiniBoard from './MiniBoard.vue';
-  import { ref } from 'vue';
-  // import CommonTitle from './CommonTitle.vue';
+  import { onMounted, ref, shallowRef } from 'vue';
+  import CommonTitle from './CommonTitle.vue';
+  import { getSummary } from '../billboard.api';
   // import mapComponent from './components/3Dmap/index.vue';
 
-  const data = ref({
-    a: '13000',
-    b: '13000',
-    c: '13000',
-    d: '98765',
-  });
+  const data = shallowRef({});
   const ventilatorCount = ref('0');
-  const treeData: TreeProps['treeData'] = [
-    {
-      title: '1号回风斜井回风系统',
-      key: '0-0',
-      children: [
-        {
-          title: '名称:松定霍洛主通风机',
-          key: '0-0-0',
-        },
-        {
-          title: '风量:6000(m³/min)',
-          key: '0-0-1',
-        },
-        {
-          title: '频率:30Hz',
-          key: '0-0-2',
-        },
-        {
-          title: '三区阻力分布:30%-40%-30%',
-          key: '0-0-3',
-        },
-      ],
-    },
-    {
-      title: '2号回风立井回风系统',
-      key: '1-0',
-      children: [
-        {
-          title: '名称:松定霍洛主通风机',
-          key: '1-0-0',
-        },
-        {
-          title: '风量:6000(m³/min)',
-          key: '1-0-1',
-        },
-        {
-          title: '频率:30Hz',
-          key: '1-0-2',
-        },
-        {
-          title: '三区阻力分布:30%-40%-30%',
-          key: '1-0-3',
-        },
-      ],
-    },
-  ];
+  const treeData = shallowRef<TreeProps['treeData']>([]);
+
+  function fetchData() {
+    getSummary().then((r) => {
+      ventilatorCount.value = r.ventInfo.fanMainList.length.toString();
+      data.value = r.ventInfo;
+      treeData.value = r.ventInfo.fanMainList.map((e, i) => {
+        const { prefix, suffix, prop, children } = VENTILATION_STATUS_TREE_CONFIG;
+        return {
+          title: `${prefix}${e[prop]}${suffix}`,
+          key: i.toString(),
+          children: children.map((child, j) => {
+            // 配置里如果指定了多个prop则进行合并
+            if (Array.isArray(child.prop)) {
+              return {
+                title: `${child.prefix}${child.prop.map((p) => `${e[p]}${suffix}`).join('-')}`,
+                key: `${i}-${j}`,
+              };
+            } else {
+              return {
+                title: `${child.prefix}${e[child.prop]}${child.suffix}`,
+                key: `${i}-${j}`,
+              };
+            }
+          }),
+        };
+      });
+    });
+  }
+
+  onMounted(() => {
+    fetchData();
+  });
 </script>
 <style lang="less" scoped>
   .ventilate-status-card {

+ 22 - 9
src/views/vent/home/billboard/index.vue

@@ -24,28 +24,40 @@
 </template>
 <script lang="ts" setup>
   import { computed, onMounted, ref } from 'vue';
-  // import BaseCard from './components/BaseCard.vue';
-  // import ArrowButton from './components/ArrowButton.vue';
-  import { BILLBOARDS, COMPONENTS_MAP } from './billboard.data';
+  import BaseCard from './components/BaseCard.vue';
+  import ArrowButton from './components/ArrowButton.vue';
+  import { COMPONENTS_MAP } from './billboard.data';
   import { useRoute } from 'vue-router';
+  import { getList } from './billboard.api';
   // import mapComponent from './components/3Dmap/index.vue';
 
   const route = useRoute();
 
-  const mainTitle = computed(() => '煤炭集团');
+  const mainTitle = '煤炭集团';
 
-  // 一页最多支持几个看板项
-  const billboards = ref(BILLBOARDS); // for test
+  // 看板相关的基础配置
+  const billboards = ref<{ title: string }[]>([]);
+
+  function fetchBillboards() {
+    getList().then((r) => {
+      billboards.value = r;
+    });
+  }
+
+  // 看板分页相关的配置
   const pageSize = 8;
-  // 当前页
   const currentPage = ref(1);
-  const totalPage = Math.ceil(billboards.value.length / pageSize) + 1;
+  const totalPage = computed(() => {
+    return Math.ceil(billboards.value.length / pageSize) + 1;
+  });
+
   // 能真正在页面上展示的看板
   const shownBillboards = computed(() => {
     return billboards.value.slice((currentPage.value - 1) * pageSize, currentPage.value * pageSize);
   });
+
   function changeCurrentPage(pagecount: number) {
-    currentPage.value = Math.max((currentPage.value + pagecount) % totalPage, 1);
+    currentPage.value = Math.max((currentPage.value + pagecount) % totalPage.value, 1);
   }
 
   const billboardType = ref('dust');
@@ -56,6 +68,7 @@
       billboardType.value = route.query.type as string;
       showBtn.value = false;
     }
+    fetchBillboards();
   });
 </script>
 <style lang="less" scoped>