Sfoglia il codice sorgente

局部风机管控系统修改-提交

lxh 7 mesi fa
parent
commit
1cc98aa7c6

BIN
src/assets/images/vent/fanlocal-page/arrow-button-hover.png


BIN
src/assets/images/vent/fanlocal-page/arrow-button.png


BIN
src/assets/images/vent/fanlocal-page/card.png


BIN
src/assets/images/vent/fanlocal-page/is-txzt.png


BIN
src/assets/images/vent/fanlocal-page/is-txzt1.png


BIN
src/assets/images/vent/fanlocal-page/is-warn.png


BIN
src/assets/images/vent/fanlocal-page/is-warn1.png


BIN
src/assets/images/vent/fanlocal-page/one-bg.png


+ 54 - 0
src/views/vent/monitorManager/fanLocalMonitor/components/fanlocal-echart-line.vue

@@ -0,0 +1,54 @@
+<template>
+    <div class="alarm-history">
+        <BarAndLine class="echarts-line" xAxisPropType="time" :dataSource="historyLists" height="90%" width="100%"
+            :chartsColumns="chartsColumn" :option="echatsOption" chartsType="listMonitor" />
+    </div>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+import BarAndLine from '../../../../../components/chart/BarAndLine.vue';
+import { echatsOption } from '../fanLocal.data';
+const props = defineProps({
+    devicekide: {
+        type: String,
+        default: '',
+    },
+    historyList: {
+        type: Array,
+        default: () => {
+            return []
+        }
+    },
+    chartsColumns: {
+        type: Array,
+        default: () => {
+            return []
+        }
+    }
+});
+let scroll = reactive({
+    y: 700,
+});
+let historyLists = ref<any[]>([])
+let chartsColumn = ref<any[]>([])
+
+watch(() => props.historyList, (newH, oldH) => {
+    historyLists.value = newH
+},
+    { immediate: true })
+watch(() => props.chartsColumns, (newC, oldC) => {
+    chartsColumn.value = newC
+}, {
+    immediate: true
+})
+
+
+</script>
+<style lang="less" scoped>
+.alarm-history {
+    width: 100%;
+    height: 730px;
+    position: fixed;
+    top: 100px;
+}
+</style>

+ 59 - 0
src/views/vent/monitorManager/fanLocalMonitor/components/fanlocal-history.vue

@@ -0,0 +1,59 @@
+<template>
+    <div class="alarm-history">
+        <template v-if="globalConfig.History_Type == 'remote'">
+                <HistoryTable
+                  :columns-type="`${selectDatas.deviceType}`"
+                  :device-type="`${devicekide}`"
+                  designScope="fanlocal-history"
+                  :scroll="scroll"
+                />
+              </template>
+              <template v-else>
+                <HistoryTable1 class="w-100% h-100%" :device-code="`${devicekide}`" :scroll="scroll" dict-code="fanlocal_dict" />
+              </template>
+    </div>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+import HistoryTable from '../../comment/HistoryTable.vue'
+import HistoryTable1 from '../../../../vent/comment/history/HistoryTable.vue'
+
+const props = defineProps({
+    selectData: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    devicekide:{
+        type:String,
+        default:'',
+    },
+    globalConfig:{
+        type:Object,
+        default:()=>{
+            return {}
+        },
+    }
+});
+
+let selectDatas = reactive({})
+let scroll = reactive({
+  y: 700,
+});
+
+watch(() => props.selectData, (newS, oldS) => {
+    selectDatas = Object.assign({}, newS)
+}, {
+    immediate: true,
+    deep: true
+})
+</script>
+<style lang="less" scoped>
+.alarm-history {
+    width: 100%;
+    height: 730px;
+    position: fixed;
+    top: 100px;
+}
+</style>

+ 36 - 0
src/views/vent/monitorManager/fanLocalMonitor/components/fanlocal-operate-history.vue

@@ -0,0 +1,36 @@
+<template>
+    <div class="alarm-history">
+        <HandlerHistoryTable
+                columns-type="operator_history"
+                :device-type="`${devicekide}`"
+                :device-list-api="baseList"
+                designScope="alarm-history"
+                :scroll="scroll"
+              />
+    </div>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+import HandlerHistoryTable from '../../comment/HandlerHistoryTable.vue';
+import { list as baseList } from '../../../deviceManager/fanTabel/fan.api';
+
+const props = defineProps({
+    devicekide:{
+        type:String,
+        default:'',
+    },
+});
+let scroll = reactive({
+  y: 700,
+});
+
+
+</script>
+<style lang="less" scoped>
+.alarm-history {
+    width: 100%;
+    height: 730px;
+    position: fixed;
+    top: 100px;
+}
+</style>

+ 29 - 0
src/views/vent/monitorManager/fanLocalMonitor/components/fanlocal-warn-history.vue

@@ -0,0 +1,29 @@
+<template>
+    <div class="alarm-history">
+        <AlarmHistoryTable columns-type="alarm" :device-type="`${devicekide}`" designScope="alarm-history" :scroll="scroll" />
+    </div>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+import AlarmHistoryTable from '../../comment/AlarmHistoryTable.vue';
+
+const props = defineProps({
+    devicekide:{
+        type:String,
+        default:'',
+    },
+});
+let scroll = reactive({
+  y: 700,
+});
+
+
+</script>
+<style lang="less" scoped>
+.alarm-history {
+    width: 100%;
+    height: 730px;
+    position: fixed;
+    top: 100px;
+}
+</style>

+ 27 - 0
src/views/vent/monitorManager/fanLocalMonitor/fanLocal.data.ts

@@ -3,6 +3,33 @@ import { FormSchema } from '/@/components/Table';
 import { rules } from '/@/utils/helper/validator';
 import { ref, reactive } from 'vue';
 import { cloneDeep } from 'lodash-es';
