Browse Source

[Feat 0000]联动配置模块开发

bobo04052021@163.com 6 days ago
parent
commit
ad27c2bb24
24 changed files with 1691 additions and 198 deletions
  1. 20 15
      LongList.vue
  2. 1 2
      package.json
  3. 8 12
      pnpm-lock.yaml
  4. BIN
      src/assets/images/home-container/configurable/hsq/4-2.png
  5. BIN
      src/assets/images/home-container/configurable/hsq/4-3.png
  6. 9 0
      src/assets/images/home-container/configurable/hsq/4-cf.svg
  7. 9 0
      src/assets/images/home-container/configurable/hsq/4-jy.svg
  8. 9 0
      src/assets/images/home-container/configurable/hsq/4-qy.svg
  9. 14 0
      src/assets/images/home-container/configurable/hsq/4-total.svg
  10. BIN
      src/assets/images/home-container/configurable/hsq/bjcf.png
  11. BIN
      src/assets/images/home-container/configurable/hsq/czgz.png
  12. BIN
      src/assets/images/home-container/configurable/hsq/jlrz.png
  13. BIN
      src/assets/images/home-container/configurable/hsq/tjpd.png
  14. BIN
      src/assets/images/home-container/configurable/hsq/zxdz.png
  15. 78 78
      src/views/vent/home/configurable/components/ModuleCommonHsq.vue
  16. 121 65
      src/views/vent/home/configurable/components/content.vue
  17. 244 0
      src/views/vent/home/configurable/components/preset/linkLog.vue
  18. 504 0
      src/views/vent/home/configurable/components/preset/linkRuleEdit.vue
  19. 152 0
      src/views/vent/home/configurable/components/preset/ruleNum.vue
  20. 192 0
      src/views/vent/home/configurable/components/preset/ruleTable.vue
  21. 216 11
      src/views/vent/home/configurable/configurable.data.ts
  22. 13 14
      src/views/vent/home/configurable/gasInjection.vue
  23. 100 0
      src/views/vent/home/configurable/linkConfiguration.vue
  24. 1 1
      tsconfig.json

+ 20 - 15
src/views/vent/home/configurable/components/preset/LongList.vue → LongList.vue

@@ -2,7 +2,7 @@
   <div class="long-list">
     <div class="list-title">
       <span>设备列表</span>
-      <span style="margin-left: 5px;">{{ count }}</span>
+      <span style="margin-left: 5px">{{ count }}</span>
     </div>
     <div class="list-content">
       <div class="list-search">
@@ -10,28 +10,32 @@
       </div>
       <div class="content-container">
         <div class="content-nav">
-          <div :class="activeIndex == index ? 'nav-item-active' : 'nav-item'" v-for="(item, index) in navList"
-            :key="index" @click="changeNav(item, index)">{{ item.label }}</div>
+          <div
+            :class="activeIndex == index ? 'nav-item-active' : 'nav-item'"
+            v-for="(item, index) in navList"
+            :key="index"
+            @click="changeNav(item, index)"
+            >{{ item.label }}</div
+          >
         </div>
         <div class="content-box">
-          <basicDevice v-for="(item,index) in 10" :key="index"></basicDevice>
+          <basicDevice v-for="(item, index) in 10" :key="index"></basicDevice>
         </div>
       </div>
     </div>
-
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
-import basicDevice from '../basicDevice.vue'
+import { ref } from 'vue';
+import basicDevice from './src/views/vent/home/configurable/components/basicDevice.vue';
 
 //设备列表数量
-let count = ref(0)
+let count = ref(0);
 //搜索条件
-let searchData = ref('')
+let searchData = ref('');
 //当前激活nav选项
-let activeIndex = ref(0)
+let activeIndex = ref(0);
 let navList = ref<any[]>([
   { label: '全部', value: '0' },
   { label: 'xxx', value: '1' },
@@ -39,11 +43,11 @@ let navList = ref<any[]>([
   { label: 'xxx', value: '3' },
   { label: 'xxx', value: '4' },
   { label: 'xxx', value: '5' },
-])
+]);
 
 //nav选项切换
 function changeNav(item, index) {
-  activeIndex.value = index
+  activeIndex.value = index;
 }
 </script>
 
@@ -51,7 +55,8 @@ function changeNav(item, index) {
 @import '/@/design/theme.less';
 
 @{theme-deepblue} {
-  .long-list {}
+  .long-list {
+  }
 }
 
 .long-list {
@@ -118,7 +123,7 @@ function changeNav(item, index) {
     background-color: #1d73a8;
     border-bottom: 2px solid #36c7ff;
   }
-  .content-box{
+  .content-box {
     height: calc(100% - 42px);
     overflow-y: auto;
   }
@@ -135,7 +140,7 @@ function changeNav(item, index) {
   .zxm-input-sm {
     padding: 0px 20px;
   }
-  ::-webkit-scrollbar{
+  ::-webkit-scrollbar {
     display: none;
   }
 }

+ 1 - 2
package.json

@@ -218,6 +218,5 @@
         "three/examples/jsm/postprocessing/UnrealBloomPass.js"
       ]
     }
-  },
-  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
+  }
 }

+ 8 - 12
pnpm-lock.yaml

@@ -87,8 +87,8 @@ importers:
         specifier: ^7.8.5
         version: 7.9.0
       dayjs:
-        specifier: ^1.11.9
-        version: 1.11.19
+        specifier: ^1.11.20
+        version: 1.11.21
       dexie:
         specifier: ^3.2.2
         version: 3.2.7
@@ -144,7 +144,7 @@ importers:
         specifier: ^1.1.0
         version: 1.1.0
       moment:
-        specifier: ^2.29.4
+        specifier: ^2.30.1
         version: 2.30.1
       nprogress:
         specifier: ^0.2.0
