Browse Source

1. 保德首页添加调用模型的组件
2. 新增预警历史汇总查询页面
3. 新增操作历史汇总查询页面
4. 新增设备历史汇总查询页面

hongrunxia 6 months ago
parent
commit
7a201966ed

+ 2 - 2
.env.development

@@ -27,6 +27,6 @@ VITE_GLOB_API_URL_PREFIX=
 
 #微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
 #VITE_APP_SUB_APP = [["micro-need-air", "//10.10.150.72:8099/"], ["micro-vent-3dModal", "//localhost:8091/"], ["micro-fire-front", "//localhost:8090/"]]
-# VITE_APP_SUB_APP = [["micro-vent-3dModal", "//192.168.183.154:8091/", "micro-vent-3dModal"], ["micro-need-air", "//localhost:8099/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
-VITE_APP_SUB_APP = [["micro-vent-3dModal", "//localhost:8091/", "micro-vent-3dModal"], ["micro-need-air", "//localhost:8099/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
+VITE_APP_SUB_APP = [["micro-vent-3dModal", "//192.168.183.154:8091/", "micro-vent-3dModal"], ["micro-need-air", "//localhost:8099/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
+# VITE_APP_SUB_APP = [["micro-vent-3dModal", "//localhost:8091/", "micro-vent-3dModal"], ["micro-need-air", "//localhost:8099/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
 # VITE_APP_SUB_APP = [["micro-vent-3dModal", "//localhost:8091/"], ["micro-need-air", "//localhost:8099/"], ["micro-fire-front", "//localhost:8090/"]]

+ 2 - 2
public/js/config.js

@@ -10,8 +10,8 @@ const History_Type = {
 }
 
 const VENT_PARAM = {
-  // simulatedPassword: '123456', //(simulatedPassword 为空时有密码输入框弹出,不为空时不弹出密码输入框,无需输入密码)
-  simulatedPassword: '',
+  simulatedPassword: '123456', //(simulatedPassword 为空时有密码输入框弹出,不为空时不弹出密码输入框,无需输入密码)
+  // simulatedPassword: '',
   showReport: true,
   isoOpenSso: 'false'
 }

+ 235 - 0
src/layouts/default/header/components/MessageBroadcast.vue

@@ -0,0 +1,235 @@
+<template>
+  <div style="position: fixed; z-index: 999; right: 95px; top: 18px; color: #fff">
+    <div class="btn" @click="showWarningBroad">
+      <a-badge :dot="isWarningDot">
+        <BellOutlined style="font-size: 22px; color: #fff; margin-right: 20px" />
+      </a-badge>
+    </div>
+    <div v-if="isShowWarningBroad" class="broadcast">
+      <div class="title">
+        <div class="message-title">预警通知</div>
+        <div class="badge-box">
+          <SoundOutlined :class="{ 'no-play': !isBroad }" style="font-size: 20px; color: #000" @click="handleBroad" />
+        </div>
+      </div>
+      <div class="broadcast-context">
+        <div class="context-tab">
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 0 }" @click="toSelectList(0)">全部</div>
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 1 }" @click="toSelectList(1)">未读</div>
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 2 }" @click="toSelectList(2)">已读</div>
+        </div>
+        <div class="context-box">
+          <div v-if="broadcastList.length == 0" class="no-context">暂无内容</div>
+          <div
+            class="context-detail"
+            v-else
+            v-for="(item, index) in broadcastList"
+            :key="index"
+            :style="{ color: item['isok'] == 0 ? 'red' : '#000' }"
+          >
+            <div>{{ item['createTime'] }}</div>
+            <div>{{ item['devicekind_dictText'] }}</div>
+            <div>{{ item['wardescrip'] || item['nwartype_dictText'] }}</div>
+            <div>{{ item['isok'] ? '已解决' : '未解决' }}</div>
+          </div>
+          <div v-if="broadcastList.length > 0" class="more" @click="toMore">更多>></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts">
+  import { Tooltip, Badge } from 'ant-design-vue';
+  import { SoundOutlined, BellOutlined, WarningOutlined } from '@ant-design/icons-vue';
+  import Icon from '/@/components/Icon';
+  import { defineComponent, ref, unref, onMounted } from 'vue';
+  import { defHttp } from '/@/utils/http/axios';
+  import { useRouter } from 'vue-router';
+  import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
+  import { getToken } from '/@/utils/auth';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import SpeakVoice from './notify/speakVoice';
+
+  export default defineComponent({
+    name: 'VoiceBroadcast',
+    components: { Icon, Tooltip, Badge, SoundOutlined, BellOutlined, WarningOutlined },
+
+    setup() {
+      const speakVoice = new SpeakVoice();
+      const userStore = useUserStore();
+      const glob = useGlobSetting();
+      const router = useRouter();
+      const list = (params) => defHttp.get({ url: '/safety/ventanalyAlarmLog/list', params });
+      const activeKey = ref(0);
+      const isShowWarningBroad = ref(false);
+      const isBroad = ref(true);
+      const isWarningDot = ref(false);
+      const broadcastList = ref([]);
+      function showWarningBroad() {
+        isShowWarningBroad.value = !isShowWarningBroad.value;
+        if (isShowWarningBroad.value) {
+          toSelectList(0);
+        }
+      }
+
+      function handleBroad() {
+        isBroad.value = !isBroad.value;
+      }
+
+      async function toSelectList(key) {
+        activeKey.value = key;
+        const res = await list({ pageSize: 20, devicetype: '', isok: key == 1 ? 0 : key == 2 ? 1 : null });
+        broadcastList.value = res['records'];
+        // const isHasWarning = broadcastList.value.findIndex((item) => !item['isok']);
+        // isWarningDot.value = isHasWarning > -1 ? true : false;
+      }
+
+      async function toMore() {
+        await router.push({ path: '/monitorChannel/device-monitor/warningHistory' });
+        showWarningBroad();
+      }
+
+      // 初始化 WebSocket
+      function initWebSocket() {
+        let token = getToken();
+        //将登录token生成一个短的标识
+        // let wsClientId = md5(token);
+        // let userId = unref(userStore.getUserInfo).id + '_' + wsClientId;
+        let userId = unref(userStore.getUserInfo).id + '?token=' + token;
+        // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
+        let url = glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
+        connectWebSocket(url);
+        onWebSocket(onWebSocketMessage);
+      }
+
+      function onWebSocketMessage(data) {
+        // console.log('WebSocket 监测消息--------------》', data);
+        if (data.topic === 'warn' || data.cmd === 'user') {
+          if (isBroad.value) {
+            const messageText = data['warndata'];
+            // const messageText = '这是一个测试';
+            speakVoice.handleReply(messageText);
+          }
+          if (!isShowWarningBroad.value) {
+            isWarningDot.value = true;
+          } else {
+            isWarningDot.value = false;
+          }
+          setTimeout(() => {
+            if (isShowWarningBroad.value) {
+              toSelectList(0);
+            }
+          }, 0);
+        }
+      }
+      onMounted(() => {
+        initWebSocket();
+      });
+
+      return { showWarningBroad, isShowWarningBroad, activeKey, toSelectList, broadcastList, toMore, isBroad, handleBroad, isWarningDot };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  .btn {
+    line-height: 30px;
+    margin-right: 20px;
+    cursor: pointer;
+    display: flex;
+  }
+  .no-play {
+    background: linear-gradient(
+      to bottom left,
+      transparent 0%,
+      transparent calc(50% - 1px),
+      #000000 50%,
+      transparent calc(50% + 1px),
+      transparent 100%
+    );
+  }
+  .broadcast {
+    width: 400px;
+    height: 250px;
+    border-radius: 4px;
+    position: fixed;
+    top: 42px;
+    right: 20px;
+    background-color: rgb(255, 255, 255);
+    z-index: 9999999;
+    color: #000;
+    .title {
+      height: 48px;
+      padding: 0 20px;
+      box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
+      :deep(.ant-badge:not(.ant-badge-status)) {
+        margin-right: 40px !important;
+      }
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 5px;
+      .message-title {
+        font-size: 20px;
+        // font-weight: 600;
+      }
+      .badge-box {
+        display: flex;
+        align-items: center;
+        .badge-title {
+          display: inline-block;
+          width: 62px;
+          line-height: 24px;
+          background-color: #2174f0;
+          border-radius: 26px;
+          text-align: center;
+          color: #fff;
+          padding-bottom: 2px;
+        }
+      }
+    }
+    .broadcast-context {
+      .context-tab {
+        display: flex;
+        .context-tab-item {
+          line-height: 30px;
+          background-color: #6b6b6b;
+          border-radius: 26px;
+          text-align: center;
+          padding: 0 10px;
+          color: #fff;
+          margin: 5px;
+          cursor: pointer;
+        }
+        .context-tab-item-active {
+          background-color: #2174f0;
+        }
+      }
+      .context-box {
+        flex: 1;
+        padding: 0 10px;
+        height: 150px;
+        overflow-y: auto;
+        .no-context {
+          display: flex;
+          justify-content: center;
+          padding-top: 30px;
+          font-size: 16px;
+        }
+        .context-detail {
+          display: flex;
+          div {
+            padding: 0 3px;
+            line-height: 24px;
+          }
+        }
+        .more {
+          cursor: pointer;
+          &:hover {
+            color: #2174f0;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 1 - 1
src/layouts/default/header/components/VoiceBroadcast.vue

@@ -6,7 +6,7 @@
       <a href="#" class="head-example"></a>
     </a-badge> -->
       <a-badge :dot="isWarningDot">
-        <!-- <BellOutlined style="font-size: 22px; color: #fff; margin-right: 20px" /> -->
+        <BellOutlined style="font-size: 22px; color: #fff; margin-right: 20px" />
         <WarningOutlined style="font-size: 22px; color: #fff" />
       </a-badge>
     </div>

+ 2 - 0
src/views/vent/home/clique/index.vue

@@ -83,6 +83,8 @@
   //文件共享中心数据
   let shareData = reactive<any[]>([]);
 
+  // let isWarnign = ref(false)
+
   //通风巷道长度统计数据
   let roadData = reactive({
     totallength: 0,

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

@@ -93,6 +93,7 @@
         </div>
       </div>
     </template>
+    <VentModal style="width: 100%; height: 100%; position: absolute" />
   </div>
 </template>
 <script lang="ts" setup>
@@ -106,6 +107,7 @@
   import ModuleCommon from './components/ModuleCommon.vue';
   import ModuleBD from './components/ModuleBD.vue';
   import { testConfigBDFire } from './configurable.data';
+  import VentModal from '/@/components/vent/micro/ventModal.vue';
 
   interface EnhancedConfig extends Config {
     visible: boolean;

+ 14 - 25
src/views/vent/monitorManager/comment/components/DetailModal.vue

@@ -1,39 +1,28 @@
 <template>
   <BasicModal @register="register" title="预警详情" width="100%" v-bind="$attrs" @ok="onSubmit" @cancel="onSubmit" :defaultFullscreen="true">
-    <div>
-      1223
-    </div>
+    <div> 1223 </div>
   </BasicModal>
 </template>
 <script lang="ts" setup>
+  import { onMounted, ref, defineEmits, onUnmounted, watch } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import deviceTable from '/@/views/vent/deviceManager/deviceTable/index.vue';
 
-import { onMounted, ref, defineEmits, onUnmounted, watch } from 'vue';
-import { BasicModal, useModalInner } from '/@/components/Modal';
-import deviceTable from '/@/views/vent/deviceManager/deviceTable/index.vue'
+  const emit = defineEmits(['close', 'register']);
+  const props = defineProps({});
 
-const emit = defineEmits(['close', 'register'])
-const props = defineProps({
-  
-})
+  // 注册 modal
+  const [register, { closeModal }] = useModalInner();
 
-// 注册 modal
-const [register, { closeModal }] = useModalInner();
+  async function onSubmit() {
+    emit('close');
+    closeModal();
+  }
 
-async function onSubmit() {
-  emit('close')
-  closeModal();
-}
-
-onMounted(async () => {
-
-});
-onUnmounted(() => {
-
-});
+  onMounted(async () => {});
+  onUnmounted(() => {});
 </script>
 <style scoped lang="less">
-
   @import '/@/design/vent/color.less';
   @import '/@/design/vent/modal.less';
-  
 </style>

+ 1 - 1
src/views/vent/monitorManager/comment/components/reportInfo.vue

@@ -83,7 +83,7 @@
               autosave: false, //是否自动保存
               forcesave: true, //定义保存按钮是否显示
               hideRightMenu: true,
-              spellcheck: false,//ture打开拼写检查,false关闭拼写检查。(默认为ture)
+              spellcheck: false, //ture打开拼写检查,false关闭拼写检查。(默认为ture)
             },
             //用户信息
             user: {

+ 286 - 0
src/views/vent/monitorManager/deviceMC/AlarmHistoryTable.vue

@@ -0,0 +1,286 @@
+<template>
+  <div class="alarm-history-table">
+    <BasicTable ref="alarmHistory" @register="registerTable">
+      <template #form-onExportXls>
+        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls()"> 导出</a-button>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+
+<script lang="ts" name="system-user" setup>
+  //ts语法
+  import { watch, ref, defineExpose, inject, onMounted } from 'vue';
+  import { BasicTable } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { defHttp } from '/@/utils/http/axios';
+  import dayjs from 'dayjs';
+  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+  import { safetyDeviceList, safetyList } from './safety.api';
+
+  const props = defineProps({
+    columnsType: {
+      type: String,
+      required: true,
+    },
+    columns: {
+      type: Array,
+      // required: true,
+      default: () => [],
+    },
+    deviceType: {
+      type: String,
+      required: true,
+    },
+    deviceListApi: {
+      type: Function,
+    },
+    designScope: {
+      type: String,
+    },
+    sysId: {
+      type: String,
+    },
+    scroll: {
+      type: Object,
+      default: { y: 0 },
+    },
+    list: {
+      type: Function,
+      default: (params) => defHttp.get({ url: '/safety/ventanalyAlarmLog/list', params }),
+    },
+  });
+
+  const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
+  const globalConfig = inject('globalConfig');
+  const alarmHistory = ref();
+  const columns = ref([]);
+  const deviceOptions = ref([]);
+  const dataTypeName = ref('');
+
+  const tableScroll = props.scroll.y ? ref({ y: props.scroll.y - 100 }) : ref({});
+
+  async function getDeviceList() {
+    let result;
+    const res = await getDeviceListApi({ devicetype: props.deviceType, filterParams: { dataTypeName: dataTypeName.value }, pageSize: 10000 });
+    if (res['records'] && res['records'].length > 0) {
+      result = res['records'];
+    } else if (res['msgTxt'] && res['msgTxt'][0] && res['msgTxt'][0]['datalist']) {
+      result = res['msgTxt'][0]['datalist'];
+    }
+
+    if (result && result.length > 0) {
+      deviceOptions.value = [];
+      deviceOptions.value = result.map((item, index) => {
+        return {
+          label: item['strinstallpos'],
+          value: item['id'] || item['deviceID'],
+          strtype: item['strtype'] || item['deviceType'],
+          strinstallpos: item['strinstallpos'],
+          devicekind: item['devicekind'],
+          stationtype: item['stationtype'],
+        };
+      });
+    } else {
+      deviceOptions.value = [];
+    }
+    await getForm().setFieldsValue({ deviceId: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '' });
+  }
+
+  watch(
+    () => {
+      return props.columnsType;
+    },
+    async (newVal) => {
+      if (!newVal) return;
+
+      const column = getTableHeaderColumns(newVal + '_history');
+      if (column && column.length < 1) {
+        const arr = newVal.split('_');
+        const columnKey = arr.reduce((prev, cur, index) => {
+          if (index !== arr.length - 2) {
+            return prev + '_' + cur;
+          } else {
+            return prev;
+          }
+        });
+        columns.value = getTableHeaderColumns(arr[0] + '_history');
+      } else {
+        columns.value = column;
+      }
+      if (alarmHistory.value) reload();
+    },
+    {
+      immediate: true,
+    }
+  );
+
+  watch(
+    () => props.deviceType,
+    async () => {
+      if (alarmHistory.value) getForm().resetFields();
+      await getDeviceList();
+    }
+  );
+
+  watch(
+    () => props.scroll.y,
+    (newVal) => {
+      if (newVal) {
+        tableScroll.value = { y: newVal - 100 };
+      } else {
+        tableScroll.value = {};
+      }
+    }
+  );
+
+  // 列表页面公共参数、方法
+  const { tableContext, onExportXls } = useListPage({
+    tableProps: {
+      api: safetyList,
+      columns: props.columnsType ? columns : (props.columns as any[]),
+      canResize: true,
+      showTableSetting: false,
+      showActionColumn: false,
+      bordered: false,
+      size: 'small',
+      scroll: tableScroll,
+      formConfig: {
+        labelAlign: 'left',
+        showAdvancedButton: false,
+        // autoAdvancedCol: 2,
+        schemas: [
+          {
+            field: 'startTime',
+            label: '开始时间',
+            component: 'DatePicker',
+            defaultValue: dayjs().add(-30, 'day').format('YYYY-MM-DD HH:mm:ss'),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            field: 'endTime',
+            label: '结束时间',
+            component: 'DatePicker',
+            defaultValue: dayjs(),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            label: '设备类型',
+            field: 'dataTypeName',
+            component: 'ApiSelect',
+            componentProps: {
+              api: safetyDeviceList.bind(null, { devicetype: 'safetymonitor', code: 'dataTypeName' }),
+              labelField: 'name',
+              valueField: 'code',
+              onChange: async (e, option) => {
+                console.log('1111', e, option);
+                dataTypeName.value = e;
+                await getDeviceList();
+              },
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            label: '查询设备',
+            field: 'deviceId',
+            component: 'Select',
+            defaultValue: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '',
+            componentProps: {
+              showSearch: true,
+              filterOption: (input: string, option: any) => {
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+              },
+              options: deviceOptions,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+        ],
+      },
+      fetchSetting: {
+        listField: 'records',
+      },
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        pageSizeOptions: ['10', '30', '50', '100'],
+      },
+      // beforeFetch(params) {
+      //   params.devicetype = props.deviceType + '*';
+      //   if (props.sysId) {
+      //     params.sysId = props.sysId;
+      //   }
+      // },
+    },
+    exportConfig: {
+      name: '预警历史列表',
+      url: '/safety/ventanalyAlarmLog/exportXls',
+    },
+  });
+  //注册table数据
+  const [registerTable, { reload, setLoading, getForm }] = tableContext;
+
+  onMounted(async () => {
+    await getDeviceList();
+  });
+
+  defineExpose({ setLoading });
+</script>
+
+<style scoped lang="less">
+  @ventSpace: zxm;
+
+  :deep(.ventSpace-table-body) {
+    height: auto !important;
+  }
+  :deep(.zxm-picker) {
+    height: 30px !important;
+  }
+  .alarm-history-table {
+    width: 100%;
+    :deep(.jeecg-basic-table-form-container) {
+      .@{ventSpace}-form {
+        padding: 0 !important;
+        border: none !important;
+        margin-bottom: 0 !important;
+        .@{ventSpace}-picker,
+        .@{ventSpace}-select-selector {
+          width: 100% !important;
+          background: #00000017;
+          border: 1px solid #b7b7b7;
+          input,
+          .@{ventSpace}-select-selection-item,
+          .@{ventSpace}-picker-suffix {
+            color: #fff;
+          }
+          .@{ventSpace}-select-selection-placeholder {
+            color: #ffffffaa;
+          }
+        }
+      }
+      .@{ventSpace}-table-title {
+        min-height: 0 !important;
+      }
+    }
+  }
+</style>

+ 486 - 0
src/views/vent/monitorManager/deviceMC/HistoryTable.vue

@@ -0,0 +1,486 @@
+<template>
+  <div class="history-table" v-if="loading">
+    <BasicTable ref="historyTable" @register="registerTable" :data-source="dataSource">
+      <template #bodyCell="{ column, record }">
+        <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == '0' ? 'green' : 'red'">{{
+          record.warnFlag == '0' ? '正常' : '报警'
+        }}</a-tag>
+        <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == '0' ? '#f00' : 'green'">{{
+          record.netStatus == '0' ? '断开' : '连接'
+        }}</a-tag>
+        <slot name="filterCell" v-bind="{ column, record }"></slot>
+      </template>
+
+      <template #form-submitBefore>
+        <a-button type="primary" preIcon="ant-design:search-outlined" @click="getDataSource">查询</a-button>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  //ts语法
+  import { watchEffect, ref, watch, defineExpose, inject, nextTick } from 'vue';
+  import { FormSchema } from '/@/components/Form/index';
+  import { BasicTable } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { defHttp } from '/@/utils/http/axios';
+  import dayjs from 'dayjs';
+  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+  import { onMounted } from 'vue';
+  import { safetyDeviceList } from './safety.api';
+
+  const globalConfig = inject('globalConfig');
+  const props = defineProps({
+    columnsType: {
+      type: String,
+    },
+    columns: {
+      type: Array,
+      // required: true,
+      default: () => [],
+    },
+    deviceType: {
+      type: String,
+      required: true,
+    },
+    deviceListApi: {
+      type: Function,
+    },
+    deviceArr: {
+      type: Array,
+      // required: true,
+      default: () => [],
+    },
+    designScope: {
+      type: String,
+    },
+    sysId: {
+      type: String,
+    },
+    deviceId: {
+      type: String,
+    },
+    scroll: {
+      type: Object,
+      default: { y: 0 },
+    },
+    formSchemas: {
+      type: Array<FormSchema>,
+      default: () => [],
+    },
+  });
+
+  const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
+  const historyTable = ref();
+  const loading = ref(false);
+  const stationType = ref('plc1');
+  const dataSource = ref([]);
+  const dataTypeName = ref('');
+  const intervalMap = new Map([
+    ['1', '1s'],
+    ['2', '5s'],
+    ['3', '10s'],
+    ['4', '30s'],
+    ['5', '1m'],
+    ['6', '10m'],
+    ['7', '30m'],
+    ['8', '1h'],
+  ]);
+
+  const getExportXlsUrl = () => {
+    if (stationType.value !== 'redis') {
+      return '/safety/ventanalyMonitorData/exportXls';
+    } else {
+      return '/monitor/history/getHistoryData/exportXls';
+    }
+  };
+  const emit = defineEmits(['change']);
+
+  const historyType = ref('');
+  const columns = ref([]);
+  const tableScroll = props.scroll.y ? ref({ y: props.scroll.y - 100 }) : ref({});
+  let deviceOptions = ref([]);
+  const deviceTypeStr = ref('');
+  loading.value = true;
+
+  watch(
+    () => {
+      return props.columnsType;
+    },
+    async (newVal) => {
+      if (!newVal) return;
+      if (historyTable.value) getForm().resetFields();
+      await getDeviceList();
+      dataSource.value = [];
+      const column = getTableHeaderColumns(newVal.includes('_history') ? newVal : newVal + '_history');
+      if (column && column.length < 1) {
+        const arr = newVal.split('_');
+        console.log('历史记录列表表头------------>', arr[0] + '_monitor');
+        columns.value = getTableHeaderColumns(arr[0] + '_history');
+      } else {
+        columns.value = column;
+      }
+      if (historyTable.value) reload();
+    },
+    {
+      immediate: true,
+    }
+  );
+
+  watch(historyType, (type) => {
+    if (!type) return;
+    // if (historyTable.value) getForm().resetFields()
+    const column = getTableHeaderColumns(type.includes('_history') ? type : type + '_history');
+    if (column && column.length < 1) {
+      const arr = type.split('_');
+      columns.value = getTableHeaderColumns(arr[0] + '_history');
+    } else {
+      columns.value = column;
+    }
+    setColumns(columns.value);
+  });
+
+  watch(
+    () => props.scroll.y,
+    (newVal) => {
+      if (newVal) {
+        tableScroll.value = { y: newVal - 100 };
+      } else {
+        tableScroll.value = {};
+      }
+    }
+  );
+
+  watch(
+    () => props.deviceId,
+    async () => {
+      await getForm().setFieldsValue({});
+      await getDeviceList();
+    }
+  );
+
+  async function getDeviceList() {
+    let result;
+    const res = await getDeviceListApi({ devicetype: props.deviceType, filterParams: { dataTypeName: dataTypeName.value }, pageSize: 10000 });
+    if (res['records'] && res['records'].length > 0) {
+      result = res['records'];
+    } else if (res['msgTxt'] && res['msgTxt'][0] && res['msgTxt'][0]['datalist']) {
+      result = res['msgTxt'][0]['datalist'];
+    }
+
+    if (result && result.length > 0) {
+      deviceOptions.value = [];
+      deviceOptions.value = result.map((item, index) => {
+        return {
+          label: item['strinstallpos'],
+          value: item['id'] || item['deviceID'],
+          strtype: item['strtype'] || item['deviceType'],
+          strinstallpos: item['strinstallpos'],
+          devicekind: item['devicekind'],
+          stationtype: item['stationtype'],
+        };
+      });
+      stationType.value = deviceOptions.value[0]['stationtype'];
+    } else {
+      deviceOptions.value = [];
+      stationType.value = '';
+    }
+    await getForm().setFieldsValue({ gdeviceid: props.deviceId ? props.deviceId : deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '' });
+  }
+
+  async function getDataSource() {
+    dataSource.value = [];
+    setLoading(true);
+    const stationTypeStr = stationType.value;
+    const formData = getForm().getFieldsValue();
+    const pagination = getPaginationRef();
+    formData['pageNo'] = pagination['current'];
+    formData['pageSize'] = pagination['pageSize'];
+    formData['column'] = 'createTime';
+    if (stationTypeStr !== 'redis') {
+      formData['strtype'] = deviceTypeStr.value
+        ? deviceTypeStr.value
+        : deviceOptions.value[0]['strtype']
+        ? deviceOptions.value[0]['strtype']
+        : props.deviceType + '*';
+      if (props.sysId) {
+        formData['sysId'] = props.sysId;
+      }
+      const result = await defHttp.get({ url: '/safety/ventanalyMonitorData/listdays', params: formData });
+      setPagination({ total: Math.abs(result['datalist']['total']) || 0 });
+      if (result['datalist']['records'].length > 0) {
+        dataSource.value = result['datalist']['records'].map((item: any) => {
+          return Object.assign(item, item['readData']);
+        });
+      } else {
+        dataSource.value = [];
+      }
+    } else {
+      const params = {
+        pageNum: pagination['current'],
+        pageSize: pagination['pageSize'],
+        column: pagination['createTime'],
+        startTime: formData['ttime_begin'],
+        endTime: formData['ttime_end'],
+        deviceId: formData['gdeviceid'],
+        strtype: props.deviceType + '*',
+        sysId: props.sysId,
+        interval: intervalMap.get(formData['skip']) ? intervalMap.get(formData['skip']) : '1h',
+        isEmployee: props.deviceType.startsWith('vehicle') ? false : true,
+      };
+      const result = await defHttp.post({ url: '/monitor/history/getHistoryData', params: params });
+      setPagination({ total: Math.abs(result['total']) || 0 });
+      dataSource.value = result['records'] || [];
+    }
+    setLoading(false);
+  }
+
+  // 列表页面公共参数、方法
+  const { tableContext, onExportXls } = useListPage({
+    tableProps: {
+      // api: list,
+      columns: props.columnsType ? columns : (props.columns as any[]),
+      canResize: true,
+      showTableSetting: false,
+      showActionColumn: false,
+      bordered: false,
+      size: 'small',
+      scroll: tableScroll,
+      showIndexColumn: true,
+      tableLayout: 'auto',
+      formConfig: {
+        labelAlign: 'left',
+        showAdvancedButton: false,
+        showSubmitButton: false,
+        showResetButton: false,
+        baseColProps: {
+          xs: 24,
+          sm: 24,
+          md: 24,
+          lg: 9,
+          xl: 7,
+          xxl: 4,
+        },
+        schemas: [
+          {
+            field: 'ttime_begin',
+            label: '开始时间',
+            component: 'DatePicker',
+            defaultValue: dayjs().startOf('date'),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            field: 'ttime_end',
+            label: '结束时间',
+            component: 'DatePicker',
+            defaultValue: dayjs(),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            label: '设备类型',
+            field: 'dataTypeName',
+            component: 'ApiSelect',
+            componentProps: {
+              api: safetyDeviceList.bind(null, { devicetype: 'safetymonitor', code: 'dataTypeName' }),
+              labelField: 'name',
+              valueField: 'code',
+              onChange: async (e, option) => {
+                console.log('1111', e, option);
+                dataTypeName.value = e;
+                await getDeviceList();
+              },
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            label: '查询设备',
+            field: 'gdeviceid',
+            component: 'Select',
+            defaultValue: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '',
+            required: true,
+            componentProps: {
+              showSearch: true,
+              filterOption: (input: string, option: any) => {
+                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+              },
+              options: deviceOptions,
+              onChange: (e, option) => {
+                if (option && (option['strinstallpos'] || option['strtype'] || option['devicekind']))
+                  historyType.value = option['strtype'] || option['devicekind'];
+                if (option['strtype']) deviceTypeStr.value = option['strtype'];
+                stationType.value = option['stationtype'];
+                nextTick(async () => {
+                  await getDataSource();
+                });
+              },
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            label: '间隔时间',
+            field: 'skip',
+            component: 'Select',
+            defaultValue: '8',
+            componentProps: {
+              options: [
+                {
+                  label: '1秒',
+                  value: '1',
+                },
+                {
+                  label: '5秒',
+                  value: '2',
+                },
+                {
+                  label: '10秒',
+                  value: '3',
+                },
+                {
+                  label: '30秒',
+                  value: '4',
+                },
+                {
+                  label: '1分钟',
+                  value: '5',
+                },
+                {
+                  label: '10分钟',
+                  value: '6',
+                },
+                {
+                  label: '30分钟',
+                  value: '7',
+                },
+                {
+                  label: '1小时',
+                  value: '8',
+                },
+              ],
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+        ],
+      },
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        pageSizeOptions: ['10', '30', '50', '100'],
+        showQuickJumper: false,
+      },
+    },
+    exportConfig: {
+      name: '历史列表',
+      url: getExportXlsUrl(),
+    },
+  });
+
+  //注册table数据
+  const [registerTable, { reload, setLoading, getForm, setColumns, getPaginationRef, setPagination }] = tableContext;
+
+  watchEffect(() => {
+    if (historyTable.value && dataSource) {
+      const data = dataSource.value || [];
+      emit('change', data);
+    }
+  });
+
+  onMounted(async () => {
+    await getDeviceList();
+    if (deviceOptions.value[0]) {
+      stationType.value = deviceOptions.value[0]['stationtype'];
+      historyType.value = deviceOptions.value[0]['strtype'] || deviceOptions.value[0]['devicekind'];
+      nextTick(async () => {
+        await getDataSource();
+      });
+    }
+
+    watch([() => getPaginationRef()['current'], () => getPaginationRef()['pageSize']], async () => {
+      if (deviceOptions.value[0]) {
+        if (deviceOptions.value[0]) {
+          await getDataSource();
+        }
+      }
+    });
+  });
+  defineExpose({ setLoading });
+</script>
+
+<style scoped lang="less">
+  @import '/@/design/vent/color.less';
+
+  :deep(.@{ventSpace}-table-body) {
+    height: auto !important;
+  }
+  :deep(.zxm-picker) {
+    height: 30px !important;
+  }
+  .history-table {
+    width: 100%;
+    :deep(.jeecg-basic-table-form-container) {
+      .@{ventSpace}-form {
+        padding: 0 !important;
+        border: none !important;
+        margin-bottom: 0 !important;
+        .@{ventSpace}-picker,
+        .@{ventSpace}-select-selector {
+          width: 100% !important;
+          height: 100%;
+          background: #00000017;
+          border: 1px solid #b7b7b7;
+          input,
+          .@{ventSpace}-select-selection-item,
+          .@{ventSpace}-picker-suffix {
+            color: #fff;
+          }
+          .@{ventSpace}-select-selection-placeholder {
+            color: #ffffffaa;
+          }
+        }
+      }
+      .@{ventSpace}-table-title {
+        min-height: 0 !important;
+      }
+    }
+    .pagination-box {
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+      .page-num {
+        border: 1px solid #0090d8;
+        padding: 4px 8px;
+        margin-right: 5px;
+        color: #0090d8;
+      }
+      .btn {
+        margin-right: 10px;
+      }
+    }
+  }
+  // :deep(.zxm-select-selector){
+  //   height: 42px !important;
+  // }
+</style>

+ 555 - 0
src/views/vent/monitorManager/deviceMC/index.vue

@@ -0,0 +1,555 @@
+<template>
+  <div class="device-box" id="monitorBox">
+    <a-tabs class="tabs-box" type="card" v-model:activeKey="activeKey" @change="tabChange" id="tabsBox" v-if="isRefresh">
+      <a-tab-pane key="1" tab="实时监测">
+        <template v-if="deviceType == 'fan' && activeKey == '1'">
+          <GroupMonitorTable :dataSource="dataSource" :columnsType="`${deviceType}_monitor`" />
+        </template>
+        <template v-else-if="activeKey == '1' && deviceType">
+          <MonitorTable
+            ref="monitorTable"
+            :columnsType="`${deviceType}_monitor`"
+            :dataSource="dataSource"
+            design-scope="device_monitor"
+            :isShowPagination="true"
+            :is-show-select="false"
+            title="设备监测"
+            :form-config="deviceType == 'safetymonitor' ? formConfig : undefined"
+            :scroll="{ y: 650 }"
+          >
+            <template #filterCell="{ column, record }">
+              <template v-if="deviceType.startsWith('gate')">
+                <template v-if="record.frontGateOpenCtrl == 1 || record.frontGateOpenCtrl === true">
+                  <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0" color="red"
+                    >正在打开</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'frontGateOpen'" color="processing">打开</a-tag>
+                </template>
+                <template v-else-if="record.frontGateOpenCtrl == 0 || record.frontGateOpenCtrl === false">
+                  <a-tag v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0" color="red"
+                    >正在关闭</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 1" color="default"
+                    >关闭</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 1 && record.frontGateClose == 0" color="default"
+                    >打开</a-tag
+                  >
+                </template>
+                <template v-if="record.rearGateOpenCtrl == 1 || record.rearGateOpenCtrl === true">
+                  <a-tag v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0" color="red"
+                    >正在打开</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'rearGateOpen'" color="processing">打开</a-tag>
+                </template>
+                <template v-else-if="record.rearGateOpenCtrl == 0 || record.rearGateOpenCtrl === false">
+                  <a-tag v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0" color="red"
+                    >正在关闭</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 1" color="default"
+                    >关闭</a-tag
+                  >
+                  <a-tag v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 1 && record.rearGateClose == 0" color="default"
+                    >打开</a-tag
+                  >
+                </template>
+              </template>
+              <template v-if="deviceType.startsWith('windrect')">
+                <a-tag v-if="column.dataIndex === 'sign'" :color="record.sign == 0 ? '#95CF65' : record.sign == 1 ? '#4590EA' : '#9876AA'">
+                  {{ record.sign == 0 ? '高位' : record.sign == 1 ? '中位' : '低位' }}</a-tag
+                >
+                <template v-if="record && column && column.dataIndex === 'isRun' && record.isRun">
+                  <a-tag v-if="record.isRun == -2 || record.isRun == -1" :color="record.isRun == -2 ? '#95CF65' : '#ED5700'">{{
+                    record.isRun == -2 ? '空闲' : '等待'
+                  }}</a-tag>
+                  <a-tag v-else-if="record.isRun == 100" color="#4693FF">完成</a-tag>
+                  <Progress v-else :percent="Number(record.isRun)" size="small" status="active" />
+                </template>
+              </template>
+              <template v-if="deviceType.startsWith('safetymonitor')">
+                <div v-if="!record.devicename && column.dataIndex === 'devicename'">-</div>
+                <div v-if="!record.V && column.dataIndex === 'V'">-</div>
+                <div v-if="!record.PointUnit && column.dataIndex === 'PointUnit'">-</div>
+                <div v-if="!record.highRange && column.dataIndex === 'highRange'">-</div>
+                <div v-if="!record.lowRange && column.dataIndex === 'lowRange'">-</div>
+                <div v-if="!record.dataTypeName && column.dataIndex === 'dataTypeName'">-</div>
+              </template>
+              <a-tag v-if="column.dataIndex === 'warnFlag'" :color="record.warnFlag == 0 ? 'green' : record.warnFlag == 1 ? '#FF5812' : 'gray'">
+                {{ record.warnFlag == 0 ? '正常' : record.warnFlag == 1 ? '报警' : record.warnFlag == 2 ? '断开' : '未监测' }}</a-tag
+              >
+              <a-tag v-if="column.dataIndex === 'netStatus'" :color="record.netStatus == 0 ? 'default' : 'green'">{{
+                record.netStatus == 0 ? '断开' : '连接'
+              }}</a-tag>
+            </template>
+          </MonitorTable>
+        </template>
+      </a-tab-pane>
+      <a-tab-pane key="2" tab="历史数据">
+        <div class="tab-item">
+          <HistoryTable
+            ref="historyTable"
+            v-if="activeKey == '2'"
+            :columns-type="`${deviceType}`"
+            :device-type="deviceType"
+            :device-list-api="getDeviceList.bind(null, { devicekind: deviceType })"
+            designScope="device-history"
+            @change="changeHis"
+          />
+        </div>
+      </a-tab-pane>
+      <a-tab-pane key="3" tab="报警历史">
+        <div class="tab-item">
+          <AlarmHistoryTable
+            ref="alarmHistoryTable"
+            v-if="activeKey == '3'"
+            columns-type="alarm"
+            :device-type="deviceType"
+            :device-list-api="getDeviceList.bind(null, { devicekind: deviceType })"
+            designScope="alarm-history"
+          />
+        </div>
+      </a-tab-pane>
+      <a-tab-pane key="4" tab="操作历史" v-if="deviceType !== 'safetymonitor'">
+        <div class="tab-item">
+          <HandlerHistoryTable
+            ref="handlerHistoryTable"
+            v-if="activeKey == '4'"
+            columns-type="operator_history"
+            :device-type="deviceType"
+            :device-list-api="getDeviceList.bind(null, { devicekind: deviceType })"
+            designScope="operator-history"
+          />
+        </div>
+      </a-tab-pane>
+    </a-tabs>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref, onMounted, onUnmounted, shallowRef, defineProps, unref } from 'vue';
+  import { list, getDeviceList } from './safety.api';
+  import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
+  import HistoryTable from '../comment/HistoryTable.vue';
+  import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
+  import MonitorTable from '../comment/MonitorTable.vue';
+  import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
+  import { Progress } from 'ant-design-vue';
+  import { useRouter } from 'vue-router';
+  import { formConfig } from './safety.data';
+  import { getDictItemsByCode } from '/@/utils/dict';
+
+  // import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
+
+  // const echartsOption = {
+  //   grid: {
+  //     top: '60px',
+  //     left: '10px',
+  //     right: '25px',
+  //     bottom: '5%',
+  //     containLabel: true,
+  //   },
+  //   toolbox: {
+  //     feature: {},
+  //   },
+  // };
+  // let alive = ref(true)
+
+  type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
+
+  const props = defineProps({
+    pageData: {
+      type: Object,
+      default: () => {},
+    },
+  });
+
+  const scroll = {
+    y: 360,
+  };
+  const monitorTable = ref();
+  const historyTable = ref();
+  const alarmHistoryTable = ref();
+  const handlerHistoryTable = ref();
+
+  const isRefresh = ref(true);
+
+  const activeKey = ref('1'); // tab key
+  const dataSource = ref([]); // 实时监测数据
+  const deviceType = ref(''); // 监测设备类型
+
+  // let dataSourceHis = shallowRef([])//历史数据
+
+  //历史数据
+  async function changeHis(data) {
+    // alive.value = false
+    // nextTick(() => {
+    //   dataSourceHis = data
+    //   alive.value = true
+    // })
+  }
+
+  async function tabChange(activeKeyVal) {
+    activeKey.value = activeKeyVal;
+    if (activeKey.value != '1') {
+      if (timer != undefined) {
+        clearTimeout(timer);
+        timer = undefined;
+      }
+    } else {
+      timer = null;
+      await getMonitor(true);
+    }
+  }
+
+  // https获取监测数据
+  let timer: null | NodeJS.Timeout = null;
+  function getMonitor(flag?) {
+    if (deviceType.value) {
+      if (timer) timer = null;
+      if (Object.prototype.toString.call(timer) === '[object Null]') {
+        timer = setTimeout(
+          async () => {
+            await getDataSource();
+            if (timer) {
+              getMonitor();
+            }
+          },
+          flag ? 0 : 1000
+        );
+      }
+    }
+  }
+
+async function getDataSource() {
+  let resultData, searchForm;
+      if (monitorTable.value) {
+        const formData = monitorTable.value.getForm();
+        searchForm = formData.getFieldsValue();
+      }
+
+      if (monitorTable.value) {
+        if (deviceType.value.startsWith('safetymonitor')) {
+          resultData = await list({ devicetype: deviceType.value, pagetype: 'normal', filterParams: { ...searchForm } });
+        } else{
+          resultData = await list({ devicetype: deviceType.value, pagetype: 'normal' });
+        }
+      } else {
+        // 非安全监控
+        resultData = await list({ devicetype: deviceType.value, pagetype: 'normal' });
+      }
+      if (resultData && resultData.msgTxt) {
+        const result = resultData.msgTxt[0];
+        if (result) {
+          const data = result['datalist'].filter((data: any) => {
+            const readData = data.readData;
+            return Object.assign(data, readData);
+          });
+          if (deviceType.value.startsWith('safetymonitor')) {
+            const resultData = <any[]>[];
+            // 如果是安全监控的数据时需要过滤常见设备数据,根据设定的常用安全监控字典去匹配
+            const dictCodes = getDictItemsByCode('safetynormal');
+            if (searchForm && !searchForm['dataTypeName'] && dictCodes && dictCodes.length) {
+              for (let i = 0; i < dictCodes.length; i++) {
+                const dict = dictCodes[i];
+                data.forEach((item) => {
+                  if (dict['value'] == item['dataTypeName']) {
+                    resultData.push(item);
+                  }
+                });
+              }
+              dataSource.value = resultData;
+            } else {
+              dataSource.value = data;
+            }
+          } else {
+            let tableData: any[] = [];
+            let noNetData: any[] = [];
+            data.filter((el) => {
+              if (el.netStatus == 1) {
+                tableData.push(el);
+              } else {
+                noNetData.push(el);
+              }
+            });
+            dataSource.value = [...tableData, ...noNetData];
+          }
+        } else {
+          dataSource.value = [];
+        }
+      } else {
+        dataSource.value = [];
+      }
+}
+
+  onMounted(async () => {
+    const { currentRoute } = useRouter();
+    if (unref(currentRoute)) {
+      const path = unref(currentRoute).path;
+      if (path) {
+        deviceType.value = path.substring(path.lastIndexOf('/') + 1);
+      }
+      await getMonitor(true);
+    }
+  });
+
+  onUnmounted(() => {
+    if (timer) {
+      clearTimeout(timer);
+    }
+    timer = undefined;
+  });
+</script>
+
+<style lang="less" scoped>
+  @import '/@/design/vent/color.less';
+  @import '/@/design/vent/modal.less';
+  @ventSpace: zxm;
+
+  .device-box {
+    width: 100%;
+    height: calc(100% - 100px);
+    padding-bottom: 10px;
+    margin-top: 20px;
+    display: flex;
+    justify-content: center;
+
+    .tabs-box {
+      width: calc(100% - 12px) !important;
+      height: 100% !important;
+      bottom: 3px !important;
+    }
+
+    .device-button-group {
+      position: absolute;
+      top: -30px;
+      display: flex;
+      width: 100%;
+
+      .device-button {
+        height: 26px;
+        padding: 0 20px;
+        background: linear-gradient(45deg, #04e6fb55, #0c5cab55);
+        clip-path: polygon(10px 0, 0 50%, 10px 100%, 100% 100%, calc(100% - 10px) 50%, 100% 0);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        color: #fff;
+        position: relative;
+        cursor: pointer;
+
+        &:nth-child(1) {
+          left: calc(-6px * 1);
+        }
+
+        &:nth-child(2) {
+          left: calc(-6px * 2);
+        }
+
+        &:nth-child(3) {
+          left: calc(-6px * 3);
+        }
+
+        &:nth-child(4) {
+          left: calc(-6px * 4);
+        }
+
+        &:nth-child(5) {
+          left: calc(-6px * 5);
+        }
+
+        &:nth-child(6) {
+          left: calc(-6px * 6);
+        }
+
+        &:nth-child(7) {
+          left: calc(-6px * 7);
+        }
+
+        &:nth-child(8) {
+          left: calc(-6px * 8);
+        }
+
+        &:nth-child(9) {
+          left: calc(-6px * 9);
+        }
+
+        &:nth-child(10) {
+          left: calc(-6px * 10);
+        }
+
+        &:nth-child(11) {
+          left: calc(-6px * 11);
+        }
+
+        &:nth-child(12) {
+          left: calc(-6px * 12);
+        }
+
+        &:nth-child(13) {
+          left: calc(-6px * 13);
+        }
+
+        &:nth-child(14) {
+          left: calc(-6px * 14);
+        }
+
+        &:nth-child(15) {
+          left: calc(-6px * 15);
+        }
+
+        &:first-child {
+          clip-path: polygon(0 0, 10px 50%, 0 100%, 100% 100%, calc(100% - 10px) 50%, 100% 0);
+        }
+      }
+
+      .device-active {
+        background: linear-gradient(45deg, #04e6fb, #0c5cab);
+
+        &::before {
+          border-color: #0efcff;
+          box-shadow: 1px 1px 3px 1px #0efcff inset;
+        }
+      }
+    }
+
+    .enter-detail {
+      color: #fff;
+      cursor: pointer;
+      position: absolute;
+      right: 120px;
+      top: -6px;
+      padding: 5px;
+      border-radius: 5px;
+      margin-left: 8px;
+      margin-right: 8px;
+      width: auto;
+      height: 33px !important;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: #fff;
+      padding: 5px 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);
+      }
+    }
+  }
+
+  :deep(.@{ventSpace}-tabs-tabpane-active) {
+    height: 100%;
+    border: 1px solid #44d3ff70;
+    border-radius: 2px;
+    -webkit-backdrop-filter: blur(8px);
+    box-shadow: 0 0 20px #44b4ff33 inset;
+    background-color: #ffffff11;
+    overflow-y: auto;
+  }
+
+  :deep(.@{ventSpace}-tabs-card) {
+    .@{ventSpace}-tabs-tab {
+      background: linear-gradient(#2cd1ff55, #1eb0ff55);
+      border-color: #74e9fe;
+      border-radius: 0%;
+
+      &:hover {
+        color: #64d5ff;
+      }
+    }
+
+    .@{ventSpace}-tabs-content {
+      height: 100% !important;
+    }
+
+    .@{ventSpace}-tabs-tab.@{ventSpace}-tabs-tab-active .@{ventSpace}-tabs-tab-btn {
+      color: aqua;
+    }
+
+    .@{ventSpace}-tabs-nav::before {
+      border-color: #74e9fe;
+    }
+
+    .@{ventSpace}-picker,
+    .@{ventSpace}-select-selector {
+      width: 100% !important;
+      background: #00000017 !important;
+      border: 1px solid @vent-form-item-boder !important;
+
+      input,
+      .@{ventSpace}-select-selection-item,
+      .@{ventSpace}-picker-suffix {
+        color: #fff !important;
+      }
+
+      .@{ventSpace}-select-selection-placeholder {
+        color: #b7b7b7 !important;
+      }
+    }
+
+    .@{ventSpace}-pagination-next,
+    .action,
+    .@{ventSpace}-select-arrow,
+    .@{ventSpace}-picker-separator {
+      color: #fff !important;
+    }
+
+    .@{ventSpace}-table-cell-row-hover {
+      background: #264d8833 !important;
+    }
+
+    .@{ventSpace}-table-row-selected {
+      background: #00c0a311 !important;
+
+      td {
+        background-color: #00000000 !important;
+      }
+    }
+
+    .@{ventSpace}-table-thead {
+      // background: linear-gradient(#004a8655 0%, #004a86aa 10%) !important;
+      background: #3d9dd45d !important;
+
+      & > tr > th,
+      .@{ventSpace}-table-column-title {
+        // color: #70f9fc !important;
+        border-color: #84f2ff !important;
+        border-left: none !important;
+        border-right: none !important;
+        padding: 7px;
+      }
+    }
+
+    .@{ventSpace}-table-tbody {
+      tr > td {
+        padding: 12px;
+      }
+    }
+
+    .@{ventSpace}-table-tbody > tr:hover.@{ventSpace}-table-row > td {
+      background-color: #26648855 !important;
+    }
+
+    .jeecg-basic-table-row__striped {
+      // background: #97efff11 !important;
+      td {
+        background-color: #97efff11 !important;
+      }
+    }
+  }
+</style>

+ 39 - 0
src/views/vent/monitorManager/deviceMC/safety.api.ts

@@ -0,0 +1,39 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  list = '/monitor/device',
+  baseList = '/safety/ventanalyDeviceInfo/list',
+  deviceTypeList = '/safety/ventanalyDeviceInfo/DeviceKind/queryBySystem',
+  itemList = '/sys/dictItem/list',
+  safetyDeviceList = '/monitor/codeDict',
+  safetyList = '/monitor/history/getAlarmHistoryData',
+  export = '/safety/reportInfo/expComReport?tempName=aqjk',
+  subStationList = '/safety/ventanalySubStation/alllist',
+  initSubStation = '/monitor/initSafetyMonitorDeviceInfo',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.post({ url: Api.list, params });
+
+// 分站查询接口
+export const subStationList = (params) => defHttp.get({ url: Api.subStationList, params });
+// 同步分站
+export const initSubStation = (params) => defHttp.post({ url: Api.initSubStation, params });
+
+export const safetyList = (params) => defHttp.post({ url: Api.safetyList, params });
+
+export const safetyDeviceList = (params) => defHttp.post({ url: Api.safetyDeviceList, params });
+
+/**
+ * 保存或者更新用户
+ * @param params
+ */
+export const getDeviceList = (params) => defHttp.get({ url: Api.baseList, params });
+
+export const getDeviceTypeList = (params) => defHttp.get({ url: Api.deviceTypeList, params });
+
+export const itemList = (params) => defHttp.get({ url: Api.itemList, params });
+
+export const getExportUrl = Api.export;

+ 226 - 0
src/views/vent/monitorManager/deviceMC/safety.data.ts

@@ -0,0 +1,226 @@
+import { safetyDeviceList } from './safety.api';
+
+export const chartsColumns = (deviceType) => {
+  if (deviceType === '') {
+    return [];
+  }
+};
+
+export const formConfig = {
+  labelAlign: 'left',
+  showAdvancedButton: false,
+  showResetButton: false,
+  showSubmitButton: false,
+  schemas: [
+    {
+      label: '设备类型',
+      field: 'dataTypeName',
+      component: 'ApiSelect',
+      componentProps: {
+        api: safetyDeviceList.bind(null, { devicetype: 'safetymonitor', code: 'dataTypeName' }),
+        labelField: 'name',
+        valueField: 'code',
+      },
+    },
+    {
+      label: '设备安装地点',
+      field: 'strinstallpos',
+      component: 'Input',
+    },
+  ],
+};
+
+//测风装置
+export const chartsColumnsRect = [
+  {
+    legend: '风量',
+    seriesName: '(m³/min)',
+    ymax: 10000,
+    yname: 'm³/min',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'm³',
+  },
+  {
+    legend: '气源压力',
+    seriesName: '(MPa)',
+    ymax: 50,
+    yname: 'MPa',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'sourcePressure',
+  },
+];
+
+//局部风机
+export const chartsColumnsFan = [
+  {
+    legend: '风筒风量1',
+    seriesName: '(m³/min)',
+    ymax: 1000,
+    yname: 'm³/min',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'windQuantity1',
+  },
+  {
+    legend: '风筒风量2',
+    seriesName: '(m³/min)',
+    ymax: 1000,
+    yname: 'm³/min',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'windQuantity2',
+  },
+];
+
+//主风
+export const chartsColumnsMain = [
+  {
+    legend: '风量',
+    seriesName: '(m³/min)',
+    ymax: 1000,
+    yname: 'm³/min',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'windQuantity1',
+  },
+  {
+    legend: '频率',
+    seriesName: '(Hz)',
+    ymax: 1000,
+    yname: 'Hz',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'windQuantity2',
+  },
+];
+//光钎测温
+export const chartsColumnsFiber = [
+  {
+    legend: '最高温度',
+    seriesName: '(°C)',
+    ymax: 100,
+    yname: '°C',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'fmax',
+  },
+  {
+    legend: '平均温度',
+    seriesName: '(°C)',
+    ymax: 100,
+    yname: '°C',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'favg',
+  },
+];
+//密闭
+export const chartsColumnsObf = [
+  {
+    legend: '温度',
+    seriesName: '(°C)',
+    ymax: 100,
+    yname: '°C',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'temperature',
+  },
+  {
+    legend: 'O2浓度',
+    seriesName: '(%)',
+    ymax: 100,
+    yname: '%',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'o2val',
+  },
+];
+
+//束管
+
+export const chartsColumnsBun = [
+  {
+    legend: 'CO浓度',
+    seriesName: '(%)',
+    ymax: 100,
+    yname: '%',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'coval',
+  },
+  {
+    legend: 'CO2浓度',
+    seriesName: '(%)',
+    ymax: 100,
+    yname: '%',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'co2val',
+  },
+];
+
+export const chartsColumnsreal = [
+  {
+    legend: '压差',
+    seriesName: '(Pa)',
+    ymax: 100,
+    yname: 'Pa',
+    linetype: 'bar',
+    yaxispos: 'left',
+    color: '#37BCF2',
+    sort: 1,
+    xRotate: 0,
+    dataIndex: 'frontRearDP',
+  },
+  {
+    legend: '气源压力',
+    seriesName: '(MPa)',
+    ymax: 50,
+    yname: 'MPa',
+    linetype: 'line',
+    yaxispos: 'right',
+    color: '#FC4327',
+    sort: 2,
+    xRotate: 0,
+    dataIndex: 'sourcePressure',
+  },
+];
+export const isHaveNoAction = ['safetymonitor', 'wasichoufang'];

+ 29 - 3
src/views/vent/monitorManager/gateMonitor/index.vue

@@ -43,6 +43,16 @@
           </a-radio-group>
           <div class="button-box" @click="playAnimation(7)">切换模式</div>
         </div>
+        <!-- 控制指令是多个 -->
+        <div class="vent-flex-m row" v-else-if="selectData.contrlMod == 'codeCtrl'">
+          <div class="control-title">控制模式:</div>
+          <a-radio-group v-model:value="selectData.autoRoManual">
+            <template v-for="(item, index) in modelList" :key="index">
+              <a-radio :value="item.value" :disabled="true">{{ item.text }}</a-radio>
+            </template>
+          </a-radio-group>
+          <div class="button-box" v-for="(item, index) in modelList" @click="playAnimation(7, item.value)" :key="index">{{ item.text }}</div>
+        </div>
         <div class="vent-flex-m row" v-else>
           <div class="control-title">控制模式:</div>
           <a-radio-group v-model:value="selectData.autoRoManual">
@@ -714,9 +724,25 @@
         }
         break;
       case '7': // 远程与就地
-        data.paramcode = 'autoRoManualControl';
-        data.value = selectData.contrlMod != 'loopCtrl' ? contrlValue : '';
-        selectData.autoRoManual = null;
+        if (selectData.contrlMod == 'codeCtrl') {
+          if (contrlValue == '1') {
+            data.paramcode = 'autoRoManualControl1';
+          } else if (contrlValue == '0') {
+            data.paramcode = 'autoRoManualControl2';
+          } else {
+            data.paramcode = 'autoRoManualControl0';
+          }
+          data.value = '';
+          selectData.autoRoManual = null;
+        } else if (selectData.contrlMod == 'loopCtrl') {
+          data.paramcode = 'autoRoManualControl';
+          data.value = '';
+          selectData.autoRoManual = null;
+        } else {
+          data.paramcode = 'autoRoManualControl';
+          data.value = contrlValue;
+          selectData.autoRoManual = null;
+        }
     }
 
     if (data.paramcode) {

+ 10 - 0
src/views/vent/monitorManager/handlerMonitor/handle.api.ts

@@ -0,0 +1,10 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  list = '/safety/ventanalyDevicesetLog/list',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });

+ 190 - 0
src/views/vent/monitorManager/handlerMonitor/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="alarm-history-table">
+    <BasicTable ref="alarmHistory" @register="registerTable">
+      <template #form-onExportXls>
+        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls()"> 导出</a-button>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+
+<script lang="ts" name="system-user" setup>
+  //ts语法
+  import { watch, ref, defineExpose, inject, onMounted } from 'vue';
+  import { BasicTable } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { defHttp } from '/@/utils/http/axios';
+  import dayjs from 'dayjs';
+  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+  import { list } from './handle.api';
+
+  const props = defineProps({
+    deviceListApi: {
+      type: Function,
+    },
+    designScope: {
+      type: String,
+    },
+    sysId: {
+      type: String,
+    },
+    list: {
+      type: Function,
+      default: (params) => defHttp.get({ url: '/ventanaly-device/safety/ventanalyDevicesetLog/list', params }),
+    },
+  });
+
+  const alarmHistory = ref();
+  const columns = getTableHeaderColumns('operator_history');
+
+  // 列表页面公共参数、方法
+  const { tableContext, onExportXls } = useListPage({
+    tableProps: {
+      api: list,
+      columns: columns,
+      canResize: true,
+      showTableSetting: false,
+      showActionColumn: false,
+      bordered: false,
+      size: 'small',
+      // scroll: { y: 600 },
+      formConfig: {
+        labelAlign: 'left',
+        showAdvancedButton: false,
+        // autoAdvancedCol: 2,
+        schemas: [
+          {
+            label: '设备类型',
+            field: 'devicetype',
+            component: 'MTreeSelect',
+            componentProps: {
+              virtual: false,
+            },
+            colProps: { span: 5 },
+          },
+          {
+            label: '操作类型',
+            field: 'nlogtype',
+            component: 'Select',
+            componentProps: {
+              options: [
+                {
+                  label: '人工远程控制',
+                  value: '1',
+                },
+                {
+                  label: '系统联动控制',
+                  value: '2',
+                },
+                {
+                  label: '其他控制',
+                  value: '3',
+                },
+              ],
+            },
+            colProps: { span: 5 },
+          },
+          {
+            field: 'createTime_begin',
+            label: '开始时间',
+            component: 'DatePicker',
+            defaultValue: dayjs().add(-30, 'day').format('YYYY-MM-DD HH:mm:ss'),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            field: 'createTime_end',
+            label: '结束时间',
+            component: 'DatePicker',
+            defaultValue: dayjs(),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+        ],
+      },
+      fetchSetting: {
+        listField: 'records',
+      },
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        pageSizeOptions: ['10', '30', '50', '100'],
+      },
+      beforeFetch(params) {
+        params.devicetype = params.devicetype ? params.devicetype + '*' : '';
+        return params;
+      },
+    },
+    exportConfig: {
+      name: '操作历史列表',
+      url: '/safety/ventanalyDevicesetLog/exportXls',
+    },
+  });
+  //注册table数据
+  const [registerTable, { setLoading }] = tableContext;
+
+  onMounted(async () => {});
+
+  defineExpose({ setLoading });
+</script>
+
+<style scoped lang="less">
+  @ventSpace: zxm;
+  :deep(.zxm-table-container) {
+    max-height: 720px !important;
+  }
+  :deep(.ventSpace-table-body) {
+    height: auto !important;
+  }
+  :deep(.zxm-picker) {
+    height: 30px !important;
+  }
+  :deep(.@{ventSpace}-picker-dropdown) {
+    position: absolute !important;
+    top: 35px !important;
+    left: 0 !important;
+  }
+  .alarm-history-table {
+    width: 100%;
+    height: 700px;
+    :deep(.jeecg-basic-table-form-container) {
+      .@{ventSpace}-form {
+        padding: 0 !important;
+        border: none !important;
+        margin-bottom: 0 !important;
+        .@{ventSpace}-picker,
+        .@{ventSpace}-select-selector {
+          width: 100% !important;
+          background: #00000017;
+          border: 1px solid #b7b7b7;
+          input,
+          .@{ventSpace}-select-selection-item,
+          .@{ventSpace}-picker-suffix {
+            color: #fff;
+          }
+          .@{ventSpace}-select-selection-placeholder {
+            color: #ffffffaa;
+          }
+        }
+      }
+      .@{ventSpace}-table-title {
+        min-height: 0 !important;
+      }
+    }
+  }
+</style>

+ 218 - 0
src/views/vent/monitorManager/historyMonitor/index.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="alarm-history-table">
+    <BasicTable ref="alarmHistory" @register="registerTable">
+      <template #form-onExportXls>
+        <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls()"> 导出</a-button>
+      </template>
+    </BasicTable>
+  </div>
+</template>
+
+<script lang="ts" name="system-user" setup>
+  //ts语法
+  import { watch, ref, defineExpose, inject, onMounted } from 'vue';
+  import { BasicTable } from '/@/components/Table';
+  import { useListPage } from '/@/hooks/system/useListPage';
+  import { getTableHeaderColumns } from '/@/hooks/web/useWebColumns';
+  import { defHttp } from '/@/utils/http/axios';
+  import dayjs from 'dayjs';
+  import { getAutoScrollContainer } from '/@/utils/common/compUtils';
+  import { list } from './warning.api';
+
+  const props = defineProps({
+    deviceListApi: {
+      type: Function,
+    },
+    designScope: {
+      type: String,
+    },
+    sysId: {
+      type: String,
+    },
+    list: {
+      type: Function,
+      default: (params) => defHttp.get({ url: '/safety/ventanalyAlarmLog/list', params }),
+    },
+  });
+
+  const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
+  const globalConfig = inject('globalConfig');
+  const alarmHistory = ref();
+  const columns = getTableHeaderColumns('alarm_history');
+  const deviceOptions = ref([]);
+  const dataTypeName = ref('');
+  const deviceType = ref('');
+
+  async function getDeviceList() {
+    let result;
+    const res = await getDeviceListApi({ devicetype: deviceType.value, filterParams: { dataTypeName: dataTypeName.value }, pageSize: 10000 });
+    if (res['records'] && res['records'].length > 0) {
+      result = res['records'];
+    } else if (res['msgTxt'] && res['msgTxt'][0] && res['msgTxt'][0]['datalist']) {
+      result = res['msgTxt'][0]['datalist'];
+    }
+
+    if (result && result.length > 0) {
+      deviceOptions.value = [];
+      deviceOptions.value = result.map((item, index) => {
+        return {
+          label: item['strinstallpos'],
+          value: item['id'] || item['deviceID'],
+          strtype: item['strtype'] || item['deviceType'],
+          strinstallpos: item['strinstallpos'],
+          devicekind: item['devicekind'],
+          stationtype: item['stationtype'],
+        };
+      });
+    } else {
+      deviceOptions.value = [];
+    }
+    await getForm().setFieldsValue({ deviceId: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '' });
+  }
+
+  // 列表页面公共参数、方法
+  const { tableContext, onExportXls } = useListPage({
+    tableProps: {
+      api: list,
+      columns: columns,
+      canResize: true,
+      showTableSetting: false,
+      showActionColumn: false,
+      bordered: false,
+      size: 'small',
+      formConfig: {
+        labelAlign: 'left',
+        showAdvancedButton: false,
+        // autoAdvancedCol: 2,
+        schemas: [
+          {
+            label: '设备类型',
+            field: 'devicetype',
+            component: 'MTreeSelect',
+            componentProps: {
+              virtual: false,
+              // onChange: async (e: any) => {
+              //   if (alarmHistory.value) getForm().resetFields();
+              //   deviceType.value = e;
+              //   await getDeviceList();
+              // },
+            },
+            colProps: { span: 6 },
+          },
+          {
+            label: '是否解决',
+            field: 'isok',
+            component: 'Select',
+            componentProps: {
+              options: [
+                {
+                  label: '未解决',
+                  value: '0',
+                },
+                {
+                  label: '已解决',
+                  value: '1',
+                },
+              ],
+            },
+            colProps: { span: 4 },
+          },
+          {
+            field: 'startTime',
+            label: '开始时间',
+            component: 'DatePicker',
+            defaultValue: dayjs().add(-30, 'day').format('YYYY-MM-DD HH:mm:ss'),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+          {
+            field: 'endTime',
+            label: '结束时间',
+            component: 'DatePicker',
+            defaultValue: dayjs(),
+            required: true,
+            componentProps: {
+              showTime: true,
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
+              getPopupContainer: getAutoScrollContainer,
+            },
+            colProps: {
+              span: 4,
+            },
+          },
+        ],
+      },
+      fetchSetting: {
+        listField: 'records',
+      },
+      pagination: {
+        current: 1,
+        pageSize: 10,
+        pageSizeOptions: ['10', '30', '50', '100'],
+      },
+      beforeFetch(params) {
+        params.devicetype = params.devicetype ? params.devicetype + '*' : '';
+        if (props.sysId) {
+          params.sysId = props.sysId;
+        }
+      },
+    },
+    exportConfig: {
+      name: '预警历史列表',
+      url: '/safety/ventanalyAlarmLog/exportXls',
+    },
+  });
+  //注册table数据
+  const [registerTable, { reload, setLoading, getForm }] = tableContext;
+
+  onMounted(async () => {});
+
+  defineExpose({ setLoading });
+</script>
+
+<style scoped lang="less">
+  @ventSpace: zxm;
+  :deep(.zxm-table-container) {
+    max-height: 720px !important;
+  }
+  :deep(.ventSpace-table-body) {
+    height: auto !important;
+  }
+  :deep(.zxm-picker) {
+    height: 30px !important;
+  }
+  .alarm-history-table {
+    width: 100%;
+    :deep(.jeecg-basic-table-form-container) {
+      .@{ventSpace}-form {
+        padding: 0 !important;
+        border: none !important;
+        margin-bottom: 0 !important;
+        .@{ventSpace}-picker,
+        .@{ventSpace}-select-selector {
+          width: 100% !important;
+          background: #00000017;
+          border: 1px solid #b7b7b7;
+          input,
+          .@{ventSpace}-select-selection-item,
+          .@{ventSpace}-picker-suffix {
+            color: #fff;
+          }
+          .@{ventSpace}-select-selection-placeholder {
+            color: #ffffffaa;
+          }
+        }
+      }
+      .@{ventSpace}-table-title {
+        min-height: 0 !important;
+      }
+    }
+  }
+</style>

+ 10 - 0
src/views/vent/monitorManager/historyMonitor/warning.api.ts

@@ -0,0 +1,10 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  list = '/safety/ventanalyAlarmLog/list',
+}
+/**
+ * 列表接口
+ * @param params
+ */
+export const list = (params) => defHttp.get({ url: Api.list, params });

+ 33 - 33
src/views/vent/monitorManager/safetyMonitor/index.vue

@@ -163,7 +163,7 @@
       </a-tab-pane>
     </a-tabs>
     <div class="right-btn-group">
-      <!-- <div class="update-btn">
+      <div class="update-btn">
         <span>同步分站:</span>
         <a-select
           v-model:value="subStation"
@@ -174,7 +174,7 @@
           style="width: 150px"
         />
         <div class="btn btn1" @click="updateSubstation">确定</div>
-      </div> -->
+      </div>
       <div class="export-btn"><div class="btn btn1" @click="exportData">一键导出</div></div>
     </div>
   </div>
@@ -298,37 +298,37 @@
       });
       if (deviceType.value == 'safetymonitor') {
         // 如果是安全监控的数据时需要过滤常见设备数据,根据设定的常用安全监控字典去匹配
-        // let dictCodes = getDictItemsByCode('safetynormal');
-        // console.log(dictCodes,'111-----------')
-        // const searchForm = formData.getFieldsValue();
-        dataSource.value = dataArr;
-        // if (searchForm && searchForm['dataTypeName'] && dictCodes && dictCodes.length > 0) {
-        //   console.log(searchForm,'000---------')
-        //   const tempData = [];
-        //   const tempData1 = [];
-        //   for (let i = 0; i < dataArr.length; i++) {
-        //     const item = dataArr[i];
-        //     let flag = false;
-        //     for (let i = 0; i < dictCodes.length; i++) {
-        //       const dict = dictCodes[i];
-        //       if (dict['value'] == item['dataTypeName']) {
-        //         flag = true;
-        //       }
-        //     }
-        //     if (flag) {
-        //       tempData.push(item);
-        //     } else {
-        //       tempData1.push(item);
-        //     }
-        //   }
-        //   if (sysOrgCode == 'zjtzqctmk' || hasPermission('btn:noGb')) {
-        //     dataSource.value = [...tempData, ...tempData1];
-        //   } else {
-        //     dataSource.value = [...tempData];
-        //   }
-        // } else {
-        //   dataSource.value = dataArr;
-        // }
+        let dictCodes = getDictItemsByCode('safetynormal');
+        console.log(dictCodes, '111-----------');
+        const searchForm = formData.getFieldsValue();
+
+        if (searchForm && searchForm['dataTypeName'] && dictCodes && dictCodes.length > 0) {
+          console.log(searchForm, '000---------');
+          const tempData = [];
+          const tempData1 = [];
+          for (let i = 0; i < dataArr.length; i++) {
+            const item = dataArr[i];
+            let flag = false;
+            for (let i = 0; i < dictCodes.length; i++) {
+              const dict = dictCodes[i];
+              if (dict['value'] == item['dataTypeName']) {
+                flag = true;
+              }
+            }
+            if (flag) {
+              tempData.push(item);
+            } else {
+              tempData1.push(item);
+            }
+          }
+          if (sysOrgCode == 'zjtzqctmk' || hasPermission('btn:noGb')) {
+            dataSource.value = [...tempData, ...tempData1];
+          } else {
+            dataSource.value = [...tempData];
+          }
+        } else {
+          dataSource.value = dataArr;
+        }
       } else {
         dataSource.value = dataArr;
       }

+ 103 - 71
src/views/vent/monitorManager/warningMonitor/index.vue

@@ -1,4 +1,5 @@
 <template>
+  <div class="data-statistics"> </div>
   <div class="alarm-history-table">
     <BasicTable ref="alarmHistory" @register="registerTable">
       <template #form-onExportXls>
@@ -35,40 +36,8 @@
     },
   });
 
-  const getDeviceListApi = (params) => defHttp.post({ url: '/monitor/device', params });
-  const globalConfig = inject('globalConfig');
   const alarmHistory = ref();
   const columns = getTableHeaderColumns('alarm_history');
-  const deviceOptions = ref([]);
-  const dataTypeName = ref('');
-  const deviceType = ref('');
-
-  async function getDeviceList() {
-    let result;
-    const res = await getDeviceListApi({ devicetype: deviceType.value, filterParams: { dataTypeName: dataTypeName.value }, pageSize: 10000 });
-    if (res['records'] && res['records'].length > 0) {
-      result = res['records'];
-    } else if (res['msgTxt'] && res['msgTxt'][0] && res['msgTxt'][0]['datalist']) {
-      result = res['msgTxt'][0]['datalist'];
-    }
-
-    if (result && result.length > 0) {
-      deviceOptions.value = [];
-      deviceOptions.value = result.map((item, index) => {
-        return {
-          label: item['strinstallpos'],
-          value: item['id'] || item['deviceID'],
-          strtype: item['strtype'] || item['deviceType'],
-          strinstallpos: item['strinstallpos'],
-          devicekind: item['devicekind'],
-          stationtype: item['stationtype'],
-        };
-      });
-    } else {
-      deviceOptions.value = [];
-    }
-    await getForm().setFieldsValue({ deviceId: deviceOptions.value[0] ? deviceOptions.value[0]['value'] : '' });
-  }
 
   // 列表页面公共参数、方法
   const { tableContext, onExportXls } = useListPage({
@@ -86,20 +55,6 @@
         // autoAdvancedCol: 2,
         schemas: [
           {
-            label: '设备类型',
-            field: 'devicetype',
-            component: 'MTreeSelect',
-            componentProps: {
-              virtual: false,
-              // onChange: async (e: any) => {
-              //   if (alarmHistory.value) getForm().resetFields();
-              //   deviceType.value = e;
-              //   await getDeviceList();
-              // },
-            },
-            colProps: { span: 6 },
-          },
-          {
             label: '是否解决',
             field: 'isok',
             component: 'Select',
@@ -118,6 +73,33 @@
             colProps: { span: 4 },
           },
           {
+            label: '所属系统',
+            field: 'kindtype',
+            component: 'Select',
+            componentProps: {
+              options: [
+                {
+                  label: '通风',
+                  value: 'ventS',
+                },
+                {
+                  label: '防灭火',
+                  value: 'fireS',
+                },
+                {
+                  label: '防尘',
+                  value: 'dustS',
+                },
+                {
+                  label: '瓦斯',
+                  value: 'gasS',
+                },
+              ],
+            },
+            colProps: { span: 4 },
+          },
+
+          {
             field: 'startTime',
             label: '开始时间',
             component: 'DatePicker',
@@ -158,7 +140,7 @@
         pageSizeOptions: ['10', '30', '50', '100'],
       },
       beforeFetch(params) {
-        params.devicetype = params.devicetype ? params.devicetype + '*' : '';
+        // params.devicetype = params.devicetype ? params.devicetype + '*' : '';
         if (props.sysId) {
           params.sysId = props.sysId;
         }
@@ -179,38 +161,88 @@
 
 <style scoped lang="less">
   @ventSpace: zxm;
-
+  :deep(.zxm-table-container) {
+    max-height: 720px !important;
+  }
   :deep(.ventSpace-table-body) {
     height: auto !important;
   }
   :deep(.zxm-picker) {
     height: 30px !important;
   }
+  :deep(.@{ventSpace}-picker-dropdown) {
+    position: absolute !important;
+    top: 35px !important;
+    left: 0 !important;
+  }
+  .data-statistics {
+    height: 200px;
+    padding: 20px;
+    margin-top: 20px;
+    background-color: #0ebbff15;
+  }
+  // .tab-button-group {
+  //   // line-height: 60px;
+  //   margin-top: 16px;
+  //   display: flex;
+  //   pointer-events: auto;
+  //   position: relative;
+  //   &::after {
+  //     position: absolute;
+  //     content: '';
+  //     width: calc(100% + 10px);
+  //     height: 2px;
+  //     top: 44px;
+  //     left: -10px;
+  //     border-bottom: 1px solid #0efcff;
+  //   }
+  //   .tab-button {
+  //     padding: 10px 30px;
+  //     position: relative;
+  //     display: flex;
+  //     justify-content: center;
+  //     align-items: center;
+  //     font-size: 16px;
+  //     color: #fff;
+  //     cursor: pointer;
+  //     margin-right: 10px;
+  //     background-color: rgba(0, 103, 103, 0.253);
+  //     &::before {
+  //       content: '';
+  //       position: absolute;
+  //       top: 0;
+  //       right: 0;
+  //       bottom: 0;
+  //       left: 0;
+  //       border: 1px solid #2bb2c4;
+  //       // transform: skewX(-38deg);
+  //       // background-color: rgba(0, 77, 103, 85%);
+  //       z-index: 0;
+  //     }
+  //     &::after {
+  //       background-color: rgba(0, 77, 103, 85%);
+  //     }
+  //   }
+  //   .active {
+  //     &::before {
+  //       border-color: #46fcff;
+  //       box-shadow: 1px 1px 10px 2px #0efcff99 inset;
+  //     }
+  //   }
+  // }
   .alarm-history-table {
     width: 100%;
-    :deep(.jeecg-basic-table-form-container) {
-      .@{ventSpace}-form {
-        padding: 0 !important;
-        border: none !important;
-        margin-bottom: 0 !important;
-        .@{ventSpace}-picker,
-        .@{ventSpace}-select-selector {
-          width: 100% !important;
-          background: #00000017;
-          border: 1px solid #b7b7b7;
-          input,
-          .@{ventSpace}-select-selection-item,
-          .@{ventSpace}-picker-suffix {
-            color: #fff;
-          }
-          .@{ventSpace}-select-selection-placeholder {
-            color: #ffffffaa;
-          }
-        }
-      }
-      .@{ventSpace}-table-title {
-        min-height: 0 !important;
-      }
+    background-color: #0ebbff15;
+    position: relative;
+    margin-top: 10px;
+    &::after {
+      position: absolute;
+      content: '';
+      width: calc(100% + 10px);
+      height: 2px;
+      top: 0px;
+      left: -10px;
+      border-bottom: 1px solid #0efcff99;
     }
   }
 </style>

+ 75 - 40
src/views/vent/monitorManager/windowMonitor/index.vue

@@ -21,31 +21,41 @@
         <div class="row" v-if="selectData.nwindownum > 1">
           <div v-if="hasPermission('window:control')" class="button-box" @click="setArea(1)">设定前窗面积</div>
           <div v-if="hasPermission('window:control')" class="button-box" @click="setArea(2)">设定后窗面积</div>
-          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
-          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div>
+          <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(7)">前窗自主调控</div>
+          <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(8)">后窗自主调控</div>
+          <!-- 展会功能 -->
+          <!-- <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
+          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div> -->
         </div>
         <div class="row" v-if="selectData.nwindownum == 1">
           <div v-if="hasPermission('window:control')" class="button-box" @click="setArea(1)">设定风窗面积</div>
-          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
-          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div>
+          <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(7)">风窗自主调控</div>
+          <!-- 展会功能 -->
+          <!-- <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
+          <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div> -->
         </div>
         <template v-if="hasPermission('window:showAngle')">
           <div class="row" v-if="selectData.nwindownum > 1">
             <div v-if="hasPermission('window:control')" class="button-box" @click="setAngle(1)">设定前窗角度</div>
             <div v-if="hasPermission('window:control')" class="button-box" @click="setAngle(2)">设定后窗角度</div>
-            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
-            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div>
+            <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(7)">前窗自主调控</div>
+            <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(8)">后窗自主调控</div>
+            <!-- 展会功能 -->
+            <!-- <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
+            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div> -->
           </div>
           <div class="row" v-if="selectData.nwindownum == 1">
             <div v-if="hasPermission('window:control')" class="button-box" @click="setAngle(1)">设定风窗角度</div>
-            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
-            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div>
+            <div v-if="hasPermission('window:fltk')" class="button-box" @click="setArea(7)">风窗自主调控</div>
+            <!-- 展会功能 -->
+            <!-- <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(3)">自主联动控制开启</div>
+            <div v-if="hasPermission('window:ldkz')" class="button-box" @click="setArea(4)">自主联动控制停止</div> -->
           </div>
         </template>
       </div>
       <div class="top-right row">
-        <div v-if="hasPermission('window:control')" class="button-box" @click="setArea(5)">一键全开</div>
-        <div v-if="hasPermission('window:control')" class="button-box" @click="setArea(6)">一键全关</div>
+        <div v-if="hasPermission('window:controlFull')" class="button-box" @click="setArea(5)">一键全开</div>
+        <div v-if="hasPermission('window:controlFull')" class="button-box" @click="setArea(6)">一键全关</div>
         <!-- <div class="control-type row">
           <div class="control-title">控制模式:</div>
           <a-radio-group v-model:value="controlType">
@@ -183,7 +193,7 @@
   import HandleModal from './modal.vue';
   import DeviceBaseInfo from '../comment/components/DeviceBaseInfo.vue';
   import { mountedThree, destroy, addMonitorText, computePlay, setModelType, initCameraCanvas } from './window.threejs';
-  import { list, getTableList, cameraList, cameraAddrList } from './window.api';
+  import { list, getTableList, windControl, cameraAddrList } from './window.api';
   import { list as baseList } from '../../deviceManager/windWindowTabel/ventanalyWindow.api';
   import { chartsColumns } from './window.data';
   import { deviceControlApi } from '/@/api/vent/index';
@@ -350,15 +360,14 @@
       if (!globalConfig?.simulatedPassword) {
         if (flag == 3) {
           modalTitle.value = '自主联动控制开启';
-        }
-        if (flag == 4) {
+        } else if (flag == 4) {
           modalTitle.value = '自主联动控制停止';
-        }
-        if (flag == 5) {
+        } else if (flag == 5) {
           modalTitle.value = '一键全开';
-        }
-        if (flag == 6) {
+        } else if (flag == 6) {
           modalTitle.value = '一键全关';
+        } else if (flag == 7 || flag == 8) {
+          modalTitle.value = '风量自主调控';
         }
         modalIsShow.value = true;
       } else {
@@ -377,18 +386,18 @@
         modalTitle.value = '设定风窗角度';
       }
       modalIsShow.value = true;
+    } else if (flag == 7 || flag == 8) {
+      modalTitle.value = '风量自主调控';
+      modalIsShow.value = true;
     } else {
       if (!globalConfig?.simulatedPassword) {
         if (flag == 3) {
           modalTitle.value = '自主联动控制开启';
-        }
-        if (flag == 4) {
+        } else if (flag == 4) {
           modalTitle.value = '自主联动控制停止';
-        }
-        if (flag == 5) {
+        } else if (flag == 5) {
           modalTitle.value = '一键全开';
-        }
-        if (flag == 6) {
+        } else if (flag == 6) {
           modalTitle.value = '一键全关';
         }
         modalIsShow.value = true;
@@ -410,29 +419,55 @@
       password: passWord || globalConfig?.simulatedPassword,
       value: null,
     };
-    if (handlerState == 1 || handlerState == 2) {
-      windowAngle.value = windowAngleNum;
-      data.paramcode = handlerState == 1 ? 'frontSetValue' : 'rearSetValue';
-      data.value = windowAngle.value;
-    } else if (handlerState == 3 || handlerState == 4) {
-      data.paramcode = 'autoRun';
-      data.value = handlerState == 3 ? 1 : 0;
-    } else if (handlerState == 5 || handlerState == 6) {
-      data.paramcode = 'frontSetValue';
-      data.value = handlerState == 5 ? selectData.maxarea : 0;
-    }
-
-    deviceControlApi(data)
-      .then(() => {
+    if (handlerState == 7 || handlerState == 8) {
+      let params;
+      if (handlerState == 7) {
+        // 单道风窗
+        params = {
+          deviceRelationId: selectData.deviceID,
+          auto: 1,
+          fengliangF: windowAngleNum,
+        };
+      } else {
+        // 双道风窗
+        params = {
+          deviceRelationId: selectData.deviceID,
+          auto: 1,
+          fengliangR: windowAngleNum,
+        };
+      }
+      windControl(params).then(() => {
         if (globalConfig.History_Type == 'remote') {
           message.success('指令已下发至生产管控平台成功!');
         } else {
           message.success('指令已下发成功!');
         }
-      })
-      .finally(() => {
-        handleCancel();
       });
+    } else {
+      if (handlerState == 1 || handlerState == 2) {
+        windowAngle.value = windowAngleNum;
+        data.paramcode = handlerState == 1 ? 'frontSetValue' : 'rearSetValue';
+        data.value = windowAngle.value;
+      } else if (handlerState == 3 || handlerState == 4) {
+        data.paramcode = 'autoRun';
+        data.value = handlerState == 3 ? 1 : 0;
+      } else if (handlerState == 5 || handlerState == 6) {
+        data.paramcode = 'frontSetValue';
+        data.value = handlerState == 5 ? selectData.maxarea : 0;
+      }
+
+      deviceControlApi(data)
+        .then(() => {
+          if (globalConfig.History_Type == 'remote') {
+            message.success('指令已下发至生产管控平台成功!');
+          } else {
+            message.success('指令已下发成功!');
+          }
+        })
+        .finally(() => {
+          handleCancel();
+        });
+    }
   };
 
   const handleCancel = () => {

+ 20 - 4
src/views/vent/monitorManager/windowMonitor/modal.vue

@@ -5,10 +5,24 @@
         <ExclamationCircleFilled style="color: #ffb700; font-size: 30px" />
         <div class="warning-text">您是否要进行{{ title }}操作?</div>
       </div>
-      <div class="vent-flex-row input-box" v-if="type == '1' || type == '2'">
-        <div class="label">{{ title.includes('角度') ? '风窗角度:' : '风窗面积:' }}</div>
-        <a-input-number size="small" placeholder="0" :min="0" :max="90" v-model:value="area" />
-      </div>
+      <template v-if="type == '1' || type == '2'">
+        <div class="vent-flex-row input-box">
+          <div class="label">{{ title.includes('角度') ? '风窗角度:' : '风窗面积:' }}</div>
+          <a-input-number size="small" placeholder="0" :min="0" :max="90" v-model:value="area" />
+        </div>
+      </template>
+      <template v-if="type == '7'">
+        <div class="vent-flex-row input-box">
+          <div class="label">前窗目标风量:</div>
+          <a-input-number size="small" placeholder="0" :min="0" :max="90" v-model:value="frontW" />
+        </div>
+      </template>
+      <template v-if="type == '8'">
+        <div class="vent-flex-row input-box">
+          <div class="label">后窗目标风量:</div>
+          <a-input-number size="small" placeholder="0" :min="0" :max="90" v-model:value="rearW" />
+        </div>
+      </template>
       <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" />
@@ -44,6 +58,8 @@
   const type = ref<String>('');
   const passWord = ref('');
   const area = ref(0);
+  const frontW = ref(0);
+  const rearW = ref(0);
 
   watch([() => props.modalIsShow, () => props.modalTitle, () => props.modalType], ([newVal, newModalTitle, newModalType]) => {
     visible.value = newVal;

+ 3 - 0
src/views/vent/monitorManager/windowMonitor/window.api.ts

@@ -5,6 +5,7 @@ enum Api {
   baseList = '/safety/ventanalyWindow/list',
   cameraList = '/safety/ventanalyCamera/list',
   cameraAddrList = '/monitor/camera/info',
+  windControl = '/device/updateWindowAutoAdjust',
 }
 /**
  * 列表接口
@@ -20,3 +21,5 @@ export const getTableList = (params) => defHttp.get({ url: Api.baseList, params
 
 export const cameraList = (params) => defHttp.get({ url: Api.cameraList, params });
 export const cameraAddrList = (params) => defHttp.post({ url: Api.cameraAddrList, params });
+
+export const windControl = (params) => defHttp.post({ url: Api.cameraAddrList, params });