+export const navList = ref([
+  {
+    title: '实时监测',
+    pathName: 'fanLocal-ssjc',
+    isHover: false,
+  },
+  {
+    title: '历史监测记录',
+    pathName: 'fanLocal-history',
+    isHover: false,
+  },
+  {
+    title: '报警历史记录',
+    pathName: 'fanLocal-warn',
+    isHover: false,
+  },
+  {
+    title: '操作历史记录',
+    pathName: 'fanLocal-operate',
+    isHover: false,
+  },
+  {
+    title: '风量实时曲线',
+    pathName: 'fanLocal-wind',
+    isHover: false,
+  },
+]);
 export const columns: BasicColumn[] = [
   {
     title: '名称',

+ 1795 - 0
src/views/vent/monitorManager/fanLocalMonitor/index-copy.vue

@@ -0,0 +1,1795 @@
+<template>
+  <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden">
+    <a-spin :spinning="loading" />
+    <div id="fanLocal3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> </div>
+    <div id="fanLocal3DCSS" class="threejs-Object-CSS" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none">
+      <div style="z-index: -1; position: relative" v-if="hasPermission('show:sensorMonitor')">
+        <div class="elementTag" id="inputBox1">
+          <div class="elementContent" v-if="selectData.windSpeed1 || selectData.gas3 || selectData.windSpeed_merge">
+            <p v-if="selectData.windSpeed1 || selectData.windSpeed_merge"
+              >风筒入口风速:<span class="value">{{
+                selectData.windSpeed1 ? selectData.windSpeed1 : selectData.windSpeed_merge ? selectData.windSpeed_merge : '-'
+              }}</span>
+              <span class="unit"> m/s</span></p
+            >
+            <p v-if="selectData.windInputSpeed1 || selectData.windInputSpeed_merge"
+              >风筒入口风速:<span class="value">{{
+                selectData.windInputSpeed1 ? selectData.windInputSpeed1 : selectData.windInputSpeed_merge ? selectData.windInputSpeed_merge : '-'
+              }}</span>
+              <span class="unit"> m/s</span></p
+            >
+            <p v-if="selectData.windQuantity1 || selectData.inletAirVolume_merge"
+              >风筒入口风量:<span class="value">{{
+                selectData.windQuantity1 ? selectData.windQuantity1 : selectData.inletAirVolume_merge ? selectData.inletAirVolume_merge : '-'
+              }}</span>
+              <span class="unit"> m³/min</span></p
+            >
+            <p v-if="selectData.gas3"
+              >风筒入口瓦斯浓度: <span class="value">{{ selectData.gas3 ? selectData.gas3 : '-' }}</span> <span class="unit"> %</span></p
+            >
+          </div>
+        </div>
+        <div class="elementTag" id="outBox">
+          <div
+            class="elementContent elementContent-r"
+            v-if="
+              selectData.windQuantity2 ||
+              selectData.gas1 ||
+              (selectData.windOutSpeed1 && selectData.windOutSpeed_merge) ||
+              selectData.ductOutletAirVolume_merge
+            "
+          >
+            <p v-if="selectData.windQuantity2 || selectData.m3 || selectData.ductOutletAirVolume_merge || selectData.windOutSpeed_merge"
+              >迎头供风量:<span class="value">{{
+                selectData.windQuantity2
+                  ? selectData.windQuantity2
+                  : selectData.m3
+                  ? selectData.m3
+                  : selectData.ductOutletAirVolume_merge
+                  ? selectData.ductOutletAirVolume_merge
+                  : '-'
+              }}</span>
+              <span class="unit"> m³/min</span></p
+            >
+            <p v-if="selectData.gas1"
+              >迎头瓦斯浓度:<span class="value">{{ selectData.gas1 ? selectData.gas1 : '-' }}</span> <span class="unit"> %</span></p
+            >
+            <p v-if="selectData.windOutSpeed1 || selectData.windOutSpeed_merge"
+              >风筒出口风速<span class="value">{{
+                selectData.windOutSpeed1 ? selectData.windOutSpeed1 : selectData.windOutSpeed_merge ? selectData.windOutSpeed_merge : '-'
+              }}</span>
+              <span class="unit"> %</span></p
+            >
+          </div>
+        </div>
+        <div class="elementTag" id="returnBox">
+          <div class="elementContent elementContent-r" v-if="selectData.gas2">
+            <p v-if="selectData.gas2"
+              >回风流瓦斯浓度:<span class="value">{{ selectData.gas2 ? selectData.gas2 : '-' }}</span> <span class="unit"> %</span></p
+            >
+          </div>
+        </div>
+        <div class="elementTag" id="gateBox">
+          <div class="elementContent">
+            <p>风门状态:关</p>
+            <p>风门过风面积:{{ selectData.gas1 ? selectData.gas1 : '-' }}</p>
+          </div>
+        </div>
+        <div class="elementTag" id="windownBox">
+          <div class="elementContent" v-if="modalType == 'fc'">
+            <p style="pointer-events: auto"
+              ><a class="action-link" @click="goDetailDevice('window_fWindowM3')">风窗详情</a> <ArrowRightOutlined :style="{ color: '#157DC8' }"
+            /></p>
+            <p v-if="selectData.windSpeed"
+              >风窗风流风速:<span class="value">{{ selectData.windSpeed ? selectData.windSpeed : '-' }}</span> <span class="unit"> m/s</span></p
+            >
+            <p v-if="selectData.fWindowM3"
+              >风窗过风量:<span class="value">{{ selectData.fWindowM3 ? selectData.fWindowM3 : '-' }}</span> <span class="unit"> m³/min</span></p
+            >
+            <p v-if="selectData.OpenDegree"
+              >风窗开度值:<span class="value">{{ selectData.OpenDegree ? selectData.OpenDegree : '-' }}</span> <span class="unit"> %</span></p
+            >
+            <p v-if="selectData.OpenDegree"
+              >风窗过风面积:<span class="value">{{ selectData.forntArea ? selectData.forntArea : '-' }}</span> <span class="unit"> ㎡</span></p
+            >
+          </div>
+        </div>
+        <!-- <div class="elementTag" id="inputBox1">
+          <div class="elementContent" v-if="selectData.windInputSpeed1 || selectData.windInputSpeed_merge">
+            <p v-if="selectData.windInputSpeed1 || selectData.windInputSpeed_merge"
+              >风筒入口风速:<span class="value">{{
+                selectData.windInputSpeed1 ? selectData.windInputSpeed1 : selectData.windInputSpeed_merge ? selectData.windInputSpeed_merge : '-'
+              }}</span>
+              <span class="unit"> m/s</span></p
+            >
+          </div>
+        </div> -->
+      </div>
+    </div>
+  </div>
+
+  <div class="scene-box">
+    <div class="top-box" v-if="!loading">
+      <div class="top-center row">
+        <div class="vent-flex-row" id="fanLocalSelectDom" v-if="!globalConfig?.simulatedPassword && getDictItemsByCode('fanlocaltype')">
+          <span style="color: #00f5fe; margin-left: 5px">风机类型:</span>
+          <JDictSelectTag
+            style="width: 180px"
+            v-model:value="devicekide"
+            dictCode="fanlocaltype"
+            :showChooseOption="false"
+            :getPopupContainer="getPopupContainer"
+            @change="changeDeviceKind"
+          />
+        </div>
+        <!-- fanlocal_systeml_zj 模拟局部风机,不显示操作按钮 -->
+        <template v-for="(item, index) in modalTypeArr.leftBtnArr" :key="index">
+          <div
+            v-if="!(selectData.fanFrequencyType == 'fandp' && item.permission.startsWith('btn:frequency')) && hasPermission(item.permission)"
+            :class="{ 'button-box': btnClick, 'button-disable': !btnClick }"
+            @click="showModal(item)"
+            >{{ item.value }}</div
+          >
+        </template>
+      </div>
+      <div class="top-right row">
+        <template v-for="(item, index) in modalTypeArr.rightBtnArr" :key="index">
+          <div
+            v-if="
+              !(selectData.fanFrequencyType == 'fandp' && (item.permission === 'fanLocal:gasAlarmSet' || item.permission === 'fanLocal:kkjc')) &&
+              hasPermission(item.permission)
+            "
+            :class="{ 'button-box': btnClick, 'button-disable': !btnClick }"
+            @click="showModal(item)"
+            >{{ item.value }}</div
+          >
+        </template>
+      </div>
+    </div>
+    <div class="title-text">
+      {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.stationname }}
+    </div>
+    <div class="data-show-box" v-if="!loading">
+      <div class="data-item" v-if="leftColumns.length > 0">
+        <div class="item-header">设备监测</div>
+        <div class="item-container">
+          <div class="tab">
+            <div class="tab-item" :class="{ 'tab-item-active-r': warningMonitorRowIndex === 0 }" @click="selectDevice('warningMonitorRowIndex', 0)"
+              >主机</div
+            >
+            <div class="tab-item" :class="{ 'tab-item-active-r': warningMonitorRowIndex === 1 }" @click="selectDevice('warningMonitorRowIndex', 1)"
+              >备机</div
+            >
+          </div>
+          <div class="container-group">
+            <!-- <div class="warning-header">
+              <div class="header-item">
+                <div class="header-title">报警总数</div>
+                <div class="header-value">0</div>
+              </div>
+              <div class="header-item">
+                <div class="header-title">未处理数</div>
+                <div class="header-value">0</div>
+              </div>
+            </div> -->
+            <div class="warning-group">
+              <template v-if="selectData.deviceType">
+                <!-- <div class="warning-item" v-for="(state, index) in leftColumns" :key="index">
+                <div class="item-name"><div class="icon"></div> {{ state.title }}</div>
+                <div v-if="state.dataIndex.startsWith('Fan')">
+                  <div class="signal-item" v-if="warningMonitorRowIndex == 0">
+                    <div
+                      class="signal-round"
+                      :class="{
+                        'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan1')],
+                        'signal-round-warning':
+                          selectData[state.dataIndex.replace('Fan', 'Fan1')] !== undefined && !selectData[state.dataIndex.replace('Fan', 'Fan1')],
+                        'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined,
+                      }"
+                    ></div>
+                    <div class="vent-margin-l-8">{{
+                      selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
+                      ? '无状态'
+                      : selectData[state.dataIndex.replace('Fan', 'Fan1')]
+                        ? '正常'
+                        : '异常'
+                    }}</div>
+                  </div>
+                  <div class="signal-item" v-if="warningMonitorRowIndex == 1">
+                    <div
+                      class="signal-round"
+                      :class="{
+                        'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan2')],
+                        'signal-round-warning':
+                          selectData[state.dataIndex.replace('Fan', 'Fan2')] !== undefined && !selectData[state.dataIndex.replace('Fan', 'Fan2')],
+                        'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan2')] === undefined,
+                      }"
+                    ></div>
+                    <div class="vent-margin-l-8">{{
+                      selectData[state.dataIndex.replace('Fan', 'Fan2')] === undefined
+                      ? '无状态'
+                      : selectData[state.dataIndex.replace('Fan', 'Fan2')]
+                        ? '正常'
+                        : '异常'
+                    }}</div>
+                  </div>
+                </div>
+                <div v-else>
+                  <div class="signal-item">
+                    <div
+                      class="signal-round vent-margin-l-8"
+                      :class="{
+                              'signal-round-run': selectData[state.dataIndex],
+                              'signal-round-warning': selectData[state.dataIndex] !== undefined && !selectData[state.dataIndex],
+                              'signal-round-gry': selectData[state.dataIndex] === undefined,
+                            }"
+                    ></div>
+                    <div class="vent-margin-l-8">{{ selectData[state.dataIndex] === undefined ? '无状态' : selectData[state.dataIndex] ? '正常' : '异常' }}</div>
+                  </div>
+                </div>
+              </div> -->
+                <div class="container-item" v-for="(data, index) in leftColumns" :key="index">
+                  <div class="item-icon">
+                    <!-- <SvgIcon class="icon-style" size="18" name="temperature" /> -->
+                    <CaretRightOutlined class="icon-style" />
+                  </div>
+                  <div class="item-name">{{ data.title }}</div>
+                  <div v-if="data.dataIndex.startsWith('Fan')">
+                    <div class="item-value" v-if="warningMonitorRowIndex == 0">{{
+                      selectData[data.dataIndex.replace('Fan', 'Fan1')] ? selectData[data.dataIndex.replace('Fan', 'Fan1')] : '-'
+                    }}</div>
+                    <div class="item-value" v-if="warningMonitorRowIndex == 1">{{
+                      selectData[data.dataIndex.replace('Fan', 'Fan2')] ? selectData[data.dataIndex.replace('Fan', 'Fan2')] : '-'
+                    }}</div>
+                  </div>
+                  <div v-else>
+                    <div class="item-value">{{ selectData[data.dataIndex] ? selectData[data.dataIndex] : '-' }}</div>
+                  </div>
+                </div>
+              </template>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div>
+        <div class="data-item" v-if="rightColumns.length > 0">
+          <div class="item-header">环境监测/设备报警</div>
+          <div class="item-container">
+            <div class="tab">
+              <div class="tab-item" :class="{ 'tab-item-active-r': dataMonitorRowIndex == 0 }" @click="selectDevice('dataMonitorRowIndex', 0)"
+                >主机</div
+              >
+              <div class="tab-item" :class="{ 'tab-item-active-r': dataMonitorRowIndex == 1 }" @click="selectDevice('dataMonitorRowIndex', 1)"
+                >备机</div
+              >
+            </div>
+            <div class="container-group container-group-l">
+              <div class="warning-group" style="max-height: 200px; overflow-y: auto">
+                <template v-if="deviceType">
+                  <div v-for="(state, index) in rightColumns" :key="index">
+                    <template v-if="!state.dataIndex.endsWith('_w')">
+                      <div class="container-item">
+                        <div class="item-icon">
+                          <CaretRightOutlined class="icon-style" />
+                        </div>
+                        <div class="item-name">{{ state.title }}</div>
+                        <div v-if="state.dataIndex.startsWith('Fan')">
+                          <div class="item-value" v-if="dataMonitorRowIndex == 0">{{
+                            selectData[state.dataIndex.replace('Fan', 'Fan1')] ? selectData[state.dataIndex.replace('Fan', 'Fan1')] : '-'
+                          }}</div>
+                          <div class="item-value" v-if="dataMonitorRowIndex == 1">{{
+                            selectData[state.dataIndex.replace('Fan', 'Fan2')] ? selectData[state.dataIndex.replace('Fan', 'Fan2')] : '-'
+                          }}</div>
+                        </div>
+                        <div v-else>
+                          <div class="item-value">{{ selectData[state.dataIndex] ? selectData[state.dataIndex] : '-' }}</div>
+                        </div>
+                      </div>
+                    </template>
+                  </div>
+                </template>
+              </div>
+              <div class="warning-group" style="max-height: 200px; overflow-y: auto">
+                <div class="warning-header">
+                  <div class="header-item">
+                    <div class="header-title">报警数量</div>
+                    <div class="header-value">{{ selectData['warnLogNotOkCount'] }} </div>
+                  </div>
+                </div>
+                <template v-if="deviceType">
+                  <div v-for="(state, index) in leftColumns" :key="index">
+                    <template v-if="state.dataIndex.endsWith('_w')">
+                      <div class="warning-item">
+                        <div class="item-name"> <div class="icon"></div> {{ state.title }} </div>
+                        <div v-if="state.dataIndex.startsWith('Fan')">
+                          <div class="signal-item" v-if="warningMonitorRowIndex == 0">
+                            <div
+                              class="signal-round"
+                              :class="{
+                                'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0',
+                                'signal-round-warning':
+                                  selectData[state.dataIndex.replace('Fan', 'Fan1')] !== undefined &&
+                                  selectData[state.dataIndex.replace('Fan', 'Fan1')] == '1',
+                                'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined,
+                              }"
+                            ></div>
+                            <div class="vent-margin-l-8">{{
+                              selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
+                                ? '无状态'
+                                : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
+                                ? '正常'
+                                : '异常'
+                            }}</div>
+                          </div>
+                          <div class="signal-item" v-if="warningMonitorRowIndex == 1">
+                            <div
+                              class="signal-round"
+                              :class="{
+                                'signal-round-run': selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0',
+                                'signal-round-warning':
+                                  selectData[state.dataIndex.replace('Fan', 'Fan2')] != undefined &&
+                                  selectData[state.dataIndex.replace('Fan', 'Fan2')] == '1',
+                                'signal-round-gry': selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined,
+                              }"
+                            ></div>
+                            <div class="vent-margin-l-8">{{
+                              selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
+                                ? '无状态'
+                                : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
+                                ? '正常'
+                                : '异常'
+                            }}</div>
+                          </div>
+                        </div>
+                        <div v-else>
+                          <div class="signal-item">
+                            <div
+                              class="signal-round vent-margin-l-8"
+                              :class="{
+                                'signal-round-run': selectData[state.dataIndex] == '0',
+                                'signal-round-warning': selectData[state.dataIndex] !== undefined && selectData[state.dataIndex] == '1',
+                                'signal-round-gry': selectData[state.dataIndex] === undefined,
+                              }"
+                            ></div>
+                            <div class="vent-margin-l-8">{{
+                              selectData[state.dataIndex] === undefined ? '无状态' : selectData[state.dataIndex] == '0' ? '正常' : '异常'
+                            }}</div>
+                          </div>
+                        </div>
+                      </div>
+                    </template>
+                  </div>
+                </template>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 170, scroll, 180)">
+      <dv-border-box8 :dur="5" class="dv_border_8" :style="`bottom: 20px; padding: 5px; height: ${scroll.y + 140}px`">
+        <!-- <div class="enter-detail" @click="goDetail()">
+          <send-outlined class=""/>风机运行详情
+        </div> -->
+        <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
+          <a-tab-pane key="1" tab="实时监测">
+            <GroupMonitorTable
+              v-if="activeKey === '1'"
+              ref="MonitorDataTable"
+              :dataSource="dataSource"
+              :columnsType="`${selectData.deviceType}_monitor`"
+              @selectRow="getSelectRow"
+              :scroll="scroll"
+              :is-action="true"
+            >
+              <template #action="{ record }">
+                <a v-if="globalConfig?.showReport" class="table-action-link" @click="deviceEdit($event, 'reportInfo', record)">报表录入</a>
+                <a class="table-action-link" @click="deviceEdit($event, 'deviceInfo', record)">设备编辑</a>
+              </template>
+            </GroupMonitorTable>
+          </a-tab-pane>
+          <a-tab-pane key="3" tab="历史数据">
+            <div class="tab-item" v-if="activeKey === '3'">
+              <template v-if="globalConfig.History_Type == 'remote'">
+                <HistoryTable
+                  :columns-type="`${selectData.deviceType}`"
+                  :device-type="`${devicekide}`"
+                  designScope="fanlocal-history"
+                  :scroll="scroll"
+                />
+              </template>
+              <template v-else>
+                <HistoryTable class="w-100% h-100%" :device-code="`${devicekide}`" :scroll="scroll" dict-code="fanlocal_dict" />
+              </template>
+            </div>
+          </a-tab-pane>
+          <a-tab-pane key="4" tab="报警历史">
+            <div class="tab-item" v-if="activeKey === '4'">
+              <AlarmHistoryTable columns-type="alarm" :device-type="`${devicekide}`" designScope="alarm-history" :scroll="scroll" />
+            </div>
+          </a-tab-pane>
+          <a-tab-pane key="5" tab="操作历史">
+            <div class="tab-item" v-if="activeKey === '5'">
+              <HandlerHistoryTable
+                columns-type="operator_history"
+                :device-type="`${devicekide}`"
+                :device-list-api="baseList"
+                designScope="alarm-history"
+                :scroll="scroll"
+              />
+            </div>
+          </a-tab-pane>
+          <a-tab-pane key="2" tab="风量实时曲线图" force-render v-if="hasPermission('echart:show')">
+            <!-- <a-tab-pane key="2" tab="风量实时曲线图" force-render> -->
+            <div class="tab-item" v-if="activeKey === '2'">
+              <!-- <div class="vent-flex-row-between" style="height: 100%">
+                <BarSingle
+                  :xAxisData="xAxisDataGas"
+                  :dataSource="dataSource[selectRowIndex]"
+                  height="100%"
+                  :chartsColumns="chartsColumns"
+                  style="flex: 3"
+                />
+                <BarSingle
+                  v-if="globalConfig?.simulatedPassword"
+                  :xAxisData="[
+                    { key: 'F1', valueKey: 'windQuantity1' },
+                    { key: 'F2', valueKey: 'windQuantity2' },
+                  ]"
+                  :dataSource="dataSource[selectRowIndex]"
+                  height="100%"
+                  :chartsColumns="chartsColumns1"
+                  style="flex: 2"
+                />
+              </div> -->
+              <div class="vent-flex-row-between" style="height: 100%">
+                <BarAndLine
+                  class="echarts-line"
+                  xAxisPropType="time"
+                  :dataSource="historyList"
+                  height="90%"
+                  width="100%"
+                  :chartsColumns="chartsColumns"
+                  :option="echatsOption"
+                  chartsType="listMonitor"
+                />
+              </div>
+            </div>
+          </a-tab-pane>
+        </a-tabs>
+        <a-button
+          v-if="hasPermission('btn:reportDown')"
+          type="primary"
+          size="small"
+          preIcon="ant-design:download-outlined"
+          style="position: absolute; right: 15px; top: 10px"
+          @click="reportDown"
+        >
+          报表导出
+        </a-button>
+      </dv-border-box8>
+    </div>
+  </div>
+  <div
+    ref="playerRef"
+    :class="{ 'to-right': rightColumns.length < 1 || leftColumns.length < 1, 'to-no-right': rightColumns.length > 0 && leftColumns.length > 0 }"
+    style="z-index: 999; position: absolute; top: 100px; right: 15px; width: 100%; height: 100%; margin: auto; pointer-events: none"
+  >
+  </div>
+  <a-modal v-model:visible="modalIsShow" :title="modalTitle" :maskStyle="{ backgroundColor: '#000000aa', backdropFilter: 'blur(3px)' }">
+    <template #footer>
+      <div v-if="controlType != 'startFan'">
+        <a-button key="back" @click="cancel">返回</a-button>
+        <a-button key="submit" type="primary" :loading="loading" @click="handleOk">确定</a-button>
+      </div>
+    </template>
+    <div class="modal-container">
+      <div class="vent-flex-row">
+        <ExclamationCircleFilled style="color: #ffb700; font-size: 30px" />
+        <div class="warning-text">您是否要进行{{ modalTitle }}操作?</div>
+      </div>
+      <div class="" v-if="controlType == 'startSmoke'">
+        <!-- 互斥控制 -->
+        <div class="startSmoke-select">
+          <div class="label">主机:</div>
+          <a-radio-group v-model:value="mainWindIsShow1" @change="changeMotor" name="localWind1">
+            <a-radio value="open">开启</a-radio>
+            <a-radio value="stop">停止</a-radio>
+          </a-radio-group>
+        </div>
+        <div class="startSmoke-select">
+          <div class="label">备机:</div>
+          <a-radio-group v-model:value="mainWindIsShow2" @change="changeMotor" name="localWind2">
+            <a-radio value="open">开启</a-radio>
+            <a-radio value="stop">停止</a-radio>
+          </a-radio-group>
+        </div>
+      </div>
+      <div class="" v-if="controlType == 'startFan'">
+        <!-- 不互斥控制 -->
+        <div class="startSmoke-select">
+          <div class="label">主机:</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1Open')">开启</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1Stop')">停止</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan1Reset')">复位</div>
+        </div>
+        <div class="startSmoke-select">
+          <div class="label">备机:</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2Open')">开启</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2Stop')">停止</div>
+          <div :class="{ 'button-box': true, 'button-disable': false }" @click="handleOk('Fan2Reset')">复位</div>
+        </div>
+      </div>
+      <!-- 调频 -->
+      <div class="vent-flex-row input-box" v-if="controlType == 'Fan1Frequency'">
+        <div class="label">主风机运行频率(Hz):</div>
+        <a-input-number size="small" v-model:value="fan1FrequencyVal" :min="20" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'Fan2Frequency'">
+        <div class="label">备风机运行频率(Hz):</div>
+        <a-input-number size="small" v-model:value="fan2FrequencyVal" :min="20" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'FanFrequency'">
+        <div class="label">风机运行频率(Hz):</div>
+        <a-input-number size="small" v-model:value="fan1FrequencyVal" :min="20" :max="50" :step="0.1" />
+      </div>
+
+      <div class="vent-flex-row input-box" v-if="controlType == 'disAirAlarm'">
+        <div class="label">漏风率(%):</div>
+        <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'diameter'">
+        <div class="label">风筒直径(m):</div>
+        <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'len'">
+        <div class="label">风筒长度(m):</div>
+        <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'windPowerLimit'">
+        <div class="label">风电闭锁限值(m³/min):</div>
+        <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'gasPowerLimit'">
+        <div class="label">瓦斯电闭锁限值(m³/min):</div>
+        <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+      </div>
+      <div v-if="controlType == 'gasAlarmSet'">
+        <div class="vent-flex-row input-box">
+          <div class="label">设置瓦斯超限浓度:</div>
+          <a-input-number size="small" v-model:value="gasWarningVal" :min="0" :max="1" :step="0.01" />
+        </div>
+      </div>
+      <div v-if="controlType == 'gasAlarm'">
+        <div class="vent-flex-row input-box">
+          <div class="label">传感器名称:</div>
+          <a-select placeholder="传感器" @change="handleChangeSensor" :options="sensorList" v-model:value="modalSensor" />
+        </div>
+        <div class="vent-flex-row input-box">
+          <div class="label">传感器值:</div>
+          <a-input-number size="small" v-model:value="frequencyVal" :min="0" :max="50" :step="0.1" />
+        </div>
+      </div>
+      <div v-if="controlType == 'airVolumeAlarm'">
+        <div class="vent-flex-row input-box">
+          <div class="label">风量上限(m³/min):</div>
+          <a-input-number size="small" v-model:value="modalTypeArr.rightBtnArr[3].min" :min="0" :max="50" :step="0.1" />
+        </div>
+        <div class="vent-flex-row input-box">
+          <div class="label">风量下限(m³/min):</div>
+          <a-input-number size="small" v-model:value="modalTypeArr.rightBtnArr[3].max" :min="0" :max="50" :step="0.1" />
+        </div>
+      </div>
+      <div class="vent-flex-row input-box" v-if="controlType == 'zhlk'">
+        <div class="label">目标风量(m³/min):</div>
+        <a-input-number size="small" v-model:value="targetVolume" />
+      </div>
+      <div v-if="controlType == 'gasOverSet'">
+        <div class="vent-flex-row input-box">
+          <div class="label">设置瓦斯超限浓度:</div>
+          <a-input-number size="small" v-model:value="gasWarningVal" :min="0" :max="1" :step="0.01" />
+        </div>
+      </div>
+      <!-- 启动或停止 -->
+      <div class="" v-if="controlType == 'startSmoke'"> </div>
+      <div v-if="!globalConfig?.simulatedPassword" class="vent-flex-row input-box">
+        <div class="label">操作密码:</div>
+        <a-input size="small" type="password" v-model:value="passWord" />
+      </div>
+    </div>
+  </a-modal>
+  <ConditionAssistance @register="registerModalAssistance" :dataSource="historySource" />
+  <DeviceBaseInfo @register="registerModal" :device-type="selectData['deviceType']" />
+  <reportInfo @register="registerModal1" :editID="editID" :fileType="fileType" />
+</template>
+
+<script setup lang="ts">
+  import { ExclamationCircleFilled, ArrowRightOutlined } from '@ant-design/icons-vue';
+  import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+  // import BarSingle from '../../../../components/chart/BarSingle.vue';
+  import BarAndLine from '../../../../components/chart/BarAndLine.vue';
+  import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
+  import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
+  import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
+  import DeviceBaseInfo from '../comment/components/DeviceBaseInfo.vue';
+  import { mountedThree, setModelType, destroy, addCssText, addText, playSmoke } from './fanLocal.three';
+  import lodash from 'lodash';
+  import { getTableList, list, autoAdjust } from '/@/views/vent/monitorManager/fanLocalMonitor/fanLocal.api';
+  import { list as baseList } from '../../deviceManager/fanTabel/fan.api';
+  import { chartsColumns1, echatsOption } from './fanLocal.data';
+  import { deviceControlApi } from '/@/api/vent/index';
+  import { setDivHeight } from '/@/utils/event';
+  import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { useRouter } from 'vue-router';
+  import { useModal } from '/@/components/Modal';
+  import type { BasicColumn } from '/@/components/Table/src/types/table';
+  import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
+  import { getPopupContainer } from '/@/utils';
+  import { getDictItemsByCode } from '/@/utils/dict';
+  import { message } from 'ant-design-vue';
+  import { useCamera } from '/@/hooks/system/useCamera';
+  import { CaretRightOutlined } from '@ant-design/icons-vue';
+  import ConditionAssistance from './components/conditionAssistance.vue';
+  import reportInfo from '../comment/components/reportInfo.vue';
+  import { save, reportList } from '../../reportManager/reportManager.api';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useMessage } from '/@/hooks/web/useMessage';
+  const globalConfig = inject('globalConfig');
+  const HistoryTable =
+    globalConfig.History_Type == 'remote'
+      ? defineAsyncComponent(() => import('../comment/HistoryTable.vue'))
+      : defineAsyncComponent(() => import('../../../vent/comment/history/HistoryTable.vue'));
+  const { hasPermission } = usePermission();
+
+  const [registerModal, { openModal, closeModal }] = useModal();
+  const [registerModal1, { openModal: openModal1, closeModal: closeModal1 }] = useModal();
+  const [registerModalAssistance, { openModal: openAssistance, closeModal: closeAssistance }] = useModal();
+  const { currentRoute } = useRouter();
+  const router = useRouter();
+  const { createConfirm } = useMessage();
+  const globSetting = useGlobSetting();
+
+  const modalTypeArr = reactive({
+    leftBtnArr: [
+      {
+        key: 'startSmoke',
+        value: '启停风机',
+        permission: 'btn:openclose',
+      },
+      {
+        key: 'startFan',
+        value: '启停风机',
+        permission: 'btn:openclose1',
+      },
+      {
+        key: 'changeSmoke', // 主备两个点位
+        value: '一键倒机',
+        permission: 'btn:change',
+      },
+      {
+        key: 'changeFan', // 主备一个点位
+        value: '一键倒机',
+        permission: 'btn:CtrlFanChange',
+      },
+      {
+        key: 'fan1ToFan2',
+        value: '主机倒备机',
+        permission: 'btn:ctrlFan1ToFan2',
+      },
+      {
+        key: 'fan2ToFan1',
+        value: '备机倒主机',
+        permission: 'btn:ctrlFan2ToFan1',
+      },
+      {
+        key: 'Fan1Frequency',
+        value: '主机调频',
+        permission: 'btn:frequency',
+      },
+      {
+        key: 'Fan2Frequency',
+        value: '备机调频',
+        permission: 'btn:frequency',
+      },
+      {
+        key: 'FanFrequency',
+        value: '风机调频',
+        permission: 'btn:frequencyMerge',
+      },
+      {
+        key: 'windPower',
+        value: '风电闭锁',
+        permission: 'fanLocal:fdbs',
+      },
+      {
+        key: 'gasPower',
+        value: '瓦斯电闭锁',
+        permission: 'fanLocal:wsdbs',
+      },
+      {
+        key: 'needAir',
+        value: '需风量',
+        permission: 'fanLocal:control',
+      },
+    ],
+    rightBtnArr: [
+      {
+        key: 'gasAlarmSet',
+        value: '瓦斯限值设定',
+        permission: 'fanLocal:gasAlarmSet',
+      },
+      {
+        key: 'gasOverSet', //
+        value: '瓦斯限值设定',
+        permission: 'fanLocal:gasOverSet',
+      },
+      {
+        key: 'kkjc',
+        value: '工况辅助决策',
+        permission: 'fanLocal:kkjc',
+      },
+      {
+        key: 'zhlk',
+        value: '自主联控',
+        permission: 'fanLocal:zhlk',
+      },
+      {
+        key: 'diameter',
+        value: '风筒直径',
+        permission: 'fanLocal:control',
+      },
+      {
+        key: 'diameter',
+        value: '风筒直径',
+        permission: 'fanLocal:control',
+      },
+      {
+        key: 'len',
+        value: '风筒长度',
+        permission: 'fanLocal:control',
+      },
+      // {
+      //   key: 'frequency',
+      //   value: '调频',
+      //   permission: 'fanLocal:control',
+      // },
+      {
+        key: 'windPowerLimit',
+        value: '风电闭锁限值',
+        permission: 'fanLocal:control',
+      },
+      {
+        key: 'gasPowerLimit',
+        value: '瓦斯电闭锁限值',
+        permission: 'fanLocal:control',
+      },
+      {
+        key: 'airVolumeAlarm',
+        value: '风量报警',
+        permission: 'fanLocal:control',
+        min: 0,
+        max: 100,
+      },
+      {
+        key: 'disAirAlarm',
+        value: '漏风率报警',
+        permission: 'fanLocal:control',
+      },
+      // {
+      //   key: 'gasAlarm',
+      //   value: '瓦斯报警',
+      //   permission: 'fanLocal:control',
+      // },
+    ],
+  });
+  const sensorList = ref<any[]>([
+    {
+      value: '1',
+      label: 'T1',
+    },
+    {
+      value: '2',
+      label: 'T2',
+    },
+    {
+      value: '3',
+      label: 'T3',
+    },
+  ]);
+
+  const scroll = reactive({
+    y: 180,
+  });
+  const deviceTypeDicts = getDictItemsByCode('fanlocaltype');
+  const gasWarningVal = ref(0.6); // 瓦斯最大报警值
+  const playerRef = ref();
+  const MonitorDataTable = ref();
+  const modalSensor = ref(null);
+  const loading = ref(false);
+  const activeKey = ref('1');
+  const player1 = ref();
+  const modalIsShow = ref<boolean>(false); // 是否显示模态框
+  const modalTitle = ref(''); // 模态框标题显示内容,根据设备操作类型决定
+  const fan1FrequencyVal = ref(40); //主机频率
+  const fan2FrequencyVal = ref(40); //备机频率
+  const mainWindIsShow1 = ref('open'); // 主机默认启动leftColumns
+  const mainWindIsShow2 = ref('stop'); // 备机默认不启动
+  const fanControl = ref('');
+  const targetVolume = ref(600);
+  const historyList = ref([]);
+  const remoteChartsColumns = getTableHeaderColumns('fanlocal_chart');
+  const chartsColumns = remoteChartsColumns && remoteChartsColumns.length > 0 ? remoteChartsColumns : chartsColumns1;
+
+  const passWord = ref('');
+  // 默认初始是第一行
+  const selectRowIndex = ref(-1);
+  const dataMonitorRowIndex = ref(0);
+  // 默认数据右边监测的是主机
+  const warningMonitorRowIndex = ref(0);
+
+  const xAxisDataGas = ref([]);
+  // 设备数据
+  const controlType = ref('');
+  const modalType = ref('');
+  // 监测数据
+  const initData = {
+    deviceID: '',
+    deviceType: '',
+    strname: '',
+    dataDh: '-', //压差
+    dataDtestq: '-', //测试风量
+    sourcePressure: '-', //气源压力
+    dataDequivalarea: '-',
+    netStatus: '0', //通信状态
+    warnLevel_str: '',
+    stationname: '',
+    fanFrequencyType: '',
+  };
+  const frequencyVal = ref(0);
+  const dataSource = ref([]);
+  const historySource = ref([]);
+  // 关联设备信息
+  const linkDeviceInfo = ref({});
+  // 监测数据
+  let selectData = reactive(lodash.cloneDeep(initData));
+  const rightColumns = ref<BasicColumn[]>([]);
+  const leftColumns = ref<BasicColumn[]>([]);
+  const devicekide = ref(deviceTypeDicts && deviceTypeDicts.length > 0 ? deviceTypeDicts[0]['value'] : 'fanlocal');
+  const deviceType = ref(selectData.deviceType);
+  const headElHeight = ref(0);
+  let btnClick = ref(true); // 判断按钮是否可点
+
+  //报表导出
+  let editID = ref<any>('');
+  let fileType = ref('');
+
+  const { getCamera, removeCamera } = useCamera();
+
+  watch(deviceType, (type) => {
+    rightColumns.value = getTableHeaderColumns(type + '_monitor_right') as [];
+    if (rightColumns.value && rightColumns.value.length < 1) {
+      rightColumns.value = getTableHeaderColumns(type.split('_')[0] + '_monitor_right') as [];
+    }
+    leftColumns.value = getTableHeaderColumns(type + '_monitor_left') as [];
+    if (leftColumns.value && leftColumns.value.length < 1) {
+      leftColumns.value = getTableHeaderColumns(type.split('_')[0] + '_monitor_left') as [];
+    }
+  });
+
+  const flvURL1 = () => {
+    // return `https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv`;
+    return '';
+  };
+
+  const changeDeviceKind = (e) => {
+    devicekide.value = e;
+    loading.value = true;
+    selectRowIndex.value = -1;
+    nextTick(() => {
+      selectData = lodash.cloneDeep(initData);
+      loading.value = false;
+      if (selectData.deviceID) MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
+      const headEl = document.querySelector(`.zxm-table-thead`);
+      if (headEl) {
+        headElHeight.value = headEl.clientHeight;
+      }
+    });
+  };
+
+  const tabChange = (activeKeyVal) => {
+    activeKey.value = activeKeyVal;
+    if (activeKeyVal == 1) {
+      nextTick(() => {
+        MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
+      });
+    }
+  };
+
+  const selectDevice = (key, val) => {
+    if (key === 'dataMonitorRowIndex') {
+      dataMonitorRowIndex.value = val;
+    } else {
+      warningMonitorRowIndex.value = val;
+    }
+  };
+
+  //报表导出点击
+  async function reportDown() {
+    openModal1();
+    let res = await save({ reportType: 'fanlocal' });
+    console.log(res, 'res-----------');
+    let list = await reportList({ id: res.id });
+    console.log(list, 'list-----------');
+    let index = list.records[0].fileName.indexOf('.');
+    fileType.value = list.records[0].fileName.substring(index + 1);
+    editID.value = list.records[0].id;
+    openModal1();
+  }
+
+  //详情
+  function goDetail() {
+    openModal();
+  }
+
+  function goDetailDevice(linkDeviceCode) {
+    let linkDeviceId = '';
+    if (linkDeviceCode.startsWith('window')) {
+      linkDeviceId = linkDeviceInfo.value[linkDeviceCode] ? linkDeviceInfo.value[linkDeviceCode]['id'] : '';
+      router.push({ path: '/monitorChannel/monitor-window', query: { id: linkDeviceId } });
+    }
+  }
+
+  //
+  async function getDataSource() {
+    if (devicekide.value) {
+      const res = await list({ devicetype: devicekide.value, pagetype: 'normal' });
+      // const res = await list({ devicetype: 'fanlocal', pagetype: 'normal' });
+      if (res.msgTxt && res.msgTxt[0] && res.msgTxt[0].datalist && res.msgTxt[0].datalist.length > 0) {
+        const dataArr = res.msgTxt[0].datalist || [];
+        dataSource.value = [];
+        dataArr.forEach((data) => {
+          const readData = data.readData;
+          data = Object.assign(data, readData);
+          if (data['Fan1StartStatus'] && data['Fan1StartStatus'] === '1.0') data['Fan1StartStatus'] = '1';
+          if (data['Fan2StartStatus'] && data['Fan2StartStatus'] === '1.0') data['Fan2StartStatus'] = '1';
+          if (data['Fan1StartStatus'] && data['Fan1StartStatus'] === '0.0') data['Fan1StartStatus'] = '0';
+          if (data['Fan2StartStatus'] && data['Fan2StartStatus'] === '0.0') data['Fan2StartStatus'] = '0';
+          data['windQuantity2'] =
+            data['windQuantity2'] ||
+            data['m3'] ||
+            data['ductOutletAirVolume_merge'] ||
+            data['windOutSpeed_merge'] ||
+            data['windOutSpeed1'] ||
+            data['windOutSpeed2'] ||
+            data['windOutSpeed_merge'];
+          // if (globSetting.sysOrgCode === 'sdmtjtbetmk') {
+          //   if (data['m3']) data['ductOutletAirVolume_merge'] = data['m3'];
+          //   if (data['m3']) data['inletAirVolume_merge'] = (Number(data['m3']) * 1.08).toFixed(2);
+          // }
+          dataSource.value.push(data);
+          if (selectRowIndex.value > -1) {
+            let echartsData = dataArr[selectRowIndex.value]['history'] || [];
+            echartsData = echartsData.filter((item) => {
+              item['FanfHz'] = data['Fan1StartStatus'] == '1' ? item['Fan1fHz'] : data['Fan2StartStatus'] == '1' ? item['Fan2fHz'] : 0;
+              item['windQuantity2'] =
+                item['windQuantity2'] ||
+                item['m3'] ||
+                item['ductOutletAirVolume_merge'] ||
+                item['windOutSpeed_merge'] ||
+                item['windOutSpeed1'] ||
+                item['windOutSpeed2'] ||
+                item['windOutSpeed_merge'];
+              return true;
+            });
+            historyList.value = echartsData;
+          }
+        });
+      } else {
+        return (dataSource.value = []);
+      }
+    } else {
+      dataSource.value = [];
+    }
+  }
+
+  // https获取监测数据
+  let timer: null | NodeJS.Timeout = null;
+  async function getMonitor(flag?) {
+    if (Object.prototype.toString.call(timer) === '[object Null]') {
+      timer = await setTimeout(
+        async () => {
+          // debugger;
+          await getDataSource();
+          if (dataSource.value.length > 0 && selectRowIndex.value == -1 && MonitorDataTable.value) {
+            // 初始打开页面
+            if (flag && currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
+              MonitorDataTable.value.setSelectedRowKeys(currentRoute.value['query']['id']);
+            } else {
+              MonitorDataTable.value.setSelectedRowKeys(dataSource.value[0]['deviceID']);
+            }
+          }
+          for (const key in selectData) {
+            selectData[key] = '';
+          }
+          if (dataSource.value.length > 0 && dataSource.value[selectRowIndex.value]) {
+            deviceType.value = dataSource.value[selectRowIndex.value]['deviceType'];
+            if (dataSource.value.length > 0 && dataSource.value[selectRowIndex.value]) {
+              Object.assign(selectData, dataSource.value[selectRowIndex.value]);
+            }
+            playSmoke(selectData);
+            addText(selectData);
+          }
+          historySource.value = selectData.history;
+          if (timer) {
+            timer = null;
+          }
+          getMonitor();
+        },
+        flag ? 0 : 1000
+      );
+    }
+  }
+
+  // 获取设备基本信息列表
+  const deviceBaseList = ref([]);
+  function getDeviceBaseList() {
+    getTableList({ pageSize: 1000 }).then((res) => {
+      deviceBaseList.value = res.records;
+    });
+  }
+
+  // 切换检测数据
+  async function getSelectRow(id) {
+    console.log('选中的设备id------->', id);
+
+    if (!id || id == selectData['deviceID']) return;
+    // loading.value = true;
+    const selectIndex: any = dataSource.value.findIndex((baseData: any) => baseData.deviceID == id);
+    selectRowIndex.value = selectIndex;
+    nextTick(() => {
+      const headEl = document.querySelector(`.zxm-table-thead`);
+      if (headEl) {
+        headElHeight.value = headEl.clientHeight;
+      }
+
+      const data = dataSource.value[selectIndex];
+      if (data) {
+        if (selectData['linkInfo']) linkDeviceInfo.value = JSON.parse(selectData['linkInfo']);
+
+        if (linkDeviceInfo.value['window_fWindowM3']) {
+          modalType.value = 'fc';
+        }
+        // 主备互斥控制
+        if (data['Fan1StartStatus'] == '1') {
+          mainWindIsShow1.value = 'open';
+          mainWindIsShow2.value = 'stop';
+          selectDevice('warningMonitorRowIndex', 0);
+          selectDevice('dataMonitorRowIndex', 0);
+        } else if (data['Fan2StartStatus'] == '1') {
+          mainWindIsShow2.value = 'open';
+          mainWindIsShow1.value = 'stop';
+          selectDevice('warningMonitorRowIndex', 1);
+          selectDevice('dataMonitorRowIndex', 1);
+        }
+        const xAxisDataGasArr = [];
+        for (const key in selectData) {
+          if (key.startsWith('gas') && key.length < 5) {
+            xAxisDataGasArr.push({ key: 'T' + key.substring(3), valueKey: key });
+          }
+        }
+        xAxisDataGas.value = xAxisDataGasArr;
+      }
+      setModelType(modalType.value);
+    });
+
+    await getCamera(id, playerRef.value);
+    return;
+  }
+
+  // 打开并设置modal的标题
+  function showModal(obj) {
+    if (!btnClick.value) return;
+    if (obj.key == 'kkjc') {
+      gasWarningVal.value = 0.6;
+      // 工况辅助决策
+      openAssistance(true, {});
+      return;
+    }
+    controlType.value = obj.key;
+    modalTitle.value = obj.value;
+    passWord.value = '';
+    modalIsShow.value = true;
+  }
+
+  function changeMotor(e) {
+    const target = e.target;
+    if (target.name === 'localWind1') {
+      if (target.value === 'open') {
+        mainWindIsShow2.value = 'stop';
+      }
+    } else if (target.name === 'localWind2') {
+      if (target.value === 'open') {
+        mainWindIsShow1.value = 'stop';
+      }
+    }
+  }
+
+  function handleOk(control?) {
+    if (passWord.value == '') {
+      message.warning('请输入密码!');
+      return;
+    }
+
+    createConfirm({
+      iconType: 'warning',
+      title: '控制',
+      content: '您确定要控制吗?',
+      onOk: () => handerFn(),
+    });
+    const handerFn = () => {
+      const handType = controlType.value;
+      const data = {
+        deviceid: selectData.deviceID,
+        devicetype: selectData.deviceType,
+        paramcode: '',
+        password: passWord.value || globalConfig?.simulatedPassword,
+        value: null,
+      };
+      if (handType === 'startSmoke') {
+        // 启动风机
+        // 以下是互斥
+        if (mainWindIsShow1.value === 'open' && mainWindIsShow2.value === 'stop') {
+          // playSmoke(handType, 'top', frequency, 'open');
+          data.paramcode = 'CtrlFan1Start';
+          deviceControlApi(data)
+            .then((res) => {
+              if (res.success) {
+                if (globalConfig.History_Type == 'remote') {
+                  message.success('指令已下发至生产管控平台成功!');
+                } else {
+                  message.success('指令已下发成功!');
+                }
+                modalTitle.value = '';
+                modalIsShow.value = false;
+              } else {
+                message.error(res.message);
+              }
+            })
+            .catch((err) => {
+              // modalIsShow.value = true;
+            });
+        } else if (mainWindIsShow2.value === 'open' && mainWindIsShow1.value === 'stop') {
+          // playSmoke(handType, 'down', frequency, 'open');
+          data.paramcode = 'CtrlFan2Start';
+          deviceControlApi(data)
+            .then(() => {
+              if (res.success) {
+                if (globalConfig.History_Type == 'remote') {
+                  message.success('指令已下发至生产管控平台成功!');
+                } else {
+                  message.success('指令已下发成功!');
+                }
+                modalTitle.value = '';
+                modalIsShow.value = false;
+              } else {
+                message.error(res.message);
+              }
+            })
+            .catch((err) => {});
+        } else if (mainWindIsShow1.value === 'stop' && mainWindIsShow2.value === 'stop') {
+          // playSmoke(handType, '', frequency, 'stop');
+        }
+      } else if (handType === 'startFan') {
+        if (control === 'Fan1Open') {
+          data.paramcode = 'CtrlFan1Start';
+        } else if (control === 'Fan2Open') {
+          data.paramcode = 'CtrlFan2Start';
+        } else if (control === 'Fan1Stop') {
+          data.paramcode = 'Fan1Stop';
+        } else if (control === 'Fan2Stop') {
+          data.paramcode = 'Fan2Stop';
+        } else if (control === 'Fan1Reset') {
+          data.paramcode = 'Fan1Reset';
+        } else if (control === 'Fan2Reset') {
+          data.paramcode = 'Fan2Reset';
+        }
+        deviceControlApi(data)
+          .then((res) => {
+            if (res.success) {
+              if (globalConfig.History_Type == 'remote') {
+                message.success('指令已下发至生产管控平台成功!');
+              } else {
+                message.success('指令已下发成功!');
+              }
+              modalTitle.value = '';
+              modalIsShow.value = false;
+            } else {
+              message.error(res.message);
+            }
+          })
+          .catch((err) => {
+            btnClick.value = true;
+          });
+      } else if (handType === 'Fan1Frequency' || handType === 'Fan2Frequency' || handType === 'FanFrequency') {
+        // 调频
+        if (handType === 'Fan1Frequency') {
+          data.paramcode = 'Fan1FreqHz';
+          data.value = fan1FrequencyVal.value;
+        } else if (handType === 'Fan2Frequency') {
+          data.paramcode = 'Fan2FreqHz';
+          data.value = fan2FrequencyVal.value;
+        } else if (handType === 'FanFrequency') {
+          data.paramcode = 'FreqHz_merge';
+          data.value = fan1FrequencyVal.value;
+        }
+
+        deviceControlApi(data)
+          .then((res) => {
+            if (res.success) {
+              if (globalConfig.History_Type == 'remote') {
+                message.success('指令已下发至生产管控平台成功!');
+              } else {
+                message.success('指令已下发成功!');
+              }
+              modalTitle.value = '';
+              modalIsShow.value = false;
+            } else {
+              message.error(res.message);
+            }
+          })
+          .catch((err) => {});
+      } else if (handType === 'changeSmoke') {
+        if (selectData['Fan1StartStatus'] == '0' || !selectData['Fan1StartStatus']) {
+          data.paramcode = 'CtrlFan1Start';
+          deviceControlApi(data)
+            .then((res) => {
+              if (res.success) {
+                if (globalConfig.History_Type == 'remote') {
+                  message.success('指令已下发至生产管控平台成功!');
+                } else {
+                  message.success('指令已下发成功!');
+                }
+                modalTitle.value = '';
+                modalIsShow.value = false;
+              } else {
+                message.error(res.message);
+              }
+
+              mainWindIsShow1.value = 'stop';
+              mainWindIsShow2.value = 'open';
+            })
+            .catch((err) => {});
+        } else if (selectData['Fan2StartStatus'] == '0' || !selectData['Fan2StartStatus']) {
+          data.paramcode = 'CtrlFan2Start';
+          deviceControlApi(data)
+            .then((res) => {
+              if (res.success) {
+                if (globalConfig.History_Type == 'remote') {
+                  message.success('指令已下发至生产管控平台成功!');
+                } else {
+                  message.success('指令已下发成功!');
+                }
+                modalTitle.value = '';
+                modalIsShow.value = false;
+              } else {
+                message.error(res.message);
+              }
+              mainWindIsShow1.value = 'open';
+              mainWindIsShow2.value = 'stop';
+            })
+            .catch((err) => {});
+        }
+        // // 一键倒机
+        // if (mainWindIsShow1.value === 'open' && mainWindIsShow2.value === 'stop') {
+        //   // playSmoke('startSmoke', 'down', frequency, 'open');
+        //   data.paramcode = 'CtrlFan2Start';
+        //   deviceControlApi(data).then((res) => {
+        //     console.log('设备操作结果', res);
+        //     modalTitle.value = '';
+        //     modalIsShow.value = false;
+        //   }).catch((err) => {
+
+        //   });
+        //   mainWindIsShow1.value = 'stop';
+        //   mainWindIsShow2.value = 'open';
+        // } else if (mainWindIsShow2.value === 'open' && mainWindIsShow1.value === 'stop') {
+        //   // playSmoke('startSmoke', 'top', frequency, 'open');
+        //   data.paramcode = 'CtrlFan1Start';
+        //   deviceControlApi(data).then((res) => {
+        //     console.log('设备操作结果', res);
+        //     modalTitle.value = '';
+        //     modalIsShow.value = false;
+        //   }).catch((err) => {
+
+        //   });
+        //   mainWindIsShow1.value = 'open';
+        //   mainWindIsShow2.value = 'stop';
+        // } else if (mainWindIsShow1.value === 'stop' && mainWindIsShow2.value === 'stop') {
+        //   // playSmoke(handType, '', frequency, 'stop');
+        // }
+      } else if (handType === 'changeFan') {
+        data.paramcode = 'CtrlFanStart';
+        deviceControlApi(data)
+          .then((res) => {
+            if (res.success) {
+              if (globalConfig.History_Type == 'remote') {
+                message.success('指令已下发至生产管控平台成功!');
+              } else {
+                message.success('指令已下发成功!');
+              }
+            } else {
+              message.error(res.message);
+            }
+            modalTitle.value = '';
+            modalIsShow.value = false;
+          })
+          .catch((err) => {});
+      } else if (handType === 'fan1ToFan2') {
+        data.paramcode = 'CtrlFan1ToFan2';
+        deviceControlApi(data).then((res) => {
+          if (res.success) {
+            if (globalConfig.History_Type == 'remote') {
+              message.success('指令已下发至生产管控平台成功!');
+            } else {
+              message.success('指令已下发成功!');
+            }
+            modalTitle.value = '';
+            modalIsShow.value = false;
+          } else {
+            message.error(res.message);
+          }
+        });
+      } else if (handType === 'fan2ToFan1') {
+        data.paramcode = 'CtrlFan2ToFan1';
+        deviceControlApi(data).then((res) => {
+          if (res.success) {
+            if (globalConfig.History_Type == 'remote') {
+              message.success('指令已下发至生产管控平台成功!');
+            } else {
+              message.success('指令已下发成功!');
+            }
+            modalTitle.value = '';
+            modalIsShow.value = false;
+          } else {
+            message.error(res.message);
+          }
+        });
+      } else if (handType === 'gasAlarmSet') {
+        if (passWord.value != '123456') {
+          message.error('密码错误,请重新输入!');
+          return;
+        }
+        if (gasWarningVal.value) {
+          modalTitle.value = '';
+          modalIsShow.value = false;
+          setTimeout(() => {
+            passWord.value = '';
+            modalIsShow.value = true;
+            controlType.value = 'toGkjc';
+            modalTitle.value = '工况决策辅助';
+          }, 500);
+        } else {
+          message.error('请核对瓦斯超限浓度值!');
+        }
+      } else if (handType === 'toGkjc') {
+        if (passWord.value != '123456') {
+          message.error('密码错误,请重新输入!');
+          return;
+        }
+        //进行工况决策
+        if (selectData.Fan1StartStatus == '1') {
+          openAssistance(true, {
+            // m3: selectData.m3 || 675.87,
+            m3: 675.87,
+            frequency: 30,
+            // m3: 525.87,  //5.0测试数据
+            // frequency: 35,
+            // frequency: selectData.Fan1fHz || selectData.Fan1FreqHz || selectData.Fan1Loop_Frequency,
+            gasWarningVal: gasWarningVal.value,
+            isCompute: false,
+          });
+        } else if (selectData.Fan2StartStatus == '1') {
+          openAssistance(true, {
+            // m3: selectData.m3 || 675.87,
+            m3: 675.87,
+            frequency: 30,
+            // frequency: selectData.Fan2fHz || selectData.Fan2FreqHz || selectData.Fan2Loop_Frequency,
+            gasWarningVal: gasWarningVal.value,
+            isCompute: false,
+          });
+        } else {
+          // openAssistance(true, { m3: 635.04, dataDh: 5748 });
+          openAssistance(true);
+        }
+        modalTitle.value = '';
+        modalIsShow.value = false;
+      } else if (handType === 'zhlk' || handType === 'gasOverSet') {
+        if (targetVolume.value) {
+          const params =
+            handType === 'zhlk'
+              ? { auto: 1, fanlocalId: selectData.deviceID, xufengliang: targetVolume.value }
+              : { auto: 1, fanlocalId: selectData.deviceID, gasMax: gasWarningVal.value };
+          autoAdjust(params).then(() => {
+            if (res.success) {
+              if (globalConfig.History_Type == 'remote') {
+                message.success('指令已下发至生产管控平台成功!');
+              } else {
+                message.success('指令已下发成功!');
+              }
+              modalTitle.value = '';
+              modalIsShow.value = false;
+            } else {
+              message.error(res.message);
+            }
+          });
+        }
+      }
+    };
+  }
+
+  function cancel() {
+    modalTitle.value = '';
+    modalIsShow.value = false;
+    gasWarningVal.value = 0;
+  }
+
+  function handleChangeSensor(value: string) {
+    console.log(value);
+  }
+
+  function addPlayVideo() {
+    if (player1.value && player1.value.play) {
+      if (!player1.value.paused()) player1.value.play();
+      document.body.removeEventListener('mousedown', addPlayVideo);
+    }
+  }
+
+  function deviceEdit(e: Event, type: string, record) {
+    e.stopPropagation();
+    openModal(true, {
+      type,
+      deviceId: record['deviceID'],
+    });
+  }
+
+  onBeforeMount(() => {
+    getDeviceBaseList();
+  });
+
+  onMounted(() => {
+    const { query } = unref(currentRoute);
+    if (query['deviceType']) devicekide.value = query['deviceType'] as string;
+    mountedThree(player1.value).then(async () => {
+      await getMonitor(true);
+      nextTick(async () => {
+        addCssText();
+      });
+    });
+
+    document.body.addEventListener('mousedown', addPlayVideo, false);
+  });
+
+  onUnmounted(() => {
+    destroy();
+    removeCamera();
+    if (timer) {
+      clearTimeout(timer);
+      timer = undefined;
+    }
+  });
+</script>
+<style scoped lang="less">
+  @import '/@/design/vent/modal.less';
+  :deep(.@{ventSpace}-tabs-tabpane-active) {
+    height: 100%;
+  }
+  .scene-box {
+    .title-text {
+      height: 32px;
+    }
+    .bottom-tabs-box {
+      height: 280px;
+      .tabs-box {
+        position: relative !important;
+      }
+    }
+  }
+
+  .data-show-box {
+    position: relative;
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    padding: 10px 5px;
+    color: #ffffff;
+    z-index: 999;
+    top: 60px;
+    .data-item {
+      pointer-events: auto;
+      .item-header {
+        width: 374px;
+        background: url('/@/assets/images/vent/lr-top-bg.png') no-repeat;
+        background-size: auto;
+        height: 32px;
+        text-align: center;
+        line-height: 34px;
+        font-size: 15px;
+        font-weight: 600;
+        color: #fafafa;
+      }
+      .item-container {
+        width: 346px;
+        margin: 0 14px;
+        padding: 10px;
+        background: #00377c33;
+        backdrop-filter: blur(2px);
+        .tab {
+          width: 323px;
+          background: url('/@/assets/images/vent/lr-tab-bg.png') no-repeat;
+          display: flex;
+          .tab-item {
+            flex: 1;
+            text-align: center;
+            padding-top: 2px;
+            color: #ffffff99;
+            cursor: pointer;
+          }
+          .tab-item-active-l {
+            color: #00ffea;
+            background-image: url('/@/assets/images/vent/l-tab-active.png');
+            background-repeat: no-repeat;
+            background-size: auto;
+            background-position: 6px 3px;
+          }
+          .tab-item-active-r {
+            color: #00ffea;
+            background-image: url('/@/assets/images/vent/r-tab-active.png');
+            background-repeat: no-repeat;
+            background-position: 0 3px;
+          }
+        }
+        .container-group {
+          width: 314px;
+          margin: 0px 4px;
+          padding: 10px 0;
+          min-height: 432px;
+          background: linear-gradient(to right, #00deff22, #2081ff05);
+          max-height: 440px;
+          overflow-y: auto;
+        }
+        .container-item {
+          width: 100%;
+          height: 60px;
+          display: flex;
+          padding: 10px 0 0 20px;
+          margin-bottom: 5px;
+          position: relative;
+          background: url('/@/assets/images/vent/plane-bottom.png') no-repeat;
+          background-size: auto;
+          background-position: bottom;
+          &::before {
+            content: '';
+            display: block;
+            width: 100%;
+            height: 5px;
+            position: absolute;
+            top: 62px;
+            left: 0;
+            background-color: #73f4ff66;
+            backdrop-filter: blur(5px);
+          }
+          .item-icon {
+            width: 54px;
+            height: 45px;
+            background: url('/@/assets/images/vent/plane-icon-bg.png') no-repeat;
+            background-size: cover;
+            .icon-style {
+              font-size: 18px;
+              margin: 10px 0 0 20px;
+              color: #ffc800;
+            }
+          }
+          .item-name {
+            width: 180px;
+            line-height: 60px;
+          }
+          .item-value {
+            position: relative;
+            height: 26px;
+            line-height: 24px;
+            margin: 15px 0;
+            text-align: center;
+            width: 80px;
+            border: 1px solid #00f5fe;
+            border-radius: 13px;
+            background: linear-gradient(to right, #00f5fe44, #0090ff44);
+            &::before {
+              width: 6px;
+              height: 6px;
+              content: '';
+              position: absolute;
+              left: -3px;
+              top: 8px;
+              background: #ffa500;
+              border-radius: 3px;
+            }
+          }
+        }
+        .warning-header {
+          display: flex;
+          font-size: 14px;
+          .header-item {
+            flex: 1;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            .header-title {
+              color: #39e7fe;
+              margin-top: 10px;
+            }
+            .header-value {
+              // width: 133px;
+              height: 36px;
+              font-weight: 600;
+              font-family: 'douyuFont';
+              font-size: 16px;
+              color: #ffa500;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              margin-top: 15px;
+              margin-left: 8px;
+              // background: url('/@/assets/images/vent/count-header-bg.png') no-repeat;
+            }
+          }
+        }
+        .warning-group {
+          padding: 0 10px;
+          position: relative;
+
+          .warning-item {
+            display: flex;
+            flex-direction: row;
+            justify-content: space-between;
+            align-items: center;
+            height: 38px;
+            .item-name {
+              display: flex;
+              align-items: center;
+              // padding-left: 5px;
+              .icon {
+                width: 6px;
+                height: 6px;
+                display: inline-block;
+                background-color: #1cd5ff;
+                border-radius: 3px;
+                position: relative;
+                margin-right: 8px;
+                &::before {
+                  content: '';
+                  width: 10px;
+                  height: 10px;
+                  display: block;
+                  border: 1px solid #34edff99;
+                  border-radius: 5px;
+                  position: absolute;
+                  top: -2px;
+                  left: -2px;
+                }
+              }
+              &::before {
+                content: '';
+                display: block;
+                width: 1px;
+                height: 38px;
+                position: absolute;
+                left: 12px;
+                background-color: #00f5fe;
+              }
+            }
+          }
+        }
+        .warning-group-r {
+          &::before {
+            content: '';
+            display: block;
+            width: 1px;
+            height: 100%;
+            position: absolute;
+            left: 12px;
+            background-color: #00f5fe;
+          }
+        }
+      }
+    }
+  }
+  .input-box {
+    display: flex;
+    align-items: center;
+    .input-title {
+      color: #73e8fe;
+      width: auto;
+    }
+    margin-right: 10px;
+  }
+  .label {
+    max-width: 220px;
+  }
+  #fanLocalSelectDom {
+    :deep(.@{ventSpace}-select-dropdown) {
+      left: 0px !important;
+      top: 35px !important;
+    }
+  }
+  .@{ventSpace}-input {
+    width: 150px;
+  }
+  :deep(#LivePlayerBox) {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-end;
+    padding-right: 380px;
+    pointer-events: none;
+    .video-parent {
+      height: 208px;
+      pointer-events: auto !important;
+    }
+  }
+  .to-right {
+    :deep(#LivePlayerBox) {
+      padding-right: 0px;
+    }
+  }
+
+  :deep(.button-box) {
+    position: relative;
+    padding: 5px;
+    // border: 1px transparent solid;
+    border-radius: 5px;
+    margin-left: 8px;
+    margin-right: 8px;
+    width: auto;
+    // height: 40px;
+    // border: 1px solid #65dbea;
+    height: 35px !important;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #fff;
+    padding: 0 15px 5px 15px;
+    cursor: pointer;
+    &:hover {
+      background: linear-gradient(#2cd1ff55, #1eb0ff55);
+    }
+    &::before {
+      width: calc(100% - 6px);
+      height: 27px;
+      content: '';
+      position: absolute;
+      top: 3px;
+      right: 0;
+      left: 3px;
+      bottom: 0;
+      z-index: -1;
+      border-radius: inherit; /*important*/
+      background: linear-gradient(#1fa6cb, #127cb5);
+    }
+    &::after {
+      width: calc(100% + 32px);
+      height: 10px;
+      content: '';
+      position: absolute;
+      top: 28px;
+      right: 0;
+      left: -16px;
+      bottom: 0;
+      z-index: -1;
+      border-radius: inherit; /*important*/
+      background-position: center;
+      background-size: 100%;
+      z-index: 999;
+    }
+  }
+</style>

File diff suppressed because it is too large
+ 545 - 886
src/views/vent/monitorManager/fanLocalMonitor/index.vue


+ 12 - 0
src/views/vent/monitorManager/fanlocalPage/fanlocal.api.ts

@@ -0,0 +1,12 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+    device = '/monitor/device',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const device = (params) => defHttp.post({ url: Api.device, params });
+
+

+ 279 - 0
src/views/vent/monitorManager/fanlocalPage/index.vue

@@ -0,0 +1,279 @@
+<template>
+    <div class="fanlocalPage">
+        <customHeader>局部风机监测</customHeader>
+        <div class="left-icon">
+            <img src="../../../../assets/images/vent/fanlocal-page/arrow-button.png" alt="">
+        </div>
+        <div class="right-icon">
+            <img class="img1" src="../../../../assets/images/vent/fanlocal-page/arrow-button.png" alt="">
+        </div>
+        <div class="fanlocal-container">
+            <div class="card-box" v-for="(item, index) in cardList" :key="index">
+                <div class="card-title" @click="getClick(item)">{{ item.label }}</div>
+                <div class="card-box-item">
+                    <div class="left-box-item">
+                        <div class="item-icon">
+                            <img src="../../../../assets/images/vent/fanlocal-page/is-txzt1.png" alt="">
+                        </div>
+                        <div class="item-content">
+                            <div class="item-content-value">{{ item.txVal }}</div>
+                            <div class="item-content-text">{{ item.txLable }}</div>
+                        </div>
+                    </div>
+                    <div class="right-box-item">
+                        <div class="item-icon">
+                            <img src="../../../../assets/images/vent/fanlocal-page/is-warn1.png" alt="">
+                        </div>
+                        <div class="item-content">
+                            <div class="item-content-value">{{ item.warnVal }}</div>
+                            <div class="item-content-text">{{ item.warnLabel }}</div>
+                        </div>
+                    </div>
+                </div>
+                <div class="card-box-list">
+                    <div class="list-item" v-for="(ite, ind) in item.listData" :key="ind">
+                        <div class="list-item-label">{{ ite.label }}</div>
+                        <div class="list-item-value">{{ `${ite.value}${ite.dw}` }}</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, ref, watch, onMounted, nextTick, defineAsyncComponent, reactive, onUnmounted, inject, unref } from 'vue';
+import customHeader from '/@/components/vent/customHeader.vue';
+import { device } from './fanlocal.api'
+import { useRouter } from 'vue-router';
+
+const props = defineProps({});
+
+let router = useRouter();
+let cardList = reactive<any[]>([])
+
+// https获取监测数据
+let timer: null | NodeJS.Timeout = null;
+function getMonitor(flag?) {
+    timer = setTimeout(
+        async () => {
+            await getList()
+            if (timer) {
+                timer = null;
+            }
+            getMonitor();
+        },
+        flag ? 0 : 3000
+    );
+}
+//点击标题页面跳转
+function getClick(item){
+    router.push('/monitorChannel/monitor-fanlocal?id='+ item.deviceID + '&deviceType=fanlocal')
+}
+//获取列表数据
+async function getList() {
+    let res = await device({ devicetype: "fanlocal", pagetype: "normal" })
+    let data = res.msgTxt[0].datalist
+    cardList.length = 0
+    if (data.length != 0) {
+        data.forEach(el => {
+            cardList.push(
+                {
+                    deviceID:el.deviceID,
+                    label: el.strinstallpos,
+                    txVal: el.netStatus==1 ? '连接' : '未连接',
+                    txLable: '通讯状态',
+                    warnVal: el.warnLevel_str,
+                    warnLabel: '是否报警',
+                    listData: [
+                        { label: '运行风机', value: el.readData.Fan1StartStatus=='1' ? '主机' : el.readData.Fan2StartStatus=='1' ? '备机' : '--', dw: '' },
+                        { label: '风量', value: el.readData.window_fWindowM3, dw: 'm³/min' },
+                        { label: '迎头风速', value:el.readData.windSpeed1, dw: 'm/s' },
+                        { label: '迎头瓦斯', value: el.readData.gas1, dw: 'm/s' },
+                        { label: '时间', value: el.readTime, dw: '' },
+                    ]
+                },
+            )
+        })
+    }
+
+}
+
+onMounted(() => {
+    getList()
+    getMonitor()
+})
+onUnmounted(() => {
+    if (timer) {
+        clearTimeout(timer);
+        timer = undefined;
+    }
+});
+
+
+</script>
+<style lang="less" scoped>
+.fanlocalPage {
+    width: 100%;
+    height: 100%;
+    padding: 100px 60px 20px 60px;
+    box-sizing: border-box;
+    position: relative;
+
+    .left-icon {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 60px;
+        height: calc(100% - 100px);
+        position: absolute;
+        left: 0;
+        top: 100px;
+
+    }
+
+    .right-icon {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 60px;
+        height: calc(100% - 100px);
+        position: absolute;
+        right: 0;
+        top: 100px;
+    }
+
+    img {
+        width: 60px;
+        height: 60px;
+    }
+
+    .img1 {
+        transform: rotate(180deg);
+    }
+
+    .fanlocal-container {
+        height: 100%;
+        display: flex;
+        flex-wrap: wrap;
+
+        .card-box {
+            position: relative;
+            width: 417px;
+            height: 380px;
+            margin: 0px 15px;
+            background: url(/src/assets/images/vent/fanlocal-page/card.png) no-repeat center;
+            background-size: 100% 100%;
+
+            .card-title {
+                position: absolute;
+                left: 50%;
+                top: 6px;
+                transform: translate(-50%, 0);
+                font-size: 14px;
+                font-family: 'douyuFont';
+                color: #fff;
+                cursor: pointer;
+            }
+
+            .card-box-item {
+                width: 100%;
+                height: 64px;
+                position: absolute;
+                left: 0;
+                top: 55px;
+                display: flex;
+                justify-content: space-around;
+
+                .left-box-item {
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                    width: 45%;
+                    height: 100%;
+                    padding: 5px 10px;
+                    background: url('../../../../assets/images/vent/fanlocal-page/is-txzt.png') no-repeat center;
+                    background-size: 100% 100%;
+                    box-sizing: border-box;
+                }
+
+                .right-box-item {
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                    width: 45%;
+                    height: 100%;
+                    padding: 5px 10px;
+                    background: url('../../../../assets/images/vent/fanlocal-page/is-warn.png') no-repeat center;
+                    background-size: 100% 100%;
+                    box-sizing: border-box;
+                }
+
+                .item-icon {
+                    width: 60px;
+                    height: 100%;
+                    margin-right: 15px;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center
+                }
+
+                .item-content {
+                    width: calc(100% - 60px);
+                    height: 100%;
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: center;
+                    color: #fff;
+                    font-size: 14px;
+                    padding-top: 10px;
+                    box-sizing: border-box;
+
+                    .item-content-value {
+                        font-family: 'douyuFont';
+                        color: #01fefc;
+                    }
+
+
+
+                }
+
+
+            }
+
+            .card-box-list {
+                width: 100%;
+                height: 245px;
+                padding: 0px 15px;
+                box-sizing: border-box;
+                position: absolute;
+                left: 0;
+                top: 130px;
+                display: flex;
+                flex-direction: column;
+                justify-content: space-around;
+                overflow-y: auto;
+
+                .list-item {
+                    height: 30px;
+                    padding: 0px 25px;
+                    box-sizing: border-box;
+                    color: #fff;
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                    background: url('../../../../assets/images/vent/fanlocal-page/one-bg.png') no-repeat center;
+                    background-size: 100% 100%;
+
+                    .list-item-value {
+                        color: #01fefc;
+                    }
+                }
+
+            }
+        }
+
+
+    }
+
+}
+</style>

Some files were not shown because too many files changed in this diff