@@ -1926,28 +1926,24 @@ packages:
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
-    libc: [glibc]
 
   '@swc/core-linux-arm64-musl@1.15.2':
     resolution: {integrity: sha512-5av6VYZZeneiYIodwzGMlnyVakpuYZryGzFIbgu1XP8wVylZxduEzup4eP8atiMDFmIm+s4wn8GySJmYqeJC0A==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
-    libc: [musl]
 
   '@swc/core-linux-x64-gnu@1.15.2':
     resolution: {integrity: sha512-1nO/UfdCLuT/uE/7oB3EZgTeZDCIa6nL72cFEpdegnqpJVNDI6Qb8U4g/4lfVPkmHq2lvxQ0L+n+JdgaZLhrRA==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
-    libc: [glibc]
 
   '@swc/core-linux-x64-musl@1.15.2':
     resolution: {integrity: sha512-Ksfrb0Tx310kr+TLiUOvB/I80lyZ3lSOp6cM18zmNRT/92NB4mW8oX2Jo7K4eVEI2JWyaQUAFubDSha2Q+439A==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
-    libc: [musl]
 
   '@swc/core-win32-arm64-msvc@1.15.2':
     resolution: {integrity: sha512-IzUb5RlMUY0r1A9IuJrQ7Tbts1wWb73/zXVXT8VhewbHGoNlBKE0qUhKMED6Tv4wDF+pmbtUJmKXDthytAvLmg==}
@@ -3568,8 +3564,8 @@ packages:
   dateformat@3.0.3:
     resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
 
-  dayjs@1.11.19:
-    resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
+  dayjs@1.11.21:
+    resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
 
   de-indent@1.0.2:
     resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@@ -9814,7 +9810,7 @@ snapshots:
       codemirror: 5.65.20
       core-js: 3.46.0
       crypto-js: 4.2.0
-      dayjs: 1.11.19
+      dayjs: 1.11.21
       echarts: 5.6.0
       echarts-gl: 2.0.9(echarts@5.6.0)
       echarts-liquidfill: 3.1.0(echarts@5.6.0)
@@ -10703,7 +10699,7 @@ snapshots:
       '@simonwep/pickr': 1.8.2
       array-tree-filter: 2.1.0
       async-validator: 4.2.5
-      dayjs: 1.11.19
+      dayjs: 1.11.21
       dom-align: 1.12.4
       dom-scroll-into-view: 2.0.1
       lodash: 4.17.21
@@ -11807,7 +11803,7 @@ snapshots:
 
   dateformat@3.0.3: {}
 
-  dayjs@1.11.19: {}
+  dayjs@1.11.21: {}
 
   de-indent@1.0.2: {}
 

BIN
src/assets/images/home-container/configurable/hsq/4-2.png


BIN
src/assets/images/home-container/configurable/hsq/4-3.png


+ 9 - 0
src/assets/images/home-container/configurable/hsq/4-cf.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14.464" height="23.677" viewBox="0 0 14.464 23.677">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#2cc1ff"/>
+      <stop offset="1" stop-color="#ccedff"/>
+    </linearGradient>
+  </defs>
+  <path id="路径_58007" data-name="路径 58007" d="M213.448,10.386V.2l-3.7,6.3,0,.006L207,11.178h6.114V21.539h.05l6.552-11.153Z" transform="translate(-206.126 1.639)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#linear-gradient)"/>
+</svg>

+ 9 - 0
src/assets/images/home-container/configurable/hsq/4-jy.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="19.111" height="19.111" viewBox="0 0 19.111 19.111">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#2cfff1"/>
+      <stop offset="1" stop-color="#ccedff"/>
+    </linearGradient>
+  </defs>
+  <path id="路径_58006" data-name="路径 58006" d="M9.056,0A9.056,9.056,0,1,1,0,9.056,9.056,9.056,0,0,1,9.056,0ZM14.23,7.762H3.881v2.587H14.23V7.762Z" transform="translate(0.5 0.5)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#linear-gradient)"/>
+</svg>

+ 9 - 0
src/assets/images/home-container/configurable/hsq/4-qy.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="19.114" height="19.114" viewBox="0 0 19.114 19.114">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#2cfff1"/>
+      <stop offset="1" stop-color="#ccedff"/>
+    </linearGradient>
+  </defs>
+  <path id="路径_58005" data-name="路径 58005" d="M17.057,26.114a9.057,9.057,0,1,1,9.057-9.057A9.057,9.057,0,0,1,17.057,26.114Zm5.032-13.083L16.05,19.07l-3.018-3.02L11.522,17.5l4.529,4.591,7.579-7.611Z" transform="translate(-7.5 -7.5)" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1" fill="url(#linear-gradient)"/>
+</svg>

+ 14 - 0
src/assets/images/home-container/configurable/hsq/4-total.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20.264" height="19.885" viewBox="0 0 20.264 19.885">
+  <defs>
+    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#2cc1ff"/>
+      <stop offset="1" stop-color="#ccedff"/>
+    </linearGradient>
+  </defs>
+  <g id="组_16864" data-name="组 16864" transform="translate(-13.092 -27.144)">
+    <path id="路径_58001" data-name="路径 58001" d="M212.072,260.015v-4.3c0-.1-.312-.173-.7-.173s-.7.078-.7.173v4.3c0,.1.312.173.7.173S212.072,260.111,212.072,260.015Zm.647,3.162,3.787,2.044c.083.045.3-.193.482-.531s.264-.651.18-.7l-3.787-2.044c-.084-.045-.3.194-.483.532S212.634,263.132,212.718,263.177Zm-6.915,1.589,3.909-1.8c.086-.04.026-.354-.135-.7s-.363-.6-.449-.562l-3.909,1.8c-.087.039-.026.355.135.7S205.717,264.805,205.8,264.766Z" transform="translate(-188.15 -223.713)" fill="url(#linear-gradient)"/>
+    <path id="路径_58002" data-name="路径 58002" d="M24.285,27.354a2.781,2.781,0,1,0,1.708,2.839A2.78,2.78,0,0,0,24.285,27.354Zm-.589,3.7a1.211,1.211,0,1,1-.456-2.326,1.2,1.2,0,0,1,1.195,1.195,1.213,1.213,0,0,1-.739,1.131ZM16.935,38.8A2.782,2.782,0,1,0,18.6,41.9a2.779,2.779,0,0,0-1.669-3.1Zm-.59,3.7a1.21,1.21,0,1,1-.455-2.325,1.2,1.2,0,0,1,1.194,1.194,1.211,1.211,0,0,1-.739,1.131Zm15.29-3.369a2.782,2.782,0,1,0,1.669,3.1,2.781,2.781,0,0,0-1.669-3.1Zm-.59,3.7a1.211,1.211,0,1,1-.454-2.326,1.2,1.2,0,0,1,1.194,1.2,1.212,1.212,0,0,1-.74,1.131Z" fill="url(#linear-gradient)"/>
+    <path id="路径_58003" data-name="路径 58003" d="M88.274,141A8.91,8.91,0,0,0,81.4,130.023l-.479,1.545a7.312,7.312,0,0,1,5.833,8.925l1.522.507ZM72.259,138.8a7.362,7.362,0,0,1,5.847-7.194l.056-1.63a8.908,8.908,0,0,0-7.269,10.835l1.564-.342a7.325,7.325,0,0,1-.2-1.668Zm13.323,4.266a7.327,7.327,0,0,1-11.744.272l-1.064,1.233a8.935,8.935,0,0,0,14.208-.7l-1.4-.8Z" transform="translate(-56.384 -100.719)" fill="url(#linear-gradient)"/>
+    <path id="路径_58004" data-name="路径 58004" d="M375.442,424.845a2.781,2.781,0,1,0,1.7,2.928A2.78,2.78,0,0,0,375.442,424.845Zm-.589,3.7a1.211,1.211,0,1,1-.456-2.325,1.2,1.2,0,0,1,1.195,1.195A1.211,1.211,0,0,1,374.853,428.548Z" transform="translate(-351.157 -389.341)" fill="url(#linear-gradient)"/>
+  </g>
+</svg>

BIN
src/assets/images/home-container/configurable/hsq/bjcf.png


BIN
src/assets/images/home-container/configurable/hsq/czgz.png


BIN
src/assets/images/home-container/configurable/hsq/jlrz.png


BIN
src/assets/images/home-container/configurable/hsq/tjpd.png


BIN
src/assets/images/home-container/configurable/hsq/zxdz.png


+ 78 - 78
src/views/vent/home/configurable/components/ModuleCommonHsq.vue

@@ -23,102 +23,102 @@
   </Transition>
 </template>
 <script lang="ts" setup>
-  import Header from './header.vue';
-  import Content from './content.vue';
-  // import ModuleLeft from './original/moduleLeft.vue';
-  // import ModuleBottom from './original/moduleBottom.vue';
-  import { computed, ref } from 'vue';
-  import ventBox1 from '/@/components/vent/ventBoxHsq.vue';
-  import { openWindow } from '/@/utils';
-  import { getFormattedText } from '../hooks/helper.js';
-  // import { ModuleProps } from '../types';
+import Header from './header.vue';
+import Content from './content.vue';
+// import ModuleLeft from './original/moduleLeft.vue';
+// import ModuleBottom from './original/moduleBottom.vue';
+import { computed, ref } from 'vue';
+import ventBox1 from '/@/components/vent/ventBoxHsq.vue';
+import { openWindow } from '/@/utils';
+import { getFormattedText } from '../hooks/helper.js';
+// import { ModuleProps } from '../types';
 
-  const props = defineProps<{
-    /** 配置的详细模块信息 */
-    moduleData: any;
-    /** 配置的详细样式信息 */
-    showStyle: any;
-    /** 该模块配置中的设备标识符 */
-    deviceType: string;
-    /** api返回的数据 */
-    data: any;
-    moduleName: string;
-    visible: boolean;
-    chartData?: any;
-  }>();
-  defineEmits(['close', 'click']);
+const props = defineProps<{
+  /** 配置的详细模块信息 */
+  moduleData: any;
+  /** 配置的详细样式信息 */
+  showStyle: any;
+  /** 该模块配置中的设备标识符 */
+  deviceType: string;
+  /** api返回的数据 */
+  data: any;
+  moduleName: string;
+  visible: boolean;
+  chartData?: any;
+}>();
+defineEmits(['close', 'click']);
 
-  const { header } = props.moduleData;
-  const selectedData = ref();
+const { header } = props.moduleData;
+const selectedData = ref();
 
-  const style = computed(() => {
-    const size = props.showStyle.size;
-    const position = props.showStyle.position;
-    return size + position + 'position: absolute; pointer-events: auto; z-index: 1';
-  });
+const style = computed(() => {
+  const size = props.showStyle.size;
+  const position = props.showStyle.position;
+  return size + position + 'position: absolute; pointer-events: auto; z-index: 1';
+});
 
-  const capitalizedPosition = computed(() => {
-    return props.showStyle.position.includes('left') ? 'Left' : 'Right';
-  });
+const capitalizedPosition = computed(() => {
+  return props.showStyle.position.includes('left') ? 'Left' : 'Right';
+});
 
-  // 根据配置里的定位判断应该使用哪个class
-  function getModuleClass({ size, position }) {
-    const [_, width] = size.match(/width:([0-9]+)px/) || [];
-    if (position.includes('bottom') || parseInt(width) > 800) {
-      return 'module-common module-common-longer';
-    }
-    return 'module-common';
+// 根据配置里的定位判断应该使用哪个class
+function getModuleClass({ size, position }) {
+  const [_, width] = size.match(/width:([0-9]+)px/) || [];
+  if (position.includes('bottom') || parseInt(width) > 800) {
+    return 'module-common module-common-longer';
   }
+  return 'module-common';
+}
 
-  function redirectTo() {
-    const { to } = props.moduleData;
-    if (!to) return;
-    openWindow(getFormattedText(selectedData.value, to));
-  }
+function redirectTo() {
+  const { to } = props.moduleData;
+  if (!to) return;
+  openWindow(getFormattedText(selectedData.value, to));
+}
 </script>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
+@import '/@/design/theme.less';
 
-  .module-common .box1-center {
-    height: calc(100% - 48px);
-  }
+.module-common .box1-center {
+  height: calc(100% - 48px);
+}
 
-  :deep(.box1-center) {
-    height: calc(100% - 48px);
-  }
-  :deep(.box1-center > .box-container) {
-    height: 100%;
-    padding: 0 !important;
-    width: 100% !important;
-  }
-  @{theme-green} {
-    .module-common-longer {
-      :deep(.box1-top) {
-        --image-box1-top: url('/@/assets/images/themify/green/vent/border/box-top.png');
-      }
-      :deep(.box1-bottom) {
-        --image-box1-bottom: url('/@/assets/images/themify/green/vent/border/box-bottom.png');
-      }
+:deep(.box1-center) {
+  height: calc(100% - 48px);
+}
+:deep(.box1-center > .box-container) {
+  height: 100%;
+  padding: 0 !important;
+  width: 100% !important;
+}
+@{theme-green} {
+  .module-common-longer {
+    :deep(.box1-top) {
+      --image-box1-top: url('/@/assets/images/themify/green/vent/border/box-top.png');
     }
-  }
-  @{theme-deepblue} {
-    .module-common-longer {
-      :deep(.box1-top) {
-        --image-box1-top: url('/@/assets/images/themify/deepblue/vent/border/box2-top-long.png');
-      }
-      :deep(.box1-bottom) {
-        --image-box1-bottom: none;
-      }
+    :deep(.box1-bottom) {
+      --image-box1-bottom: url('/@/assets/images/themify/green/vent/border/box-bottom.png');
     }
   }
+}
+@{theme-deepblue} {
   .module-common-longer {
     :deep(.box1-top) {
-      --image-box1-top: url('/@/assets/images/vent/box-top-bg.png');
-      background-image: var(--image-box1-top);
+      --image-box1-top: url('/@/assets/images/themify/deepblue/vent/border/box2-top-long.png');
     }
     :deep(.box1-bottom) {
-      --image-box1-bottom: url('/@/assets/images/vent/box-bottom-bg.png');
-      background-image: var(--image-box1-bottom);
+      --image-box1-bottom: none;
     }
   }
+}
+.module-common-longer {
+  :deep(.box1-top) {
+    --image-box1-top: url('/@/assets/images/vent/box-top-bg.png');
+    background-image: var(--image-box1-top);
+  }
+  :deep(.box1-bottom) {
+    --image-box1-bottom: url('/@/assets/images/vent/box-bottom-bg.png');
+    background-image: var(--image-box1-bottom);
+  }
+}
 </style>

+ 121 - 65
src/views/vent/home/configurable/components/content.vue

@@ -4,40 +4,75 @@
   <div class="content">
     <!-- 背景 -->
     <img v-if="background.show && background.type === 'image'" class="content__background" :src="background.link" />
-    <video v-if="background.show && background.type === 'video' && background.isBoard" class="content__background"
-      width="100%" autoplay loop muted disablepictureinpicture playsinline>
+    <video
+      v-if="background.show && background.type === 'video' && background.isBoard"
+      class="content__background"
+      width="100%"
+      autoplay
+      loop
+      muted
+      disablepictureinpicture
+      playsinline
+    >
       <source :src="background.link" />
       Not Supportted Link Or Browser
     </video>
-    <video v-if="background.show && background.type === 'video' && !background.isBoard" class="content__background_1"
-      width="100%" autoplay loop muted disablepictureinpicture playsinline>
+    <video
+      v-if="background.show && background.type === 'video' && !background.isBoard"
+      class="content__background_1"
+      width="100%"
+      autoplay
+      loop
+      muted
+      disablepictureinpicture
+      playsinline
+    >
       <source :src="background.link" />
       Not Supportted Link Or Browser
     </video>
     <div class="flex w-full h-full" :style="{ flexDirection: layout.direction }">
-      <div v-for="config in layoutConfig" :key="config.name"
-        :style="{ flexBasis: config.basis, overflow: config.overflow ? 'auto' : 'hidden' }">
+      <div v-for="config in layoutConfig" :key="config.name" :style="{ flexBasis: config.basis, overflow: config.overflow ? 'auto' : 'hidden' }">
         <!-- 告示板部分 -->
         <template v-if="config.name === 'board'">
-          <div v-if="config.pageType == 'vent_New'" style="padding-top: 11%"
-            class="content__module content__module1 flex flex-justify-around flex-items-center flex-wrap">
-            <MiniBoard v-for="item in config.items" :key="item.prop" :label="item.label" :value="item.value"
-              :type="config.type" :layout="config.layout" />
+          <div
+            v-if="config.pageType == 'vent_New'"
+            style="padding-top: 11%"
+            class="content__module content__module1 flex flex-justify-around flex-items-center flex-wrap"
+          >
+            <MiniBoard
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :type="config.type"
+              :layout="config.layout"
+            />
           </div>
-          <div v-else-if="config.pageType == 'New_fire'"
-            class="content__module flex flex-justify-around flex-items-center flex-wrap">
-            <MiniBoardNew v-for="item in config.items" :key="item.prop" :label="item.label" :value="item.value"
-              :type="config.type" :layout="config.layout" />
+          <div v-else-if="config.pageType == 'New_fire'" class="content__module flex flex-justify-around flex-items-center flex-wrap">
+            <MiniBoardNew
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :type="config.type"
+              :layout="config.layout"
+            />
           </div>
           <div v-else class="content__module flex flex-justify-around flex-items-center flex-wrap">
-            <MiniBoard v-for="item in config.items" :key="item.prop" :label="item.label" :value="item.value"
-              :unit="item.unit" :type="config.type" :layout="config.layout" />
+            <MiniBoard
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :unit="item.unit"
+              :type="config.type"
+              :layout="config.layout"
+            />
           </div>
         </template>
         <!-- 图表部分,这部分通常需要填充,有告示板、Header等内容需要填充父级 -->
         <template v-if="config.name === 'chart'">
-          <CustomChart v-if="config.pageType == 'New_dust'" class="content__module_dust" :chart-config="config.config"
-            :chart-data="config.data" />
+          <CustomChart v-if="config.pageType == 'New_dust'" class="content__module_dust" :chart-config="config.config" :chart-data="config.data" />
           <CustomChart v-else class="content__module" :chart-config="config.config" :chart-data="config.data" />
         </template>
         <!-- 通常列表部分 -->
@@ -61,8 +96,7 @@
         </template>
         <!-- 复杂列表部分 -->
         <template v-if="config.name === 'gallery_list'">
-          <GalleryList class="content__module" :type="config.type" :list-config="config.items"
-            :gallery-config="config.galleryItems" />
+          <GalleryList class="content__module" :type="config.type" :list-config="config.items" :gallery-config="config.galleryItems" />
         </template>
         <!-- 复杂列表部分 -->
         <template v-if="config.name === 'complex_list'">
@@ -70,22 +104,28 @@
         </template>
         <!-- 表格部分,这部分通常是占一整个模块的 -->
         <template v-if="config.name === 'table'">
-          <CustomTable class="content__module text-center overflow-auto" :type="config.type" :columns="config.columns"
-            :auto-scroll="config.autoScroll" :data="config.data" />
+          <CustomTable
+            class="content__module text-center overflow-auto"
+            :type="config.type"
+            :columns="config.columns"
+            :auto-scroll="config.autoScroll"
+            :data="config.data"
+          />
         </template>
         <template v-if="config.name === 'tabs'">
-          <CustomTabs class="content__module" :type="config.type" :tab-config="config.items"
-            :overflow="config.overflow" />
+          <CustomTabs class="content__module" :type="config.type" :tab-config="config.items" :overflow="config.overflow" />
         </template>
         <template v-if="config.name === 'blast_delta'">
-          <BlastDelta v-if="config.pageType === 'New_fire'" class="content__moduleFire" :pos-monitor="config.data"
-            :canvasSize="{ width: 250, height: 200 }" />
-          <BlastDelta v-else class="content__module" :pos-monitor="config.data"
-            :canvasSize="{ width: 250, height: 200 }" />
+          <BlastDelta
+            v-if="config.pageType === 'New_fire'"
+            class="content__moduleFire"
+            :pos-monitor="config.data"
+            :canvasSize="{ width: 250, height: 200 }"
+          />
+          <BlastDelta v-else class="content__module" :pos-monitor="config.data" :canvasSize="{ width: 250, height: 200 }" />
         </template>
         <template v-if="config.name === 'qh_curve'">
-          <QHCurve class="content__module" :mainfan="config.data" :fan1-prop="config.config.fan1Prop"
-            :fan2-prop="config.config.fan2Prop" />
+          <QHCurve class="content__module" :mainfan="config.data" :fan1-prop="config.config.fan1Prop" :fan2-prop="config.config.fan2Prop" />
         </template>
         <template v-if="config.name === 'ai_chat'">
           <AIChat class="content__module" />
@@ -98,8 +138,13 @@
           <SelectCs :devicedata="config.data" :config="config.config" />
         </template>
         <template v-if="config.name === 'measure_detail'">
-          <MeasureDetail class="content__module" :show-title="false" :composite-data="config.data"
-            :topconfig="config.config.topconfig" :btnconfig="config.config.btnconfig" />
+          <MeasureDetail
+            class="content__module"
+            :show-title="false"
+            :composite-data="config.data"
+            :topconfig="config.config.topconfig"
+            :btnconfig="config.config.btnconfig"
+          />
         </template>
         <template v-if="config.name === 'partition'">
           <Partition class="content__module" :type="config.type" :label="config.label" :icon="config.icon" />
@@ -111,8 +156,7 @@
           <RadioLabel class="content__module" :type="config.config.type" :config="config.config" />
         </template>
         <template v-if="config.name === 'button_list'">
-          <ButtonList class="content__module" :type="config.config.type" :config="config.config"
-            :buttonList="config.config.buttonList" />
+          <ButtonList class="content__module" :type="config.config.type" :config="config.config" :buttonList="config.config.buttonList" />
         </template>
         <template v-if="config.name === 'card_list'">
           <cardList class="content__module" :cardData="config.data" />
@@ -143,8 +187,7 @@
           <gasDeviceStatusControl class="content__module" :option="config.config.option" :statusData="config.data" />
         </template>
         <template v-else-if="config.name === 'board_gas'">
-          <gasBoard class="content__module" :option="config.config.option" :type="config.config.type"
-            :listData="config.data" />
+          <gasBoard class="content__module" :option="config.config.option" :type="config.config.type" :listData="config.data" />
         </template>
         <template v-else-if="config.name === 'gas_inject_card'">
           <gasInjectCard class="content__module" :option="config.config.option" />
@@ -156,8 +199,7 @@
           <cameraModal class="content__module" :deviceId="config.data" />
         </template>
         <template v-if="config.name === 'gallery_new'">
-          <CustomGalleryNew class="content__module" :type="config.config.type" :option="config.config.items"
-            :galleryData="config.data" />
+          <CustomGalleryNew class="content__module" :type="config.config.type" :option="config.config.items" :galleryData="config.data" />
         </template>
         <template v-if="config.name === 'card_new'">
           <ImageCardNew :option="config.config.items" :cardData="config.data"></ImageCardNew>
@@ -171,18 +213,30 @@
         <template v-if="config.name === 'long_list'">
           <LongList></LongList>
         </template>
-         <template v-if="config.name === 'long_list1'">
+        <template v-if="config.name === 'long_list1'">
           <LongList1></LongList1>
         </template>
-         <template v-if="config.name === 'long_list2'">
+        <template v-if="config.name === 'long_list2'">
           <LongList2></LongList2>
         </template>
-         <template v-if="config.name === 'search_table'">
+        <template v-if="config.name === 'search_table'">
           <SearchTable></SearchTable>
         </template>
         <template v-if="config.name === 'mixed_warn'">
           <MixedWarn></MixedWarn>
         </template>
+        <template v-if="config.name === 'rule_table'">
+          <RuleTable></RuleTable>
+        </template>
+        <template v-if="config.name === 'link_log'">
+          <LinkLog></LinkLog>
+        </template>
+        <template v-if="config.name === 'rule_num'">
+          <RuleNum></RuleNum>
+        </template>
+        <template v-if="config.name === 'link_edit'">
+          <LinkRuleEdit></LinkRuleEdit>
+        </template>
       </div>
     </div>
   </div>
@@ -223,27 +277,32 @@ import RadioLabel from './preset/radioLabel.vue';
 import ButtonList from './preset/buttonList.vue';
 import cardList from './preset/cardList.vue';
 import generalList from './preset/generalList.vue';
-import upDown from './preset/upDown.vue'
-import gasInjectChart from './preset/gasInjectChart.vue'
-import gasInjectList from './preset/gasInjectList.vue'
-import gasZyChart from './preset/gasZyChart.vue'
-import gasZyList from './preset/gasZyList.vue'
-import gasWarnChart from './preset/gasWarnChart.vue'
-import gasWarnList from './preset/gasWarnList.vue'
-import gasDeviceStatusControl from './preset/gasDeviceStatusControl.vue'
-import gasBoard from './preset/gasBoard.vue'
-import gasInjectCard from './preset/gasInjectCard.vue'
-import cameraModal from './preset/cameraModal.vue'
-import echartLine from './preset/echartLine.vue'
-import CustomGalleryNew from './preset/CustomGalleryNew.vue'
-import ImageCardNew from './preset/ImageCardNew.vue'
-import BtnListNew from './preset/BtnListNew.vue'
-import OperateNew from './preset/OperateNew.vue'
-import LongList from './preset/LongList.vue'
-import LongList1 from './preset/LongList1.vue'
-import LongList2 from './preset/LongList2.vue'
-import SearchTable from './preset/SearchTable.vue'
-import MixedWarn from './preset/MixedWarn.vue'
+import upDown from './preset/upDown.vue';
+import gasInjectChart from './preset/gasInjectChart.vue';
+import gasInjectList from './preset/gasInjectList.vue';
+import gasZyChart from './preset/gasZyChart.vue';
+import gasZyList from './preset/gasZyList.vue';
+import gasWarnChart from './preset/gasWarnChart.vue';
+import gasWarnList from './preset/gasWarnList.vue';
+import gasDeviceStatusControl from './preset/gasDeviceStatusControl.vue';
+import gasBoard from './preset/gasBoard.vue';
+import gasInjectCard from './preset/gasInjectCard.vue';
+import cameraModal from './preset/cameraModal.vue';
+import echartLine from './preset/echartLine.vue';
+import CustomGalleryNew from './preset/CustomGalleryNew.vue';
+import ImageCardNew from './preset/ImageCardNew.vue';
+import BtnListNew from './preset/BtnListNew.vue';
+import OperateNew from './preset/OperateNew.vue';
+import LongList from '../../../../../../LongList.vue';
+import LongList1 from './preset/LongList1.vue';
+import LongList2 from './preset/LongList2.vue';
+import SearchTable from './preset/SearchTable.vue';
+import RuleTable from './preset/ruleTable.vue';
+import MixedWarn from './preset/MixedWarn.vue';
+import LinkLog from './preset/linkLog.vue';
+import RuleNum from './preset/ruleNum.vue';
+import LinkRuleEdit from './preset/linkRuleEdit.vue';
+import linkFlowChart from './preset/linkFlowChart.vue';
 // import FIreWarn from './preset/FIreWarn.vue';
 // import FIreControl from './preset/FIreControl.vue';
 
@@ -255,8 +314,6 @@ const props = defineProps<{
 
 const { background, layout } = props.moduleData;
 
-
-
 // 获取当原始配置带 items 项时的最终 items 配置
 function getItems(raw, items: CommonItem[]) {
   return items.map((i) => {
@@ -507,7 +564,6 @@ const layoutConfig = computed(() => {
     return arr;
   }, []);
 });
-
 </script>
 <style lang="less" scoped>
 @import '@/design/theme.less';

+ 244 - 0
src/views/vent/home/configurable/components/preset/linkLog.vue

@@ -0,0 +1,244 @@
+<template>
+  <div class="search-table">
+    <div class="list-title">
+      <span>联动日志</span>
+    </div>
+    <div class="search-area">
+      <div class="title-top">
+        <a-input v-model:value="searchWarn" placeholder="搜索" size="small" class="zxm-input zxm-input-sm" />
+        <div class="search-btn">
+          <div class="btn-item">筛选</div>
+        </div>
+      </div>
+    </div>
+
+    <div class="time-select">
+      <span class="span">时间范围:</span>
+      <div class="btn" :class="{ active: timeType === 1 }" @click="timeType = 1">今日</div>
+      <div class="btn" :class="{ active: timeType === 2 }" @click="timeType = 2">近三天</div>
+      <div class="btn" :class="{ active: timeType === 3 }" @click="timeType = 3">近七天</div>
+      <div class="btn" :class="{ active: timeType === 4 }" @click="timeType = 4">自定义</div>
+    </div>
+    <div class="content-area">
+      <div class="content-title">
+        <div class="title-item col-time">时间</div>
+        <div class="title-item col-rule">规则</div>
+        <div class="title-item col-grade">动作</div>
+        <div class="title-item col-status">说明</div>
+      </div>
+      <div class="history-content">
+        <div class="content-item" v-for="(item, index) in tableData" :key="index">
+          <div class="item-text col-time">{{ item.time }}</div>
+          <div class="item-text col-rule">{{ item.rule }} </div>
+          <div class="item-text col-grade item-text2">{{ item.grade }}</div>
+          <div class="item-text col-status item-text1">{{ item.status }}</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+
+let searchWarn = ref('');
+const timeType = ref(1);
+let tableData = ref<any[]>([
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+  { time: '14:31:22', rule: '001', grade: '触发', status: '温度65.3℃超过阈值' },
+]);
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .search-table {
+  }
+}
+
+.search-table {
+  --image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
+  --image-box-bg1: url('@/assets/images/home-container/configurable/hsq/2-6.png');
+  --image-box-bg2: url('@/assets/images/home-container/configurable/hsq/2-24.png');
+  position: relative;
+  width: 100%;
+  height: 100%;
+  padding: 15px;
+  box-sizing: border-box;
+}
+
+.list-title {
+  width: 177px;
+  height: 30px;
+  line-height: 22px;
+  padding-left: 16px;
+  color: #01fefc;
+  font-weight: bolder;
+  background: var(--image-box-bg) no-repeat;
+  background-size: 100% 100%;
+  margin-bottom: 8px;
+}
+
+/* 搜索区域:内部横向排列 */
+.search-area {
+  margin-bottom: 5px;
+}
+.title-top {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.search-btn {
+  width: 85px;
+  height: 30px;
+  border: 1px solid #01fefc;
+  border-radius: 4px;
+  padding: 3px;
+  cursor: pointer;
+}
+
+.time-select {
+  display: flex;
+  align-items: center;
+  padding-left: 10px;
+  margin-bottom: 8px;
+  gap: 6px;
+  white-space: nowrap;
+
+  .span {
+    color: #fff;
+    font-size: 13px;
+  }
+
+  .btn {
+    padding: 3px 8px;
+    background: #114268;
+    border: 1px solid transparent;
+    border-radius: 4px;
+    color: #fff;
+    cursor: pointer;
+    margin-right: 4px;
+    font-size: 13px;
+    white-space: nowrap;
+    transition: all 0.2s;
+
+    &.active {
+      background-color: #185f8e;
+      border-color: #2084c0;
+    }
+  }
+}
+
+.btn-item {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(32, 166, 169);
+}
+
+.content-area {
+  height: calc(100% - 120px);
+}
+
+.content-title {
+  display: flex;
+  align-items: center;
+  height: 34px;
+  background: var(--image-box-bg2) no-repeat;
+  background-size: 100% 100%;
+  margin-bottom: 6px;
+}
+
+.title-item {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #01fefc;
+}
+
+.history-content {
+  height: calc(100% - 40px);
+  overflow-y: auto;
+}
+
+.content-item {
+  display: flex;
+  align-items: center;
+  height: 36px;
+
+  &:nth-child(odd) {
+    background-color: #0e3455;
+  }
+
+  &:nth-child(even) {
+    background-color: #114268;
+  }
+}
+
+.item-text {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.col-time {
+  width: 90px;
+  flex-shrink: 0;
+}
+.col-rule {
+  width: 70px;
+  flex-shrink: 0;
+}
+.col-grade {
+  width: 70px;
+  flex-shrink: 0;
+}
+.col-status {
+  flex: 1;
+  padding: 0 10px;
+  justify-content: center;
+}
+
+.item-text1 {
+  color: #fc002a;
+}
+
+.item-text2 {
+  color: orange;
+}
+
+.text-look {
+  padding: 0px 3px;
+  margin: 0px 2px;
+  border-radius: 2px;
+  background-color: #2484bc;
+  cursor: pointer;
+}
+
+.zxm-input {
+  height: 42px;
+  color: #fff;
+  background-color: transparent;
+  border: none;
+  background: var(--image-box-bg1) no-repeat;
+  background-size: 100% 100%;
+}
+
+.zxm-input-sm {
+  padding: 0px 20px;
+}
+</style>

+ 504 - 0
src/views/vent/home/configurable/components/preset/linkRuleEdit.vue

@@ -0,0 +1,504 @@
+<template>
+  <div class="long-list1">
+    <div class="list-title">
+      <span>联动规则编辑器</span>
+    </div>
+    <div class="basic-box">
+      <basicBorder title="触发条件设置">
+        <div class="edit-wrap">
+          <div class="edit-left">
+            <div class="edit-item">
+              <div class="item-label">规则名称 : </div>
+              <a-input class="item-input" v-model:value="formData.gzmc" placeholder="请输入" size="small" />
+            </div>
+            <div class="edit-item">
+              <div class="item-label">报警级别 : </div>
+              <a-input class="item-input" v-model:value="formData.bjjb" placeholder="请输入" size="small" />
+            </div>
+            <div class="edit-item">
+              <div class="item-label">温度阈值 : </div>
+              <a-input class="item-input" v-model:value="formData.wdyz" placeholder="请输入" size="small" />
+            </div>
+            <div class="edit-item">
+              <div class="item-label">触发设备 : </div>
+              <a-input class="item-input" v-model:value="formData.cfsb" placeholder="请输入" size="small" />
+            </div>
+            <div class="edit-item">
+              <div class="item-label">持续时间 : </div>
+              <a-input class="item-input" v-model:value="formData.time" placeholder="请输入" size="small" />
+            </div>
+            <div class="edit-item">
+              <div class="item-label">时间范围 : </div>
+              <a-input class="item-input" v-model:value="formData.sjfw" placeholder="请输入" size="small" />
+            </div>
+          </div>
+          <div class="edit-right">
+            <div class="right-title">条件逻辑说明</div>
+            <div class="right-content">
+              <div class="right-item">IF 温度≥60℃</div>
+              <div class="right-item">AND 持续时间≥30s</div>
+              <div class="right-item">AND 设备状态-在线</div>
+              <div class="right-item">AND 时间在生效范围内</div>
+              <div class="right-item">THEN 执行联动动作</div>
+              <div class="right-item">【优先级:高】</div>
+              <div class="right-item">此规则触发后将组织低优先级</div>
+            </div>
+          </div>
+        </div>
+      </basicBorder>
+    </div>
+    <div class="basic-box">
+      <basicBorder title="联动动作设置">
+        <div class="basic-table">
+          <div class="table-title">
+            <div class="title-item" v-for="(item, index) in titleList" :key="index">{{ item.label }}</div>
+          </div>
+          <div class="table-content">
+            <div class="content-item" v-for="(item, index) in tableData" :key="index">
+              <div class="item-text">{{ item.indnex }}</div>
+              <div class="item-text item-text1">
+                <span>{{ item.actionType }}</span>
+              </div>
+              <div class="item-text">
+                <span> {{ item.action }}</span>
+              </div>
+              <div class="item-text">
+                <div class="switch" :class="{ active: item.qy === 1 }">
+                  <div class="switch-dot"></div>
+                </div>
+              </div>
+              <div class="item-text">{{ item.delayTime }}</div>
+              <div class="item-text">{{ item.duration }}</div>
+              <div class="item-text">
+                <span class="text-look">查看</span>
+              </div>
+            </div>
+          </div>
+        </div>
+      </basicBorder>
+    </div>
+    <div class="basic-box1">
+      <basicBorder title="联动执行流程图">
+        <div class="workflow-container">
+          <div class="steps-wrapper">
+            <div v-for="(step, index) in steps" :key="index" class="step-item">
+              <div class="step-icon" :class="`icon-${step.id}`"></div>
+              <div class="step-frame" :class="`frame-${step.id}`">
+                <span class="step-text">{{ step.name }}</span>
+              </div>
+              <!-- 底部小圆点 -->
+              <div class="step-dot">
+                <div class="dot" :class="{ active: step.status === 1 }"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </basicBorder>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref, onMounted } from 'vue';
+import basicBorder from '../BasicBorder.vue';
+
+let titleList = ref<any[]>([
+  { label: '序号', value: '1' },
+  { label: '动作类型', value: '2' },
+  { label: '动作说明', value: '3' },
+  { label: '启用', value: '4' },
+  { label: '延迟', value: '5' },
+  { label: '持续', value: '6' },
+  { label: '操作', value: '7' },
+]);
+
+let tableData = ref<any[]>([
+  { indnex: '1', actionType: '声光报警器', action: '触发报警声+红色闪烁', qy: 1, delayTime: '0s', duration: '300s' },
+  { indnex: '2', actionType: '声光报警器', action: '触发报警声', qy: 1, delayTime: '1s', duration: '200s' },
+  { indnex: '3', actionType: '声光报警器', action: '红色闪烁', qy: 0, delayTime: '2s', duration: '180s' },
+]);
+
+// 流程图,添加 status 字段
+const steps = ref([
+  { id: 'alarm', name: '报警触发', status: 0 },
+  { id: 'condition', name: '条件判断', status: 0 },
+  { id: 'search', name: '查找规则', status: 0 },
+  { id: 'action', name: '执行动作', status: 0 },
+  { id: 'log', name: '记录日志', status: 0 },
+]);
+
+// 模拟接口请求获取步骤状态
+const fetchStepStatus = async () => {
+  // 模拟 API 返回的数据,实际使用时替换为真实接口
+  const apiResponse = {
+    alarmTrigger: 1, // 报警触发为 1
+    conditionJudge: 0, // 条件判断为 1
+    searchRule: 0, // 查找规则为 0
+    executeAction: 0, // 执行动作为 0
+    recordLog: 0, // 记录日志为 0
+  };
+
+  // 根据接口返回数据更新状态
+  steps.value[0].status = apiResponse.alarmTrigger;
+  steps.value[1].status = apiResponse.conditionJudge;
+  steps.value[2].status = apiResponse.searchRule;
+  steps.value[3].status = apiResponse.executeAction;
+  steps.value[4].status = apiResponse.recordLog;
+};
+let formData = reactive({
+  gzmc: '一级温度报警',
+  bjjb: '一级(严重)',
+  wdyz: '60℃',
+  cfsb: '声光报警',
+  time: '≥30s',
+  sjfw: '值班人员',
+});
+onMounted(() => {
+  fetchStepStatus();
+});
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .long-list1 {
+  }
+}
+
+.long-list1 {
+  --image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
+  --image-box-bg1: url('@/assets/images/home-container/configurable/hsq/2-13.png');
+  --image-box-bg2: url('@/assets/images/home-container/configurable/hsq/2-14.png');
+  --image-box-bg3: url('@/assets/images/home-container/configurable/hsq/2-15.png');
+  --image-box-bg4: url('@/assets/images/home-container/configurable/hsq/2-16.png');
+  --image-box-bg5: url('@/assets/images/home-container/configurable/hsq/2-17.png');
+  --image-box-bg6: url('@/assets/images/home-container/configurable/hsq/2-24.png');
+
+  position: relative;
+  width: 100%;
+  height: 100%;
+  padding: 15px 30px;
+  box-sizing: border-box;
+
+  /* 核心:垂直均分布局 */
+  display: flex;
+  flex-direction: column;
+  gap: 12px; /* 模块之间统一间距 */
+
+  .item-input {
+    background: transparent !important;
+    border-color: #4a9eff;
+    color: #fff;
+    &:hover,
+    &:focus {
+      background: transparent !important;
+    }
+    box-shadow: none !important;
+  }
+
+  .edit-wrap {
+    display: flex;
+    width: 100%;
+    height: 100%;
+    gap: 30px;
+    box-sizing: border-box;
+  }
+  .edit-left {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    margin: 10px;
+  }
+
+  .edit-item {
+    display: flex;
+    align-items: center;
+    height: 32px;
+    background: #134266;
+    margin-bottom: 10px;
+    padding: 0 10px;
+    box-sizing: border-box;
+    &:last-child {
+      margin-bottom: 10px;
+    }
+  }
+
+  .item-label {
+    width: 80px;
+    text-align: right;
+    margin-right: 5px;
+  }
+  .edit-right {
+    background: #0e395d;
+    width: 50%;
+    height: 93%;
+    display: flex;
+    flex-direction: column;
+    margin-right: 20px;
+  }
+
+  .right-title {
+    display: flex;
+    align-items: center;
+    width: 146px;
+    height: 30px;
+    color: #fff;
+    padding-left: 15px;
+    background: var(--image-box-bg) no-repeat;
+    background-size: 100% 100%;
+    font-size: 15px;
+    font-weight: bold;
+    line-height: 24px;
+  }
+
+  .right-content {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    margin-top: 5px;
+    margin-left: 10px;
+  }
+
+  .right-item {
+    color: #fff;
+    font-size: 14px;
+    line-height: 25px;
+    font-weight: bold;
+    padding: 0 5px;
+  }
+  .right-item:nth-child(1) {
+    color: #fc002a;
+  }
+  .right-item:nth-child(2),
+  .right-item:nth-child(3),
+  .right-item:nth-child(4) {
+    color: #29a1d4;
+  }
+  .right-item:nth-child(5) {
+    color: #46e8a3;
+  }
+  .right-item:nth-child(6) {
+    margin-top: 10px;
+  }
+  .list-title {
+    width: 177px;
+    height: 30px;
+    line-height: 22px;
+    padding-left: 16px;
+    color: #01fefc;
+    font-weight: bolder;
+    background: var(--image-box-bg) no-repeat;
+    background-size: 100% 100%;
+    margin-bottom: 5px;
+  }
+
+  .basic-table {
+    height: 100%;
+  }
+
+  .item-label1 {
+    width: 105px;
+    text-align: right;
+    margin-right: 5px;
+  }
+  .basic-box,
+  .basic-box1 {
+    flex: 1;
+    width: 100%;
+  }
+
+  .table-title {
+    display: flex;
+    align-items: center;
+    height: 34px;
+    background: var(--image-box-bg6) no-repeat;
+    background-size: 100% 100%;
+    margin-bottom: 6px;
+  }
+
+  .title-item {
+    flex: 1;
+    min-width: 60px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    color: #01fefc;
+    white-space: nowrap;
+    padding: 0 6px;
+  }
+
+  .table-content {
+    height: calc(100% - 40px);
+    overflow-y: auto;
+  }
+
+  .content-item {
+    display: flex;
+    align-items: center;
+    height: 36px;
+    &:nth-child(odd) {
+      background-color: #0e3455;
+    }
+    &:nth-child(even) {
+      background-color: #114268;
+    }
+  }
+
+  .item-text {
+    flex: 1;
+    min-width: 60px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    color: #fff;
+    white-space: nowrap;
+    padding: 0 6px;
+  }
+
+  .item-text1 {
+    color: #fc002a;
+  }
+  .item-text2 {
+    color: orange;
+  }
+
+  .switch {
+    width: 36px;
+    height: 18px;
+    border-radius: 9px;
+    background: none;
+    position: relative;
+    transition: all 0.2s ease;
+    cursor: pointer;
+    border: 1px solid #35d9e5;
+    .switch-dot {
+      width: 14px;
+      height: 14px;
+      border-radius: 50%;
+      background: #fff;
+      position: absolute;
+      top: 1px;
+      left: 1px;
+      transition: all 0.2s ease;
+    }
+  }
+  .switch.active .switch-dot {
+    left: 20px;
+  }
+
+  .text-look {
+    color: #01fefc;
+    cursor: pointer;
+  }
+}
+
+/*流程图*/
+.workflow-container {
+  width: 100%;
+  padding: 20px 0;
+  position: relative;
+}
+
+.steps-wrapper {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-end;
+  padding: 0 20px;
+  position: relative;
+  z-index: 2;
+  &::before {
+    content: '';
+    position: absolute;
+    left: 12%;
+    right: 12%;
+    top: 95%;
+    transform: translateY(-50%);
+    height: 2px;
+    background: #4a9eff;
+    z-index: 1;
+  }
+}
+
+.step-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  flex: 1;
+  position: relative;
+}
+
+.step-icon {
+  width: 56px;
+  height: 56px;
+  background-size: contain;
+  background-repeat: no-repeat;
+  background-position: center;
+}
+
+.icon-alarm {
+  background-image: url('@/assets/images/home-container/configurable/hsq/bjcf.png');
+}
+.icon-condition {
+  background-image: url('@/assets/images/home-container/configurable/hsq/tjpd.png');
+}
+.icon-search {
+  background-image: url('@/assets/images/home-container/configurable/hsq/czgz.png');
+}
+.icon-action {
+  background-image: url('@/assets/images/home-container/configurable/hsq/zxdz.png');
+}
+.icon-log {
+  background-image: url('@/assets/images/home-container/configurable/hsq/jlrz.png');
+}
+
+.step-frame {
+  position: relative;
+  width: 100%;
+  max-width: 120px;
+  padding: 8px 12px;
+  background-image: url('@/assets/images/home-container/configurable/hsq/3-6.png');
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 5px;
+}
+
+.step-text {
+  font-size: 14px;
+  font-weight: 500;
+  color: #fff;
+  text-align: center;
+  position: relative;
+  z-index: 1;
+}
+
+.step-dot {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: relative;
+  z-index: 3;
+  background: transparent;
+  width: 12px;
+  height: 12px;
+}
+
+.dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background: #2c5a8c;
+  border: 1px solid #4a9eff;
+  transition: all 0.3s ease;
+}
+.dot.active {
+  background: #4facfe;
+  border-color: #4facfe;
+  box-shadow: 0 0 6px rgba(79, 172, 254, 0.6);
+}
+</style>

+ 152 - 0
src/views/vent/home/configurable/components/preset/ruleNum.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="long-list">
+    <div class="list-title">
+      <span>规则统计</span>
+    </div>
+    <div class="list-content">
+      <div class="stat-item blue">
+        <div class="icon-wrap icon-1"></div>
+        <div class="label-wrap">
+          <span>总规则</span>
+        </div>
+        <div class="num-wrap">
+          <span>10</span>
+        </div>
+      </div>
+      <div class="stat-item green">
+        <div class="icon-wrap icon-2"></div>
+        <div class="label-wrap">
+          <span>已启用</span>
+        </div>
+        <div class="num-wrap">
+          <span>7</span>
+        </div>
+      </div>
+      <div class="stat-item green">
+        <div class="icon-wrap icon-3"></div>
+        <div class="label-wrap">
+          <span>已禁用</span>
+        </div>
+        <div class="num-wrap">
+          <span>2</span>
+        </div>
+      </div>
+      <div class="stat-item blue">
+        <div class="icon-wrap icon-4"></div>
+        <div class="label-wrap">
+          <span>触发中</span>
+        </div>
+        <div class="num-wrap">
+          <span>2</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+.long-list {
+  --image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
+  --image-box-bg-green: url('@/assets/images/home-container/configurable/hsq/4-2.png');
+  --image-box-bg-blue: url('@/assets/images/home-container/configurable/hsq/4-3.png');
+  --image-box-svg1: url('@/assets/images/home-container/configurable/hsq/4-total.svg');
+  --image-box-svg2: url('@/assets/images/home-container/configurable/hsq/4-cf.svg');
+  --image-box-svg3: url('@/assets/images/home-container/configurable/hsq/4-qy.svg');
+  --image-box-svg4: url('@/assets/images/home-container/configurable/hsq/4-jy.svg');
+
+  position: relative;
+  width: 100%;
+  height: 100%;
+  padding: 15px;
+  box-sizing: border-box;
+
+  .list-title {
+    width: 177px;
+    height: 30px;
+    line-height: 22px;
+    padding-left: 16px;
+    color: #01fefc;
+    font-weight: bolder;
+    background: var(--image-box-bg) no-repeat;
+    background-size: 100% 100%;
+    margin-bottom: 20px;
+  }
+
+  /* 外层改为 Flex 两列布局 */
+  .list-content {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+  }
+
+  .stat-item {
+    width: calc(50% - 10px);
+    display: flex;
+    align-items: center;
+    height: 40px;
+    padding-left: 6px;
+    box-sizing: border-box;
+    background-repeat: no-repeat;
+    background-position: center;
+    background-size: 100% 100%;
+
+    &.blue {
+      background-image: var(--image-box-bg-blue);
+    }
+    &.green {
+      background-image: var(--image-box-bg-green);
+    }
+  }
+
+  .icon-wrap {
+    width: 30px;
+    height: 30px;
+    flex-shrink: 0;
+    background-repeat: no-repeat;
+    background-position: center;
+    background-size: 60% 70%;
+
+    &.icon-1 {
+      background-image: var(--image-box-svg1);
+    }
+    &.icon-2 {
+      background-image: var(--image-box-svg3);
+    }
+    &.icon-3 {
+      background-image: var(--image-box-svg4);
+    }
+    &.icon-4 {
+      background-image: var(--image-box-svg2);
+    }
+  }
+
+  .label-wrap {
+    margin-left: 10px;
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    font-size: 14px;
+    white-space: nowrap;
+  }
+
+  .num-wrap {
+    width: 100px;
+    height: 100%;
+    flex-shrink: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    color: #fff;
+    font-size: 16px;
+    font-weight: bold;
+  }
+}
+</style>

+ 192 - 0
src/views/vent/home/configurable/components/preset/ruleTable.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="search-table">
+    <div class="search-area">
+      <div class="list-title">
+        <span>联动规则列表</span>
+      </div>
+      <div class="search-btn">
+        <div class="btn-item">新增</div>
+      </div>
+    </div>
+    <div class="content-area">
+      <div class="content-title">
+        <div class="title-item" v-for="(item, index) in titleList" :key="index">{{ item.label }}</div>
+      </div>
+      <div class="history-content">
+        <div class="content-item" v-for="(item, index) in tableData" :key="index">
+          <div class="item-text">{{ item.time }}</div>
+          <div class="item-text">{{ item.address }} </div>
+          <div class="item-text item-text1">{{ item.grade }}</div>
+          <div class="item-text">{{ item.action }}</div>
+          <div class="item-text item-text2">{{ item.status }}</div>
+          <div class="item-text">
+            <span class="text-look">确认</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+
+let searchWarn = ref('');
+let titleList = ref<any[]>([
+  { label: '规则ID', value: '1' },
+  { label: '规则名称', value: '2' },
+  { label: '触发条件', value: '3' },
+  { label: '联动动作', value: '4' },
+  { label: '状态', value: '5' },
+  { label: '操作', value: '6' },
+]);
+let tableData = ref<any[]>([
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+  { time: 'LINK001', address: '温度报警联动', grade: '温度>60℃', action: '声光报警', status: '启用' },
+]);
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+.search-table {
+  --image-box-bg: url('@/assets/images/home-container/configurable/hsq/2-5.png');
+  --image-box-bg1: url('@/assets/images/home-container/configurable/hsq/2-6.png');
+  --image-box-bg2: url('@/assets/images/home-container/configurable/hsq/2-24.png');
+  position: relative;
+  width: 100%;
+  height: 100%;
+  padding: 15px;
+  box-sizing: border-box;
+}
+.list-title {
+  width: 177px;
+  height: 30px;
+  line-height: 22px;
+  padding-left: 16px;
+  color: #01fefc;
+  font-weight: bolder;
+  background: var(--image-box-bg) no-repeat;
+  background-size: 100% 100%;
+  margin-bottom: 5px;
+}
+.search-area {
+  height: 42px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 5px;
+}
+
+.search-btn {
+  width: 85px;
+  height: 30px;
+  border: 1px solid #01fefc;
+  border-radius: 4px;
+  padding: 3px;
+  cursor: pointer;
+}
+
+.btn-item {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(32, 166, 169);
+}
+
+.content-area {
+  height: calc(100% - 47px);
+}
+
+.content-title {
+  display: flex;
+  align-items: center;
+  height: 34px;
+  background: var(--image-box-bg2) no-repeat;
+  background-size: 100% 100%;
+  margin-bottom: 6px;
+}
+
+.title-item {
+  display: flex;
+  flex: 1;
+  justify-content: center;
+  align-items: center;
+  color: #01fefc;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.history-content {
+  height: calc(100% - 40px);
+  overflow-y: auto;
+}
+
+.content-item {
+  display: flex;
+  align-items: center;
+  height: 36px;
+  &:nth-child(odd) {
+    background-color: #0e3455;
+  }
+  &:nth-child(even) {
+    background-color: #114268;
+  }
+}
+
+.item-text {
+  display: flex;
+  flex: 1;
+  justify-content: center;
+  align-items: center;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  padding: 0 4px;
+}
+.title-item:nth-child(2),
+.item-text:nth-child(2) {
+  flex: 1.8;
+}
+.title-item:nth-child(3),
+.item-text:nth-child(3) {
+  flex: 1.5;
+}
+.title-item:nth-child(4),
+.item-text:nth-child(4) {
+  flex: 1.5;
+}
+.item-text1 {
+  color: #f4933d;
+}
+.item-text2 {
+  color: #4cf3a6;
+}
+.text-look {
+  padding: 0px 3px;
+  margin: 0px 2px;
+  border-radius: 2px;
+  background-color: #2484bc;
+  cursor: pointer;
+}
+
+.zxm-input {
+  height: 42px;
+  color: #fff;
+  background-color: transparent;
+  border: none;
+  background: var(--image-box-bg1) no-repeat;
+  background-size: 100% 100%;
+}
+.zxm-input-sm {
+  padding: 0px 20px;
+}
+</style>

+ 216 - 11
src/views/vent/home/configurable/configurable.data.ts

@@ -147,7 +147,7 @@ export const testConfigFire: Config[] = [
           },
           legend: { show: false },
           xAxis: [{ show: true }],
-          yAxis: [{ show: true, name: '(℃)', position: 'left', }],
+          yAxis: [{ show: true, name: '(℃)', position: 'left' }],
           series: [
             {
               label: '温度',
@@ -158,9 +158,7 @@ export const testConfigFire: Config[] = [
           ],
         },
       ],
-      table: [
-
-      ],
+      table: [],
       gallery: [],
       complex_list: [],
       gallery_list: [],
@@ -275,7 +273,7 @@ export const testConfigFire: Config[] = [
           {
             name: 'table',
             basis: '75%',
-            overflow: true
+            overflow: true,
           },
         ],
       },
@@ -440,7 +438,7 @@ export const testConfigFire: Config[] = [
             { label: '粉尘浓度', value: 'fcnd', unit: 'mg/m³' },
             { label: '烟雾指数', value: 'ywzs', unit: '' },
             { label: '地表温度', value: 'tempDb', unit: '℃' },
-          ]
+          ],
         },
       ],
       to: '/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=fanmain',
@@ -502,7 +500,7 @@ export const testConfigFire: Config[] = [
             { firstLabel: '报警确认', secondLabel: '视屏调取', thirdLabel: '联动控制' },
             { firstLabel: '数据导出', secondLabel: '生成报表', thirdLabel: '定位追踪' },
             { firstLabel: '声光关闭', secondLabel: '应急广播', thirdLabel: '系统设置' },
-          ]
+          ],
         },
       ],
       to: '/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=fanmain',
