Browse Source

Merge branch 'master' of http://182.92.126.35:3000/hrx/mky-vent-base

lxh 4 days ago
parent
commit
f1d1a511c1

+ 2 - 1
src/views/vent/deviceManager/configurationTable/index.vue

@@ -27,7 +27,7 @@
 </template>
 
 <script lang="ts" setup>
-  //ts语法
+  // 本页面是用于配置可配置首页的管理页面,更详细的文档见 /views/vent/home/configurable
   import { ref, provide, reactive, toRaw } from 'vue';
   import { BasicTable } from '/@/components/Table';
   import { BasicForm, useForm } from '/@/components/Form';
@@ -43,6 +43,7 @@
   import CodeEditor from '/@/components/CodeEditor/src/CodeEditor.vue';
   import { ModulePresetMap } from './options';
   import _ from 'lodash';
+  /** @ts-ignore-next-line */
   import helpContext from './types?raw';
 
   const formData = reactive<any>({});

+ 62 - 8
src/views/vent/home/configurable/README.md

@@ -1,15 +1,69 @@
-可配置首页文档
+### 可配置首页文档及开发指引
 
-1. 目的
+可配置首页是一个能让用户在线上进行灵活变更的首页,它可以在配置正确的布局、模块后展示它们,提供了更为低成本的维护方式。
 
-这个首页是可以通过前端进行配置的首页,首页各个模块需要展示的字段可以通过表格进行配置。例如:左上角的主通风机模块默认展示风筒长度、风筒直径、风筒位置这三个字段,那么你可以通过配置表调整为其他的字段。
+## 配置详解
 
-模块可以配置大小、外框样式、位置、标题、模块头、内容布局、布局细节、内容细节等内容。具体的配置字段见 vent/deviceManager/configurationTable/types.ts
+具体的配置字段及注释请/views/vent/deviceManager/configurationTable/types.ts
 
-2. 使用
+## 目录结构
 
-首页组件 ./index.vue(保德的包括 ./fireBD.vue ./dustBD.vue 两个) 配置表格 vent/deviceManager/configurationTable/index.vue
+/configurable
 
-将上述两个组件配置到菜单中之后:
+--xxx.vue 各个首页的具体文件,每个首页需要一个文件来承载它们的子模块,放置特殊的装饰以及实现特定的功能。例如 dustBD 即为保德煤矿专用的粉尘首页。
 
-点开配置表格,对各个设备进行配置,配置好的点表/字段/样式等将用于在首页展示。
+--mock.ts 提供一些模拟数据
+
+--configurable.data.ts 该文件及类似命名的文件用来提供测试用的首页具体配置,测试通过后可以依据此配置上线。
+
+--configurable.api.ts 获取数据的 api 都在这里维护。
+
+--/hooks 包含了可供各个首页、子模块核心组件复用的 setup 内容,包括初始化配置、初始化选项等。
+
+--/components
+
+----/detail 包含了各个细分模块的具体组件,例如列表、表格、告示板、画廊、时间线等等组件,它们根据自己的 prop 实现自己的内容。
+
+----/enhanced 包含了新版子模块的各样组件,例如底部特化、边缘特化等,子模块内由上述的细分模块组成。
+
+----/original 包含了原版子模块的组件,例如底部特化、边缘特化等,子模块内由上述的细分模块组成。
+
+----/preset 包含了预设类细分模块的具体组件,预设类指的是较为复杂,难以可配置化的组件,通常仅需提供数据,即首页配置内指向的数据。
+
+----ModuleXXX.vue 各个子模块的具体组件,子模块通常负责边框、Header、Content的布局,样式在这里维护。特殊情况下更为底层的实现也可以在这里。
+
+----content.vue 内容组件,核心组件之一,这个组件负责将首页配置转换为可以供细分模块使用的 prop,并且引用所有细分模块,负责它们的布局。
+
+----header.vue 头部组件,核心组件之一,这个组件负责将首页配置的 Header 项实现为下拉框,并向外提供了获取所选内容的方法,供其他与 content 类同的组件使用。
+
+## 开发流程
+
+从开发一个新的首页开始,需要的步骤可能如下:
+
+1、新建 xxx.vue 作为该首页的具体文件,引入 hooks 里的初始化方法,并对配置进行初始化,配置存在自己的作用域(即 deviceType),初始化数据。
+
+2、根据作用域向项目的数据字典里添加新的项目,code 在配置表处定义(目前为 configurable_homepage),内容为上述的 deviceType。
+
+3、参照已有的首页文件,将需要支持的子模块类型加入其中,添加必要的装饰并提供子模块需要的 prop。
+
+4、新建 configurable.data.ts 或类同的文件,根据先前的文件配置,写出一份适用于本首页的配置来。
+
+5、测试完毕后提交代码、打包项目,部署后在线上的首页配置页面将写好的配置以JSON写入即可。
+
+## 菜单事项
+
+完整的首页需要对应的配置页面来配合,首页名称各不相同,但是配置页只有一个:vent/deviceManager/configurationTable/index
+
+## 开发新的细分模块/子模块
+
+如果需要开发新的细分模块,那么基本流程如下:
+
+1、在配置类型声明处添加子项,可参考 chart、table、list、board 项的结构。
+
+2、开发细分模块,将其放置在 /components/detail 下。
+
+3、根据细分模块的 prop,在 content.vue 下参考已有实现将新的细分模块标识添加进处理结构中,添加能把配置转换为 prop 的逻辑,同时添加该细分模块至相关布局代码中。
+
+4、测试修改并提交。
+
+如果是子模块,那么参考 ModuleEnhanced.vue 即可。

