Browse Source

[Feat 0000]工作面防火门监控系统页面开发

wangkeyi 2 weeks ago
parent
commit
b8e9d4cf3b

BIN
src/assets/images/beltFire/fireMonitor/2-13.png


BIN
src/assets/images/fireDoorMonitor.png


+ 2 - 0
src/views/vent/deviceManager/configurationTable/types.ts

@@ -103,6 +103,8 @@ export interface ModuleData {
         | 'nitrogenBtnList'
         | 'complex_list1'
         | 'gateBoard'
+        | 'fire_gate_board'
+        | 'person_position'
         | 'sensor_status'
         | 'fire_sensor_analysis'
         | 'warning_result'

+ 379 - 0
src/views/vent/home/configurable/belt/components/detail/fireGateBoard.vue

@@ -0,0 +1,379 @@
+<template>
+  <div class="wind-control-page">
+    <!-- <div class="title">
+      <div class="panel-title">
+        <div class="tab-title">短路风门管控详情</div>
+        <div>
+          <a-button class="btn" @click="yjControl()">应急控制</a-button>
+        </div>
+      </div>
+    </div> -->
+    <div class="content-layout">
+      <div class="left-panel">
+        <!-- 风门数据列表 -->
+        <div class="door-list">
+          <div class="door-card" v-for="(item, index) in props.data" :key="index">
+            <div class="door-position">
+              <div class="position"></div>
+              <div class="door-name"
+                ><span>{{ item.strname }}</span></div
+              >
+              <a-button class="door-btn" @click="oneKeyClose(index)">一键关闭</a-button>
+              <a-button class="door-btn" @click="oneKeyOpen(index)">一键打开</a-button>
+            </div>
+            <div class="door-header">
+              <div class="info-column" v-for="(i, idx) in config.config.items" :key="idx">
+                <span class="col-label">{{ i.label }}</span>
+                <span
+                  class="col-value"
+                  :class="[
+                    getFormattedText(item, i.value, i.trans) === '打开' || getFormattedText(item, i.value, i.trans) === '连接'
+                      ? 'status-open'
+                      : 'status-close',
+                    'status-dot',
+                  ]"
+                >
+                  {{ getFormattedText(item, i.value, i.trans) }}
+                </span>
+              </div>
+            </div>
+            <!-- 模型展示占位div -->
+            <!-- <div class="model-placeholder">
+              <gateSVG :ref="(el) => setChildRef(el, index)" :identify="String(index)" />
+            </div> -->
+            <div class="model-placeholder">
+              <img :src="gatePng" alt="风门" class="model-img" />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, onMounted, watch } from 'vue';
+  import { getFormattedText } from '../../../hooks/helper';
+  // import gateSVG from '../gateSVG.vue';
+  import gateSVG from '../../../../../monitorManager/gateMonitor/components/gateDualSVG.vue';
+  import gatePng from '/@/assets/images/fireDoorMonitor.png'; //暂时用图片
+  import { nextTick } from 'process';
+  const props = withDefaults(
+    defineProps<{
+      config: {
+        config: {
+          tilte: string;
+          items: {
+            value: string;
+            label: string;
+            trans?: any;
+          }[];
+        };
+      };
+      data?: any;
+    }>(),
+    {
+      data: () => ({
+        data: {
+          strname: '设备1',
+          readData: {
+            frontGateOpen: 1,
+            rearGateOpen: 0,
+          },
+          netStatus: 1,
+        },
+      }),
+    }
+  );
+  const childRefs = ref<(InstanceType<typeof gateSVG> | null)[]>([]);
+  const setChildRef = (el, index) => {
+    if (el) {
+      childRefs.value[index] = el;
+    }
+  };
+  function yjControl() {
+    console.log('应急控制');
+  }
+  function monitorAnimation(selectData, index) {
+    if (!selectData?.readData) return;
+    const frontOpen = selectData.readData.frontGateOpen === '1';
+    const midOpen = selectData.readData.midGateOpen === '1';
+    const rearOpen = selectData.readData.rearGateOpen === '1';
+    if (childRefs.value[index]) {
+      childRefs.value[index].animate(frontOpen, midOpen, rearOpen);
+    }
+  }
+
+  function oneKeyOpen(index) {
+    if (childRefs.value[index]) {
+      childRefs.value[index].animate(true, true, true);
+    }
+  }
+  function oneKeyClose(index) {
+    if (childRefs.value[index]) {
+      childRefs.value[index].animate(false, false, false);
+    }
+  }
+
+  watch(
+    () => props.data,
+    (newData) => {
+      console.log('数据更新11111', newData);
+      if (!newData || !newData.length) return;
+      newData.forEach((item, index) => {
+        monitorAnimation(item, index);
+      });
+    },
+    {
+      deep: true,
+      immediate: true, // 初始化立刻执行
+    }
+  );
+  onMounted(() => {
+    console.log('页面加载完成', props.data);
+  });
+</script>
+
+<style scoped lang="less">
+  .wind-control-page {
+    width: 100%;
+    height: 100%;
+    margin-top: -20px;
+  }
+
+  .title {
+    margin-top: 20px;
+    background: url('/@/assets/images/beltFire/yjkf/1-2title.png') no-repeat;
+    background-size: 100% 100%;
+    height: 50px;
+  }
+
+  .panel-title {
+    height: 40px;
+    color: #fff;
+    border-radius: 4px;
+    font-size: 14px;
+    line-height: 50px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .tab-title {
+    margin-top: 12px;
+    margin-left: 20px;
+    background: linear-gradient(180deg, #34b7f1 0%, #1890ff 100%);
+    border: 1px solid #40c4ff;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    height: 24px;
+    box-shadow: 0 0 6px 2px rgba(24, 144, 255, 0.4);
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .btn {
+    margin-top: 22px;
+    margin-right: 20px;
+    background: linear-gradient(180deg, #34b7f1 0%, #1890ff 100%);
+    border: 1px solid #40c4ff;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    height: 24px;
+    box-shadow: 0 0 6px 2px rgba(24, 144, 255, 0.4);
+  }
+  .content-layout {
+    display: flex;
+    height: calc(100%);
+    width: 100%;
+  }
+
+  .center-panel {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+  }
+  .left-panel {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    padding: 10px;
+  }
+
+  .door-list {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    flex: 1;
+  }
+
+  .door-card {
+    background: url('/@/assets/images/beltFire/yjkf/2-1.png') no-repeat;
+    background-size: 100% 100%;
+    border-radius: 4px;
+    padding: 12px;
+    display: flex;
+    flex-direction: column;
+    gap: 15px;
+  }
+  .door-position {
+    background: url('/@/assets/images/beltFire/yjkf/1-5.png') no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    color: #fff;
+  }
+  .door-position .position {
+    background: url('/@/assets/images/beltFire/yjkf/1-1-1.svg') no-repeat;
+    background-size: 100% 100%;
+    width: 16px;
+    height: 16px;
+    margin-top: 8px;
+    margin-left: 8px;
+  }
+  .door-position .door-name {
+    flex: 1;
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    font-size: 12px;
+    font-weight: bold;
+    color: #c4ebff;
+    margin: 0 25px 0 25px;
+    line-height: 33px;
+    font-style: italic;
+  }
+  .door-btn {
+    background: linear-gradient(180deg, #34b7f1 0%, #1890ff 100%);
+    border: 1px solid #40c4ff;
+    color: #fff;
+    font-size: 12px;
+    padding: 2px 8px;
+    height: 24px;
+    margin: auto;
+    margin-left: 3px;
+    box-shadow: 0 0 6px 2px rgba(24, 144, 255, 0.4);
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .door-header {
+    display: flex;
+    flex-direction: row;
+    gap: 6px;
+  }
+  .info-column {
+    background: url('/@/assets/images/beltFire/yjkf/1-3-1.png') no-repeat;
+    background-size: 100% 100%;
+    height: 60px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 6px;
+    flex: 1;
+  }
+  .col-label {
+    padding-top: 5px;
+    font-size: 12px;
+    color: #fff;
+    font-weight: normal;
+  }
+  .col-value {
+    font-size: 12px;
+    font-weight: bold;
+    font-style: italic;
+    color: #fff;
+  }
+  .col {
+    flex: 1;
+    padding: 0 4px;
+  }
+  .status-dot {
+    position: relative;
+    padding-left: 12px;
+  }
+  .status-open::before {
+    content: '';
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background: #52c41a;
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    box-shadow: 0 0 6px 2px rgba(82, 196, 26, 0.6);
+  }
+  .status-close::before {
+    content: '';
+    width: 6px;
+    height: 6px;
+    border-radius: 50%;
+    background: #3a3a3a;
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    box-shadow: 0 0 6px 2px rgba(90, 90, 90, 0.6);
+  }
+  .one-key-btn {
+    padding: 2px 8px;
+    font-size: 12px;
+    background: #1890ff;
+    border-color: #1890ff;
+  }
+  .model-placeholder {
+    width: 100%;
+    height: 180px;
+    background: url('/@/assets/images/beltFire/yjkf/1-4-1.png') no-repeat;
+    background-size: 100% 100%;
+    border-radius: 4px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .model-img {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+    gap: 10px;
+    object-fit: cover;
+  }
+  .right-panel {
+    width: 22%;
+    display: flex;
+    flex-direction: column;
+  }
+  .video-card {
+    background: rgba(15, 36, 87, 0.6);
+    border: 1px solid #2a52be;
+    border-radius: 4px;
+    padding: 12px;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+  }
+  .video-title {
+    margin: 0;
+    font-size: 18px;
+    color: #fff;
+    text-align: center;
+  }
+  .video-placeholder {
+    flex: 1;
+  }
+  .video-frame {
+    width: 100%;
+    height: 100%;
+    border: 2px solid #2a52be;
+    border-radius: 4px;
+    background: transparent;
+    position: relative;
+  }
+</style>

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

@@ -233,6 +233,13 @@
         <template v-if="config.name === 'vehicle_co_analysis'">
           <VehicleCOAnalysis class="content__module" :list="config.config.list" :data="config.data" />
         </template>
+
+        <template v-if="config.name === 'fire_gate_board'">
+          <fireGateBoard class="content__module" :config="config.config" :data="config.data" />
+        </template>
+        <template v-if="config.name === 'person_position'">
+          <PersonPositioning class="content__module" :list="config.config.list" :data="config.data" />
+        </template>
       </div>
     </div>
   </div>
@@ -268,6 +275,8 @@ import NitrogenBtnList from './preset/nitrogenBtnList.vue';
 import cardList from './preset/cardList.vue';
 import generalList from './preset/generalList.vue';
 import GateBoard from '../belt/components/detail/gateBoard.vue';
+import fireGateBoard from '../belt/components/detail/fireGateBoard.vue';
+import PersonPositioning from './preset/PersonPositioning.vue';
 // ==================== 新增皮带巷火灾监测组件 ====================
 import SensorStatusPanel from './belt/SensorStatusPanel.vue';
 import FireSensorAnalysis from './belt/FireSensorAnalysis.vue';

+ 394 - 0
src/views/vent/home/configurable/components/preset/PersonPositioning.vue

@@ -0,0 +1,394 @@
+<template>
+  <div class="monitor-container">
+    <!-- 1. 车辆干扰排除 (Control Section) -->
+    <div class="section" v-for="item in filteredList('control')" :key="item.type">
+      <div class="control-row">
+        <div class="tip">
+          <div class="icon-wrapper">
+            <div class="icon"></div>
+          </div>
+          <div class="label-container">
+            <span class="label">人员位置</span>
+          </div>
+          <div class="text-container">
+            <span class="text">XXXXXXX</span>
+          </div>
+        </div>
+        <!-- <button class="btn-history">应急管控</button> -->
+      </div>
+    </div>
+
+    <div class="section">
+      <div class="warning-content" v-for="(item, index) in 5" :key="index">
+        <div class="warning-text">人员信息: XXXXXX</div>
+      </div>
+    </div>
+
+    <!-- 3. 设备情况 -->
+    <!-- <div class="section">
+      <div v-for="item in filteredList('analysis')" :key="item.type">
+        <div class="section-title">{{ item.title }}</div>
+        <div class="analysis-list">
+          <div class="analysis-item" v-for="(ana, idx) in monitData.analysisList" :key="idx">
+            <span class="label">{{ ana.pos }}</span>
+            <span class="text">{{ ana.analysisText }}</span>
+          </div>
+          <div class="analysis-item">
+            <span class="label">设备位置</span>
+            <span class="text">XXXXXXXXXX</span>
+          </div>
+          <div class="analysis-item">
+            <span class="label">设备状态</span>
+            <span class="text">异常</span>
+          </div>
+        </div>
+      </div>
+    </div> -->
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { computed, ref } from 'vue';
+
+  // 定义 Props,接收配置和模拟数据
+  interface ConfigItem {
+    type: string;
+    title: string;
+    readFrom?: string;
+    items?: Array<{ label: string; code?: string; trans?: any }>;
+  }
+
+  const props = withDefaults(
+    defineProps<{
+      list: ConfigItem[];
+      data: {
+        isRisk: boolean;
+        activityList: Array<{ pos: string; vehicle: string; status: string }>;
+        analysisList: Array<{ pos: string; analysisText: string }>;
+        possibleCause: string;
+        recommendation: string;
+      };
+    }>(),
+    {
+      list: () => [],
+      data: () => ({
+        isRisk: false,
+        activityList: [],
+        analysisList: [],
+        possibleCause: '',
+        recommendation: '',
+      }),
+    }
+  );
+
+  const monitData = computed(() => props.data);
+  // 自己维护开关状态,不依赖任何外部数据
+  const isOn = ref(false);
+  // --- 计算属性:根据 type 筛选配置项 ---
+  const filteredList = (type: string) => {
+    return props.list.filter((item) => item.type === type);
+  };
+  //滑块滑动
+  const toggleSwitch = () => {
+    // const isOn = monitData.isRisk;
+    isOn.value = !isOn.value;
+  };
+  // --- 方法:获取状态类名和文本 ---
+  const getStatusClass = (status: string) => {
+    if (status === '0') return 'status-excluded';
+    if (status === '1') return 'status-warning';
+    return '';
+  };
+
+  const getStatusText = (status: string) => {
+    if (status === '0') return '已排除';
+    if (status === '1') return '预警';
+    return '未知';
+  };
+</script>
+
+<style scoped lang="less">
+  /* 基础样式重置 */
+  * {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+  }
+
+  .monitor-container {
+    width: 100%;
+    height: 410px;
+    background: linear-gradient(180deg, #0d213f 0%, #0a162a 100%);
+    border: 1px solid #1a3b5d;
+    color: #fafafa;
+    font-family: 'Microsoft YaHei', sans-serif;
+    padding: 10px 15px;
+    position: relative;
+    /* overflow: hidden; */
+  }
+
+  /* 顶部标题样式*/
+  .monitor-container::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 3px;
+    background: linear-gradient(90deg, #00c6ff, #0072ff);
+  }
+
+  /* 通用区块样式 */
+  .section {
+    margin-bottom: 15px;
+    background: url('@/assets/images/beltFire/fireMonitor/2-1.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .section-title {
+    background: url('@/assets/images/beltFire/fireMonitor/2-2.png') no-repeat;
+    background-size: 35% 100%;
+    font-size: 12px;
+    color: #fafafa;
+    font-weight: bold;
+    font-style: italic;
+    margin-bottom: 8px;
+    padding-bottom: 5px;
+    padding-left: 10px;
+    padding-top: 5px;
+  }
+
+  /* 1. 控制区样式 (开关) */
+  .control-row {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    border-radius: 4px;
+  }
+
+  .tip {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    height: 30px;
+    width: 100%;
+    background: url('@/assets/images/beltFire/fireMonitor/2-11.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .icon-wrapper {
+    width: 32px;
+    height: 30px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .icon {
+    background: url('@/assets/images/beltFire/fireMonitor/2-10.svg') no-repeat;
+    background-size: 100% 100%;
+    height: 12px;
+    width: 12px;
+    margin-left: 5px;
+  }
+
+  .label-container {
+    display: flex;
+    width: 145px;
+    text-align: center;
+  }
+
+  .label {
+    padding-left: 15px;
+    font-size: 12px;
+    color: #fafafa;
+  }
+
+  .text-container {
+    width: 60px;
+    text-align: center;
+    padding: 0 10px;
+  }
+
+  .text {
+    font-size: 11px;
+  }
+
+  .toggle-switch {
+    display: inline-flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 5px 5px;
+    width: 50px;
+    height: 20px;
+    border-radius: 15px;
+    position: relative;
+    cursor: pointer;
+    border: 1px solid #15567f;
+    box-sizing: border-box;
+    margin-top: 3px;
+    background: #0f2840; /* 加背景更美观 */
+
+    .slider {
+      position: absolute;
+      top: 3px;
+      width: 12px;
+      height: 12px;
+      border-radius: 50%;
+      background-color: #ffffff;
+      transition: all 0.3s ease;
+      z-index: 1;
+
+      /* 默认关闭:左边 */
+      left: 3px;
+    }
+    /* 开启:滑块去右边 */
+    &.is-on {
+      .slider {
+        left: calc(100% - 15px);
+      }
+    }
+  }
+
+  .btn-history {
+    background: linear-gradient(180deg, #34b7f1 0%, #1890ff 100%);
+    border: 1px solid #40c4ff;
+    color: #fafafa;
+    font-size: 11px;
+    padding: 2px 6px;
+    margin-left: 20px;
+    box-shadow: 0 0 6px 2px rgba(24, 144, 255, 0.4);
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .btn-history:hover {
+    background: rgba(0, 198, 255, 0.2);
+  }
+
+  /* 2. 报警信息 */
+  .warning-content {
+    display: flex;
+    width: 100%;
+    height: 30px;
+    align-items: center;
+    background: url('@/assets/images/beltFire/fireMonitor/2-13.png') no-repeat;
+    background-size: 100% 100%;
+    padding: 0 10px;
+  }
+  .activity-list {
+    border-radius: 4px;
+    width: 100%;
+  }
+
+  .activity-item {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    padding: 5px 8px;
+    color: #fafafa;
+    font-size: 12px;
+
+    .info {
+      display: flex;
+      align-items: center;
+      padding: 2px 12px;
+      gap: 60px;
+      color: #fafafa;
+      background: url('@/assets/images/beltFire/fireMonitor/2-4.png') no-repeat;
+      background-size: 100% 100%;
+      .pos,
+      .vehicle {
+        color: #fafafa;
+      }
+    }
+
+    .warnStatus {
+      display: flex;
+      align-items: center;
+      justify-content: space-around;
+      margin-left: 10px;
+      padding: 2px 10px;
+      flex: 1;
+      background: url('@/assets/images/beltFire/fireMonitor/2-4.png') no-repeat;
+      background-size: 100% 100%;
+      .status {
+        width: 6px;
+        height: 6px;
+        border-radius: 50%;
+        background-color: #38e4ef;
+        box-shadow: 0 0 8px 2px rgba(56, 228, 239, 0.6);
+        &.status-excluded {
+          background-color: #006eff;
+          box-shadow: 0 0 8px 2px rgba(29, 146, 255, 0);
+        }
+        &.status-warning {
+          background-color: #ff3333;
+          box-shadow: 0 0 8px 2px rgba(255, 51, 51, 0.6);
+        }
+      }
+      .status-text {
+        color: #fafafa;
+      }
+    }
+  }
+
+  /* 3. CO浓度分析列表 */
+  .analysis-list {
+    font-size: 12px;
+  }
+
+  .analysis-item {
+    background: url('@/assets/images/beltFire/fireMonitor/2-4.png') no-repeat;
+    background-size: 100% 100%;
+    padding: 4px 8px;
+    margin-bottom: 8px;
+    margin: 10px 8px;
+    width: 95%;
+    height: 30px;
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .analysis-item .label {
+    color: #fafafa;
+    font-weight: bold;
+    height: 100%;
+  }
+
+  .analysis-item .text {
+    color: #fafafa;
+    margin-left: 5px;
+    height: 100%;
+  }
+
+  /* 4. 原因与建议 */
+  .reason-item {
+    background: url('@/assets/images/beltFire/fireMonitor/2-4.png') no-repeat;
+    background-size: 100% 100%;
+    padding: 4px 8px;
+    margin-bottom: 8px;
+    margin: 10px 8px;
+    width: 95%;
+    display: flex;
+    font-size: 12px;
+    font-weight: bold;
+    justify-content: space-between;
+  }
+
+  .reason-item .label {
+    color: #fafafa;
+    width: 55px;
+    margin-right: 10px;
+    white-space: nowrap;
+  }
+
+  .reason-item .reason {
+    color: #e6941f;
+  }
+
+  .recommendation {
+    color: #ff5252;
+  }
+</style>

+ 490 - 0
src/views/vent/home/configurable/configurable.data.fireDoorMonitor.ts

@@ -0,0 +1,490 @@
+import type { Config } from '../../deviceManager/configurationTable/types';
+
+export const testFireDoorMonitor: Config[] = [
+  // ==================== 左侧栏:火灾监测设备状态 ====================
+  {
+    deviceType: 'fireMonitor',
+    moduleName: '设备监测与分析',
+    pageType: 'fireMonitorLeft',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: true,
+          value: '${beltName}',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'sensor_status',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      chart: [],
+      gallery: [],
+      gallery_list: [],
+      table: [],
+      complex_list: [],
+      list: [],
+      preset: [
+        {
+          readFrom: 'fmhjcInfo[0]',
+          list: [
+            {
+              title: '温度传感器',
+              contentTop: [
+                {
+                  label: '平均值',
+                  code: 'avg',
+                  color: 'white',
+                },
+                {
+                  label: '最大值',
+                  code: 'max',
+                  color: 'white',
+                },
+                {
+                  label: '最小值',
+                  code: 'min',
+                  color: 'white',
+                },
+              ],
+              contents: [
+                {
+                  label: '是否报警',
+                  code: 'alarm',
+                  trans: {
+                    true: '报警',
+                    false: '正常',
+                  },
+                  color: 'green',
+                },
+                {
+                  label: '最大值产生于',
+                  code: 'maxTime',
+                  color: 'white',
+                  info: {
+                    code: 'pos',
+                  },
+                },
+              ],
+            },
+            {
+              title: 'CO传感器',
+              contentTop: [
+                {
+                  label: '平均值',
+                  code: 'avg',
+                  color: 'white',
+                },
+                {
+                  label: '最大值',
+                  code: 'max',
+                  color: 'white',
+                },
+                {
+                  label: '最小值',
+                  code: 'min',
+                  color: 'white',
+                },
+              ],
+              contents: [
+                {
+                  label: '是否报警',
+                  code: 'alarm',
+                  trans: {
+                    true: '报警',
+                    false: '正常',
+                  },
+                  color: 'green',
+                },
+                {
+                  label: '最大值产生于',
+                  code: 'maxTime',
+                  color: 'white',
+                  info: {
+                    code: 'pos',
+                  },
+                },
+              ],
+            },
+            {
+              title: 'HCl传感器',
+              contentTop: [
+                {
+                  label: '平均值',
+                  code: '${avg}',
+                  color: 'white',
+                },
+                {
+                  label: '最大值',
+                  code: 'max',
+                  color: 'white',
+                },
+                {
+                  label: '最小值',
+                  code: 'min',
+                  color: 'white',
+                },
+              ],
+              contents: [
+                {
+                  label: '是否报警',
+                  code: 'alarm',
+                  trans: {
+                    true: '报警',
+                    false: '正常',
+                  },
+                  color: 'green',
+                },
+                {
+                  label: '最大值产生于',
+                  code: 'maxTime',
+                  color: 'white',
+                  info: {
+                    code: 'pos',
+                  },
+                },
+              ],
+            },
+            {
+              title: '光纤测温传感器',
+              contentTop: [
+                {
+                  label: '平均值',
+                  code: 'avg',
+                  color: 'white',
+                },
+                {
+                  label: '最大值',
+                  code: 'max',
+                  color: 'white',
+                },
+                {
+                  label: '最小值',
+                  code: 'min',
+                  color: 'white',
+                },
+              ],
+              contents: [
+                {
+                  label: '是否报警',
+                  code: 'alarm',
+                  trans: {
+                    true: '报警',
+                    false: '正常',
+                  },
+                  color: 'green',
+                },
+                {
+                  label: '最大值产生于',
+                  code: 'maxTime',
+                  color: 'white',
+                  info: {
+                    code: 'pos',
+                  },
+                },
+              ],
+            },
+          ],
+        },
+      ],
+    },
+    showStyle: {
+      size: 'width:400px;height:800px;',
+      version: '原版',
+      position: 'top:60px;left:25px;',
+    },
+  },
+
+  // ==================== 右侧 ====================
+
+  // {
+  //   deviceType: 'fireMonitor',
+  //   moduleName: '设备监测与分析',
+  //   pageType: 'fireMonitor',
+  //   moduleData: {
+  //     header: {
+  //       show: false,
+  //       readFrom: '',
+  //       selector: {
+  //         show: false,
+  //         value: '${beltName}',
+  //       },
+  //       slot: {
+  //         show: false,
+  //         value: '',
+  //       },
+  //     },
+  //     background: {
+  //       show: false,
+  //       type: 'video',
+  //       link: '',
+  //     },
+  //     layout: {
+  //       direction: 'column',
+  //       items: [
+  //         {
+  //           name: 'fire_sensor_analysis',
+  //           basis: '100%',
+  //         },
+  //       ],
+  //     },
+  //     board: [],
+  //     chart: [],
+  //     gallery: [],
+  //     gallery_list: [],
+  //     table: [],
+  //     list: [],
+  //     complex_list: [],
+  //     preset: [
+  //       {
+  //         readFrom: 'sensorAnalysis',
+  //         config: [
+  //           {
+  //             title: '火焰传感器', // 对应 UI 图中的组标题
+  //             items: [
+  //               {
+  //                 label: '是否报警',
+  //                 code: '${hy.alarm}', // 占位符
+  //                 status: '1', // 状态映射逻辑 (0:正常, 1:报警)
+  //               },
+  //               {
+  //                 label: '最大值产生于',
+  //                 code: 'maxTime',
+  //                 info: '1521胶运顺槽600m',
+  //               },
+  //             ],
+  //           },
+  //           {
+  //             title: '温度传感器',
+  //             items: [
+  //               {
+  //                 label: '是否报警',
+  //                 code: 'isAlarm', // 占位符
+  //                 status: 'isAlarm', // 状态映射逻辑 (0:正常, 1:报警)
+  //               },
+  //               {
+  //                 label: '最大值产生于',
+  //                 code: 'maxTime',
+  //                 info: '1521胶运顺槽600m',
+  //               },
+  //             ],
+  //           },
+  //           {
+  //             title: '烟雾传感器',
+  //             items: [
+  //               {
+  //                 label: '是否报警',
+  //                 code: 'isAlarm', // 占位符
+  //                 status: 'isAlarm', // 状态映射逻辑 (0:正常, 1:报警)
+  //               },
+  //               {
+  //                 label: '最大值产生于',
+  //                 code: 'maxTime',
+  //                 info: '1521胶运顺槽600m',
+  //               },
+  //             ],
+  //           },
+  //         ],
+  //       },
+  //     ],
+  //   },
+  //   showStyle: {
+  //     size: 'width:400px;height:225px;',
+  //     version: '原版',
+  //     position: 'top:10px;right:25px;',
+  //   },
+  // },
+  {
+    deviceType: 'fireMonitor', //
+    moduleName: '防火门监测',
+    pageType: 'fireMonitor',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '',
+        },
+        slot: {
+          show: false,
+          value: '',
+          trans: {},
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'fire_gate_board',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      chart: [],
+      gallery: [],
+      gallery_list: [],
+      table: [],
+      list: [],
+      complex_list: [],
+      preset: [
+        {
+          readFrom: 'deviceInfo.gate.datalist',
+          type: 'C',
+          config: {
+            tilte: 'strname',
+            items: [
+              {
+                label: '前门状态',
+                value: '${readData.frontGateOpen}',
+                trans: {
+                  1: '打开',
+                  0: '关闭',
+                },
+              },
+              {
+                label: '后门状态',
+                value: '${readData.rearGateOpen}',
+                trans: {
+                  1: '打开',
+                  0: '关闭',
+                },
+              },
+              {
+                label: '网络状态',
+                value: '${netStatus}',
+                trans: {
+                  1: '连接',
+                  0: '断开',
+                },
+              },
+            ],
+          },
+        },
+      ],
+      mock: {
+        deviceInfo: {
+          gate: {
+            datalist: [
+              {
+                strname: '设备1',
+                readData: {
+                  frontGateOpen: 1,
+                  rearGateOpen: 0,
+                },
+                netStatus: 1,
+              },
+            ],
+          },
+        },
+      },
+    },
+    showStyle: {
+      size: 'width:400px;height:420px;',
+      version: '原版',
+      position: 'top:15px;right:25px;',
+    },
+  },
+  // ==================== 人员定位与CO浓度关联分析 ====================
+  {
+    deviceType: 'fireMonitor',
+    moduleName: '人员定位',
+    pageType: 'fireMonitor',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '${beltName}',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'person_position',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      chart: [],
+      gallery: [],
+      gallery_list: [],
+      table: [],
+      list: [],
+      complex_list: [],
+      preset: [
+        {
+          readFrom: 'vehicleCOAnalysis',
+          list: [
+            {
+              type: 'control',
+              title: '人员位置',
+              layout: 'horizontal',
+              readFrom: '',
+              items: [
+                {
+                  label: '状态',
+                  code: 'isRisk',
+                },
+              ],
+            },
+            {
+              type: 'analysis',
+              title: '设备情况',
+              readFrom: 'analysisList',
+              items: [
+                {
+                  label: '',
+                  code: 'pos',
+                },
+                {
+                  label: '',
+                  code: 'analysisText',
+                },
+              ],
+            },
+          ],
+        },
+      ],
+    },
+    showStyle: {
+      size: 'width:400px;height:400px;',
+      version: '原版',
+      position: 'bottom:20px;right:25px;',
+    },
+  },
+];

+ 303 - 0
src/views/vent/home/configurable/fireDoorMonitor.vue

@@ -0,0 +1,303 @@
+<!-- belt-new.vue -->
+<template>
+  <div class="company-home">
+    <!-- 顶部标题栏 + 下拉选择 -->
+    <customHeader> XXX工作面防火门监控系统 </customHeader>
+    <div class="modal-box" id="modalBox">
+      <!-- <Three3D :modal-name="modalName" /> -->
+    </div>
+    <!-- 主体区域 -->
+    <div class="border">
+      <div class="container-title">
+        <a-select
+          class="title-select"
+          ref="select"
+          v-model:value="deviceId"
+          @change="handleDeviceChange"
+          popupClassName="drop"
+          :field-names="fieldNames"
+          :options="selectorOptions"
+          :dropdownStyle="{
+            width: '380px',
+            background: 'transparent',
+            borderBottom: '1px solid #ececec66',
+            backdropFilter: 'blur(50px)',
+            color: '#fff',
+          }"
+        />
+      </div>
+      <!-- 配置模块区 -->
+      <template v-if="pageType == 'fireMonitor'">
+        <ModuleCommon
+          v-for="cfg in configs"
+          :key="cfg.deviceType"
+          :show-style="cfg.showStyle"
+          :module-data="cfg.moduleData"
+          :module-name="cfg.moduleName"
+          :device-type="cfg.deviceType"
+          :page-type="cfg.pageType"
+          :data="data"
+          :visible="true"
+        />
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, ref, watch, computed } from 'vue';
+  import customHeader from './belt/components/customHeader-belt.vue';
+  import { useInitConfigs, useInitPage } from './hooks/useInit';
+  import { testFireDoorMonitor } from './configurable.data.fireDoorMonitor';
+  import ModuleCommon from './belt/components/ModuleCommon.vue';
+  import Three3D from '/@/views/vent/home/configurable/components/three3D.vue';
+  import { useRouter, useRoute } from 'vue-router';
+  import { modalAnimate } from './belt/threejs/belt.threejs';
+
+  // 初始化配置
+  const { configs, fetchConfigs } = useInitConfigs();
+  const { updateEnhancedConfigs, updateData, data } = useInitPage('工作面防火门监控系统');
+  // const modalName = ref('Fire-doorf');
+  // const pageType = computed(() => {
+  //   const currentType = route.params.type as string;
+  //   return currentType;
+  // });
+  const pageType = ref('fireMonitor');
+  const fieldNames = { label: 'name', value: 'id' }; // 下拉框字段映射
+
+  // 下拉框选项
+  const selectorOptions = [
+    { id: '1', label: '主运巷皮带 1' },
+    { id: '2', label: '主运巷皮带 2' },
+  ];
+
+  const deviceId = ref('1');
+
+  // 切换设备事件
+  function handleDeviceChange(param) {
+    // 查询数据接口
+  }
+
+  // 刷新数据
+  function refresh() {
+    fetchConfigs('belt').then(() => {
+      configs.value = testFireDoorMonitor;
+      updateEnhancedConfigs(configs.value);
+    });
+  }
+
+  // // 定时刷新
+  // function initInterval() {
+  //   setInterval(() => {
+  //     refresh();
+  //   }, 60000);
+  // }
+
+  function changePage(pageTypeStr: string) {
+    pageType.value = pageTypeStr;
+    refresh();
+  }
+
+  // watch(
+  //   // 监听动态路由参数 :type
+  //   () => route.params.type,
+  //   (newVal) => {
+  //     if (newVal) {
+  //       console.log('切换页面类型:', newVal);
+  //       refresh(); // 切换路由自动刷新
+  //     }
+  //   }
+  // );
+
+  function initModalAnimate(modal) {
+    console.log('初始化模型', modal);
+    modal.isRender = true;
+
+    modalAnimate(modal);
+  }
+
+  onMounted(() => {
+    refresh();
+    // initInterval();
+  });
+</script>
+
+<style lang="less" scoped>
+  .company-home {
+    background: url('/@/assets/images/beltFire/baseMap.png') no-repeat center;
+    width: 100%;
+    height: 100%;
+    color: @white;
+    position: relative;
+    font-family: 'Microsoft YaHei', sans-serif;
+    .top-bg {
+      width: 100%;
+      height: 56px;
+      position: absolute;
+      margin-top: 10px;
+      z-index: 1;
+    }
+    .header-container {
+      position: absolute;
+      top: 20px;
+      left: 20px;
+      z-index: 10;
+    }
+
+    .border {
+      width: 100%;
+      height: 94%;
+      background: url('/@/assets/images/beltFire/mainbj.png') no-repeat;
+      background-size: 100% 100%;
+      position: relative;
+      overflow: hidden;
+
+      .box-container {
+        position: relative;
+        width: 100%;
+        height: 100%;
+      }
+    }
+    .container-title {
+      width: 380px;
+      height: 34px;
+      left: 25px;
+      background: url('/src/assets/images/vent/home/select-bg.png') no-repeat;
+      background-size: contain;
+      padding: 0 0 0 40px;
+      // padding: 0 0 0 180px;
+      font-size: 20px;
+      pointer-events: auto;
+      position: relative;
+      z-index: 9999;
+      .title-select {
+        color: #fff;
+        width: 340px;
+        position: absolute;
+
+        top: 0;
+        // left: 160px;
+      }
+    }
+    :deep(.zxm-select-selector) {
+      width: 300px;
+      border: none !important;
+      background: transparent !important;
+      box-shadow: none !important;
+      :deep(.zxm-select-selection-item) {
+        color: #fff !important;
+        font-size: 20px;
+      }
+      .zxm-select-arrow {
+        color: #fff;
+      }
+    }
+
+    // 中间预警结果区
+    .center-warning-container {
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      top: 50%;
+      width: 600px;
+      height: 200px;
+      background-color: rgba(0, 0, 0, 0.7);
+      border-radius: 10px;
+      padding: 15px;
+      box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
+      z-index: 5;
+      color: #fff;
+
+      .warning-header {
+        font-size: 18px;
+        font-weight: bold;
+        margin-bottom: 10px;
+        color: #ff6b6b;
+      }
+
+      .warning-list {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+      }
+
+      .warning-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 8px;
+        background-color: rgba(0, 0, 0, 0.5);
+        border-radius: 5px;
+        border-left: 4px solid #ff6b6b;
+
+        .warning-time {
+          font-size: 14px;
+          color: #ccc;
+        }
+
+        .warning-level {
+          font-size: 14px;
+          font-weight: bold;
+          padding: 4px 8px;
+          border-radius: 4px;
+          &.level-critical {
+            background-color: #ff6b6b;
+            color: white;
+          }
+          &.level-high {
+            background-color: #ffcc00;
+            color: black;
+          }
+          &.level-normal {
+            background-color: #66cc66;
+            color: white;
+          }
+        }
+
+        .warning-action {
+          .btn-start-spray {
+            background-color: #00e1ff;
+            color: #000;
+            border: none;
+            padding: 4px 10px;
+            border-radius: 4px;
+            cursor: pointer;
+            font-size: 12px;
+            transition: all 0.3s;
+            &:hover {
+              background-color: #00c3e6;
+            }
+          }
+        }
+      }
+    }
+
+    // 巷道示意图
+    .belt-diagram {
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      bottom: 50px;
+      width: 800px;
+      height: 100px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+
+      img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+  }
+  .modal-box {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    z-index: 1;
+  }
+</style>