@@ -650,7 +648,6 @@ export const testConfigDevice: Config[] = [
           {
             name: 'long_list2',
             basis: '100%',
-
           },
         ],
       },
@@ -887,7 +884,217 @@ export const testConfigWarn: Config[] = [
     },
   },
 ];
-
+//联动配置
+export const testConfigLink: Config[] = [
+  {
+    deviceType: '',
+    moduleName: '联动规则列表',
+    pageType: 'gas_injection',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '${strinstallpos}',
+        },
+        slot: {
+          show: false,
+          value: '',
+          trans: {},
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'rule_table',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      list: [],
+      chart: [],
+      table: [],
+      gallery: [],
+      complex_list: [],
+      gallery_list: [],
+      preset: [
+        {
+          readFrom: '',
+        },
+      ],
+      to: '',
+    },
+    showStyle: {
+      size: 'width:420px;height:calc(100% - 200px);',
+      version: '普通版',
+      position: 'top:0px;left:10px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '规则统计',
+    pageType: 'gas_injection',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '${strinstallpos}',
+        },
+        slot: {
+          show: false,
+          value: '',
+          trans: {},
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'rule_num',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      list: [],
+      chart: [],
+      table: [],
+      gallery: [],
+      complex_list: [],
+      gallery_list: [],
+      preset: [
+        {
+          readFrom: '',
+        },
+      ],
+      to: '',
+    },
+    showStyle: {
+      size: 'width:420px;height:180px;',
+      version: '普通版',
+      position: 'bottom:10px;bottom:10px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '联动规则编辑器',
+    pageType: 'gas_injection',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '${strinstallpos}',
+        },
+        slot: {
+          show: false,
+          value: '',
+          trans: {},
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'link_edit',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      list: [],
+      chart: [],
+      table: [],
+      gallery: [],
+      complex_list: [],
+      gallery_list: [],
+      preset: [
+        {
+          readFrom: '',
+        },
+      ],
+      to: '',
+    },
+    showStyle: {
+      size: 'width:1020px;height:calc(100% - 76px);',
+      version: '普通版',
+      position: 'top:65px;left:440px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '联动日志',
+    pageType: 'gas_injection',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '${strinstallpos}',
+        },
+        slot: {
+          show: false,
+          value: '',
+          trans: {},
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'link_log',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      list: [],
+      chart: [],
+      table: [],
+      gallery: [],
+      complex_list: [],
+      gallery_list: [],
+      preset: [
+        {
+          readFrom: '',
+        },
+      ],
+      to: '',
+    },
+    showStyle: {
+      size: 'width:420px;height:calc(100% - 10px);',
+      version: '普通版',
+      position: 'top:0px;right:10px;',
+    },
+  },
+];
 export const testConfigVentSsl: Config[] = [
   {
     deviceType: 'fanmain',
@@ -2608,8 +2815,6 @@ export const testConfigDustGreen: Config[] = [
   },
 ];
 
-
-
 export const testConfigFireGreen: Config[] = [
   {
     deviceType: 'fireManageInfo',

+ 13 - 14
src/views/vent/home/configurable/gasInjection.vue

@@ -13,30 +13,31 @@
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref } from 'vue';
 import { useInitConfigs, useInitPage } from './hooks/useInit';
-import navMenu from './components/navMenu.vue'
-import fireMonitor from './fireMonitor.vue'
-import deviceManger from './deviceManger.vue'
-import warnMonitor from './warnMonitor.vue'
-
+import navMenu from './components/navMenu.vue';
+import fireMonitor from './fireMonitor.vue';
+import deviceManger from './deviceManger.vue';
+import warnMonitor from './warnMonitor.vue';
+import linkConfiguration from './linkConfiguration.vue';
 const { mainTitle, data, updateData } = useInitPage('红纱泉露天煤矿系统');
 //当前激活界面
-const activeComponente = ref<any>(fireMonitor)
+const activeComponente = ref<any>(fireMonitor);
 
 //menu选项切换
 function changeMenu(param) {
   switch (param) {
     case 'yzt':
-      activeComponente.value = fireMonitor
+      activeComponente.value = fireMonitor;
       break;
     case 'jcyj':
-      activeComponente.value = warnMonitor
+      activeComponente.value = warnMonitor;
       break;
     case 'sbgl':
-      activeComponente.value = deviceManger
+      activeComponente.value = deviceManger;
       break;
     case 'ldpz':
+      activeComponente.value = linkConfiguration;
       break;
     case 'xtgl':
       break;
@@ -44,14 +45,14 @@ function changeMenu(param) {
       break;
   }
 }
-
 </script>
 
 <style lang="less" scoped>
 @import '/@/design/theme.less';
 
 @{theme-deepblue} {
-  .deviceManger {}
+  .deviceManger {
+  }
 }
 
 .deviceManger {
@@ -98,8 +99,6 @@ function changeMenu(param) {
     height: calc(100% - 70px);
     padding: 0px 10px;
     box-sizing: border-box;
-
-
   }
 }
 </style>

+ 100 - 0
src/views/vent/home/configurable/linkConfiguration.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="device-manger">
+    <ModuleCommonHsq
+      v-for="cfg in configs"
+      :key="cfg.deviceType"
+      :show-style="cfg.showStyle"
+      :module-data="cfg.moduleData"
+      :module-name="cfg.moduleName"
+      :device-type="cfg.deviceType"
+      :data="data"
+      :visible="true"
+    />
+  </div>
+</template>
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref, computed, nextTick, onBeforeMount, watch } from 'vue';
+import { useInitConfigs, useInitPage } from './hooks/useInit';
+import ModuleCommonHsq from './components/ModuleCommonHsq.vue';
+import { getSystemApi } from './configurable.api';
+import { testConfigLink } from './configurable.data';
+
+const { configs, fetchConfigs } = useInitConfigs();
+const { data, updateData } = useInitPage('红纱泉露天煤矿系统');
+let interval: number | undefined;
+
+// https获取监测数据
+let timer: null | NodeJS.Timeout = null;
+function getMonitor() {
+  // timer = setTimeout(async () => {
+  //   getSystemApi({ devicetype: 'sys', systemID: '2036323791827165185' }).then(updateData);
+  //   if (timer) {
+  //     timer = null;
+  //   }
+  //   getMonitor();
+  // }, 10000);
+}
+
+onMounted(() => {
+  fetchConfigs('gas_injection').then(() => {
+    configs.value = testConfigLink;
+    // getSystemApi({ devicetype: 'sys', systemID: '2036323791827165185' }).then(updateData);
+  });
+  getMonitor();
+});
+
+watch(
+  () => data.value,
+  (newData, oldData) => {}
+);
+
+onUnmounted(() => {
+  clearInterval(interval);
+});
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .device-manger {
+  }
+}
+
+.device-manger {
+  --image-box1-bg: url('@/assets/images/home-container/configurable/hsq/2-4.png');
+  --image-box1-bg1: url('@/assets/images/home-container/configurable/hsq/2-10.png');
+  width: 100%;
+  height: 100%;
+  color: @white;
+  position: relative;
+  padding: 0px 10px;
+  box-sizing: border-box;
+  background-color: #09172c;
+}
+
+.vent-box1-bg {
+  &:nth-child(1) {
+    background: var(--image-box1-bg) no-repeat;
+    background-size: 100% 100%;
+  }
+
+  &:nth-child(2) {
+    background: var(--image-box1-bg1) no-repeat;
+    background-size: 100% 100%;
+  }
+
+  &:nth-child(3) {
+    background: var(--image-box1-bg) no-repeat;
+    background-size: 100% 100%;
+  }
+  &:nth-child(4) {
+    background: var(--image-box1-bg) no-repeat;
+    background-size: 100% 100%;
+  }
+}
+
+:deep(.loading-box) {
+  position: unset;
+}
+</style>

+ 1 - 1
tsconfig.json

@@ -44,6 +44,6 @@
     "mock/**/*.ts",
     "vite.config.ts",
     "src/**/*.glsl"
-, "src/utils/threejs/loadModel.worker.js", "src/utils/threejs/loadModel.worker.js"  ],
+, "src/utils/threejs/loadModel.worker.js", "src/utils/threejs/loadModel.worker.js", "LongList.vue"  ],
   "exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"]
 }