+ 8 - 8
src/views/vent/home/configurable/configurable.data.bd.ts

@@ -110,7 +110,7 @@ export const testConfigBDDust: Config[] = [
     },
   },
   {
-    deviceType: '',
+    deviceType: 'atomizing',
     moduleName: '喷雾设备信息',
     pageType: 'BD_dust',
     moduleData: {
@@ -147,25 +147,25 @@ export const testConfigBDDust: Config[] = [
       table: [
         {
           type: 'A',
-          readFrom: '',
           columns: [
             {
               name: '设备名称',
-              prop: 'name',
+              prop: 'strinstallpos',
             },
             {
-              name: '连接状态',
-              prop: 'st',
+              name: '排污状态',
+              prop: 'airStatus_str',
             },
             {
               name: '应用场景',
-              prop: 'sc',
+              prop: 'appScenes_str',
             },
             {
               name: '喷雾状态',
-              prop: 'pw',
+              prop: 'stateSpray_str',
             },
           ],
+          readFrom: 'atomizingArray',
         },
       ],
       list: [],
@@ -628,7 +628,7 @@ export const testConfigBDFire: Config[] = [
         readFrom: '',
         selector: {
           show: true,
-          value: '${strinstallpos}',
+          value: '${systemname}',
         },
         slot: {
           show: false,

+ 13 - 0
src/views/vent/home/configurable/dustBD.vue

@@ -55,6 +55,14 @@
     />
     <div style="width: 1000px; height: 570px; position: absolute; left: calc(50% - 500px); top: 60px">
       <VentModal />
+      <a-button
+        type="primary"
+        shape="circle"
+        style="width: 34px; height: 34px; position: absolute; right: 5px; bottom: 5px; z-index: 5"
+        @click="redirectTo('/micro-vent-3dModal/dashboard/analysis?type=model3D&deviceType=model3D')"
+      >
+        <EyeFilled />
+      </a-button>
     </div>
   </div>
 </template>
@@ -66,6 +74,7 @@
   import ModuleBD from './components/ModuleBD.vue';
   import VentModal from '/@/components/vent/micro/ventModal.vue';
   import { getDisHome } from './configurable.api';
+  import { EyeFilled } from '@ant-design/icons-vue';
   // import { testConfigBDDust } from './configurable.data.bd';
   // import { getToken } from '/@/utils/auth';
 
@@ -93,6 +102,10 @@
   onUnmounted(() => {
     clearInterval(interval);
   });
+
+  function redirectTo(url) {
+    window.open(url);
+  }
 </script>
 <style lang="less" scoped>
   @font-face {

+ 9 - 0
src/views/vent/home/configurable/fireBD.vue

@@ -65,6 +65,14 @@
     />
     <div style="width: 1000px; height: 550px; position: absolute; left: calc(50% - 500px); top: 60px">
       <VentModal />
+      <a-button
+        type="primary"
+        shape="circle"
+        style="width: 34px; height: 34px; position: absolute; right: 5px; bottom: 5px; z-index: 5"
+        @click="redirectTo('/micro-vent-3dModal/dashboard/analysis?type=model3D&deviceType=model3D')"
+      >
+        <EyeFilled />
+      </a-button>
     </div>
   </div>
 </template>
@@ -77,6 +85,7 @@
   import ModuleBDDual from './components/ModuleBDDual.vue';
   import VentModal from '/@/components/vent/micro/ventModal.vue';
   import { getDisHome } from './configurable.api';
+  import { EyeFilled } from '@ant-design/icons-vue';
   // import { testConfigBDFire } from './configurable.data.bd';
   // import { getToken } from '/@/utils/auth';
 

+ 9 - 0
src/views/vent/home/configurable/index.vue

@@ -65,6 +65,14 @@
     />
     <div style="width: 1000px; height: 550px; position: absolute; left: calc(50% - 500px); top: 60px">
       <VentModal />
+      <a-button
+        type="primary"
+        shape="circle"
+        style="width: 34px; height: 34px; position: absolute; right: 5px; bottom: 5px"
+        @click="redirectTo('/micro-vent-3dModal/dashboard/analysis?type=model3D&deviceType=model3D')"
+      >
+        <EyeFilled />
+      </a-button>
     </div>
   </div>
 </template>
@@ -78,6 +86,7 @@
   import VentModal from '/@/components/vent/micro/ventModal.vue';
   import { getDisHome } from './configurable.api';
   import { testConfigBDFire } from './configurable.data.bd';
+  import { EyeFilled } from '@ant-design/icons-vue';
   // import { getToken } from '/@/utils/auth';
 
   const cfgs = computed(() =>

+ 170 - 0
src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.fireF.ts

@@ -0,0 +1,170 @@
+import * as THREE from 'three';
+import { useAppStore } from '/@/store/modules/app';
+
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class FireDoorF {
+  modelName = 'fireDoorF';
+  model; //
+  group;
+  isLRAnimation = true; // 是否开启左右摇摆动画
+  direction = 1; // 摇摆方向
+  animationTimer: NodeJS.Timeout | null = null; // 摇摆开启定时器
+  player1;
+  player2;
+  deviceDetailCSS3D;
+  playerStartClickTime1 = new Date().getTime();
+  playerStartClickTime2 = new Date().getTime();
+
+  fmClock = new THREE.Clock();
+  mixers: THREE.AnimationMixer | undefined;
+  appStore = useAppStore();
+  damperOpenMesh;
+  damperClosedMesh;
+
+  clipActionArr = {
+    door: null as unknown as THREE.AnimationAction,
+  };
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
+    directionalLight.position.set(344, 690, 344);
+    this.group?.add(directionalLight);
+    directionalLight.target = this.group as THREE.Object3D;
+
+    const pointLight2 = new THREE.PointLight(0xffeeee, 1, 300);
+    pointLight2.position.set(-4, 10, 1.8);
+    pointLight2.shadow.bias = 0.05;
+    this.group?.add(pointLight2);
+
+    const pointLight3 = new THREE.PointLight(0xffeeee, 1, 200);
+    pointLight3.position.set(-0.5, -0.5, 0.75);
+    pointLight3.shadow.bias = 0.05;
+    this.group?.add(pointLight3);
+  }
+  resetCamera() {
+    this.model.camera.far = 274;
+    this.model.orbitControls?.update();
+    this.model.camera.updateProjectionMatrix();
+  }
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-20, 20, 9);
+  }
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.mixers && this.fmClock.running) {
+      this.mixers.update(2);
+    }
+  }
+
+  /* 点击风门 */
+  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
+    console.log('摄像头控制信息', this.model?.orbitControls, this.model?.camera);
+  }
+
+  mouseUpModel() {}
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    debugger;
+    const fireGroup = this.group.children[0]?.getObjectByName('Fire-doorf');
+    if (fireGroup) {
+      const tracks = fireGroup.animations[0].tracks;
+      debugger;
+      this.mixers = new THREE.AnimationMixer(fireGroup.children[0]);
+
+      const door = new THREE.AnimationClip('door', 100, tracks);
+      const frontClipAction = this.mixers.clipAction(door, fireGroup);
+      frontClipAction.clampWhenFinished = true;
+      frontClipAction.loop = THREE.LoopOnce;
+      this.clipActionArr.door = frontClipAction;
+    }
+  }
+
+  // 播放动画
+  play(handlerState, timeScale = 0.01) {
+    debugger;
+    let handler = () => {};
+    if (this.clipActionArr.door) {
+      switch (handlerState) {
+        case 1: // 打开门
+          handler = () => {
+            this.clipActionArr.door.paused = true;
+            this.clipActionArr.door.reset();
+            this.clipActionArr.door.time = 1.7;
+            this.clipActionArr.door.timeScale = -timeScale;
+            // this.clipActionArr.door.clampWhenFinished = true;
+            this.clipActionArr.door.play();
+            this.fmClock.start();
+
+            // 显示打开前门文字
+            if (this.damperOpenMesh) this.damperOpenMesh.visible = true;
+          };
+          break;
+        case 2: // 关闭门
+          handler = () => {
+            this.clipActionArr.door.paused = true;
+            this.clipActionArr.door.reset(); //
+            this.clipActionArr.door.time = 0;
+            this.clipActionArr.door.timeScale = timeScale;
+            // this.clipActionArr.door.clampWhenFinished = true;
+            this.clipActionArr.door.play();
+            this.fmClock.start();
+
+            if (this.damperOpenMesh) this.damperOpenMesh.visible = false;
+          };
+          break;
+        default:
+      }
+      handler();
+    }
+  }
+
+  mountedThree() {
+    this.group = new THREE.Object3D();
+    this.group.name = this.modelName;
+
+    return new Promise((resolve) => {
+      if (!this.model) {
+        resolve(null);
+      }
+      this.model.setGLTFModel(['Fire-doorf'], this.group).then(() => {
+        this.setModalPosition();
+        console.log(this.group);
+        // 初始化左右摇摆动画;
+        this.initAnimation();
+      });
+    });
+  }
+
+  destroy() {
+    if (this.model) {
+      if (this.mixers) {
+        this.mixers.uncacheClip(this.clipActionArr.door.getClip());
+        this.mixers.uncacheAction(this.clipActionArr.door.getClip(), this.group);
+        this.mixers.uncacheRoot(this.group);
+
+        if (this.model.animations[0]) this.model.animations[0].tracks = [];
+      }
+      this.model.clearGroup(this.group);
+      this.clipActionArr.door = undefined;
+
+      this.mixers = undefined;
+
+      // document.getElementById('damper3D').parentElement.remove(document.getElementById('damper3D'))
+    }
+  }
+}
+export default FireDoorF;