Browse Source

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

lxh 18 hours ago
parent
commit
e1646ad419
39 changed files with 4929 additions and 2384 deletions
  1. 3 1
      package.json
  2. 329 344
      pnpm-lock.yaml
  3. 1 1
      public/js/config.js
  4. BIN
      src/assets/images/vent/camera_bg.png
  5. 913 156
      src/components/AIChat/MiniChat.vue
  6. 10 8
      src/design/vent/comment.less
  7. 76 111
      src/hooks/system/useCamera.ts
  8. 3 0
      src/layouts/default/header/index.vue
  9. 0 1
      src/views/vent/comment/components/bottomMenu.vue
  10. 3 0
      src/views/vent/dataCenter/APICenter/ApiAddModal.vue
  11. 20 14
      src/views/vent/home/colliery/index.vue
  12. 90 81
      src/views/vent/monitorManager/airDoor/components/Modal.vue
  13. 214 217
      src/views/vent/monitorManager/airDoor/components/cameraModal.vue
  14. 5 6
      src/views/vent/monitorManager/airDoor/index.vue
  15. 4 4
      src/views/vent/monitorManager/alarmMonitor/common.data.ts
  16. 188 179
      src/views/vent/monitorManager/alarmMonitor/common/top-area.vue
  17. 473 464
      src/views/vent/monitorManager/alarmMonitor/warn/ventilateWarn.vue
  18. 230 230
      src/views/vent/monitorManager/balancePressMonitor/components/balancePressHome.vue
  19. 183 46
      src/views/vent/monitorManager/balancePressMonitor/components/balancePressHomeSP.vue
  20. 136 0
      src/views/vent/monitorManager/balancePressMonitor/hooks/useControl.ts
  21. 2 10
      src/views/vent/monitorManager/camera/common/cameraTree.vue
  22. 430 420
      src/views/vent/monitorManager/camera/index.vue
  23. 2 0
      src/views/vent/monitorManager/comment/comment.api.ts
  24. 8 7
      src/views/vent/monitorManager/fanLocalMonitor/index.vue
  25. 3 3
      src/views/vent/monitorManager/fireDoorMonitor/index.vue
  26. 353 0
      src/views/vent/monitorManager/gateMonitor/dandaoFcBd3.threejs.ts
  27. 343 0
      src/views/vent/monitorManager/gateMonitor/gate.threejs.tj.ssl.ts
  28. 147 0
      src/views/vent/monitorManager/gateMonitor/gate.threejs.ts
  29. 353 0
      src/views/vent/monitorManager/gateMonitor/gate.threejs.two.ssl.ts
  30. 64 37
      src/views/vent/monitorManager/gateMonitor/index.vue
  31. 2 6
      src/views/vent/monitorManager/gateMonitor/modal.vue
  32. 22 21
      src/views/vent/monitorManager/mainFanMonitor/index.vue
  33. 2 3
      src/views/vent/monitorManager/safetyMonitor/index.vue
  34. 2 2
      src/views/vent/monitorManager/sensorMonitor/index.vue
  35. 290 0
      src/views/vent/monitorManager/sprayMonitor/component.vue
  36. 6 0
      src/views/vent/monitorManager/sprayMonitor/index.vue
  37. 12 4
      src/views/vent/monitorManager/windowMonitor/index.vue
  38. 4 5
      src/views/vent/monitorManager/windrectMonitor/index.vue
  39. 3 3
      src/views/vent/safetyList/safetyList.api.ts

+ 3 - 1
package.json

@@ -92,7 +92,7 @@
     "vxe-table": "4.5.12",
     "vxe-table": "4.5.12",
     "vxe-table-plugin-antd": "3.1.0",
     "vxe-table-plugin-antd": "3.1.0",
     "xe-utils": "3.5.13",
     "xe-utils": "3.5.13",
-    "xgplayer": "^3.0.14",
+    "xgplayer": "^3.0.23",
     "xgplayer-flv": "^3.0.14",
     "xgplayer-flv": "^3.0.14",
     "xgplayer-hls": "^3.0.14",
     "xgplayer-hls": "^3.0.14",
     "xgplayer-mp4": "^3.0.21",
     "xgplayer-mp4": "^3.0.21",
@@ -170,6 +170,8 @@
     "ts-node": "^10.9.1",
     "ts-node": "^10.9.1",
     "typescript": "^4.9.5",
     "typescript": "^4.9.5",
     "unocss": "^0.55.3",
     "unocss": "^0.55.3",
+    "video.js": "^8.23.4",
+    "videojs-flvjs-es6": "^1.0.1",
     "vite": "^4.4.9",
     "vite": "^4.4.9",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-html": "^3.2.0",
     "vite-plugin-html": "^3.2.0",

File diff suppressed because it is too large
+ 329 - 344
pnpm-lock.yaml


+ 1 - 1
public/js/config.js

@@ -20,5 +20,5 @@ const VENT_PARAM = {
   gasControlMock: true, // 项目关于瓦斯自主调控,是否模拟演示, true: 真实调控; false 模拟调控
   gasControlMock: true, // 项目关于瓦斯自主调控,是否模拟演示, true: 真实调控; false 模拟调控
   historyIsMultiple: false, // 设备历史数据是否支持多选
   historyIsMultiple: false, // 设备历史数据是否支持多选
   isShowQy: true, // 是否显示气压
   isShowQy: true, // 是否显示气压
-  is2DModel: true, // 是否尽最大可能使用2D模型
+  is2DModel: false, // 是否尽最大可能使用2D模型
 }
 }

BIN
src/assets/images/vent/camera_bg.png


+ 913 - 156
src/components/AIChat/MiniChat.vue

@@ -1,56 +1,135 @@
 <!-- eslint-disable vue/no-v-html -->
 <!-- eslint-disable vue/no-v-html -->
 <template>
 <template>
-  <div class="mini-chat">
+  <div class="btn" @click="showAIChat">
+    <div style="display: flex; flex-direction: row" class="btn-header">
+      <img src="@/assets/images/vent/home/wakeBtn.png" />
+    </div>
+  </div>
+  <div v-if="isShowChatBroad" class="mini-chat">
     <!-- 左侧折叠区域 -->
     <!-- 左侧折叠区域 -->
-    <div class="left-side">
-      <SvgIcon name="add" size="20" />
-      <Popover trigger="click" :overlay-inner-style="{ padding: '1px' }">
-        <template #content>
-          <AIChat style="width: 700px; height: 500px" :visible="true" />
-        </template>
-        <SvgIcon :name="dialogVisible ? 'zoom-out' : 'zoom-in'" size="20" @click="openDialog" />
-      </Popover>
+    <div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
+      <div
+        v-if="isFold"
+        class="historyBtn"
+        :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/history.svg' : '/src/assets/images/vent/home/history.svg'})` }"
+        @click="addNew"
+      ></div>
+      <div v-else class="historyBtn1">
+        <span
+          class="btn-text-bg"
+          :style="{
+            backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/history.svg' : ''})`,
+          }"
+        ></span>
+        <span v-if="!isFold" class="btn-text">历史对话</span>
+        <a-list style="width: 130px" :split="false" :data-source="sessionHistory" :scroll="200" class="custom-list">
+          <template #renderItem="{ item }">
+            <a-list-item
+              class="session-item"
+              :style="{
+                padding: '8px 10px 0 8px',
+                color: '#5e7081',
+                fontSize: '10px',
+                position: 'relative', // 新增定位
+              }"
+            >
+              <!-- 新增flex布局容器 -->
+              <div style="width: 100%">
+                <div v-if="editingId !== item.id" class="text-container">
+                  <span class="edit-text" @click="sessionsHistory(item.id)">{{ item.name }}</span>
+                  <div class="btn-container">
+                    <EditOutlined class="edit-icon" @click="startEditing(item)" />
+                    <DeleteOutlined class="delete-icon" @click="startDelete(item)" />
+                  </div>
+                </div>
+                <!-- 输入框 -->
+                <a-input
+                  size="small"
+                  v-else
+                  v-model:value="editText"
+                  v-focus
+                  @blur="handleSave(item)"
+                  @keyup.enter="handleSave(item)"
+                  class="edit-input"
+                />
+              </div>
+            </a-list-item>
+          </template>
+        </a-list>
+      </div>
+      <div
+        class="foldBtn"
+        :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/Fold.svg' : '/src/assets/images/vent/home/unfold.svg'})` }"
+        @click="fold"
+      ></div>
     </div>
     </div>
     <!-- 右侧对话框 -->
     <!-- 右侧对话框 -->
     <div class="right-side">
     <div class="right-side">
       <!-- 对话区域 -->
       <!-- 对话区域 -->
-      <div ref="dialogRef" class="dialog-area">
+      <div class="dialog-area">
         <div
         <div
-          v-for="message in store.getMessageHistory"
+          v-for="message in messageHistory"
           :key="message.id"
           :key="message.id"
           class="flex items-center w-100%"
           class="flex items-center w-100%"
           :style="{ alignSelf: message.type === 'user' ? 'flex-end' : 'flex-start' }"
           :style="{ alignSelf: message.type === 'user' ? 'flex-end' : 'flex-start' }"
         >
         >
           <template v-if="message.type === 'user'">
           <template v-if="message.type === 'user'">
             <div class="flex-grow-1"></div>
             <div class="flex-grow-1"></div>
-            <div class="ask-message">{{ message.content }}</div>
+            <div class="message-wrapper user-message-wrapper">
+              <div class="ask-message">{{ message.content }}</div>
+              <CopyOutlined class="copy-icon" @click="copyToClipboard(message.content)" title="复制消息" />
+            </div>
           </template>
           </template>
           <template v-else>
           <template v-else>
-            <SvgIcon size="30" class="ml-2px mr-2px" name="ai-logo" />
-            <div class="answer-message">
-              <div v-if="message.contentR1" class="color-gray font-size-12px" v-html="formatMessage(message.contentR1)"> </div>
-              <div v-html="formatMessage(message.content)"> </div>
+            <SvgIcon size="80" class="ml-2px mr-2px" name="ai-logo" />
+            <div class="message-wrapper ai-message-wrapper">
+              <div class="answer-message">
+                <div v-if="message.contentR1" class="color-gray font-size-12px" v-html="message.contentR1"> </div>
+                <div v-else v-html="message.content"> </div>
+              </div>
+              <CopyOutlined class="copy-icon" @click="copyToClipboard(message.contentR1 || message.content)" title="复制消息" />
             </div>
             </div>
           </template>
           </template>
         </div>
         </div>
       </div>
       </div>
-      <!-- 底部操作栏 -->
+      <!-- 底部输入区 -->
       <div class="input-area">
       <div class="input-area">
-        <TextArea v-model:value="inputText" placeholder="请输入你的问题" />
-        <div class="action-bar">
-          <!-- 左侧深度思考按钮 -->
-          <div class="think-btn" :class="{ active: store.deepseekR1Enable }" @click="toggleThinking"> <span>深度思考</span> </div>
-
-          <!-- 右侧操作按钮 -->
-          <Space>
-            <SvgIcon name="send-image" />
-            <SvgIcon name="send-file" />
-            <Button type="primary" shape="circle" size="small" :loading="store.streaming" @click="handleSend">
-              <template #icon>
-                <SvgIcon name="send" />
-              </template>
-            </Button>
-          </Space>
+        <a-input v-model:value="inputText" placeholder="请输入你的问题" @keyup.enter="handleSend(inputText)" class="ant-input" />
+        <div class="ctrl-btn">
+          <div class="input-controls">
+            <button class="control-btn">深度学习</button>
+            <button class="control-btn" @click="stopReq()">停止响应</button>
+          </div>
+          <div class="action-bar">
+            <Space>
+              <Button class="control-btn1" size="small" @click="showModal()">
+                <template #icon>
+                  <SvgIcon name="send-file" />
+                </template>
+              </Button>
+              <Button class="control-btn2" size="small" @click="handleSend(inputText)">
+                <template #icon>
+                  <SvgIcon name="send" />
+                </template>
+              </Button>
+            </Space>
+          </div>
+        </div>
+        <!-- 右侧文件上传区 -->
+        <div v-if="open" class="file-upload">
+          <!-- 输入框区域,包含确认按钮 -->
+          <div class="input-container">
+            <a-input v-model:value="filePath" placeholder="输入文件连接" class="file-input" @pressEnter="handlePathConfirm" />
+            <button class="confirm-btn" @click="handlePathConfirm">确认</button>
+          </div>
+          <!-- 上传按钮 -->
+          <!-- <a-upload> <button class="upload-btn" @click="customUpload">从本地上传</button></a-upload> -->
+          <a-upload class="custom-upload" name="file" :multiple="false">
+            <a-button class="upload-btn" @click="customUpload">
+              <UploadOutlined></UploadOutlined>
+              从本地上传
+            </a-button>
+          </a-upload>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
@@ -58,151 +137,829 @@
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-  import { ref, onMounted } from 'vue';
-  import { SvgIcon } from '../Icon';
-  import { Space, Button, Popover, Input } from 'ant-design-vue';
-  import AIChat from './index.vue';
-  import { useAIChat } from '/@/store/modules/AIChat';
-  const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
-  const dialogRef = ref<HTMLElement | null>(null);
-  const dialogVisible = ref(true);
-  const inputText = ref(''); // 输入框内容
-  const store = useAIChat(); //获取用户信息
-
-  const openDialog = () => {
-    dialogVisible.value = !dialogVisible.value;
-  };
-  //启用深度思考
-  const toggleThinking = () => {
-    store.deepseekR1Enable = !store.deepseekR1Enable;
-  };
-
-  //获取消息列表
-  async function handleSend() {
-    store
-      .sendQuestion(inputText.value, () => {
-        if (dialogRef.value) {
-          dialogRef.value.scrollTop = dialogRef.value.scrollHeight;
+import { ref, onMounted } from 'vue';
+import { SvgIcon } from '../Icon';
+import { Space, Button, Modal, Input, message } from 'ant-design-vue';
+// import AIChat from './index.vue';
+import { useUserStore } from '/@/store/modules/user';
+import { EditOutlined, DeleteOutlined, UploadOutlined, CopyOutlined } from '@ant-design/icons-vue';
+import { createVNode } from 'vue';
+const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
+const inputText = ref(''); // 输入框内容
+const sessionHistory = ref([]);
+const isShowChatBroad = ref(false);
+const editingId = ref<number | null>(null);
+const editText = ref('');
+const currentSessionID = ref('');
+const taskID = ref('');
+const open = ref<boolean>(false);
+interface ListItem {
+  id: number;
+  name?: string;
+}
+interface Message {
+  id: string; // 唯一标识(可用时间戳生成)
+  type: 'user' | 'system' | 'response';
+  content: string;
+  /** 深度思考时的文本 */
+  contentR1: string;
+  timestamp: number; // 排序依据
+}
+// 定义消息历史数组类型
+const messageHistory = ref<Message[]>([]);
+const isFold = ref(true); // 是否折叠
+const userid = useUserStore().getUserInfo.id as string;
+const filePath = ref(''); // 绑定输入框值
+const showConfirmBtn = ref(false); // 控制确认按钮显示状态
+const fileList = ref([]);
+function showAIChat() {
+  isShowChatBroad.value = !isShowChatBroad.value;
+}
+//获取消息列表
+// async function handleSend(data) {
+//   messageHistory.value.push({
+//     id: `user_${Date.now()}`,
+//     type: 'user',
+//     content: data,
+//     contentR1: '',
+//     timestamp: Date.now(),
+//   });
+//   // 发送 POST 请求
+//   fetch('http://39.97.59.228:8000/v1/chat-messages', {
+//     method: 'POST',
+//     headers: {
+//       'Content-Type': 'application/json',
+//       Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+//     },
+//     body: JSON.stringify({
+//       conversation_id: currentSessionID.value,
+//       query: data,
+//       response_mode: 'streaming',
+//       user: userid,
+//       inputs: {},
+//     }),
+//   }).then((response) => {
+//     const decoder = new TextDecoder('utf-8');
+//     let buffer = [];
+//     // 获取可读流
+//     const reader = response.body.getReader();
+//     const newMessage = {
+//       id: `response_${Date.now()}`,
+//       type: 'response' as any,
+//       content: '',
+//       contentR1: '',
+//       timestamp: Date.now(),
+//     };
+//     messageHistory.value.push(newMessage);
+//     // 读取数据
+//     function read() {
+//       return reader.read().then(({ done, value }) => {
+//         if (done) {
+//           return buffer;
+//         }
+//         // 解码数据块
+//         const chunk = decoder.decode(value, { stream: false });
+//         // 处理每段数据
+//         const processedData = processStreamChunk(chunk);
+//         buffer = buffer.concat(processedData);
+//         // 继续读取
+//         return read();
+//       });
+//     }
+//     // 开始读取
+//     return read();
+//     function processStreamChunk(chunk) {
+//       try {
+//         // 移除 "data: " 前缀
+//         const jsonStr = chunk.replace('data: ', '');
+//         const data = JSON.parse(jsonStr);
+//         const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
+//         if (!targetMessage) return;
+//         // 根据事件类型分发处理
+//         switch (data.event) {
+//           case 'message':
+//             if (!taskID.value && !currentSessionID.value) {
+//               taskID.value = data.task_id;
+//               currentSessionID.value = data.conversation_id;
+//             }
+//             targetMessage.content += data.answer; // 追加内容
+//             break;
+//         }
+//         return data;
+//       } catch (error) {
+//         // 请求失败时设置系统消息
+//         return null;
+//       }
+//     }
+//   });
+//   inputText.value = '';
+// }
+// 复制消息
+function copyToClipboard(text) {
+  if (!text || text.trim() === '') {
+    message.warn('没有可复制的内容');
+    return;
+  }
+  // 2. 创建临时textarea 元素
+  const textarea = document.createElement('textarea');
+  textarea.value = text;
+  textarea.style.position = 'fixed';
+  textarea.style.top = '-999px';
+  textarea.style.left = '-999px';
+  textarea.style.width = '200px';
+  textarea.style.height = '200px';
+  document.body.appendChild(textarea);
+  try {
+    textarea.select();
+    textarea.setSelectionRange(0, text.length);
+    const isSuccessful = document.execCommand('copy');
+    if (isSuccessful) {
+      message.success('复制成功!');
+    } else {
+      throw new Error('复制命令执行失败');
+    }
+  } catch (err) {
+    console.error('复制失败:', err);
+    message.error('复制失败,请手动复制');
+  } finally {
+    document.body.removeChild(textarea);
+  }
+}
+async function handleSend(data) {
+  inputText.value = '';
+  messageHistory.value.push({
+    id: `user_${Date.now()}`,
+    type: 'user',
+    content: data,
+    contentR1: '',
+    timestamp: Date.now(),
+  });
+  try {
+    const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+      body: JSON.stringify({
+        conversation_id: currentSessionID.value,
+        query: data,
+        response_mode: 'streaming',
+        user: userid,
+        inputs: {},
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`);
+    }
+
+    const decoder = new TextDecoder('utf-8');
+    const reader = response.body.getReader();
+    let textBuffer = ''; // 使用字符串缓冲区来累积数据
+    const newMessage = {
+      id: `response_${Date.now()}`,
+      type: 'response',
+      content: '',
+      contentR1: '',
+      timestamp: Date.now(),
+    };
+    messageHistory.value.push(newMessage);
+    while (true) {
+      const { done, value } = await reader.read();
+      if (done) {
+        if (textBuffer) {
+          processLine(textBuffer);
+        }
+        break;
+      }
+      textBuffer += decoder.decode(value, { stream: true });
+      // 处理每一行数据
+      let lineIndex;
+      while ((lineIndex = textBuffer.indexOf('\n')) !== -1) {
+        const line = textBuffer.substring(0, lineIndex).trim();
+        textBuffer = textBuffer.substring(lineIndex + 1);
+
+        if (line) {
+          processLine(line);
         }
         }
-      })
-      .then(() => {
-        inputText.value = '';
+      }
+    }
+    function processLine(line) {
+      if (line.startsWith('data: ')) {
+        try {
+          const jsonStr = line.substring('data: '.length);
+          const data = JSON.parse(jsonStr);
+          switch (data.event) {
+            case 'message':
+              if (data.answer) {
+                const targetMessage = messageHistory.value.find((msg) => msg.id === newMessage.id);
+                if (targetMessage) {
+                  targetMessage.content += data.answer;
+                }
+              }
+              if (data.task_id && !taskID.value) taskID.value = data.task_id;
+              if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
+              break;
+          }
+        } catch (error) {
+          console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
+        }
+      }
+    }
+  } catch (error) {
+    console.error('Error in handleSend:', error);
+    // 在 UI 上显示错误信息
+    messageHistory.value.push({
+      id: `system_${Date.now()}`,
+      type: 'system',
+      content: '请求错误',
+      contentR1: '',
+      timestamp: Date.now(),
+    });
+  }
+}
+// 上传文件
+const showModal = () => {
+  open.value = !open.value;
+};
+async function customUpload(data) {
+  const formData = new FormData();
+  if (!data) {
+    return message.warn('请选择文件');
+  }
+  // 添加文件参数
+  formData.append('file', data.file);
+  // 添加用户标识参数
+  formData.append('user', userid);
+  try {
+    let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
+      method: 'POST',
+      headers: {
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+      body: formData,
+    });
+    // if (response) {
+    //   message.success('上传成功');
+    // }
+    console.log(response, '123');
+    if (!response) {
+      throw new Error('Network response was not ok');
+    }
+  } catch (error) {
+    console.error('保存失败:', error);
+  }
+}
+//停止响应
+async function stopReq() {
+  try {
+    let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID}/stop`, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+      body: JSON.stringify({
+        user: userid,
+      }),
+    });
+    if (!response) {
+      throw new Error('Network response was not ok');
+    }
+  } catch (error) {
+    console.error('保存失败:', error);
+  }
+}
+//获取具体会话记录
+async function sessionsHistory(id: string) {
+  console.log(id, '123');
+  try {
+    let response = await fetch(`http://39.97.59.228:8000/v1/messages?conversation_id=${id}&user=${userid}`, {
+      method: 'GET',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+    });
+    const data = await response.json();
+    console.log(data, '123');
+    if (data.data.length > 0) {
+      messageHistory.value = [];
+      data.data.forEach((item: any) => {
+        messageHistory.value.push({
+          id: `user_${Date.now()}`,
+          type: 'user',
+          content: item.query,
+          contentR1: '',
+          timestamp: Date.now(),
+        });
+        messageHistory.value.push({
+          id: `system_${Date.now()}`,
+          type: 'system',
+          content: item.answer,
+          contentR1: '',
+          timestamp: Date.now(),
+        });
       });
       });
+    }
+    if (!response.ok) {
+      throw new Error('Network response was not ok');
+    }
+  } catch (error) {
+    console.error('保存失败:', error);
   }
   }
-
-  //格式化消息
-  function formatMessage(text: string) {
-    let formatted = text
-      // 处理换行
-      .replace(/\n\n/g, '<br>')
-      .replace(/\n###/g, '<br> ')
-      .replace(/###/g, '')
-      .replace(/---/g, '')
-      // 处理粗体
-      .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
-      // 处理斜体
-      .replace(/\*(.*?)\*/g, '<em>$1</em>')
-      // 处理行内代码
-      .replace(/`([^`]+)`/g, '<code>$1</code>');
-    return formatted;
-  }
-
-  // 初始化按钮定位
-  onMounted(() => {
-    // store.getSessionHistory();
+  editingId.value = null;
+}
+//编辑标题
+const startEditing = (item: ListItem) => {
+  editingId.value = item.id;
+  editText.value = item.name || '';
+};
+// 输入框确认按钮点击事件
+async function handlePathConfirm() {
+  const formData = new FormData();
+  // 添加文件参数
+  formData.append('file', filePath.value);
+  // 添加用户标识参数
+  formData.append('user', userid);
+  try {
+    let response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
+      method: 'POST',
+      headers: {
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+      body: formData,
+    });
+    if (!response.ok) {
+      throw new Error('Network response was not ok');
+    }
+  } catch (error) {
+    console.error('保存失败:', error);
+  }
+  console.log('确认的文件路径:', filePath.value);
+  // 这里可以添加路径验证、保存等逻辑
+  filePath.value = ''; // 可选:确认后清空输入框
+  showConfirmBtn.value = false;
+}
+// 保存修改
+const handleSave = async (item: ListItem) => {
+  try {
+    let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}/name`, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+      },
+      body: JSON.stringify({
+        name: editText.value,
+        user: userid,
+      }),
+    });
+    if (!response.ok) {
+      throw new Error('Network response was not ok');
+    }
+    item.name = editText.value;
+  } catch (error) {
+    console.error('保存失败:', error);
+  }
+  editingId.value = null;
+};
+// 删除会话
+const startDelete = async (item: ListItem) => {
+  Modal.confirm({
+    title: '确认删除',
+    content: `确定要删除会话 "${item.name || '新会话'}" 吗?此操作不可撤销。`,
+    okText: '确认',
+    cancelText: '取消',
+    onOk: async () => {
+      // 原有删除逻辑不变
+      try {
+        let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}`, {
+          method: 'DELETE',
+          headers: {
+            'Content-Type': 'application/json',
+            Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+          },
+          body: JSON.stringify({
+            user: userid,
+          }),
+        });
+        if (!response.ok) {
+          throw new Error('Network response was not ok');
+        }
+        getHistoryList();
+      } catch (error) {
+        console.error('删除失败:', error);
+        Modal.error({
+          title: '删除失败',
+          content: '删除会话时出现错误,请稍后重试。',
+        });
+      }
+    },
   });
   });
+};
+const fold = () => {
+  isFold.value = !isFold.value;
+  if (!isFold.value) {
+    getHistoryList();
+  }
+};
+// 获取历史会话列表
+async function getHistoryList() {
+  let response = await fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json',
+      Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+    },
+  });
+  const data = await response.json();
+  sessionHistory.value = data.data;
+  console.log(sessionHistory.value, '123');
+}
+
+// 初始化按钮定位
+onMounted(() => {
+  getHistoryList();
+});
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>
-  .mini-chat {
+.btn-header {
+  width: 40px;
+  height: 40px;
+  margin-right: 5px;
+  margin-bottom: 5px;
+}
+.mini-chat {
+  display: flex;
+  width: 500px;
+  height: 400px;
+  border-radius: 4px;
+  position: fixed;
+  top: 60px;
+  right: 20px;
+  background-color: rgb(255, 255, 255);
+  background: url('../../assets/images/warn-dialog-bg.png') no-repeat center;
+  background-size: 100% 100%;
+  z-index: 9999999;
+  color: #fff;
+}
+
+.left-side {
+  background: #0c2842;
+  transition: width 0.5s ease; /* 平滑过渡动画 */
+  width: 140px; /* 展开时宽度 */
+  position: relative; /* 用于按钮定位 */
+}
+.left-side.collapsed {
+  width: 40px; /* 折叠时宽度 */
+}
+
+.custom-list {
+  height: 325px;
+  overflow-y: auto;
+}
+.text-container {
+  display: flex;
+  justify-content: space-between;
+  width: 100%;
+  overflow: hidden;
+}
+.btn-container {
+  display: flex;
+}
+.jeecg-layout-header-action span[role='img'] {
+  padding: 0;
+}
+.text-ellipsis {
+  flex: 1;
+}
+.edit-text {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 90px;
+  color: #fff;
+  font-size: 12px;
+  cursor: pointer;
+}
+.edit-icon {
+  flex-shrink: 0;
+  margin-left: auto;
+  line-height: 23px;
+}
+.delete-icon {
+  flex-shrink: 0;
+  margin-left: auto;
+  line-height: 23px;
+}
+.edit-icon:hover {
+  color: #1890ff !important;
+  cursor: pointer;
+}
+.delete-icon:hover {
+  color: #1890ff !important;
+  cursor: pointer;
+}
+.edit-input {
+  font-size: 10px;
+}
+.btn-text-bg {
+  width: 14px;
+  height: 14px;
+  position: absolute;
+  background-size: 100% 100%;
+  right: 10px;
+  top: 10px;
+  left: 10px;
+  bottom: 10px;
+}
+.btn-text {
+  margin-left: 3px;
+  font-size: 12px;
+  color: #fff;
+  white-space: nowrap;
+  margin-left: 30px;
+  line-height: 35px;
+}
+.historyBtn {
+  width: 20px;
+  height: 20px;
+  position: absolute;
+  background-size: 100% 100%;
+  background-position: center;
+  padding: 2px;
+  right: 10px;
+  top: 10px;
+}
+.historyBtn1 {
+  width: 20px;
+  height: 20px;
+  position: absolute;
+  background-size: 100% 100%;
+  background-position: center;
+  left: 3px;
+}
+.divider0 {
+  border-bottom: 1px solid #1074c1;
+  width: auto;
+  margin: 0 10px;
+  height: 13%;
+  display: block;
+  background: transparent;
+}
+.foldBtn {
+  width: 20px;
+  height: 20px;
+  position: absolute;
+  background-size: 100% 100%;
+  background-position: center;
+  padding: 2px;
+  right: 10px;
+  bottom: 10px;
+  cursor: pointer;
+}
+
+.right-side {
+  flex: 1; /* 占据剩余空间 */
+  display: flex;
+  flex-direction: column;
+
+  .dialog-area {
+    flex: 1; /* 占据剩余空间 */
+    gap: 10px; /* 消息块间隔统一控制 */
+    overflow-y: auto; /* 垂直滚动条 */
+    padding: 5px;
     display: flex;
     display: flex;
+    flex-direction: column;
+    color: #fff;
+
+    .ask-message {
+      padding: 10px;
+      border-radius: 5px;
+      background: #0c2842;
+      max-width: 80%;
+    }
+    .answer-message {
+      padding: 10px;
+      border-radius: 5px;
+      background: #0c2842;
+      max-width: 90%;
+    }
   }
   }
 
 
-  .left-side {
-    width: 40px; /* 折叠时宽度 */
-    background: #0c2842;
-    transition: width 0.5s ease; /* 平滑过渡动画 */
+  .input-area {
+    margin: 10px 10px 20px 10px;
+    padding: 10px;
+    background-color: #043256;
+    border: 1px solid #2cb6ff;
+    border-radius: 5px;
     display: flex;
     display: flex;
     flex-direction: column;
     flex-direction: column;
-    justify-content: space-around;
+    justify-content: space-between;
+    gap: 5px;
+    height: 25%;
+  }
+  /* 文件列表容器 */
+  .uploaded-files {
+    padding: 8px;
+    background-color: #f5f5f5;
+    border-radius: 4px;
+    min-height: 40px;
+  }
+
+  /* 单个文件项 */
+  .file-item {
+    display: inline-flex;
     align-items: center;
     align-items: center;
+    padding: 4px 8px;
+    margin-right: 8px;
+    margin-bottom: 8px;
+    background-color: #fff;
+    border: 1px solid #e9e9e9;
+    border-radius: 4px;
   }
   }
 
 
-  .right-side {
-    flex: 1; /* 占据剩余空间 */
-    background: #09172c;
+  /* 文件名 */
+  .file-name {
+    margin-left: 8px;
+    margin-right: 8px;
+    max-width: 150px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .ant-input {
+    background-color: rgba(255, 255, 255, 0) !important;
+    border: none;
+    outline: none;
+  }
+  .ant-input:focus {
+    border: none; /* 聚焦时无边框 */
+    outline: none; /* 聚焦时无轮廓 */
+    box-shadow: none; /* 移除可能存在的阴影效果 */
+  }
+  .ctrl-btn {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+  }
+  .question-input {
+    background-color: #1e293b !important;
+    border-color: #334155 !important;
+    color: #e2e8f0 !important;
+    border-radius: 8px !important;
+    padding: 12px 16px !important;
+    font-size: 14px !important;
+  }
+
+  .question-input::placeholder {
+    color: #64748b !important;
+  }
+
+  .control-btn {
+    height: 25px;
+    background-color: #043256;
+    border: 1px solid #2cb6ff;
+    color: #fff;
+    font-size: 10px;
+    margin-right: 10px;
+    cursor: pointer;
+    transition: all 0.2s;
+  }
+
+  .control-btn:hover {
+    background-color: #043256;
+    color: #e2e8f0;
+  }
+  .control-btn1 {
+    height: 20px;
+    background-color: #234a6b;
+    border: 1px solid #234a6b;
+    color: #fff;
+    font-size: 10px;
+    margin-right: 10px;
+    cursor: pointer;
+    transition: all 0.2s;
+  }
+  .control-btn2 {
+    height: 20px;
+    background-color: #2cb6ff;
+    border: 1px solid #2cb6ff;
+    color: #fff;
+    font-size: 10px;
+    margin-right: 10px;
+    cursor: pointer;
+    transition: all 0.2s;
+  }
+  /* 文件上传区 */
+  .file-upload {
+    position: absolute;
+    right: 20px;
+    bottom: 70px;
+    width: 180px;
     display: flex;
     display: flex;
     flex-direction: column;
     flex-direction: column;
+    gap: 10px;
+    border: 1px solid #2cb6ff;
+    background-color: #234a6b;
+    border-radius: 6px;
+    padding: 10px;
+  }
 
 
-    .dialog-area {
-      flex: 1; /* 占据剩余空间 */
-      gap: 10px; /* 消息块间隔统一控制 */
-      overflow-y: auto; /* 垂直滚动条 */
-      padding: 5px;
-      display: flex;
-      flex-direction: column;
-      color: #fff;
-
-      .ask-message {
-        padding: 10px;
-        border-radius: 5px;
-        background: #0c2842;
-        max-width: 80%;
-      }
-      .answer-message {
-        padding: 10px;
-        border-radius: 5px;
-        background: #0c2842;
-        max-width: 90%;
-      }
-    }
+  .input-container {
+    position: relative;
+    display: flex;
+    align-items: center;
+    width: 100%;
+  }
 
 
-    .input-area {
-      background-color: #043256;
-      padding: 5px;
-
-      textarea {
-        background-color: transparent;
-        width: 100%;
-        height: 40px;
-        border: none;
-        resize: none;
-        outline: none;
-        overflow: hidden;
-        padding: 10px; /* 统一内边距 */
-        color: #fff;
-      }
+  .file-input {
+    flex: 1;
+    background-color: #234a6b;
+    border-color: #2cb6ff !important;
+    color: #e2e8f0 !important;
+    border-radius: 6px !important;
+    font-size: 10px !important;
+    padding-right: 70px !important;
+    height: 36px !important;
+    width: 100% !important;
+  }
 
 
-      .action-bar {
-        height: 30px;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-
-        .think-btn {
-          border: 1px solid #ccc;
-          width: 100px;
-          height: 20px;
-          line-height: 20px;
-          text-align: center;
-          border-radius: 5px;
-          cursor: pointer;
-          background: transparent;
-          color: white;
-          transition: background 0.3s;
-        }
+  .confirm-btn {
+    position: absolute;
+    right: 5px;
+    background-color: #2cb6ff;
+    border: none;
+    color: #fff;
+    border-radius: 4px;
+    font-size: 12px;
+    padding: 4px 10px;
+    cursor: pointer;
+    transition: all 0.2s;
+    height: 28px;
+  }
 
 
-        .think-btn.active {
-          background: #1890ff;
-          color: white;
-          border-color: #1890ff;
-        }
-      }
-    }
+  .confirm-btn:hover {
+    background-color: #2cb6ff;
   }
   }
-</style>
-<style>
-  .zxm-popover-inner-content {
-    padding: 1px;
+
+  .custom-upload {
+    width: 100%;
+    padding: 0 !important;
   }
   }
+
+  .upload-btn {
+    background-color: #234a6b !important;
+    border: 1px solid #2188c3 !important;
+    color: #dbeafe !important;
+    border-radius: 6px !important;
+    font-size: 12px !important;
+    cursor: pointer;
+    transition: all 0.2s;
+    padding: 8px 0 !important;
+    width: 190% !important;
+    height: 36px !important;
+    box-sizing: border-box !important;
+  }
+  .upload-btn:hover {
+    background-color: #1f84bd !important;
+    color: #fff !important;
+  }
+  .custom-upload .ant-upload-select:hover .ant-btn {
+    border-color: #1f84bd !important;
+  }
+}
+</style>
+<style scoped>
+.zxm-popover-inner-content {
+  padding: 1px;
+}
+.message-wrapper {
+  display: flex;
+  align-items: flex-start;
+  position: relative;
+}
+.user-message-wrapper {
+  flex-direction: row-reverse;
+}
+.ai-message-wrapper {
+  flex-direction: row;
+}
+/* 复制图标样式 */
+.copy-icon {
+  font-size: 16px;
+  cursor: pointer;
+  margin: 4px 8px;
+  color: #fff;
+}
+.message-wrapper:hover .copy-icon {
+  opacity: 1;
+}
+.copy-icon:hover {
+  color: #1890ff;
+}
 </style>
 </style>

+ 10 - 8
src/design/vent/comment.less

@@ -209,18 +209,18 @@
   --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
   --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
   margin-top: 10px;
   margin-top: 10px;
   position: relative;
   position: relative;
-  // width: 314px;
-  // height: 208px;
-  // width: 614px;
-  // height: 508px;
+  width: 460px;
+  height: 269px;
   background: var(--image-camera_bg);
   background: var(--image-camera_bg);
   background-size: cover;
   background-size: cover;
   padding: 10px;
   padding: 10px;
+  align-self: end;
+  padding: 10px;
 
 
   .rtspVideo {
   .rtspVideo {
     border: 1px solid #ffffff22;
     border: 1px solid #ffffff22;
-    height: 180px;
-    width: 320px;
+    width: 100%;
+    height: 100%;
   }
   }
 
 
   .video-name {
   .video-name {
@@ -245,14 +245,16 @@
   height: 100%;
   height: 100%;
   // height: 208px;
   // height: 208px;
   position: relative;
   position: relative;
-
+  display: flex;
+  flex-direction: column;
   .liveVideo {
   .liveVideo {
     pointer-events: auto !important;
     pointer-events: auto !important;
     width: 460px !important;
     width: 460px !important;
-    height: 305px !important;
+    height: 269px !important;
     padding: 10px;
     padding: 10px;
     background: var(--image-camera_bg);
     background: var(--image-camera_bg);
     background-size: cover;
     background-size: cover;
+    align-self: flex-end;
     // .player {
     // .player {
     //     width: 314px;
     //     width: 314px;
     //     height: 208px;
     //     height: 208px;

+ 76 - 111
src/hooks/system/useCamera.ts

@@ -7,6 +7,7 @@ import HlsPlugin from 'xgplayer-hls';
 import FlvPlugin from 'xgplayer-flv';
 import FlvPlugin from 'xgplayer-flv';
 import 'xgplayer/dist/index.min.css';
 import 'xgplayer/dist/index.min.css';
 import ZH from 'xgplayer/es/lang/zh-cn';
 import ZH from 'xgplayer/es/lang/zh-cn';
+import videojs from 'video.js';
 I18N.use(ZH);
 I18N.use(ZH);
 
 
 export function useCamera() {
 export function useCamera() {
@@ -188,117 +189,78 @@ export function useCamera() {
           videoParentDom.setAttribute('class', 'liveVideo');
           videoParentDom.setAttribute('class', 'liveVideo');
           livePlayerDiv?.appendChild(videoParentDom);
           livePlayerDiv?.appendChild(videoParentDom);
           useDrag(videoParentDom);
           useDrag(videoParentDom);
-          const videoDom: HTMLElement = document.createElement('div');
-          videoDom.setAttribute('id', videoParent[0]);
-          videoParentDom.appendChild(videoDom);
+
           if (videoParent[1] && videoParent[1].addr) {
           if (videoParent[1] && videoParent[1].addr) {
+            let player;
             const fileExtension = videoParent[1].addr.split('.').pop();
             const fileExtension = videoParent[1].addr.split('.').pop();
-            const player = getPlayer(fileExtension, videoParent[0], videoParent[1].devicekind, videoParent[1].addr, videoParent[1].cameraRate);
-            // let player;
-            // if (fileExtension === 'flv' || videoParent[1].devicekind == 'flv') {
-            //   player = new Player({
-            //     lang: 'zh',
-            //     id: videoParent[0],
-            //     url: videoParent[1].addr,
-            //     width: 294,
-            //     height: 188,
-            //     poster: '/src/assets/images/vent/noSinge.png',
-            //     plugins: [FlvPlugin],
-            //     fluid: true,
-            //     autoplay: true,
-            //     isLive: true,
-            //     playsinline: true,
-            //     screenShot: true,
-            //     whitelist: [''],
-            //     ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
-            //     closeVideoClick: true,
-            //     customConfig: {
-            //       isClickPlayBack: false,
-            //     },
-            //     controlsList: ['nodownload', 'nofullscreen', 'noremoteplayback'],
-            //     defaultPlaybackRate: videoParent[1].cameraRate || 1,
-            //     flv: {
-            //       retryCount: 3, // 重试 3 次,默认值
-            //       retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-            //       loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            //       fetchOptions: {
-            //         // 该参数会透传给 fetch,默认值为 undefined
-            //         mode: 'cors',
-            //       },
-            //       targetLatency: 10, // 直播目标延迟,默认 10 秒
-            //       maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-            //       disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-            //       maxJumpDistance: 10,
-            //     },
-            //   });
-
-            //   playerList.push(player);
-            // }
-            // if (fileExtension === 'm3u8' || videoParent[1].devicekind == 'm3u8') {
-            //   let player;
-            //   if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
-            //     // 原生支持 hls 播放
-            //     player = new Player({
-            //       lang: 'zh',
-            //       id: videoParent[0],
-            //       url: videoParent[1].addr,
-            //       width: 294,
-            //       height: 188,
-            //       isLive: true,
-            //       autoplay: true,
-            //       autoplayMuted: true,
-            //       cors: true,
-            //       ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
-            //       poster: '/src/assets/images/vent/noSinge.png',
-            //       defaultPlaybackRate: videoParent[1].cameraRate || 1,
-            //       controlsList: ['nodownload', 'nofullscreen', 'noremoteplayback'],
-            //       hls: {
-            //         retryCount: 10, // 重试 3 次,默认值
-            //         retryDelay: 30000, // 每次重试间隔 1 秒,默认值
-            //         loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            //         disconnectTime: 30, //直播断流时间,
-            //         fetchOptions: {
-            //           // 该参数会透传给 fetch,默认值为 undefined
-            //           mode: 'cors',
-            //         },
-            //         targetLatency: 10, // 直播目标延迟,默认 10 秒
-            //         maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-            //         maxJumpDistance: 10,
-            //       },
-            //     });
-            //   } else if (HlsPlugin.isSupported()) {
-            //     // 第一步
-            //     player = new Player({
-            //       lang: 'zh',
-            //       id: videoParent[0],
-            //       url: videoParent[1].addr,
-            //       width: 294,
-            //       height: 188,
-            //       isLive: true,
-            //       autoplay: true,
-            //       autoplayMuted: true,
-            //       ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
-            //       plugins: [HlsPlugin], // 第二步
-            //       poster: '/src/assets/images/vent/noSinge.png',
-            //       defaultPlaybackRate: videoParent[1].cameraRate || 1,
-            //       controlsList: ['nodownload', 'nofullscreen', 'noremoteplayback'],
-            //       hls: {
-            //         retryCount: 10, // 重试 3 次,默认值
-            //         retryDelay: 30000, // 每次重试间隔 1 秒,默认值
-            //         loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            //         disconnectTime: 30,
-            //         fetchOptions: {
-            //           // 该参数会透传给 fetch,默认值为 undefined
-            //           mode: 'cors',
-            //         },
-            //         targetLatency: 10, // 直播目标延迟,默认 10 秒
-            //         maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-            //         maxJumpDistance: 10,
-            //       },
-            //     });
-            //   }
-            //   playerList.push(player);
-            // }
+            // const player = getPlayer(fileExtension, videoParent[0], videoParent[1].devicekind, videoParent[1].addr, videoParent[1].cameraRate);
+            if (fileExtension === 'flv' || videoParent[1].devicekind == 'flv') {
+              const videoDom: HTMLElement = document.createElement('div');
+              videoDom.setAttribute('id', videoParent[0]);
+              videoDom.style = 'width: 100%; height: 100%;';
+              videoParentDom.appendChild(videoDom);
+              player = new Player({
+                lang: 'zh',
+                id: videoParent[0],
+                url: videoParent[1].addr,
+                width: 294,
+                height: 188,
+                poster: '/src/assets/images/vent/noSinge.png',
+                plugins: [FlvPlugin],
+                fluid: true,
+                autoplay: true,
+                isLive: true,
+                playsinline: true,
+                screenShot: true,
+                whitelist: [''],
+                ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+                closeVideoClick: true,
+                customConfig: {
+                  isClickPlayBack: false,
+                },
+                controlsList: ['nodownload', 'nofullscreen', 'noremoteplayback'],
+                defaultPlaybackRate: videoParent[1].cameraRate || 1,
+                flv: {
+                  retryCount: 3, // 重试 3 次,默认值
+                  retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+                  loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+                  fetchOptions: {
+                    // 该参数会透传给 fetch,默认值为 undefined
+                    mode: 'cors',
+                  },
+                  targetLatency: 10, // 直播目标延迟,默认 10 秒
+                  maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+                  disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+                  maxJumpDistance: 10,
+                },
+              });
+            } else {
+              const videoDom: HTMLElement = document.createElement('video');
+              videoDom.setAttribute('id', videoParent[0]);
+              videoDom.style = 'width: 100%; height: 100%;';
+              videoParentDom.appendChild(videoDom);
+              player = videojs(
+                videoParent[0],
+                {
+                  controls: false,
+                  autoplay: true,
+                  preload: 'auto',
+                  muted: true,
+                  // src: cameraUrl,
+                  sources: [
+                    {
+                      src: videoParent[1].addr,
+                      type: 'application/x-mpegURL',
+                    },
+                  ],
+                },
+                () => {
+                  videojs.log('播放器准备好了!');
+                  // this.play();
+                }
+              );
+              player.play();
+            }
             playerList.push(player);
             playerList.push(player);
           }
           }
         } else {
         } else {
@@ -439,7 +401,6 @@ export function useCamera() {
           },
           },
         });
         });
       }
       }
-      // playerList.push(player);
     }
     }
     return player;
     return player;
   }
   }
@@ -455,7 +416,11 @@ export function useCamera() {
       dom?.remove();
       dom?.remove();
     });
     });
     playerList.forEach((player) => {
     playerList.forEach((player) => {
-      if (player && player.destroy) player.destroy();
+      if (player) {
+        if (player.destroy) player.destroy();
+        if (player.dispose) player.dispose();
+        if (player.remove) player.remove();
+      }
       player = null;
       player = null;
     });
     });
     videoParentDomList.length = 0;
     videoParentDomList.length = 0;

+ 3 - 0
src/layouts/default/header/index.vue

@@ -60,6 +60,7 @@
       <!-- 公司端不显示语音播报功能 weatherBroadcast.vue-->
       <!-- 公司端不显示语音播报功能 weatherBroadcast.vue-->
       <WeatherBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && isShowQy && portValue != '8062'" />
       <WeatherBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && isShowQy && portValue != '8062'" />
       <VoiceBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && portValue != '8062'" />
       <VoiceBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && portValue != '8062'" />
+      <AIChat></AIChat>
       <VoiceBroadcastGsd v-if="sysOrgCode == 'sdmtjtgsd'" />
       <VoiceBroadcastGsd v-if="sysOrgCode == 'sdmtjtgsd'" />
       <UserDropDown v-if="showUserDropdown" :theme="getHeaderTheme" />
       <UserDropDown v-if="showUserDropdown" :theme="getHeaderTheme" />
       <LoginSelect ref="loginSelectRef" @success="loginSelectOk" />
       <LoginSelect ref="loginSelectRef" @success="loginSelectOk" />
@@ -96,6 +97,7 @@ import { useLocale } from '/@/locales/useLocale';
 import WeatherBroadcast from './components/weatherBroadcast.vue';
 import WeatherBroadcast from './components/weatherBroadcast.vue';
 import VoiceBroadcast from './components/VoiceBroadcast.vue';
 import VoiceBroadcast from './components/VoiceBroadcast.vue';
 import VoiceBroadcastGsd from './components/VoiceBroadcastGsd.vue';
 import VoiceBroadcastGsd from './components/VoiceBroadcastGsd.vue';
+import AIChat from '/@/components/AIChat/MiniChat.vue';
 
 
 import LoginSelect from '/@/views/sys/login/LoginSelect.vue';
 import LoginSelect from '/@/views/sys/login/LoginSelect.vue';
 import { useUserStore } from '/@/store/modules/user';
 import { useUserStore } from '/@/store/modules/user';
@@ -121,6 +123,7 @@ export default defineComponent({
     LockScreen,
     LockScreen,
     LoginSelect,
     LoginSelect,
     VoiceBroadcast,
     VoiceBroadcast,
+    AIChat,
     VoiceBroadcastGsd,
     VoiceBroadcastGsd,
     WeatherBroadcast,
     WeatherBroadcast,
     SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue'), {
     SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue'), {

+ 0 - 1
src/views/vent/comment/components/bottomMenu.vue

@@ -20,7 +20,6 @@
 </template>
 </template>
 <script lang="ts">
 <script lang="ts">
   import { ref, defineComponent } from 'vue';
   import { ref, defineComponent } from 'vue';
-  import { string } from 'vue-types';
   type navListType = { title: string; pathName: string; isHover: Boolean };
   type navListType = { title: string; pathName: string; isHover: Boolean };
 
 
   export default defineComponent({
   export default defineComponent({

+ 3 - 0
src/views/vent/dataCenter/APICenter/ApiAddModal.vue

@@ -10,6 +10,7 @@ import { BasicForm, useForm } from '/@/components/Form/index';
 import { formAddSchema } from './apiManger.data';
 import { formAddSchema } from './apiManger.data';
 import { apiManageList, AddOrEdit, apiManageQueryByID } from './apiManger.api';
 import { apiManageList, AddOrEdit, apiManageQueryByID } from './apiManger.api';
 const isAdd = ref(true);
 const isAdd = ref(true);
+const currentRecordId = ref('');
 // 声明Emits
 // 声明Emits
 const emit = defineEmits(['success', 'register']);
 const emit = defineEmits(['success', 'register']);
 //表单配置
 //表单配置
@@ -24,6 +25,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
   setModalProps({ confirmLoading: false });
   setModalProps({ confirmLoading: false });
   //获取详情
   //获取详情
   data.record = await apiManageQueryByID({ id: data?.record.id });
   data.record = await apiManageQueryByID({ id: data?.record.id });
+  currentRecordId.value = data.record.id;
   isAdd.value = !!data?.isUpdate;
   isAdd.value = !!data?.isUpdate;
   await setFieldsValue({
   await setFieldsValue({
     ...data.record,
     ...data.record,
@@ -33,6 +35,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
 async function handleSubmit() {
 async function handleSubmit() {
   try {
   try {
     let values = await validate();
     let values = await validate();
+    values.id = currentRecordId.value;
     setModalProps({ confirmLoading: true });
     setModalProps({ confirmLoading: true });
     //提交表单
     //提交表单
     await AddOrEdit(values, unref(isAdd.value));
     await AddOrEdit(values, unref(isAdd.value));

+ 20 - 14
src/views/vent/home/colliery/index.vue

@@ -15,15 +15,15 @@
         <div class="left-content">
         <div class="left-content">
           <!-- 主通风机 -->
           <!-- 主通风机 -->
           <div class="monitor-box">
           <div class="monitor-box">
-            <mainMonitor :maindata="mainList" @goDetail="goDetail" />
+            <mainMonitor :maindata="mainList" @go-detail="goDetail" />
           </div>
           </div>
           <!-- 局部通风机 -->
           <!-- 局部通风机 -->
           <div class="monitor-box monitor-box1">
           <div class="monitor-box monitor-box1">
-            <fanMonitor @goDetail="goDetail" :fandata="fanLocalList" />
+            <fanMonitor @go-detail="goDetail" :fandata="fanLocalList" />
           </div>
           </div>
           <!-- 通风设备远程控制 -->
           <!-- 通风设备远程控制 -->
           <div class="monitor-box">
           <div class="monitor-box">
-            <windDevice :devicedata="deviceData" @goDetail="goDetail" />
+            <windDevice :devicedata="deviceData" @go-detail="goDetail" />
           </div>
           </div>
         </div>
         </div>
         <div class="center-content">
         <div class="center-content">
@@ -31,7 +31,7 @@
           <div class="three-box">
           <div class="three-box">
             <div class="three-nav" v-if="!hasPermission('monitor:show')">
             <div class="three-nav" v-if="!hasPermission('monitor:show')">
               <template v-for="(item, index) in navList" :key="index">
               <template v-for="(item, index) in navList" :key="index">
-                <div class="nav-item" v-if="(item.valList && item.valList.length > 0) || item.val">
+                <div class="nav-item" v-if="!item.disabled && ((item.valList && item.valList.length > 0) || item.val)">
                   <div class="item-label">{{ item.name }}</div>
                   <div class="item-label">{{ item.name }}</div>
                   <div class="item-value">
                   <div class="item-value">
                     <template v-if="item.isShow">
                     <template v-if="item.isShow">
@@ -57,27 +57,33 @@
           </div>
           </div>
           <!-- 风量监测 -->
           <!-- 风量监测 -->
           <div class="wind-box">
           <div class="wind-box">
-            <windMonitor :flList="flList" @goDetail="goDetail" />
+            <windMonitor :flList="flList" @go-detail="goDetail" />
           </div>
           </div>
         </div>
         </div>
         <div class="right-content">
         <div class="right-content">
           <!-- 关键通风路线 -->
           <!-- 关键通风路线 -->
           <div class="monitor-box">
           <div class="monitor-box">
-            <windLine :lineList="lineList" @goDetail="goDetail" />
+            <windLine :lineList="lineList" @go-detail="goDetail" />
           </div>
           </div>
           <!-- 工作面智能管控 -->
           <!-- 工作面智能管控 -->
           <div class="monitor-box monitor-box1">
           <div class="monitor-box monitor-box1">
-            <workMonitor :workList="workList" @goDetail="goDetail" />
+            <workMonitor :workList="workList" @go-detail="goDetail" />
           </div>
           </div>
           <!-- 设备预警 -->
           <!-- 设备预警 -->
           <div class="monitor-box">
           <div class="monitor-box">
-            <deviceWarn :warnData="warnData" @goDetail="goDetail" />
+            <deviceWarn :warnData="warnData" @go-detail="goDetail" />
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
-  <Network ref="NetworkRef" v-if="pageType == 'timesolution'" :pageResult="pageResult" @changePageType="changePageType" style="position: absolute" />
+  <Network
+    ref="NetworkRef"
+    v-if="pageType == 'timesolution'"
+    :pageResult="pageResult"
+    @change-page-type="changePageType"
+    style="position: absolute"
+  />
   <VentModal
   <VentModal
     v-if="pageType == 'model3D' || pageType == 'timesolution'"
     v-if="pageType == 'model3D' || pageType == 'timesolution'"
     ref="fullModalRef"
     ref="fullModalRef"
@@ -123,13 +129,13 @@
   let warnData = ref<any>([]); //预警数据
   let warnData = ref<any>([]); //预警数据
   let deviceData = ref<any>({}); //设备监测数据
   let deviceData = ref<any>({}); //设备监测数据
   let navList = reactive([
   let navList = reactive([
-    { name: '总回风量(m³/min)', isShow: true, valList: [] },
-    { name: '总进风量(m³/min)', isShow: true, valList: [] },
-    { name: '计划风量(m³/min)', isShow: true, valList: [] },
+    { name: '总回风量(m³/min)', isShow: true, valList: [], disabled: false },
+    { name: '总进风量(m³/min)', isShow: true, valList: [], disabled: false },
+    { name: '计划风量(m³/min)', isShow: true, valList: [], disabled: true },
     // { name: '有效风量(m³/min)', isShow: true, valList: [] },
     // { name: '有效风量(m³/min)', isShow: true, valList: [] },
     // { name: '等积孔(m²)', isShow: true, valList: [] },
     // { name: '等积孔(m²)', isShow: true, valList: [] },
-    { name: '有效风量率', isShow: false, val: '0%' },
-    { name: '外部漏风率', isShow: false, val: 0 },
+    { name: '有效风量率', isShow: false, val: '0%', disabled: false },
+    { name: '外部漏风率', isShow: false, val: 0, disabled: false },
   ]);
   ]);
   let nowTimeYear = ref('');
   let nowTimeYear = ref('');
   let nowTimeWeek = ref('');
   let nowTimeWeek = ref('');

+ 90 - 81
src/views/vent/monitorManager/airDoor/components/Modal.vue

@@ -1,7 +1,17 @@
 <template>
 <template>
-  <a-modal ref="modalRef"   width="850px" :visible="props.visible" :wrap-style="{ overflow: 'hidden' }" @ok="handleOk"
-    :mask-closable="maskClosable" :centered="props.centered" :footer="props.footer" @cancel="handleCancel"
-    :confirm-loading="props.confirmLoading" :destroyOnClose="props.destroyOnClose">
+  <a-modal
+    ref="modalRef"
+    width="850px"
+    :visible="props.visible"
+    :wrap-style="{ overflow: 'hidden' }"
+    @ok="handleOk"
+    :mask-closable="maskClosable"
+    :centered="props.centered"
+    :footer="props.footer"
+    @cancel="handleCancel"
+    :confirm-loading="props.confirmLoading"
+    :destroyOnClose="props.destroyOnClose"
+  >
     <slot></slot>
     <slot></slot>
 
 
     <template #title>
     <template #title>
@@ -16,94 +26,93 @@
   </a-modal>
   </a-modal>
 </template>
 </template>
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { computed, CSSProperties, ref, watch, watchEffect } from "vue";
-import { useDraggable } from "@vueuse/core";
+  import { computed, CSSProperties, ref, watch, watchEffect } from 'vue';
+  import { useDraggable } from '@vueuse/core';
 
 
-// 定义Props接口,描述组件的属性
-interface Props {
-  title?: string, // 对话框的标题,默认为"提示"
-  footer?: null, // 对话框的页脚,默认为null
-  visible: boolean, // 对话框是否可见
-  confirmLoading?: boolean, // 确认按钮是否处于加载状态,默认为false
-  destroyOnClose?: boolean, // 对话框关闭时是否销毁组件,默认为false
-  maskClosable?: boolean, // 点击遮罩层是否可关闭对话框,默认为false
-  centered?: boolean, // 对话框是否居中显示,默认为false
-}
-
-// 设置props的默认值
-const props = withDefaults(defineProps<Props>(), { title: '摄像头信息', visible: false, destroyOnClose: false, maskClosable: true })
+  // 定义Props接口,描述组件的属性
+  interface Props {
+    title?: string; // 对话框的标题,默认为"提示"
+    footer?: null; // 对话框的页脚,默认为null
+    visible: boolean; // 对话框是否可见
+    confirmLoading?: boolean; // 确认按钮是否处于加载状态,默认为false
+    destroyOnClose?: boolean; // 对话框关闭时是否销毁组件,默认为false
+    maskClosable?: boolean; // 点击遮罩层是否可关闭对话框,默认为false
+    centered?: boolean; // 对话框是否居中显示,默认为false
+  }
 
 
-// 创建ref引用modalTitleRef,用于引用组件中的标题元素
-const modalTitleRef = ref<HTMLElement | null | any>(null);
+  // 设置props的默认值
+  const props = withDefaults(defineProps<Props>(), { title: '摄像头信息', visible: false, destroyOnClose: false, maskClosable: true });
 
 
-// 使用useDraggable钩子,获取拖拽相关属性
-const { x, y, isDragging } = useDraggable(modalTitleRef);
+  // 创建ref引用modalTitleRef,用于引用组件中的标题元素
+  const modalTitleRef = ref<HTMLElement | null | any>(null);
 
 
-// 创建emit函数,用于触发自定义事件
-const emit = defineEmits(['ok', 'update:visible', 'cancel'])
+  // 使用useDraggable钩子,获取拖拽相关属性
+  const { x, y, isDragging } = useDraggable(modalTitleRef);
 
 
-// 处理ok事件的函数
-const handleOk = (e: MouseEvent) => {
-  emit('ok')
-};
+  // 创建emit函数,用于触发自定义事件
+  const emit = defineEmits(['ok', 'update:visible', 'cancel']);
 
 
-// 处理cancel事件的函数
-const handleCancel = () => {
-  emit('update:visible', false)
-  emit('cancel')
-}
+  // 处理ok事件的函数
+  const handleOk = (e: MouseEvent) => {
+    emit('ok');
+  };
 
 
-// 创建各种响应式数据
-const startX = ref<number>(0); // 记录起始点的 x 坐标
-const startY = ref<number>(0); // 记录起始点的 y 坐标
-const startedDrag = ref(false); // 标志位,表示是否开始拖拽,默认为 false
-const transformX = ref(0); // x 偏移量
-const transformY = ref(0); // y 偏移量
-const preTransformX = ref(0); // 拖拽前的 x 偏移量
-const preTransformY = ref(0); // 拖拽前的 y 偏移量
-const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 }); // 可拖拽的边界
+  // 处理cancel事件的函数
+  const handleCancel = () => {
+    emit('update:visible', false);
+    emit('cancel');
+  };
 
 
-// 监听x和y的变化
-watch([x, y], () => { // 监听鼠标移动事件的函数
-  if (!startedDrag.value) { // 如果尚未开始拖拽
-    startX.value = x.value; // 记录起始点的 x 坐标
-    startY.value = y.value; // 记录起始点的 y 坐标
-    const bodyRect = document.body.getBoundingClientRect(); // 获取页面 body 元素的边界信息
-    const titleRect = modalTitleRef.value.getBoundingClientRect(); // 获取 modalTitle 元素的边界信息
-    dragRect.value.right = bodyRect.width - titleRect.width; // 计算可拖拽的最大右边界
-    dragRect.value.bottom = bodyRect.height - titleRect.height; // 计算可拖拽的最大下边界
-    preTransformX.value = transformX.value; // 记录拖拽前的 x 偏移量
-    preTransformY.value = transformY.value; // 记录拖拽前的 y 偏移量
-  }
-  startedDrag.value = true; // 设置已开始拖拽标志
-});
+  // 创建各种响应式数据
+  const startX = ref<number>(0); // 记录起始点的 x 坐标
+  const startY = ref<number>(0); // 记录起始点的 y 坐标
+  const startedDrag = ref(false); // 标志位,表示是否开始拖拽,默认为 false
+  const transformX = ref(0); // x 偏移量
+  const transformY = ref(0); // y 偏移量
+  const preTransformX = ref(0); // 拖拽前的 x 偏移量
+  const preTransformY = ref(0); // 拖拽前的 y 偏移量
+  const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 }); // 可拖拽的边界
 
 
-// 监听isDragging的变化
-watch(isDragging, () => { // 监听 isDragging 变量的改变
-  if (!isDragging) { // 如果 isDragging 变为 false
-    startedDrag.value = false; // 将 startedDrag.value 设置为 false,表示拖拽操作结束
-  }
-});
+  // 监听x和y的变化
+  watch([x, y], () => {
+    // 监听鼠标移动事件的函数
+    if (!startedDrag.value) {
+      // 如果尚未开始拖拽
+      startX.value = x.value; // 记录起始点的 x 坐标
+      startY.value = y.value; // 记录起始点的 y 坐标
+      const bodyRect = document.body.getBoundingClientRect(); // 获取页面 body 元素的边界信息
+      const titleRect = modalTitleRef.value.getBoundingClientRect(); // 获取 modalTitle 元素的边界信息
+      dragRect.value.right = bodyRect.width - titleRect.width; // 计算可拖拽的最大右边界
+      dragRect.value.bottom = bodyRect.height - titleRect.height; // 计算可拖拽的最大下边界
+      preTransformX.value = transformX.value; // 记录拖拽前的 x 偏移量
+      preTransformY.value = transformY.value; // 记录拖拽前的 y 偏移量
+    }
+    startedDrag.value = true; // 设置已开始拖拽标志
+  });
 
 
-// 使用watchEffect监听响应式数据的变化
-watchEffect(() => { // 响应 startedDrag.value 的变化
-  if (startedDrag.value) { // 如果 startedDrag.value 为 true,表示正在进行拖拽操作
-    transformX.value = // 计算 x 偏移量
-      preTransformX.value +
-      Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
-      startX.value;
-    transformY.value = // 计算 y 偏移量
-      preTransformY.value +
-      Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
-      startY.value;
-  }
-});
+  // 监听isDragging的变化
+  watch(isDragging, () => {
+    // 监听 isDragging 变量的改变
+    if (!isDragging) {
+      // 如果 isDragging 变为 false
+      startedDrag.value = false; // 将 startedDrag.value 设置为 false,表示拖拽操作结束
+    }
+  });
 
 
-// 计算transformStyle,用于动态设置对话框的位置
-const transformStyle = computed<CSSProperties>(() => {
-  return {
-    transform: `translate(${transformX.value}px, ${transformY.value}px)`,
-  };
-});
+  // 使用watchEffect监听响应式数据的变化
+  watchEffect(() => {
+    // 响应 startedDrag.value 的变化
+    if (startedDrag.value) {
+      // 如果 startedDrag.value 为 true,表示正在进行拖拽操作
+      transformX.value = preTransformX.value + Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) - startX.value; // 计算 x 偏移量
+      transformY.value = preTransformY.value + Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) - startY.value; // 计算 y 偏移量
+    }
+  });
 
 
+  // 计算transformStyle,用于动态设置对话框的位置
+  const transformStyle = computed<CSSProperties>(() => {
+    return {
+      transform: `translate(${transformX.value}px, ${transformY.value}px)`,
+    };
+  });
 </script>
 </script>

+ 214 - 217
src/views/vent/monitorManager/airDoor/components/cameraModal.vue

@@ -16,180 +16,113 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
-import { useRouter } from 'vue-router';
-import Player, { I18N } from 'xgplayer';
-import ZH from 'xgplayer/es/lang/zh-cn';
-import HlsPlugin from 'xgplayer-hls';
-import FlvPlugin from 'xgplayer-flv';
-import 'xgplayer/dist/index.min.css';
-import { cameraAddr } from '../airdoor.api';
-let props = defineProps({
-  cameraData: {
-    type: Object,
-    default: () => {
-      return {}
-    }
-  }
-})
-
-const playerList = ref([]);
-let addrList = ref<{ name: string; addr: string; cameraRate: number; devicekind: string }[]>([]);
-const webRtcServerList = <any[]>[];
+  import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
+  import { useRouter } from 'vue-router';
+  import Player, { I18N } from 'xgplayer';
+  import ZH from 'xgplayer/es/lang/zh-cn';
+  import HlsPlugin from 'xgplayer-hls';
+  import FlvPlugin from 'xgplayer-flv';
+  import 'xgplayer/dist/index.min.css';
+  import { cameraAddr } from '../airdoor.api';
+  let props = defineProps({
+    cameraData: {
+      type: Object,
+      default: () => {
+        return {};
+      },
+    },
+  });
 
 
-async function getVideoAddrs() {
-  console.log(props.cameraData, 'camera---')
-  clearCamera();
-  playerList.value = [];
+  const playerList = ref([]);
+  let addrList = ref<{ name: string; addr: string; cameraRate: number; devicekind: string }[]>([]);
+  const webRtcServerList = <any[]>[];
 
 
+  async function getVideoAddrs() {
+    console.log(props.cameraData, 'camera---');
+    clearCamera();
+    playerList.value = [];
 
 
-  const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
-  const cameras = props.cameraData.cameras;
-  for (let i = 0; i < cameras.length; i++) {
-    const item = cameras[i];
-    if (item['devicekind'] === 'toHKRtsp' || item['devicekind'] === 'toHKHLs' || item['devicekind'] === 'HLL' || item['devicekind'] === 'YZG_URL') {
-      // 从海康平台接口获取视频流
-      const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
-      const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : ''
-      try {
-        const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
-        if (data && data['url']) {
-          cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+    const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
+    const cameras = props.cameraData.cameras;
+    for (let i = 0; i < cameras.length; i++) {
+      const item = cameras[i];
+      if (item['devicekind'] === 'toHKRtsp' || item['devicekind'] === 'toHKHLs' || item['devicekind'] === 'HLL' || item['devicekind'] === 'YZG_URL') {
+        // 从海康平台接口获取视频流
+        const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
+        const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : '';
+        try {
+          const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
+          if (data && data['url']) {
+            cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+          }
+        } catch (error) {}
+      } else {
+        if (item['addr'].includes('0.0.0.0')) {
+          item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
         }
         }
-
-      } catch (error) { }
-    } else {
-      if (item['addr'].includes('0.0.0.0')) {
-        item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
+        cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
       }
       }
-      cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
     }
     }
+    addrList.value = cameraList;
+    console.log(addrList.value, ' addrList.value-------------');
   }
   }
-  addrList.value = cameraList;
-  console.log(addrList.value, ' addrList.value-------------');
-
-}
 
 
-function getVideo() {
-  const ip = VUE_APP_URL.webRtcUrl;
-  for (let i = 0; i < addrList.value.length; i++) {
-    const item = addrList.value[i];
-    if (item.addr.startsWith('rtsp://')) {
-      const dom = document.getElementById('video' + i) as HTMLVideoElement;
-      dom.muted = true;
-      dom.volume = 0;
-      const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip);
-      webRtcServerList.push(webRtcServer);
-      webRtcServer.connect(item.addr);
-    } else {
-      setNoRtspVideo('player' + i, item.addr, item.cameraRate, item.devicekind);
+  function getVideo() {
+    const ip = VUE_APP_URL.webRtcUrl;
+    for (let i = 0; i < addrList.value.length; i++) {
+      const item = addrList.value[i];
+      if (item.addr.startsWith('rtsp://')) {
+        const dom = document.getElementById('video' + i) as HTMLVideoElement;
+        dom.muted = true;
+        dom.volume = 0;
+        const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip);
+        webRtcServerList.push(webRtcServer);
+        webRtcServer.connect(item.addr);
+      } else {
+        setNoRtspVideo('player' + i, item.addr, item.cameraRate, item.devicekind);
+      }
     }
     }
   }
   }
-}
-function clearCamera() {
-  const num = webRtcServerList.length;
-  for (let i = 0; i < num; i++) {
-    if (webRtcServerList[i]) {
-      webRtcServerList[i].disconnect();
-      webRtcServerList[i] = null;
+  function clearCamera() {
+    const num = webRtcServerList.length;
+    for (let i = 0; i < num; i++) {
+      if (webRtcServerList[i]) {
+        webRtcServerList[i].disconnect();
+        webRtcServerList[i] = null;
+      }
     }
     }
+    for (let i = 0; i < playerList.value.length; i++) {
+      const player = playerList.value[i];
+      if (player.destroy) player.destroy();
+    }
+    playerList.value = [];
   }
   }
-  for (let i = 0; i < playerList.value.length; i++) {
-    const player = playerList.value[i];
-    if (player.destroy) player.destroy();
-  }
-  playerList.value = [];
-}
 
 
-function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
-  const fileExtension = videoAddr.split('.').pop();
-  if (fileExtension === 'flv' || devicekind == 'flv') {
-    const player = new Player({
-      lang: 'zh',
-      id: id,
-      url: videoAddr,
-      width: 589,
-      height: 330,
-      poster: '/src/assets/images/vent/noSinge.png',
-      plugins: [FlvPlugin],
-      fluid: true,
-      autoplay: true,
-      isLive: true,
-      playsinline: true,
-      screenShot: true,
-      whitelist: [''],
-      ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
-      closeVideoClick: true,
-      customConfig: {
-        isClickPlayBack: false,
-      },
-      defaultPlaybackRate: cameraRate || 1,
-      controls: false,
-      flv: {
-        retryCount: 3, // 重试 3 次,默认值
-        retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-        loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-        fetchOptions: {
-          // 该参数会透传给 fetch,默认值为 undefined
-          mode: 'cors',
-        },
-        targetLatency: 10, // 直播目标延迟,默认 10 秒
-        maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-        disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-        maxJumpDistance: 10,
-      },
-    });
-    playerList.value.push(player);
-  }
-  if (fileExtension === 'm3u8' || devicekind == 'm3u8') {
-    let player;
-    if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
-      // 原生支持 hls 播放
-      player = new Player({
+  function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
+    const fileExtension = videoAddr.split('.').pop();
+    if (fileExtension === 'flv' || devicekind == 'flv') {
+      const player = new Player({
         lang: 'zh',
         lang: 'zh',
         id: id,
         id: id,
         url: videoAddr,
         url: videoAddr,
-        width: 376,
-        height: 210,
-        isLive: true,
-        autoplay: true,
-        autoplayMuted: true,
-        cors: true,
-        ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+        width: 589,
+        height: 330,
         poster: '/src/assets/images/vent/noSinge.png',
         poster: '/src/assets/images/vent/noSinge.png',
-        defaultPlaybackRate: cameraRate || 1,
-        controls: false,
-        hls: {
-          retryCount: 3, // 重试 3 次,默认值
-          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-          fetchOptions: {
-            // 该参数会透传给 fetch,默认值为 undefined
-            mode: 'cors',
-          },
-          targetLatency: 10, // 直播目标延迟,默认 10 秒
-          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-          maxJumpDistance: 10,
-        },
-      });
-    } else if (HlsPlugin.isSupported()) {
-      // 第一步
-      player = new Player({
-        lang: 'zh',
-        id: id,
-        url: videoAddr,
-        width: 376,
-        height: 210,
-        isLive: true,
+        plugins: [FlvPlugin],
+        fluid: true,
         autoplay: true,
         autoplay: true,
-        autoplayMuted: true,
-        plugins: [HlsPlugin], // 第二步
-        poster: '/src/assets/images/vent/noSinge.png',
+        isLive: true,
+        playsinline: true,
+        screenShot: true,
+        whitelist: [''],
         ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
         ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+        closeVideoClick: true,
+        customConfig: {
+          isClickPlayBack: false,
+        },
         defaultPlaybackRate: cameraRate || 1,
         defaultPlaybackRate: cameraRate || 1,
         controls: false,
         controls: false,
-        hls: {
+        flv: {
           retryCount: 3, // 重试 3 次,默认值
           retryCount: 3, // 重试 3 次,默认值
           retryDelay: 1000, // 每次重试间隔 1 秒,默认值
           retryDelay: 1000, // 每次重试间隔 1 秒,默认值
           loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
           loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
@@ -203,85 +136,149 @@ function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
           maxJumpDistance: 10,
           maxJumpDistance: 10,
         },
         },
       });
       });
+      playerList.value.push(player);
+    }
+    if (fileExtension === 'm3u8' || devicekind == 'm3u8') {
+      let player;
+      if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
+        // 原生支持 hls 播放
+        player = new Player({
+          lang: 'zh',
+          id: id,
+          url: videoAddr,
+          width: 376,
+          height: 210,
+          isLive: true,
+          autoplay: true,
+          autoplayMuted: true,
+          cors: true,
+          ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+          poster: '/src/assets/images/vent/noSinge.png',
+          defaultPlaybackRate: cameraRate || 1,
+          controls: false,
+          hls: {
+            retryCount: 3, // 重试 3 次,默认值
+            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+            fetchOptions: {
+              // 该参数会透传给 fetch,默认值为 undefined
+              mode: 'cors',
+            },
+            targetLatency: 10, // 直播目标延迟,默认 10 秒
+            maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+            maxJumpDistance: 10,
+          },
+        });
+      } else if (HlsPlugin.isSupported()) {
+        // 第一步
+        player = new Player({
+          lang: 'zh',
+          id: id,
+          url: videoAddr,
+          width: 376,
+          height: 210,
+          isLive: true,
+          autoplay: true,
+          autoplayMuted: true,
+          plugins: [HlsPlugin], // 第二步
+          poster: '/src/assets/images/vent/noSinge.png',
+          ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+          defaultPlaybackRate: cameraRate || 1,
+          controls: false,
+          hls: {
+            retryCount: 3, // 重试 3 次,默认值
+            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+            fetchOptions: {
+              // 该参数会透传给 fetch,默认值为 undefined
+              mode: 'cors',
+            },
+            targetLatency: 10, // 直播目标延迟,默认 10 秒
+            maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+            maxJumpDistance: 10,
+          },
+        });
+      }
+      playerList.value.push(player);
     }
     }
-    playerList.value.push(player);
   }
   }
-}
 
 
-function goFullScreen(domId) {
-  const videoDom = document.getElementById(domId) as HTMLVideoElement;
-  if (videoDom.requestFullscreen) {
-    videoDom.requestFullscreen();
-    videoDom.play();
-  } else if (videoDom.mozRequestFullscreen) {
-    videoDom.mozRequestFullscreen();
-    videoDom.play();
-  } else if (videoDom.webkitRequestFullscreen) {
-    videoDom.webkitRequestFullscreen();
-    videoDom.play();
-  } else if (videoDom.msRequestFullscreen) {
-    videoDom.msRequestFullscreen();
-    videoDom.play();
+  function goFullScreen(domId) {
+    const videoDom = document.getElementById(domId) as HTMLVideoElement;
+    if (videoDom.requestFullscreen) {
+      videoDom.requestFullscreen();
+      videoDom.play();
+    } else if (videoDom.mozRequestFullscreen) {
+      videoDom.mozRequestFullscreen();
+      videoDom.play();
+    } else if (videoDom.webkitRequestFullscreen) {
+      videoDom.webkitRequestFullscreen();
+      videoDom.play();
+    } else if (videoDom.msRequestFullscreen) {
+      videoDom.msRequestFullscreen();
+      videoDom.play();
+    }
   }
   }
-}
 
 
-onMounted(async () => {
-  await getVideoAddrs();
-  getVideo();
-});
+  onMounted(async () => {
+    await getVideoAddrs();
+    getVideo();
+  });
 
 
-onUnmounted(() => {
-  clearCamera();
-});
+  onUnmounted(() => {
+    clearCamera();
+  });
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>
-@import '/@/design/theme.less';
+  @import '/@/design/theme.less';
 
 
-@{theme-deepblue} {
-  .camera-modal {
-    --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+  @{theme-deepblue} {
+    .camera-modal {
+      --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+    }
   }
   }
-}
 
 
-.camera-modal {
-  --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
-  display: flex;
-  justify-content: space-between;
-  flex-wrap: wrap;
+  .camera-modal {
+    --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: wrap;
 
 
-  .player-box {
-    width: 400px;
-    height: 235px;
-    padding: 10px 12px;
-    background: var(--image-camera_bg);
-    background-size: 100% 100%;
-    position: relative;
-    margin: 10px;
+    .player-box {
+      width: 400px;
+      height: 235px;
+      padding: 10px 12px;
+      background: var(--image-camera_bg);
+      background-size: 100% 100%;
+      position: relative;
+      margin: 10px;
 
 
-    .player-name {
-      font-size: 14px;
-      position: absolute;
-      top: 12px;
-      right: 12px;
-      color: #fff;
-      background-color: hsla(0, 0%, 50%, 0.5);
-      border-radius: 2px;
-      padding: 1px 5px;
-      max-width: 120px;
-      overflow: hidden;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-      z-index: 999;
-    }
+      .player-name {
+        font-size: 14px;
+        position: absolute;
+        top: 12px;
+        right: 12px;
+        color: #fff;
+        background-color: hsla(0, 0%, 50%, 0.5);
+        border-radius: 2px;
+        padding: 1px 5px;
+        max-width: 120px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        z-index: 999;
+      }
 
 
-    .click-box {
-      position: absolute;
-      width: 100%;
-      height: 100%;
-      top: 0;
-      left: 0;
+      .click-box {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        top: 0;
+        left: 0;
+      }
     }
     }
   }
   }
-}
 </style>
 </style>

+ 5 - 6
src/views/vent/monitorManager/airDoor/index.vue

@@ -3,10 +3,10 @@
     <customHeader>风门集中同控</customHeader>
     <customHeader>风门集中同控</customHeader>
     <div class="main-container">
     <div class="main-container">
       <div class="container-left">
       <div class="container-left">
-        <doorMenuL :menuData="menuData"></doorMenuL>
+        <doorMenuL :menuData="menuData" />
       </div>
       </div>
       <div class="container-right">
       <div class="container-right">
-        <doorContentR :infoData="menuData"></doorContentR>
+        <doorContentR :infoData="menuData" :visibleTs30="visibleTs30" />
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
@@ -19,10 +19,9 @@ import doorMenuL from './components/door-menu-l.vue'
 import doorContentR from './components/door-content-r.vue'
 import doorContentR from './components/door-content-r.vue'
 import { getDevice, upcoming } from './airdoor.api'
 import { getDevice, upcoming } from './airdoor.api'
 
 
-let menuData = ref<any[]>([])
-//控制定时设置提示弹窗显示/隐藏
-let visibleTs30 = ref(false)
-
+  let menuData = ref<any[]>([]);
+  //控制定时设置提示弹窗显示/隐藏
+  let visibleTs30 = ref(false);
 
 
 // https获取监测数据
 // https获取监测数据
 let timer: null | NodeJS.Timeout = null;
 let timer: null | NodeJS.Timeout = null;

+ 4 - 4
src/views/vent/monitorManager/alarmMonitor/common.data.ts

@@ -184,16 +184,16 @@ export function getMonitorFlag() {
     case 'sdmtjthlgmk': //哈拉钩
     case 'sdmtjthlgmk': //哈拉钩
       typeFlag = '2';
       typeFlag = '2';
       return typeFlag;
       return typeFlag;
-       case 'sdmtjtdltmk': //大柳塔
+    case 'sdmtjtdltmk': //大柳塔
       typeFlag = '2';
       typeFlag = '2';
       return typeFlag;
       return typeFlag;
-       case 'sdmtjtdltmkhjtj': //活鸡兔
+    case 'sdmtjtdltmkhjtj': //活鸡兔
       typeFlag = '2';
       typeFlag = '2';
       return typeFlag;
       return typeFlag;
-        case 'sdmtjtcctrk': //寸草塔
+    case 'sdmtjtcctrk': //寸草塔
       typeFlag = '2';
       typeFlag = '2';
       return typeFlag;
       return typeFlag;
-       case 'sdmtjtwlmlmk': //乌兰木伦
+    case 'sdmtjtwlmlmk': //乌兰木伦
       typeFlag = '2';
       typeFlag = '2';
       return typeFlag;
       return typeFlag;
     default:
     default:

+ 188 - 179
src/views/vent/monitorManager/alarmMonitor/common/top-area.vue

@@ -1,155 +1,166 @@
 <template>
 <template>
-    <div class="topArea">
-
-        <div v-if="!activeIndex">
-            <div class="work-nav">
-                <div class="nav" v-for="(item, index) in statusD" :key="index">
-                    <div class="pic" v-if="item.imgSrc"></div>
-                    <div class="content" v-if="item.label && item.value">
-                        <span>{{ item.label }}</span>
-                        <span>{{ item.value }}</span>
-                    </div>
-                    <div :style="{ color: item.text == '正常' ? 'var(--vent-table-action-link)' : '#ff2313' }"
-                        style="width: 100%; padding: 0px 10px; text-align: center; font-weight: bold" v-if="item.text">
-                        {{ item.text }}
-                    </div>
-                    <div class="percent" v-if="item.list.length != 0">
-                        <div class="title">{{ item.label }}</div>
-                        <div class="value">
-                            <div class="content-box" v-for="(items, ind) in item.list" :key="ind">
-                                <span style="color: #b3b8cc">{{ `${items.label} :` }}</span>
-                                <span style="color: var(--vent-table-action-link); margin-left: 10px">{{ items.value
-                                    }}</span>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="bot-area">
-                <div class="title-t">
-                    <div class="text-t">通风信息状态监测</div>
-                </div>
-                <div class="echart-boxd">
-                    <echartLine :echartDataGq="echartD" :maxY="maxY" :minY="minY" :echartDw="echartDw" />
-                </div>
+  <div class="topArea">
+    <div v-if="!activeIndex">
+      <div class="work-nav">
+        <div class="nav" v-for="(item, index) in statusD" :key="index">
+          <div class="pic" v-if="item.imgSrc"></div>
+          <div class="content" v-if="item.label && item.value">
+            <span>{{ item.label }}</span>
+            <span>{{ item.value }}</span>
+          </div>
+          <div
+            :style="{ color: item.text == '正常' ? 'var(--vent-table-action-link)' : '#ff2313' }"
+            style="width: 100%; padding: 0px 10px; text-align: center; font-weight: bold"
+            v-if="item.text"
+          >
+            {{ item.text }}
+          </div>
+          <div class="percent" v-if="item.list.length != 0">
+            <div class="title">{{ item.label }}</div>
+            <div class="value">
+              <div class="content-box" v-for="(items, ind) in item.list" :key="ind">
+                <span style="color: #b3b8cc">{{ `${items.label} :` }}</span>
+                <span style="color: var(--vent-table-action-link); margin-left: 10px">{{ items.value }}</span>
+              </div>
             </div>
             </div>
+          </div>
         </div>
         </div>
-        <div v-else>
-            <MonitorTable ref="tunMonitorRef" :columns="ventTunColumns" :dataSource="ventTunDataSource"
-                :isShowSelect="false" :scroll="{ y: 300 }" title="巷道阻力分析" />
+      </div>
+      <div class="bot-area">
+        <div class="title-t">
+          <div class="text-t">通风信息状态监测</div>
         </div>
         </div>
-
+        <div class="echart-boxd">
+          <echartLine :echartDataGq="echartD" :maxY="maxY" :minY="minY" :echartDw="echartDw" />
+        </div>
+      </div>
     </div>
     </div>
+    <div v-else>
+      <MonitorTable
+        ref="tunMonitorRef"
+        :columns="ventTunColumns"
+        :dataSource="ventTunDataSource"
+        :isShowSelect="false"
+        :scroll="{ y: 300 }"
+        title="巷道阻力分析"
+      />
+    </div>
+  </div>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref, reactive, watch } from 'vue'
-import { ventTunColumns } from '../alarm.data';
-import MonitorTable from '../../comment/MonitorTable.vue';
-import echartLine from '../common/echartLine.vue';
-let props = defineProps({
+  import { ref, reactive, watch } from 'vue';
+  import { ventTunColumns } from '../alarm.data';
+  import MonitorTable from '../../comment/MonitorTable.vue';
+  import echartLine from '../common/echartLine.vue';
+  let props = defineProps({
     activeIndex: {
     activeIndex: {
-        type: Number,
-        default: 0
+      type: Number,
+      default: 0,
     },
     },
     statusData: {
     statusData: {
-        type: Array,
-        default: () => {
-            return []
-        }
+      type: Array,
+      default: () => {
+        return [];
+      },
     },
     },
-    echartData:{
-        type:Object,
-        default:()=>{
-            return {}
-        }
+    echartData: {
+      type: Object,
+      default: () => {
+        return {};
+      },
     },
     },
-    maxY:{
-        type:Number,
-        default:0
+    maxY: {
+      type: Number,
+      default: 0,
     },
     },
-    minY:{
-        type:Number,
-        default:0
+    minY: {
+      type: Number,
+      default: 0,
     },
     },
-    ventTunDataSource:{
-        type:Array,
-        default:()=>{
-            return []
-        }
-    }
-})
-
-let statusD = ref<any[]>([])
-let echartD=reactive({})
-let echartDw = ref('(m³/min)');
-
-
-
-watch(() => props.statusData, (newV, oldV) => {
-    statusD.value = newV
-}, { immediate: true })
+    ventTunDataSource: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+  });
 
 
-watch(()=>props.echartData,(newE,oldE)=>{
-    console.log(newE,'999')
-    echartD=Object.assign({},newE)
-},{immediate:true})
+  let statusD = ref<any[]>([]);
+  let echartD = reactive({});
+  let echartDw = ref('(m³/min)');
 
 
+  watch(
+    () => props.statusData,
+    (newV, oldV) => {
+      statusD.value = newV;
+    },
+    { immediate: true }
+  );
+
+  watch(
+    () => props.echartData,
+    (newE, oldE) => {
+      console.log(newE, '999');
+      echartD = Object.assign({}, newE);
+    },
+    { immediate: true }
+  );
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>
-.topArea {
+  .topArea {
     position: relative;
     position: relative;
     width: 100%;
     width: 100%;
     height: 100%;
     height: 100%;
 
 
     .work-nav {
     .work-nav {
-        height: 30%;
-        width: 100%;
-        background: var(--image-bj1) no-repeat center;
-        background-size: 100% 100%;
+      height: 30%;
+      width: 100%;
+      background: var(--image-bj1) no-repeat center;
+      background-size: 100% 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: 3px solid;
+      border-image: var(--border-image-1) 1 1 1;
+
+      .nav {
         display: flex;
         display: flex;
-        justify-content: space-between;
+        justify-content: center;
         align-items: center;
         align-items: center;
-        border-bottom: 3px solid;
-        border-image: var(--border-image-1) 1 1 1;
-
-        .nav {
-            display: flex;
-            justify-content: center;
-            align-items: center;
 
 
-            &:nth-child(1) {
-                flex: 1;
-                height: 100%;
-                border-right: 2px solid;
-                border-image: var(--border-image-2) 1 1 1;
-            }
+        &:nth-child(1) {
+          flex: 1;
+          height: 100%;
+          border-right: 2px solid;
+          border-image: var(--border-image-2) 1 1 1;
+        }
 
 
-            &:nth-child(2) {
-                flex: 1;
-                height: 100%;
-                border-right: 2px solid;
-                border-image: var(--border-image-2) 1 1 1;
-            }
+        &:nth-child(2) {
+          flex: 1;
+          height: 100%;
+          border-right: 2px solid;
+          border-image: var(--border-image-2) 1 1 1;
+        }
 
 
-            &:nth-child(3) {
-                flex: 1;
-                height: 100%;
-                border-right: 2px solid;
-                border-image: var(--border-image-2) 1 1 1;
-            }
+        &:nth-child(3) {
+          flex: 1;
+          height: 100%;
+          border-right: 2px solid;
+          border-image: var(--border-image-2) 1 1 1;
+        }
 
 
-            &:nth-child(4) {
-                flex: 1;
-                color: #b3b8cc;
-                font-size: 16px;
-                height: 100%;
-                border-right: 2px solid;
-                border-image: var(--border-image-2) 1 1 1;
-            }
+        &:nth-child(4) {
+          flex: 1;
+          color: #b3b8cc;
+          font-size: 16px;
+          height: 100%;
+          border-right: 2px solid;
+          border-image: var(--border-image-2) 1 1 1;
+        }
 
 
-            /**
+        /**
             &:nth-child(5) {
             &:nth-child(5) {
               flex: 1.4;
               flex: 1.4;
               height: 100%;
               height: 100%;
@@ -182,78 +193,76 @@ watch(()=>props.echartData,(newE,oldE)=>{
               }
               }
             }*/
             }*/
 
 
-            .pic {
-                width: 90px;
-                height: 90px;
-            }
+        .pic {
+          width: 90px;
+          height: 90px;
+        }
 
 
-            .content {
-                height: 82%;
-                margin-left: 15px;
-                color: var(--vent-font-color);
-                display: flex;
-                flex-direction: column;
-                justify-content: space-around;
+        .content {
+          height: 82%;
+          margin-left: 15px;
+          color: var(--vent-font-color);
+          display: flex;
+          flex-direction: column;
+          justify-content: space-around;
 
 
-                span {
-                    font-size: 14px;
+          span {
+            font-size: 14px;
 
 
-                    &:nth-child(1) {
-                        padding: 5px 0px;
-                        color: #b3b8cc;
-                    }
+            &:nth-child(1) {
+              padding: 5px 0px;
+              color: #b3b8cc;
+            }
 
 
-                    &:nth-child(2) {
-                        font-family: 'douyuFont';
-                        font-size: 16px;
-                        color: var(--vent-table-action-link);
-                    }
-                }
+            &:nth-child(2) {
+              font-family: 'douyuFont';
+              font-size: 16px;
+              color: var(--vent-table-action-link);
             }
             }
+          }
         }
         }
+      }
 
 
-        .nav:nth-child(1) .pic {
-            background: var(--image-jinfengliang) no-repeat center;
-            background-size: 100% 100%;
-        }
+      .nav:nth-child(1) .pic {
+        background: var(--image-jinfengliang) no-repeat center;
+        background-size: 100% 100%;
+      }
 
 
-        .nav:nth-child(2) .pic {
-            background: var(--image-huifengliang) no-repeat center;
-            background-size: 100% 100%;
-        }
+      .nav:nth-child(2) .pic {
+        background: var(--image-huifengliang) no-repeat center;
+        background-size: 100% 100%;
+      }
 
 
-        .nav:nth-child(3) .pic {
-            background: var(--image-xufengliang) no-repeat center;
-            background-size: 100% 100%;
-        }
+      .nav:nth-child(3) .pic {
+        background: var(--image-xufengliang) no-repeat center;
+        background-size: 100% 100%;
+      }
     }
     }
 
 
     .bot-area {
     .bot-area {
-        height: calc(100% - 30% - 3px);
-        padding: 10px;
-        background: var(--image-bj1) no-repeat;
-        background-size: 100% 100%;
-        box-sizing: border-box;
-
-        .title-t {
-            height: 30px;
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
+      height: calc(100% - 30% - 3px);
+      padding: 10px;
+      background: var(--image-bj1) no-repeat;
+      background-size: 100% 100%;
+      box-sizing: border-box;
+
+      .title-t {
+        height: 30px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
 
 
-            .text-t {
-                font-family: 'douyuFont';
-                font-size: 14px;
-                color: var(--vent-font-color);
-            }
+        .text-t {
+          font-family: 'douyuFont';
+          font-size: 14px;
+          color: var(--vent-font-color);
         }
         }
+      }
 
 
-        .echart-boxd {
-            width: 100%;
-            height: 250px;
-        }
+      .echart-boxd {
+        width: 100%;
+        height: 250px;
+      }
     }
     }
-
-
-}
-</style>
+  }
+</style>

+ 473 - 464
src/views/vent/monitorManager/alarmMonitor/warn/ventilateWarn.vue

@@ -6,21 +6,25 @@
       <img v-else src="@/assets/images/vent/report-toggle.png" alt="" />
       <img v-else src="@/assets/images/vent/report-toggle.png" alt="" />
     </div>
     </div>
     <div class="ventilate-top">
     <div class="ventilate-top">
-      <a-button v-if="!hasPermission('ventilateWarn:return')" preIcon="ant-design:rollback-outlined" type="text"
-        size="small" style="position: absolute; left: 15px; top: 15px; color: var(--vent-font-color)" @click="getBack">
+      <a-button
+        v-if="!hasPermission('ventilateWarn:return')"
+        preIcon="ant-design:rollback-outlined"
+        type="text"
+        size="small"
+        style="position: absolute; left: 15px; top: 15px; color: var(--vent-font-color)"
+        @click="getBack"
+      >
         返回
         返回
       </a-button>
       </a-button>
       <div class="alarm-menu">
       <div class="alarm-menu">
         <div class="type-btn">
         <div class="type-btn">
-          <div :class="activeIndex == index ? 'btn1' : 'btn'" v-for="(item, index) in typeMenuListTf" :key="index"
-            @click="btnClick(index)">
+          <div :class="activeIndex == index ? 'btn1' : 'btn'" v-for="(item, index) in typeMenuListTf" :key="index" @click="btnClick(index)">
             {{ item.name }}
             {{ item.name }}
           </div>
           </div>
         </div>
         </div>
         <div class="card-btn">
         <div class="card-btn">
           <div style="width: 100%; height: 100%" v-if="menuList.length">
           <div style="width: 100%; height: 100%" v-if="menuList.length">
-            <div :class="activeIndex1 == ind ? 'btn1' : 'btn'" v-for="(item, ind) in menuList" :key="ind"
-              @click="cardClick(ind, item)">
+            <div :class="activeIndex1 == ind ? 'btn1' : 'btn'" v-for="(item, ind) in menuList" :key="ind" @click="cardClick(ind, item)">
               <div class="text">{{ item.name }}</div>
               <div class="text">{{ item.name }}</div>
               <div class="warn">{{ item.warn }}</div>
               <div class="warn">{{ item.warn }}</div>
             </div>
             </div>
@@ -33,8 +37,14 @@
       </div>
       </div>
       <div class="ventilate-content">
       <div class="ventilate-content">
         <a-spin :spinning="loading">
         <a-spin :spinning="loading">
-          <TopArea :activeIndex="activeIndex" :statusData="ventilateTopList" :echartData="echartDataFc1"
-            :ventTunDataSource="ventTunDataSource" :maxY="maxY" :minY="minY"></TopArea>
+          <TopArea
+            :activeIndex="activeIndex"
+            :statusData="ventilateTopList"
+            :echartData="echartDataFc1"
+            :ventTunDataSource="ventTunDataSource"
+            :maxY="maxY"
+            :minY="minY"
+          />
         </a-spin>
         </a-spin>
       </div>
       </div>
     </div>
     </div>
@@ -47,522 +57,521 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
-import { useSystemSelect } from '/@/hooks/vent/useSystemSelect';
-import { usePermission } from '/@/hooks/web/usePermission';
-import { useGlobSetting } from '/@/hooks/setting';
-import { useRouter } from 'vue-router';
-import { sysTypeWarnList, sysWarn, getDevice } from '../common.api';
-import { ventilateTopList, typeMenuListTf, getMaxY, getMinY } from '../common.data';
-import CustomHeader from '/@/components/vent/customHeader.vue';
-import MeasurePoint from '../common/measurePoint.vue';
-import { realTimeNetCal, modalParam } from '../alarm.api';
-import TopArea from '../common/top-area.vue';
-
-//巷道阻力分析数据
-let hdData = reactive({
-  maxLevel: '',
-  address: '',
-});
-//通风选项激活索引
-let activeIndex = ref(0);
-let monitor = ref(true);
-let toggleData = reactive<any>({});
-const { hasPermission } = usePermission();
-const { options, optionValue, getSelectRow, getSysDataSource } = useSystemSelect('sys_surface_caimei'); // 参数为场景类型(设备类型管理中可以查询到)
-let router = useRouter();
-//左侧数据列表
-let menuList = reactive<any[]>([]);
-const ventTunDataSource = ref([]);
-//当前左侧激活菜单的索引
-let activeIndex1 = ref(0);
-let maxY = ref<any>(0);
-let minY = ref<any>(0)
-const loading = ref(false);
-
-//通风图表数据
-const echartDataFc1 = reactive<any>({
-  maxData: {
-    lengedData: '进风量',
-    data: [],
-  },
-  minData: {
-    lengedData: '回风量',
-    data: [],
-  },
-  aveValue: {
-    lengedData: '需风量',
-    data: [],
-  },
-  xData: [],
-});
-let cardListTf = ref<any[]>([]);
-const chartListTf = ref<any[]>([]);
-let showToggle = ref('report');
-
-// https获取监测数据
-let timer: null | NodeJS.Timeout = null;
-function getMonitor(flag?) {
-  timer = setTimeout(
-    async () => {
-      //获取左侧菜单数据
-      await getMenuList();
-      await getWindDeviceList();
-      getMonitor(false);
+  import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
+  import { useSystemSelect } from '/@/hooks/vent/useSystemSelect';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useRouter } from 'vue-router';
+  import { sysTypeWarnList, sysWarn, getDevice } from '../common.api';
+  import { ventilateTopList, typeMenuListTf, getMaxY, getMinY } from '../common.data';
+  import CustomHeader from '/@/components/vent/customHeader.vue';
+  import MeasurePoint from '../common/measurePoint.vue';
+  import { realTimeNetCal, modalParam } from '../alarm.api';
+  import TopArea from '../common/top-area.vue';
+
+  //巷道阻力分析数据
+  let hdData = reactive({
+    maxLevel: '',
+    address: '',
+  });
+  //通风选项激活索引
+  let activeIndex = ref(0);
+  let monitor = ref(true);
+  let toggleData = reactive<any>({});
+  const { hasPermission } = usePermission();
+  const { options, optionValue, getSelectRow, getSysDataSource } = useSystemSelect('sys_surface_caimei'); // 参数为场景类型(设备类型管理中可以查询到)
+  let router = useRouter();
+  //左侧数据列表
+  let menuList = reactive<any[]>([]);
+  const ventTunDataSource = ref([]);
+  //当前左侧激活菜单的索引
+  let activeIndex1 = ref(0);
+  let maxY = ref<any>(0);
+  let minY = ref<any>(0);
+  const loading = ref(false);
+
+  //通风图表数据
+  const echartDataFc1 = reactive<any>({
+    maxData: {
+      lengedData: '进风量',
+      data: [],
     },
     },
-    flag ? 0 : 1500
-  );
-}
-//获取巷道阻力分析数据
-let timer1: null | NodeJS.Timeout = null;
-function getMonitor1(flag?) {
-  timer1 = setTimeout(
-    async () => {
-      await getRealTimeNetData();
-      getMonitor1(false);
+    minData: {
+      lengedData: '回风量',
+      data: [],
     },
     },
-    flag ? 0 : 30000
-  );
-}
-
-//返回首页
-function getBack() {
-  router.push('/monitorChannel/monitor-alarm-home');
-}
-//点击切换实时\报表数据
-let handlerToggle = () => {
-  monitor.value = !monitor.value;
-  echartDataFc1.maxData.data.length = 0;
-  echartDataFc1.minData.data.length = 0;
-  echartDataFc1.aveValue.data.length = 0;
-  echartDataFc1.xData.length = 0;
-  getData()
-};
-//实时报表数据
-function getData(){
-  ventilateTopList[0].value = monitor.value ? toggleData.faceIntM3 : toggleData.jin;
-  ventilateTopList[1].value = monitor.value ? toggleData.faceRetM3 : toggleData.hui;
-  if (monitor.value) {
-    toggleData.history_report.forEach((v) => {
-      echartDataFc1.maxData.data.push(parseFloat(v.faceIntM3));
-      echartDataFc1.minData.data.push(parseFloat(v.faceRetM3));
-      echartDataFc1.aveValue.data.push(0);
-      echartDataFc1.xData.push(v.time);
-    });
-  } else {
-    toggleData.history.forEach((v) => {
-      echartDataFc1.maxData.data.push(parseFloat(v.jin));
-      echartDataFc1.minData.data.push(parseFloat(v.hui));
-      if (ventilateTopList[2].value && ventilateTopList[2].value != '--') {
-        echartDataFc1.aveValue.data.push(ventilateTopList[2].value);
-      } else {
-        echartDataFc1.aveValue.data.push(0);
-      }
-      echartDataFc1.xData.push(v.time);
-    });
+    aveValue: {
+      lengedData: '需风量',
+      data: [],
+    },
+    xData: [],
+  });
+  let cardListTf = ref<any[]>([]);
+  const chartListTf = ref<any[]>([]);
+  let showToggle = ref('report');
+
+  // https获取监测数据
+  let timer: null | NodeJS.Timeout = null;
+  function getMonitor(flag?) {
+    timer = setTimeout(
+      async () => {
+        //获取左侧菜单数据
+        await getMenuList();
+        await getWindDeviceList();
+        getMonitor(false);
+      },
+      flag ? 0 : 1500
+    );
   }
   }
-}
-//获取左侧数据列表
-async function getMenuList() {
-  let res = await sysTypeWarnList({ type: 'vent' });
-  if (res.length != 0) {
-    menuList.length = 0;
-    res.forEach((el) => {
-      menuList.push({
-        name: el.deviceName,
-        warn: el.warnDes.indexOf('预警') != -1 ? '风量不足' : el.warnDes,
-        deviceID: el.deviceID,
-        strtype: el.deviceType,
-        detail: el.detail
-      });
-    });
-    //获取右侧详情数据
-    getDetailList(menuList[activeIndex1.value].detail)
+  //获取巷道阻力分析数据
+  let timer1: null | NodeJS.Timeout = null;
+  function getMonitor1(flag?) {
+    timer1 = setTimeout(
+      async () => {
+        await getRealTimeNetData();
+        getMonitor1(false);
+      },
+      flag ? 0 : 30000
+    );
   }
   }
-}
-
-//获取右侧详情数据
-function getDetailList(param) {
-  echartDataFc1.maxData.data.length = 0;
-  echartDataFc1.minData.data.length = 0;
-  echartDataFc1.aveValue.data.length = 0;
-  echartDataFc1.xData.length = 0;
-  if (JSON.stringify(param) != '{}') {
-    toggleData = Object.assign({}, param);
-    ventilateTopList[2].value = param.xufengliang || '--';
-    ventilateTopList[3].text = param.warnFlag ? param.warnDes : '正常';
-    if (showToggle.value == 'monitor') {
-      ventilateTopList[0].value = param.jin;
-      ventilateTopList[1].value = param.hui;
-      if (param.history.length != 0) {
-        param.history.forEach((v) => {
-          echartDataFc1.maxData.data.push(parseFloat(v.jin));
-          echartDataFc1.minData.data.push(parseFloat(v.hui));
-          if (ventilateTopList[2].value && ventilateTopList[2].value != '--') {
-            echartDataFc1.aveValue.data.push(ventilateTopList[2].value);
-          } else {
-            echartDataFc1.aveValue.data.push(0);
-          }
-          echartDataFc1.xData.push(v.time);
-        });
-      }
-    } else if (showToggle.value == 'report') {
-      ventilateTopList[0].value = param.faceIntM3;
-      ventilateTopList[1].value = param.faceRetM3;
-      param.history_report.forEach((v) => {
+
+  //返回首页
+  function getBack() {
+    router.push('/monitorChannel/monitor-alarm-home');
+  }
+  //点击切换实时\报表数据
+  let handlerToggle = () => {
+    monitor.value = !monitor.value;
+    echartDataFc1.maxData.data.length = 0;
+    echartDataFc1.minData.data.length = 0;
+    echartDataFc1.aveValue.data.length = 0;
+    echartDataFc1.xData.length = 0;
+    getData();
+  };
+  //实时报表数据
+  function getData() {
+    ventilateTopList[0].value = monitor.value ? toggleData.faceIntM3 : toggleData.jin;
+    ventilateTopList[1].value = monitor.value ? toggleData.faceRetM3 : toggleData.hui;
+    if (monitor.value) {
+      toggleData.history_report.forEach((v) => {
         echartDataFc1.maxData.data.push(parseFloat(v.faceIntM3));
         echartDataFc1.maxData.data.push(parseFloat(v.faceIntM3));
         echartDataFc1.minData.data.push(parseFloat(v.faceRetM3));
         echartDataFc1.minData.data.push(parseFloat(v.faceRetM3));
         echartDataFc1.aveValue.data.push(0);
         echartDataFc1.aveValue.data.push(0);
         echartDataFc1.xData.push(v.time);
         echartDataFc1.xData.push(v.time);
       });
       });
     } else {
     } else {
-      getData()
+      toggleData.history.forEach((v) => {
+        echartDataFc1.maxData.data.push(parseFloat(v.jin));
+        echartDataFc1.minData.data.push(parseFloat(v.hui));
+        if (ventilateTopList[2].value && ventilateTopList[2].value != '--') {
+          echartDataFc1.aveValue.data.push(ventilateTopList[2].value);
+        } else {
+          echartDataFc1.aveValue.data.push(0);
+        }
+        echartDataFc1.xData.push(v.time);
+      });
     }
     }
-
-    let echartD = [echartDataFc1.maxData.data, echartDataFc1.minData.data]
-    maxY.value = getMaxY(echartD)
-    minY.value = getMinY(echartD)
   }
   }
-}
-
-
-//通风选项切换
-function btnClick(ind) {
-  activeIndex.value = ind;
-  switch (ind) {
-    case 0:
-      if (timer1) clearTimeout(timer1);
-      activeIndex1.value = 0;
-      getMenuList();
-      break;
-    case 1:
-      if (timer) clearTimeout(timer);
-      activeIndex1.value = 0;
+  //获取左侧数据列表
+  async function getMenuList() {
+    let res = await sysTypeWarnList({ type: 'vent' });
+    if (res.length != 0) {
       menuList.length = 0;
       menuList.length = 0;
-      getRealTimeNetData();
-      getMonitor1();
-      break;
-  }
-}
-
-//菜单选项切换
-function cardClick(ind, item) {
-  clearTimeout(timer);
-  if (!activeIndex.value && !loading.value) {
-    activeIndex1.value = ind;
-    loading.value = true;
-    setTimeout(() => {
-      getMonitor();
-      loading.value = false;
-    }, 2000);
-  } 
-}
-
-//获取通风监控测点信息
-async function getWindDeviceList() {
-  const cardTfList: any[] = [];
-  const chartTfList: any[] = [];
-  let res = await getDevice({ devicetype: 'windrect', pagetype: 'normal' });
-  if (res && res.msgTxt[0]) {
-    let list = res.msgTxt[0].datalist || [];
-    if (list.length > 0) {
-      list.forEach((el: any) => {
-        const readData = el.readData;
-        el = Object.assign(el, readData);
-        cardTfList.push({
-          label: '通信状态',
-          value: el.netStatus == '0' ? '断开' : '连接',
-          listR: [
-            { id: 0, label: '安装位置', dw: '', value: el.strinstallpos },
-            { id: 1, label: '风量', dw: 'm³/min', value: el.m3 || '--' },
-            { id: 2, label: '风速', dw: 'm/s', value: el.va || '--' },
-            { id: 4, label: '时间', dw: '', value: el.readTime },
-            {
-              id: 3,
-              label: '是否报警',
-              dw: '',
-              value: el.warnFlag == '0' ? '正常' : el.warnFlag == 1 ? '报警' : el.warnFlag == 2 ? '断开' : '未监测',
-            },
-          ],
-        });
-        // 初始化预测曲线配置,分别为x轴时间、平均、最大、最小、实时
-        const avgParam = el.avgParam || {
-          avg_vent_value: 0,
-          max_vent_value: 0,
-          min_vent_value: 0,
-        };
-        chartTfList.push({
-          label: el.strinstallpos,
-          time: new Date(),
-          data: [avgParam.avg_m3_value, avgParam.max_m3_value, avgParam.min_m3_value, el.readData.m3],
+      res.forEach((el) => {
+        menuList.push({
+          name: el.deviceName,
+          warn: el.warnDes.indexOf('预警') != -1 ? '风量不足' : el.warnDes,
+          deviceID: el.deviceID,
+          strtype: el.deviceType,
+          detail: el.detail,
         });
         });
       });
       });
+      //获取右侧详情数据
+      getDetailList(menuList[activeIndex1.value].detail);
     }
     }
-    cardListTf.value = cardTfList;
-    chartListTf.value = chartTfList;
   }
   }
-}
-async function getRealTimeNetData() {
-  const modalData = await modalParam({});
-  if (modalData && modalData.param && modalData.param.records.length && modalData.param.records.length > 0) {
-    const res = await realTimeNetCal({ modelID: modalData.param.records[0]['defaultmodelid'] });
-    if (res && res['result']) ventTunDataSource.value = res['result']['tuns'];
-    let data = [];
-    ventTunDataSource.value.forEach((el) => {
-      if (el['dHTotal'] && el['oldHTotal']) {
-        el['leveld'] = ((el['dHTotal'] - el['oldHTotal']) / el['oldHTotal']) * 100;
-        data.push(el['leveld']);
+
+  //获取右侧详情数据
+  function getDetailList(param) {
+    echartDataFc1.maxData.data.length = 0;
+    echartDataFc1.minData.data.length = 0;
+    echartDataFc1.aveValue.data.length = 0;
+    echartDataFc1.xData.length = 0;
+    if (JSON.stringify(param) != '{}') {
+      toggleData = Object.assign({}, param);
+      ventilateTopList[2].value = param.xufengliang || '--';
+      ventilateTopList[3].text = param.warnFlag ? param.warnDes : '正常';
+      if (showToggle.value == 'monitor') {
+        ventilateTopList[0].value = param.jin;
+        ventilateTopList[1].value = param.hui;
+        if (param.history.length != 0) {
+          param.history.forEach((v) => {
+            echartDataFc1.maxData.data.push(parseFloat(v.jin));
+            echartDataFc1.minData.data.push(parseFloat(v.hui));
+            if (ventilateTopList[2].value && ventilateTopList[2].value != '--') {
+              echartDataFc1.aveValue.data.push(ventilateTopList[2].value);
+            } else {
+              echartDataFc1.aveValue.data.push(0);
+            }
+            echartDataFc1.xData.push(v.time);
+          });
+        }
+      } else if (showToggle.value == 'report') {
+        ventilateTopList[0].value = param.faceIntM3;
+        ventilateTopList[1].value = param.faceRetM3;
+        param.history_report.forEach((v) => {
+          echartDataFc1.maxData.data.push(parseFloat(v.faceIntM3));
+          echartDataFc1.minData.data.push(parseFloat(v.faceRetM3));
+          echartDataFc1.aveValue.data.push(0);
+          echartDataFc1.xData.push(v.time);
+        });
       } else {
       } else {
-        data = [];
+        getData();
       }
       }
-    });
-    hdData.maxLevel = '正常';
+
+      let echartD = [echartDataFc1.maxData.data, echartDataFc1.minData.data];
+      maxY.value = getMaxY(echartD);
+      minY.value = getMinY(echartD);
+    }
+  }
+
+  //通风选项切换
+  function btnClick(ind) {
+    activeIndex.value = ind;
+    switch (ind) {
+      case 0:
+        if (timer1) clearTimeout(timer1);
+        activeIndex1.value = 0;
+        getMenuList();
+        break;
+      case 1:
+        if (timer) clearTimeout(timer);
+        activeIndex1.value = 0;
+        menuList.length = 0;
+        getRealTimeNetData();
+        getMonitor1();
+        break;
+    }
   }
   }
-}
-
-onMounted(async () => {
-  const { sysOrgCode, sysDataType } = useGlobSetting();
-  showToggle.value = sysDataType || 'report';
-  await getMenuList();
-  await getMonitor();
-});
-onUnmounted(() => {
-  if (timer) {
+
+  //菜单选项切换
+  function cardClick(ind, item) {
     clearTimeout(timer);
     clearTimeout(timer);
-    timer = undefined;
+    if (!activeIndex.value && !loading.value) {
+      activeIndex1.value = ind;
+      loading.value = true;
+      setTimeout(() => {
+        getMonitor();
+        loading.value = false;
+      }, 2000);
+    }
   }
   }
-  if (timer1) {
-    clearTimeout(timer1);
-    timer1 = undefined;
+
+  //获取通风监控测点信息
+  async function getWindDeviceList() {
+    const cardTfList: any[] = [];
+    const chartTfList: any[] = [];
+    let res = await getDevice({ devicetype: 'windrect', pagetype: 'normal' });
+    if (res && res.msgTxt[0]) {
+      let list = res.msgTxt[0].datalist || [];
+      if (list.length > 0) {
+        list.forEach((el: any) => {
+          const readData = el.readData;
+          el = Object.assign(el, readData);
+          cardTfList.push({
+            label: '通信状态',
+            value: el.netStatus == '0' ? '断开' : '连接',
+            listR: [
+              { id: 0, label: '安装位置', dw: '', value: el.strinstallpos },
+              { id: 1, label: '风量', dw: 'm³/min', value: el.m3 || '--' },
+              { id: 2, label: '风速', dw: 'm/s', value: el.va || '--' },
+              { id: 4, label: '时间', dw: '', value: el.readTime },
+              {
+                id: 3,
+                label: '是否报警',
+                dw: '',
+                value: el.warnFlag == '0' ? '正常' : el.warnFlag == 1 ? '报警' : el.warnFlag == 2 ? '断开' : '未监测',
+              },
+            ],
+          });
+          // 初始化预测曲线配置,分别为x轴时间、平均、最大、最小、实时
+          const avgParam = el.avgParam || {
+            avg_vent_value: 0,
+            max_vent_value: 0,
+            min_vent_value: 0,
+          };
+          chartTfList.push({
+            label: el.strinstallpos,
+            time: new Date(),
+            data: [avgParam.avg_m3_value, avgParam.max_m3_value, avgParam.min_m3_value, el.readData.m3],
+          });
+        });
+      }
+      cardListTf.value = cardTfList;
+      chartListTf.value = chartTfList;
+    }
   }
   }
-});
+  async function getRealTimeNetData() {
+    const modalData = await modalParam({});
+    if (modalData && modalData.param && modalData.param.records.length && modalData.param.records.length > 0) {
+      const res = await realTimeNetCal({ modelID: modalData.param.records[0]['defaultmodelid'] });
+      if (res && res['result']) ventTunDataSource.value = res['result']['tuns'];
+      let data = [];
+      ventTunDataSource.value.forEach((el) => {
+        if (el['dHTotal'] && el['oldHTotal']) {
+          el['leveld'] = ((el['dHTotal'] - el['oldHTotal']) / el['oldHTotal']) * 100;
+          data.push(el['leveld']);
+        } else {
+          data = [];
+        }
+      });
+      hdData.maxLevel = '正常';
+    }
+  }
+
+  onMounted(async () => {
+    const { sysOrgCode, sysDataType } = useGlobSetting();
+    showToggle.value = sysDataType || 'report';
+    await getMenuList();
+    await getMonitor();
+  });
+  onUnmounted(() => {
+    if (timer) {
+      clearTimeout(timer);
+      timer = undefined;
+    }
+    if (timer1) {
+      clearTimeout(timer1);
+      timer1 = undefined;
+    }
+  });
 </script>
 </script>
 
 
 <style lang="less" scoped>
 <style lang="less" scoped>
-@import '/@/design/theme.less';
+  @import '/@/design/theme.less';
+
+  @{theme-deepblue} {
+    .ventilateWarn {
+      --image-border: url('/@/assets/images/themify/deepblue/fire/border.png');
+      --image-no-choice: url('/@/assets/images/themify/deepblue/fire/no-choice.png');
+      --image-choice: url('/@/assets/images/themify/deepblue/fire/choice.png');
+      --image-bj1: url('/@/assets/images/themify/deepblue/fire/bj1.png');
+      --image-jinfengliang: url('/@/assets/images/themify/deepblue/fire/jinfengliang.png');
+      --image-huifengliang: url('/@/assets/images/themify/deepblue/fire/huifengliang.png');
+      --image-xufengliang: url('/@/assets/images/themify/deepblue/fire/xufengliang.png');
+    }
+  }
 
 
-@{theme-deepblue} {
   .ventilateWarn {
   .ventilateWarn {
-    --image-border: url('/@/assets/images/themify/deepblue/fire/border.png');
-    --image-no-choice: url('/@/assets/images/themify/deepblue/fire/no-choice.png');
-    --image-choice: url('/@/assets/images/themify/deepblue/fire/choice.png');
-    --image-bj1: url('/@/assets/images/themify/deepblue/fire/bj1.png');
-    --image-jinfengliang: url('/@/assets/images/themify/deepblue/fire/jinfengliang.png');
-    --image-huifengliang: url('/@/assets/images/themify/deepblue/fire/huifengliang.png');
-    --image-xufengliang: url('/@/assets/images/themify/deepblue/fire/xufengliang.png');
-  }
-}
-
-.ventilateWarn {
-  --image-border: url('/@/assets/images/fire/border.png');
-  --image-no-choice: url('/@/assets/images/fire/no-choice.png');
-  --image-choice: url('/@/assets/images/fire/choice.png');
-  --image-bj1: url('/@/assets/images/fire/bj1.png');
-  --image-jinfengliang: url('/@/assets/images/fire/jinfengliang.png');
-  --image-huifengliang: url('/@/assets/images/fire/huifengliang.png');
-  --image-xufengliang: url('/@/assets/images/fire/xufengliang.png');
-  --border-image-1: linear-gradient(to bottom, #2d74a0, #2d74a0, #2d74a0);
-  --border-image-2: linear-gradient(to bottom, transparent, #024688, transparent);
-  position: reactive;
-  width: 100%;
-  height: 100%;
-  padding: 80px 10px 15px 10px;
-  box-sizing: border-box;
-
-  .ventilate-top {
+    --image-border: url('/@/assets/images/fire/border.png');
+    --image-no-choice: url('/@/assets/images/fire/no-choice.png');
+    --image-choice: url('/@/assets/images/fire/choice.png');
+    --image-bj1: url('/@/assets/images/fire/bj1.png');
+    --image-jinfengliang: url('/@/assets/images/fire/jinfengliang.png');
+    --image-huifengliang: url('/@/assets/images/fire/huifengliang.png');
+    --image-xufengliang: url('/@/assets/images/fire/xufengliang.png');
+    --border-image-1: linear-gradient(to bottom, #2d74a0, #2d74a0, #2d74a0);
+    --border-image-2: linear-gradient(to bottom, transparent, #024688, transparent);
+    position: reactive;
     width: 100%;
     width: 100%;
-    display: flex;
-    justify-content: space-between;
-    height: 50%;
-    margin-bottom: 15px;
-    background: var(--image-border) no-repeat center;
-    background-size: 100% 100%;
-    padding-right: 15px;
-
-    .alarm-menu {
-      height: 100%;
-      width: 332px;
-      padding: 10px;
-      box-sizing: border-box;
-
-      .type-btn {
-        width: 100%;
-        height: 28px;
-        line-height: 28px;
-        background-color: var(--vent-warn-tab-bg);
-        border: 2px solid var(--vent-warn-tab-border);
-        margin-bottom: 20px;
-        border-radius: 5px;
-        box-sizing: border-box;
-        display: flex;
-        justify-content: space-between;
-
-        .btn {
-          width: 50%;
-          height: 24px;
-          line-height: 24px;
-          font-size: 14px;
-          text-align: center;
-          color: var(--vent-font-color);
-          cursor: pointer;
-        }
-
-        .btn1 {
-          width: 50%;
-          height: 24px;
-          line-height: 24px;
-          font-size: 14px;
-          color: var(--vent-font-color);
-          text-align: center;
-          border-radius: 2px;
-          background: var(--vent-warn-tab-bg-actived);
-          cursor: pointer;
-        }
-      }
+    height: 100%;
+    padding: 80px 10px 15px 10px;
+    box-sizing: border-box;
 
 
-      .card-btn {
-        width: 100%;
-        height: calc(100% - 50px);
-        overflow-y: auto;
-
-        .btn {
-          position: relative;
-          width: 81%;
-          height: 24%;
-          margin-bottom: 6%;
-          font-family: 'douyuFont';
-          background: var(--image-no-choice) no-repeat;
-          background-size: 100% 100%;
-          cursor: pointer;
-
-          .text {
-            width: 80%;
-            position: absolute;
-            left: 50%;
-            top: 28px;
-            font-size: 14px;
-            color: var(--vent-table-action-link);
-            text-align: center;
-            transform: translate(-50%, 0);
-          }
+    .ventilate-top {
+      width: 100%;
+      display: flex;
+      justify-content: space-between;
+      height: 50%;
+      margin-bottom: 15px;
+      background: var(--image-border) no-repeat center;
+      background-size: 100% 100%;
+      padding-right: 15px;
 
 
-          .warn {
-            width: 100%;
-            position: absolute;
-            left: 50%;
-            bottom: 8px;
-            font-size: 12px;
-            color: var(--vent-font-color);
-            text-align: center;
-            transform: translate(-50%, 0);
-          }
-        }
+      .alarm-menu {
+        height: 100%;
+        width: 332px;
+        padding: 10px;
+        box-sizing: border-box;
 
 
-        .btn1 {
-          position: relative;
+        .type-btn {
           width: 100%;
           width: 100%;
-          height: 24%;
-          margin-bottom: 6%;
-          font-family: 'douyuFont';
-          background: var(--image-choice) no-repeat;
-          background-size: 100% 100%;
-          cursor: pointer;
-
-          .text {
-            width: 80%;
-            position: absolute;
-            left: 50%;
-            top: 28px;
+          height: 28px;
+          line-height: 28px;
+          background-color: var(--vent-warn-tab-bg);
+          border: 2px solid var(--vent-warn-tab-border);
+          margin-bottom: 20px;
+          border-radius: 5px;
+          box-sizing: border-box;
+          display: flex;
+          justify-content: space-between;
+
+          .btn {
+            width: 50%;
+            height: 24px;
+            line-height: 24px;
             font-size: 14px;
             font-size: 14px;
-            color: var(--vent-table-action-link);
             text-align: center;
             text-align: center;
-            transform: translate(-62%, 0);
+            color: var(--vent-font-color);
+            cursor: pointer;
           }
           }
 
 
-          .warn {
-            width: 100%;
-            position: absolute;
-            left: 50%;
-            bottom: 8px;
+          .btn1 {
+            width: 50%;
+            height: 24px;
+            line-height: 24px;
             font-size: 14px;
             font-size: 14px;
             color: var(--vent-font-color);
             color: var(--vent-font-color);
             text-align: center;
             text-align: center;
-            transform: translate(-60%, 0);
+            border-radius: 2px;
+            background: var(--vent-warn-tab-bg-actived);
+            cursor: pointer;
           }
           }
         }
         }
 
 
-        .hd-content {
-          position: relative;
+        .card-btn {
           width: 100%;
           width: 100%;
-          height: 300px;
-          padding: 0 20px;
-          background: var(--image-no-choice) no-repeat;
-          background-size: 100% 100%;
+          height: calc(100% - 50px);
+          overflow-y: auto;
+
+          .btn {
+            position: relative;
+            width: 81%;
+            height: 24%;
+            margin-bottom: 6%;
+            font-family: 'douyuFont';
+            background: var(--image-no-choice) no-repeat;
+            background-size: 100% 100%;
+            cursor: pointer;
+
+            .text {
+              width: 80%;
+              position: absolute;
+              left: 50%;
+              top: 28px;
+              font-size: 14px;
+              color: var(--vent-table-action-link);
+              text-align: center;
+              transform: translate(-50%, 0);
+            }
+
+            .warn {
+              width: 100%;
+              position: absolute;
+              left: 50%;
+              bottom: 8px;
+              font-size: 12px;
+              color: var(--vent-font-color);
+              text-align: center;
+              transform: translate(-50%, 0);
+            }
+          }
 
 
-          .hd-content-text {
-            display: flex;
+          .btn1 {
+            position: relative;
             width: 100%;
             width: 100%;
-            height: 200px;
-            align-items: center;
-            justify-content: center;
-            font-size: 18px;
-            color: var(--vent-font-yellow-color);
+            height: 24%;
+            margin-bottom: 6%;
+            font-family: 'douyuFont';
+            background: var(--image-choice) no-repeat;
+            background-size: 100% 100%;
+            cursor: pointer;
+
+            .text {
+              width: 80%;
+              position: absolute;
+              left: 50%;
+              top: 28px;
+              font-size: 14px;
+              color: var(--vent-table-action-link);
+              text-align: center;
+              transform: translate(-62%, 0);
+            }
+
+            .warn {
+              width: 100%;
+              position: absolute;
+              left: 50%;
+              bottom: 8px;
+              font-size: 14px;
+              color: var(--vent-font-color);
+              text-align: center;
+              transform: translate(-60%, 0);
+            }
           }
           }
 
 
-          .hd-content-val {
-            position: absolute;
-            width: calc(100% - 40px);
-            display: flex;
-            justify-content: center;
-            font-family: 'douyuFont';
-            bottom: 80px;
-            font-size: 20px;
-            color: var(--vent-table-action-link);
+          .hd-content {
+            position: relative;
+            width: 100%;
+            height: 300px;
+            padding: 0 20px;
+            background: var(--image-no-choice) no-repeat;
+            background-size: 100% 100%;
+
+            .hd-content-text {
+              display: flex;
+              width: 100%;
+              height: 200px;
+              align-items: center;
+              justify-content: center;
+              font-size: 18px;
+              color: var(--vent-font-yellow-color);
+            }
+
+            .hd-content-val {
+              position: absolute;
+              width: calc(100% - 40px);
+              display: flex;
+              justify-content: center;
+              font-family: 'douyuFont';
+              bottom: 80px;
+              font-size: 20px;
+              color: var(--vent-table-action-link);
+            }
           }
           }
         }
         }
       }
       }
-    }
 
 
-    .ventilate-content {
-      height: 100%;
-      width: calc(100% - 332px);
-      padding: 10px 0px;
-      box-sizing: border-box;
-      margin-right: 10px;
+      .ventilate-content {
+        height: 100%;
+        width: calc(100% - 332px);
+        padding: 10px 0px;
+        box-sizing: border-box;
+        margin-right: 10px;
+      }
     }
     }
-  }
 
 
-  .tun-box {
-    width: 600px;
-    height: 300px;
-  }
-
-  .ventilate-bottom {
-    height: calc(50% - 15px);
-    background: var(--image-border) no-repeat center;
-    background-size: 100% 100%;
-    padding: 10px;
-    box-sizing: border-box;
+    .tun-box {
+      width: 600px;
+      height: 300px;
+    }
 
 
-    .bot-area {
-      height: 100%;
-      padding: 10px;
-      background: var(--image-bj1) no-repeat center;
+    .ventilate-bottom {
+      height: calc(50% - 15px);
+      background: var(--image-border) no-repeat center;
       background-size: 100% 100%;
       background-size: 100% 100%;
+      padding: 10px;
       box-sizing: border-box;
       box-sizing: border-box;
+
+      .bot-area {
+        height: 100%;
+        padding: 10px;
+        background: var(--image-bj1) no-repeat center;
+        background-size: 100% 100%;
+        box-sizing: border-box;
+      }
     }
     }
-  }
 
 
-  .icon-toggle {
-    position: absolute;
-    right: 345px;
-    top: 17px;
+    .icon-toggle {
+      position: absolute;
+      right: 345px;
+      top: 17px;
 
 
-    img {
-      width: 26px;
-      height: 26px;
-      cursor: pointer;
+      img {
+        width: 26px;
+        height: 26px;
+        cursor: pointer;
+      }
     }
     }
   }
   }
-}
 </style>
 </style>

+ 230 - 230
src/views/vent/monitorManager/balancePressMonitor/components/balancePressHome.vue

@@ -139,260 +139,260 @@
   </a-spin>
   </a-spin>
 </template>
 </template>
 <script setup lang="ts" name="balancePressHome">
 <script setup lang="ts" name="balancePressHome">
-import { onBeforeMount, ref, onMounted, onUnmounted, reactive, defineProps, watch, computed } from 'vue';
-import ventBox1 from '/@/components/vent/ventBox1.vue';
-import CustomChart from '@/views/vent/home/configurable/components/detail/CustomChart.vue';
-import { PressO2Option, mockData1 } from '../balancePressO2.data';
-import { SvgIcon } from '/@/components/Icon';
-import { mountedThree, destroy, setModelType, updateText, play } from '../balancePress.threejs';
-import { settingParam1, settingParam2, settingParam3, windowParam, localFanParam, windrectParam } from '../balancePress.data';
-import { list, submit, subList, submitEdit, getO2PressData } from '../balancePress.api';
-import { message } from 'ant-design-vue';
-import { get } from 'lodash-es';
-const props = defineProps({
-  deviceId: {
-    type: String,
-    require: true,
-  },
-});
-const loading = ref(false);
-// 默认初始是第一行
-const isAutoControl = ref('1');
-const O2PressDataFetched = ref(false);
-// 监测数据
-const selectData = reactive({
-  frontRearDP: '-',
-  sourcePressure: '-',
-  fault: '-',
-});
-const monitorParam = [
-  {
-    title: '监测值',
-    code: 'readData.ss_analog_value',
-    unit: '',
-  },
-];
-const changeType = (isAutoControl) => {
-  isAutoControl;
-  //
-};
+  import { onBeforeMount, ref, onMounted, onUnmounted, reactive, defineProps, watch, computed } from 'vue';
+  import ventBox1 from '/@/components/vent/ventBox1.vue';
+  import CustomChart from '@/views/vent/home/configurable/components/detail/CustomChart.vue';
+  import { PressO2Option, mockData1 } from '../balancePressO2.data';
+  import { SvgIcon } from '/@/components/Icon';
+  import { mountedThree, destroy, setModelType, updateText, play } from '../balancePress.threejs';
+  import { settingParam1, settingParam2, settingParam3, windowParam, localFanParam, windrectParam } from '../balancePress.data';
+  import { list, submit, subList, submitEdit, getO2PressData } from '../balancePress.api';
+  import { message } from 'ant-design-vue';
+  import { get } from 'lodash-es';
+  const props = defineProps({
+    deviceId: {
+      type: String,
+      require: true,
+    },
+  });
+  const loading = ref(false);
+  // 默认初始是第一行
+  const isAutoControl = ref('1');
+  const O2PressDataFetched = ref(false);
+  // 监测数据
+  const selectData = reactive({
+    frontRearDP: '-',
+    sourcePressure: '-',
+    fault: '-',
+  });
+  const monitorParam = [
+    {
+      title: '监测值',
+      code: 'readData.ss_analog_value',
+      unit: '',
+    },
+  ];
+  const changeType = (isAutoControl) => {
+    isAutoControl;
+    //
+  };
 
 
-let listData = ref<any[]>([]);
+  let listData = ref<any[]>([]);
 
 
-// https获取监测数据
-let timer: any = null;
-function getMonitor(flag?) {
-  if (Object.prototype.toString.call(timer) === '[object Null]') {
-    timer = setTimeout(
-      async () => {
-        if (props.deviceId) {
-          const data = await getDataSource(props.deviceId);
-          Object.assign(selectData, data);
-          updateText(selectData);
-        }
-        if (timer) {
-          timer = null;
-        }
-        await getMonitor();
-        loading.value = false;
-      },
-      flag ? 0 : 1000
-    );
+  // https获取监测数据
+  let timer: any = null;
+  function getMonitor(flag?) {
+    if (Object.prototype.toString.call(timer) === '[object Null]') {
+      timer = setTimeout(
+        async () => {
+          if (props.deviceId) {
+            const data = await getDataSource(props.deviceId);
+            Object.assign(selectData, data);
+            updateText(selectData);
+          }
+          if (timer) {
+            timer = null;
+          }
+          await getMonitor();
+          loading.value = false;
+        },
+        flag ? 0 : 1000
+      );
+    }
   }
   }
-}
 
 
-const safetyMonitorData = ref<any[]>([]);
-const fanlocalMonitorData = ref<any[]>([]);
-const windowMonitorData = ref<any[]>([]);
-const windrectMonitorData = ref<any[]>([]);
-const modelsensorO2Data = ref<any[]>([]);
-const mockData = ref<{ chartData: { dateTime: string; o2Val: string; pressureVal: string }[] }>({
-  chartData: [],
-});
-const chartData1 = ref<any[]>([]);
-async function getDataSource(systemID) {
-  const res = await list({ devicetype: 'sys', systemID });
-  const result = res.msgTxt;
-  safetyMonitorData.value = [];
-  fanlocalMonitorData.value = [];
-  windowMonitorData.value = [];
-  windrectMonitorData.value = [];
-  modelsensorO2Data.value = [];
-  result.forEach((item) => {
-    if (item.type.startsWith('safetymonitor')) {
-      safetyMonitorData.value.push(...item.datalist);
-    }
-    if (item.type.startsWith('fanlocal')) {
-      item.datalist.forEach((e) => {
-        const f1Run = e.readData.Fan1StartStatus == '1';
-        e.FanfHz = f1Run ? e.readData.Fan1_Frequency : e.readData.Fan2_Frequency;
-        e.FanRun = f1Run ? '1#风机' : '2#风机';
-      });
-      fanlocalMonitorData.value.push(...item.datalist);
-    }
-    if (item.type.startsWith('window')) {
-      windowMonitorData.value.push(...item.datalist);
-    }
-    if (item.type.startsWith('windrect')) {
-      windrectMonitorData.value.push(...item.datalist);
-    }
-    if (item.type.startsWith('modelsensor_o2')) {
-      modelsensorO2Data.value.push(...item.datalist);
-    }
+  const safetyMonitorData = ref<any[]>([]);
+  const fanlocalMonitorData = ref<any[]>([]);
+  const windowMonitorData = ref<any[]>([]);
+  const windrectMonitorData = ref<any[]>([]);
+  const modelsensorO2Data = ref<any[]>([]);
+  const mockData = ref<{ chartData: { dateTime: string; o2Val: string; pressureVal: string }[] }>({
+    chartData: [],
   });
   });
-  if (!O2PressDataFetched.value) {
-    O2PressDataFetched.value = true;
-    getO2Press(modelsensorO2Data.value);
+  const chartData1 = ref<any[]>([]);
+  async function getDataSource(systemID) {
+    const res = await list({ devicetype: 'sys', systemID });
+    const result = res.msgTxt;
+    safetyMonitorData.value = [];
+    fanlocalMonitorData.value = [];
+    windowMonitorData.value = [];
+    windrectMonitorData.value = [];
+    modelsensorO2Data.value = [];
+    result.forEach((item) => {
+      if (item.type.startsWith('safetymonitor')) {
+        safetyMonitorData.value.push(...item.datalist);
+      }
+      if (item.type.startsWith('fanlocal')) {
+        item.datalist.forEach((e) => {
+          const f1Run = e.readData.Fan1StartStatus == '1';
+          e.FanfHz = f1Run ? e.readData.Fan1_Frequency : e.readData.Fan2_Frequency;
+          e.FanRun = f1Run ? '1#风机' : '2#风机';
+        });
+        fanlocalMonitorData.value.push(...item.datalist);
+      }
+      if (item.type.startsWith('window')) {
+        windowMonitorData.value.push(...item.datalist);
+      }
+      if (item.type.startsWith('windrect')) {
+        windrectMonitorData.value.push(...item.datalist);
+      }
+      if (item.type.startsWith('modelsensor_o2')) {
+        modelsensorO2Data.value.push(...item.datalist);
+      }
+    });
+    if (!O2PressDataFetched.value) {
+      O2PressDataFetched.value = true;
+      getO2Press(modelsensorO2Data.value);
+    }
   }
   }
-}
-async function getO2Press(params) {
-  const deviceID = params[0].deviceID;
-  const param = {
-    deviceId: deviceID,
-  };
-  const res = await getO2PressData(param);
+  async function getO2Press(params) {
+    const deviceID = params[0].deviceID;
+    const param = {
+      deviceId: deviceID,
+    };
+    const res = await getO2PressData(param);
 
 
-  const chartData = [...res.o2HistoryDataList, ...res.o2List];
-  mockData.value = { chartData };
-  O2PressDataFetched.value = true;
-}
+    const chartData = [...res.o2HistoryDataList, ...res.o2List];
+    mockData.value = { chartData };
+    O2PressDataFetched.value = true;
+  }
 
 
-// 喷粉操作
-async function onSubmit() {
-  if (listData.value.length != 0) {
-    //编辑
-    let res = await submitEdit(listData.value[0]);
-    if (res) {
-      initParamList();
-    }
-  } else {
-    //新增
-    let res = await submit(formData.value);
-    if (res) {
-      initParamList();
-      message.success('提交成功');
+  // 喷粉操作
+  async function onSubmit() {
+    if (listData.value.length != 0) {
+      //编辑
+      let res = await submitEdit(listData.value[0]);
+      if (res) {
+        initParamList();
+      }
+    } else {
+      //新增
+      let res = await submit(formData.value);
+      if (res) {
+        initParamList();
+        message.success('提交成功');
+      }
     }
     }
   }
   }
-}
 
 
-//获取低氧参数监测返显列表
-async function initParamList() {
-  let res = await subList();
-  if (res && res.records && res.records.length != 0) {
-    listData.value = res.records;
-    formData.value = res.records[0];
+  //获取低氧参数监测返显列表
+  async function initParamList() {
+    let res = await subList();
+    if (res && res.records && res.records.length != 0) {
+      listData.value = res.records;
+      formData.value = res.records[0];
+    }
   }
   }
-}
 
 
-watch(
-  () => props.deviceId,
-  (newVal, oldVal) => {
-    if (newVal && oldVal != undefined) {
-      setModelType('balancePressBase');
+  watch(
+    () => props.deviceId,
+    (newVal, oldVal) => {
+      if (newVal && oldVal != undefined) {
+        setModelType('balancePressBase');
+      }
+      loading.value = true;
     }
     }
-    loading.value = true;
-  }
-);
+  );
 
 
-const formData = ref({
-  id: '',
-  coMaxStart: 0,
-  o2MinStart: 0,
-  coRiseStart: 0,
-  o2DownStart: 0,
-  windowAreaSetGrad: 0,
-  windowSetTime: 0,
-  windowMinArea: 0,
-  windowAreaDef: 0,
-  setMinTime: 0,
-  coEnd: 0,
-  coTimeEnd: 0,
-  o2End: 0,
-  o2TimeEnd: 0,
-});
+  const formData = ref({
+    id: '',
+    coMaxStart: 0,
+    o2MinStart: 0,
+    coRiseStart: 0,
+    o2DownStart: 0,
+    windowAreaSetGrad: 0,
+    windowSetTime: 0,
+    windowMinArea: 0,
+    windowAreaDef: 0,
+    setMinTime: 0,
+    coEnd: 0,
+    coTimeEnd: 0,
+    o2End: 0,
+    o2TimeEnd: 0,
+  });
 
 
-onBeforeMount(() => {});
+  onBeforeMount(() => {});
 
 
-onMounted(() => {
-  loading.value = true;
-  mountedThree().then(async () => {
-    await setModelType('balancePressBase'); //balancePressBase
-    loading.value = false;
-    timer = null;
-    await initParamList();
-    await getMonitor(true);
-    play('startSmoke', 'top', 30, 'open', 0);
+  onMounted(() => {
+    loading.value = true;
+    mountedThree().then(async () => {
+      await setModelType('balancePressBase'); //balancePressBase
+      loading.value = false;
+      timer = null;
+      await initParamList();
+      await getMonitor(true);
+      play('startSmoke', 'top', 30, 'open', 0);
+    });
   });
   });
-});
 
 
-onUnmounted(() => {
-  destroy();
-  if (timer) {
-    clearTimeout(timer);
-  }
-});
+  onUnmounted(() => {
+    destroy();
+    if (timer) {
+      clearTimeout(timer);
+    }
+  });
 </script>
 </script>
 <style lang="less" scoped>
 <style lang="less" scoped>
-@import '/@/design/vent/modal.less';
-@import '../../comment/less/workFace.less';
-@ventSpace: zxm;
-.monitor-container {
-  margin-top: 60px;
-}
-.lr {
-  width: 340px !important;
-}
-.auto-control {
-  padding: 10px 8px;
-  margin: 0 4px 4px 4px;
-  border-radius: 4px;
-  border: 1px solid #ffffff05;
-  background-image: linear-gradient(to left, #39deff15, #3977e500, #39deff15);
-}
-.divider-line {
-  position: relative;
-  color: aqua;
-  padding-left: 20px;
-  font-size: 14px;
-  &::before {
-    position: absolute;
-    content: '';
-    display: block;
-    top: 10px;
-    left: 0;
-    height: 1px;
-    width: 15px;
-    background-color: #ffffff33;
+  @import '/@/design/vent/modal.less';
+  @import '../../comment/less/workFace.less';
+  @ventSpace: zxm;
+  .monitor-container {
+    margin-top: 60px;
+  }
+  .lr {
+    width: 340px !important;
   }
   }
-  &::after {
-    position: absolute;
-    content: '';
-    display: block;
-    top: 10px;
-    right: 0;
-    height: 1px;
-    width: calc(100% - 85px);
-    background-color: #ffffff33;
+  .auto-control {
+    padding: 10px 8px;
+    margin: 0 4px 4px 4px;
+    border-radius: 4px;
+    border: 1px solid #ffffff05;
+    background-image: linear-gradient(to left, #39deff15, #3977e500, #39deff15);
   }
   }
-}
-.input-value {
-  width: 120px !important;
-}
-.unit {
-  text-align: right;
-}
-.btn-box {
-  margin: 10px 4px;
-  .btn1 {
-    padding: 4px 0;
+  .divider-line {
+    position: relative;
+    color: aqua;
+    padding-left: 20px;
+    font-size: 14px;
+    &::before {
+      position: absolute;
+      content: '';
+      display: block;
+      top: 10px;
+      left: 0;
+      height: 1px;
+      width: 15px;
+      background-color: #ffffff33;
+    }
+    &::after {
+      position: absolute;
+      content: '';
+      display: block;
+      top: 10px;
+      right: 0;
+      height: 1px;
+      width: calc(100% - 85px);
+      background-color: #ffffff33;
+    }
+  }
+  .input-value {
+    width: 120px !important;
+  }
+  .unit {
+    text-align: right;
+  }
+  .btn-box {
+    margin: 10px 4px;
+    .btn1 {
+      padding: 4px 0;
+    }
   }
   }
-}
 
 
-:deep(.@{ventSpace}-tabs-tabpane-active) {
-  overflow: auto;
-}
+  :deep(.@{ventSpace}-tabs-tabpane-active) {
+    overflow: auto;
+  }
 
 
-:deep(.@{ventSpace}-input-number) {
-  border-color: #ffffff88 !important;
-}
+  :deep(.@{ventSpace}-input-number) {
+    border-color: #ffffff88 !important;
+  }
 </style>
 </style>

+ 183 - 46
src/views/vent/monitorManager/balancePressMonitor/components/balancePressHomeSP.vue

@@ -87,7 +87,7 @@
   </a-spin>
   </a-spin>
 </template>
 </template>
 <script setup lang="ts">
 <script setup lang="ts">
-  import { ref, onMounted, onUnmounted, defineProps } from 'vue';
+  import { ref, onMounted, onUnmounted, defineProps, h } from 'vue';
   import { mountedThree, destroy, setModelType, updateText, play } from '../balancePress.threejs';
   import { mountedThree, destroy, setModelType, updateText, play } from '../balancePress.threejs';
   import { list } from '../balancePress.api';
   import { list } from '../balancePress.api';
   import ModuleCommon from '../../../home/configurable/components/ModuleCommon.vue';
   import ModuleCommon from '../../../home/configurable/components/ModuleCommon.vue';
@@ -102,7 +102,12 @@
   // import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
   // import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
   // import { getToken } from '/@/utils/auth';
   // import { getToken } from '/@/utils/auth';
   // import { useUserStore } from '/@/store/modules/user';
   // import { useUserStore } from '/@/store/modules/user';
-  import { usePressControl } from '../hooks/useControl';
+  import { usePressControlSP } from '../hooks/useControl';
+  import { Modal } from 'ant-design-vue';
+  import dayjs from 'dayjs';
+  import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
+  import { useUserStore } from '/@/store/modules/user';
+  import { getToken } from '/@/utils/auth';
   // import dayjs from 'dayjs';
   // import dayjs from 'dayjs';
   // import { Config } from '../../../deviceManager/configurationTable/types';
   // import { Config } from '../../../deviceManager/configurationTable/types';
 
 
@@ -150,26 +155,6 @@
       (obj: any, e: any) => {
       (obj: any, e: any) => {
         obj[e.type] = e;
         obj[e.type] = e;
 
 
-        // if (true) {
-        if (sysOrgCode === 'sdmtjtswmk') {
-          if (e.type.startsWith('fanlocal')) {
-            obj.fanlocal.datalist.push(...e.datalist);
-          }
-          if (e.type.startsWith('safetymonitor')) {
-            e.datalist.forEach((ele) => {
-              if (ele.strinstallpos.includes('风门')) {
-                obj.gate.datalist.push(ele);
-              } else if (ele.strinstallpos.includes('风窗')) {
-                obj.window.datalist.push(ele);
-              } else if (ele.strinstallpos.includes('工作面')) {
-                obj.work_surface.datalist.push(ele);
-              } else {
-                obj.others.datalist.push(ele);
-              }
-            });
-          }
-        }
-
         return obj;
         return obj;
       },
       },
       {
       {
@@ -193,13 +178,13 @@
     // avePressLinkage,
     // avePressLinkage,
     // gateLinkage,
     // gateLinkage,
     formData,
     formData,
-    // getAvePress,
+    getAvePress,
     changePassword,
     changePassword,
     // linkageControl,
     // linkageControl,
-    // settingControl,
-    // autoControl,
-    // cancelControl,
-  } = usePressControl();
+    settingControl,
+    autoControl,
+    cancelControl,
+  } = usePressControlSP();
 
 
   const modalVisible = ref(false);
   const modalVisible = ref(false);
 
 
@@ -229,30 +214,30 @@
   //     });
   //     });
   //   };
   //   };
   // }
   // }
-  function changeAvePressState({ target }, __key) {
+  function changeAvePressState({ target }, key) {
     formData.value.temp = target.value;
     formData.value.temp = target.value;
     modalVisible.value = true;
     modalVisible.value = true;
-    // resolver = (password) => {
-    //   settingControl(
-    //     { password, id: avePressSetting.value.id },
-    //     {
-    //       [key]: formData.value.temp,
-    //     }
-    //   ).finally(() => {
-    //     modalVisible.value = false;
-    //   });
-    // };
+    resolver = (password) => {
+      settingControl(
+        { password, id: avePressSetting.value.id },
+        {
+          [key]: formData.value.temp,
+        }
+      ).finally(() => {
+        modalVisible.value = false;
+      });
+    };
   }
   }
 
 
   // function submitLinkageForm(password) {}
   // function submitLinkageForm(password) {}
   function submitSettingForm() {
   function submitSettingForm() {
     modalVisible.value = true;
     modalVisible.value = true;
-    // resolver = (password) => {
-    //   settingControl({ password, id: avePressSetting.value.id }, avePressSetting.value).finally(() => {
-    //     modalVisible.value = false;
-    //     settingFormDisabled.value = true;
-    //   });
-    // };
+    resolver = (password) => {
+      settingControl({ password, id: avePressSetting.value.id }, avePressSetting.value).finally(() => {
+        modalVisible.value = false;
+        settingFormDisabled.value = true;
+      });
+    };
   }
   }
 
 
   let resolver: any = null;
   let resolver: any = null;
@@ -281,13 +266,165 @@
     settingFormDisabled.value = !settingFormDisabled.value;
     settingFormDisabled.value = !settingFormDisabled.value;
     /**  如果取消了编辑模式,那么需要重置表单 */
     /**  如果取消了编辑模式,那么需要重置表单 */
     if (settingFormDisabled.value) {
     if (settingFormDisabled.value) {
-      // getAvePress();
+      getAvePress();
     }
     }
   }
   }
 
 
+  // const [warnRegister1, warnModal1] = useModal();
+  // const [warnRegister2, warnModal2] = useModal();
+  // const [warnRegister3, warnModal3] = useModal();
+  let warnModal1: { destroy: () => void } | null = null;
+  let warnModal2: { destroy: () => void } | null = null;
+  let warnModal3: { destroy: () => void } | null = null;
+  // const warnModalText1 = ref('');
+  // const warnModalText2 = ref('');
+  // const warnModalText3 = ref('');
+
+  // 初始化 WebSocket
+  function initWebSocket() {
+    const token = getToken();
+    const userStore = useUserStore();
+    const glob = useGlobSetting();
+    // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
+    const url = `${glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://')}/websocket/${userStore.getUserInfo.id}?token=${token}`;
+    connectWebSocket(url);
+    onWebSocket((data: any) => {
+      if (data.cmd !== 'topic' || data.topic !== 'warn') return;
+      if (!data.msgTxt) return;
+
+      const { info = '', type = '', avgPressureLogId, date } = JSON.parse(data.msgTxt);
+      const datestr = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
+      switch (type) {
+        case 'o2':
+          // 如果已经存在报警模态框,则不需要处理
+          if (warnModal1) break;
+          warnModal1 = Modal.confirm({
+            title: data.msgTitle,
+            content: h('div', { style: { color: '#fff' } }, [h('p', datestr), h('p', info)]),
+            centered: true,
+            okText: '下发调节指令',
+            mask: true,
+            class: 'balancePress',
+            onOk() {
+              // 点击确定按钮后,执行调节指令。调节指令需要确认密码,所以需要先弹出密码框
+              return new Promise((resolve, reject) => {
+                modalVisible.value = true;
+                // 弹出密码框后,输入密码并验证成功则关闭密码弹窗和报警弹窗,失败则关闭密码弹窗但不关闭报警弹窗
+                resolver = (password) => {
+                  autoControl({ password, id: avePressSetting.value.id }, { avgPressLogId: avgPressureLogId })
+                    .then(() => {
+                      modalVisible.value = false;
+                      resolve(true);
+                      warnModal1?.destroy();
+                      warnModal1 = null;
+                    })
+                    .catch(() => {
+                      modalVisible.value = false;
+                      reject();
+                    });
+                };
+                // 弹出密码框取消操作则关闭密码弹窗但不关闭报警弹窗
+                rejecter = () => {
+                  modalVisible.value = false;
+                  reject();
+                };
+              });
+            },
+            onCancel() {
+              return cancelControl({}, { avgPressLogId: avgPressureLogId }).finally(() => {
+                warnModal1?.destroy();
+                warnModal1 = null;
+              });
+            },
+          });
+          // warnModalText1.value = info;
+          // warnModal1.openModal();
+
+          break;
+        case 'pressure':
+          // warnModalText1.value = info;
+          // warnModal1.openModal();
+          if (warnModal1) break;
+          warnModal1 = Modal.confirm({
+            title: data.msgTitle,
+            content: h('div', { style: { color: '#fff' } }, [h('p', datestr), h('p', info)]),
+            centered: true,
+            okText: '下发调节指令',
+            mask: true,
+            class: 'balancePress',
+            onOk() {
+              // 点击确定按钮后,执行调节指令。调节指令需要确认密码,所以需要先弹出密码框
+              return new Promise((resolve, reject) => {
+                modalVisible.value = true;
+                // 弹出密码框后,输入密码并验证成功则关闭密码弹窗和报警弹窗,失败则关闭密码弹窗但不关闭报警弹窗
+                resolver = (password) => {
+                  autoControl({ password, id: avePressSetting.value.id }, { avgPressLogId: avgPressureLogId })
+                    .then(() => {
+                      modalVisible.value = false;
+                      resolve(true);
+                      warnModal1?.destroy();
+                      warnModal1 = null;
+                    })
+                    .catch(() => {
+                      modalVisible.value = false;
+                      reject();
+                    });
+                };
+                // 弹出密码框取消操作则关闭密码弹窗但不关闭报警弹窗
+                rejecter = () => {
+                  modalVisible.value = false;
+                  reject();
+                };
+              });
+            },
+            onCancel() {
+              return cancelControl({}, { avgPressLogId: avgPressureLogId }).finally(() => {
+                warnModal1?.destroy();
+                warnModal1 = null;
+              });
+            },
+          });
+
+          break;
+        case 'gate':
+          if (warnModal2) break;
+          warnModal2 = Modal.warning({
+            title: data.msgTitle,
+            content: info,
+            mask: true,
+            class: 'balancePress',
+            onOk: () => {
+              warnModal2?.destroy();
+              warnModal2 = null;
+            },
+          });
+          break;
+        case 'fansys':
+          if (warnModal3) break;
+          warnModal3 = Modal.warning({
+            title: data.msgTitle,
+            content: info,
+            mask: true,
+            class: 'balancePress',
+            style: 'top: 700px',
+            onOk: () => {
+              warnModal3?.destroy();
+              warnModal3 = null;
+            },
+          });
+
+          break;
+
+        default:
+          break;
+      }
+    });
+  }
+
   onMounted(() => {
   onMounted(() => {
     fetchConfigs('balancePressHome');
     fetchConfigs('balancePressHome');
-    // getAvePress();
+    initWebSocket();
+    getAvePress();
     loading.value = true;
     loading.value = true;
     mountedThree().then(async () => {
     mountedThree().then(async () => {
       if (sysOrgCode === 'jsnyspmy') {
       if (sysOrgCode === 'jsnyspmy') {

+ 136 - 0
src/views/vent/monitorManager/balancePressMonitor/hooks/useControl.ts

@@ -154,3 +154,139 @@ export function usePressControl() {
     formData,
     formData,
   };
   };
 }
 }
+
+export function usePressControlSP() {
+  /** 参数与设置、风机风门联动表单数据 */
+  const formData = ref({
+    /** 风机风门联动的自动调控 */
+    isAuto: false,
+    /** 参数与设置的状态暂存字段 */
+    temp: false,
+  });
+  /** 参数与设置、风机风门联动的数据 */
+  const avePressSetting = ref<any>({ isAuto: false });
+
+  /** 获取 参数与设置、风机风门联动 的数据 */
+  function getAvePress() {
+    return Promise.all([
+      subList2({
+        strType: 'sp_pressure_to_window',
+      }),
+    ]).then(([settingData]) => {
+      avePressSetting.value = get(settingData, '[0]', {});
+    });
+  }
+
+  function changePassword({ password, oldpassword, id }) {
+    return updatePassword({
+      id,
+      newPassword: password,
+      oldPassword: oldpassword,
+    })
+      .then(() => {
+        message.success('操作成功');
+      })
+      .catch((e) => {
+        message.error(e);
+      })
+      .finally(() => {
+        getAvePress();
+      });
+  }
+
+  function linkageControl({ password, id }, formData) {
+    return validPassword({
+      id,
+      password,
+    })
+      .then(() => {
+        return submitEdit({
+          id,
+          ...formData,
+        })
+          .then(() => {
+            message.success('操作成功');
+          })
+          .catch(() => {
+            message.error('操作失败');
+          });
+      })
+      .catch((e) => {
+        message.error(e);
+      })
+      .finally(() => {
+        getAvePress();
+      });
+  }
+
+  function settingControl({ password, id }, formData) {
+    return validPassword({
+      id,
+      password,
+    })
+      .then(() => {
+        return submitEdit({
+          id,
+          ...formData,
+        })
+          .then(() => {
+            message.success('操作成功');
+          })
+          .catch(() => {
+            message.error('操作失败');
+          });
+      })
+      .catch((e) => {
+        message.error(e);
+      })
+      .finally(() => {
+        getAvePress();
+      });
+  }
+
+  function autoControl({ password, id }, { avgPressLogId }) {
+    return validPassword({
+      id,
+      password,
+    })
+      .then(() => {
+        return controlWindow({ avgPressId: avePressSetting.value.id, avgPressLogId })
+          .then(() => {
+            message.success('预警已处理');
+          })
+          .catch((e) => {
+            message.error('下发失败');
+            throw e;
+          });
+      })
+      .catch((e) => {
+        message.error(e);
+        throw e;
+      })
+      .finally(() => {
+        getAvePress();
+      });
+  }
+
+  function cancelControl(___, { avgPressLogId }) {
+    return cancelcontrolWindow({ avgPressId: avePressSetting.value.id, avgPressLogId })
+      .then(() => {
+        message.success('已取消');
+      })
+      .catch((e) => {
+        message.error('下发失败');
+        throw e;
+      });
+  }
+
+  return {
+    settingControl,
+    linkageControl,
+    changePassword,
+    getAvePress,
+    autoControl,
+    cancelControl,
+    avePressSetting,
+    formData,
+  };
+}

+ 2 - 10
src/views/vent/monitorManager/camera/common/cameraTree.vue

@@ -1,11 +1,5 @@
 <template>
 <template>
-  <treeList
-    v-for="model in list"
-    v-bind="$attrs"
-    :model="model"
-    :key="model.id"
-    @detail-node="onDetail"
-  >
+  <treeList v-for="model in list" v-bind="$attrs" :model="model" :key="model.id" @detail-node="onDetail">
     <template #icon="slotProps">
     <template #icon="slotProps">
       <slot name="icon" v-bind="slotProps"></slot>
       <slot name="icon" v-bind="slotProps"></slot>
     </template>
     </template>
@@ -17,7 +11,7 @@
 <script setup lang="ts">
 <script setup lang="ts">
   import { ref } from 'vue';
   import { ref } from 'vue';
   import treeList from './treeList.vue';
   import treeList from './treeList.vue';
-  const emit = defineEmits([ 'detailNode']);
+  const emit = defineEmits(['detailNode']);
   interface IFileSystem {
   interface IFileSystem {
     id: string;
     id: string;
     title: string;
     title: string;
@@ -57,7 +51,5 @@
       eventType: 'detail',
       eventType: 'detail',
     });
     });
   };
   };
- 
-  
 </script>
 </script>
 <style scoped></style>
 <style scoped></style>

+ 430 - 420
src/views/vent/monitorManager/camera/index.vue

@@ -40,336 +40,282 @@
   </div>
   </div>
 </template>
 </template>
 <script lang="ts" setup>
 <script lang="ts" setup>
-import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
-import { useRouter } from 'vue-router';
-import { Pagination, Empty } from 'ant-design-vue';
-import { list, cameraAddr, getCameraDevKind, getDevice, getVentanalyCamera } from './camera.api';
-import Player, { I18N } from 'xgplayer';
-import ZH from 'xgplayer/es/lang/zh-cn';
-import HlsPlugin from 'xgplayer-hls';
-import FlvPlugin from 'xgplayer-flv';
-import 'xgplayer/dist/index.min.css';
-import cameraTree from './common/cameraTree.vue';
-import { SvgIcon } from '/@/components/Icon';
-import treeIcon from './common/Icon/treeIcon.vue';
+  import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
+  import { useRouter } from 'vue-router';
+  import { Pagination, Empty } from 'ant-design-vue';
+  import { list, cameraAddr, getCameraDevKind, getDevice, getVentanalyCamera } from './camera.api';
+  import Player, { I18N } from 'xgplayer';
+  import ZH from 'xgplayer/es/lang/zh-cn';
+  import HlsPlugin from 'xgplayer-hls';
+  import FlvPlugin from 'xgplayer-flv';
+  import 'xgplayer/dist/index.min.css';
+  import cameraTree from './common/cameraTree.vue';
+  import { SvgIcon } from '/@/components/Icon';
+  import treeIcon from './common/Icon/treeIcon.vue';
 
 
-//当前选中树节点
-let selected = reactive<any>({
-  id: null,
-  pid: null,
-  title: '',
-  isFolder: false,
-});
-//tree菜单列表
-let listArr = reactive<any[]>([]);
-let searchParam = reactive({
-  devKind: '',
-  strType: '',
-});
+  //当前选中树节点
+  let selected = reactive<any>({
+    id: null,
+    pid: null,
+    title: '',
+    isFolder: false,
+  });
+  //tree菜单列表
+  let listArr = reactive<any[]>([]);
+  let searchParam = reactive({
+    devKind: '',
+    strType: '',
+  });
 
 
-I18N.use(ZH);
-let router = useRouter(); //路由
-const pageSize = ref(4);
-const current = ref(1);
-const total = ref(0);
-const playerList = ref([]);
-const webRtcServerList = <any[]>[];
-let addrList = ref<{ name: string; addr: string; cameraRate: number; devicekind: string }[]>([]);
-async function getCameraDevKindList() {
-  let res = await getCameraDevKind();
-  if (res.length != 0) {
-    listArr.length = 0;
-    listArr.push({
-      pid: 'root',
-      isFolder: true,
-      expanded: true,
-      title: '全部',
-      id: 0,
-      children: [],
-    });
-    res.forEach((el) => {
-      el.pid = 0;
-      el.isFolder = true;
-      el.expanded = false;
-      el.title = el.itemText;
-      el.id = el.subDictId;
-      el.children = [];
-      listArr[0].children.push(el);
-    });
-    selected.id = listArr[0].id;
-    selected.pid = listArr[0].pid;
-    selected.title = listArr[0].title;
-    selected.isFolder = listArr[0].isFolder;
+  I18N.use(ZH);
+  let router = useRouter(); //路由
+  const pageSize = ref(4);
+  const current = ref(1);
+  const total = ref(0);
+  const playerList = ref([]);
+  const webRtcServerList = <any[]>[];
+  let addrList = ref<{ name: string; addr: string; cameraRate: number; devicekind: string }[]>([]);
+  async function getCameraDevKindList() {
+    let res = await getCameraDevKind();
+    if (res.length != 0) {
+      listArr.length = 0;
+      listArr.push({
+        pid: 'root',
+        isFolder: true,
+        expanded: true,
+        title: '全部',
+        id: 0,
+        children: [],
+      });
+      res.forEach((el) => {
+        el.pid = 0;
+        el.isFolder = true;
+        el.expanded = false;
+        el.title = el.itemText;
+        el.id = el.subDictId;
+        el.children = [];
+        listArr[0].children.push(el);
+      });
+      selected.id = listArr[0].id;
+      selected.pid = listArr[0].pid;
+      selected.title = listArr[0].title;
+      selected.isFolder = listArr[0].isFolder;
+    }
   }
   }
-}
 
 
-//点击目录
-async function onClick(node) {
-  if (selected.title === node.title && selected.id === node.id) return;
-  current.value = 1;
-  selected.id = node.id;
-  selected.pid = node.pid;
-  selected.title = node.title;
-  selected.isFolder = node.isFolder;
-  if (node.pid != 'root') {
-    if (node.isFolder) {
-      let types, devicetype;
-      if (node.itemValue.indexOf('&') != -1) {
-        types = node.itemValue.substring(node.itemValue.indexOf('&') + 1);
-        devicetype = node.itemValue.substring(0, node.itemValue.indexOf('&'));
+  //点击目录
+  async function onClick(node) {
+    if (selected.title === node.title && selected.id === node.id) return;
+    current.value = 1;
+    selected.id = node.id;
+    selected.pid = node.pid;
+    selected.title = node.title;
+    selected.isFolder = node.isFolder;
+    if (node.pid != 'root') {
+      if (node.isFolder) {
+        let types, devicetype;
+        if (node.itemValue.indexOf('&') != -1) {
+          types = node.itemValue.substring(node.itemValue.indexOf('&') + 1);
+          devicetype = node.itemValue.substring(0, node.itemValue.indexOf('&'));
+        } else {
+          types = '';
+          devicetype = '';
+        }
+        let res = await getDevice({ ids: types, devicetype: devicetype });
+        if (res.msgTxt.length != 0) {
+          res.msgTxt[0].datalist.forEach((el) => {
+            el.pid = node.id;
+            el.isFolder = false;
+            el.title = el.strinstallpos;
+            el.id = el.deviceID;
+          });
+          listArr[0].children.forEach((v) => {
+            if (v.id == node.id) {
+              v.children = res.msgTxt[0].datalist;
+            }
+          });
+        }
+        searchParam.devKind = node.itemValue;
+        searchParam.strType = '';
+        await getVideoAddrs();
+        getVideo();
       } else {
       } else {
-        types = '';
-        devicetype = '';
+        await getVideoAddrsSon(node.deviceID);
+        getVideo();
       }
       }
-      let res = await getDevice({ ids: types, devicetype: devicetype });
-      if (res.msgTxt.length != 0) {
-        res.msgTxt[0].datalist.forEach((el) => {
-          el.pid = node.id;
-          el.isFolder = false;
-          el.title = el.strinstallpos;
-          el.id = el.deviceID;
-        });
-        listArr[0].children.forEach((v) => {
-          if (v.id == node.id) {
-            v.children = res.msgTxt[0].datalist;
-          }
-        });
-      }
-      searchParam.devKind = node.itemValue;
+    } else {
+      searchParam.devKind = '';
       searchParam.strType = '';
       searchParam.strType = '';
       await getVideoAddrs();
       await getVideoAddrs();
       getVideo();
       getVideo();
-    } else {
-      await getVideoAddrsSon(node.deviceID);
-      getVideo();
     }
     }
-  } else {
-    searchParam.devKind = '';
-    searchParam.strType = '';
-    await getVideoAddrs();
-    getVideo();
   }
   }
-}
 
 
-//点击详情跳转
-function onDetail(node) {
-  let str = listArr[0].children.filter((v) => v.id == node.pid)[0].itemValue;
-  let type = str.indexOf('&') != -1 ? str.substring(0, str.indexOf('&')) : '';
-  console.log(type, 'type--------');
-  switch (type) {
-    case 'pulping': //注浆
-      router.push('/grout-home');
-      break;
-    case 'window': //自动风窗
-      router.push('/monitorChannel/monitor-window?id=' + node.deviceID);
-      break;
-    case 'gate': //自动风门
-      router.push('/monitorChannel/monitor-gate?id=' + node.deviceID + '&deviceType=' + node.deviceType);
-      break;
-    case 'fanlocal': //局部风机
-      router.push('/monitorChannel/monitor-fanlocal?id=' + node.deviceID + '&deviceType=fanlocal');
-      break;
-    case 'fanmain': //主风机
-      router.push('/monitorChannel/monitor-fanmain?id=' + node.deviceID);
-      break;
-    case 'forcFan': //压风机
-      router.push('/forcFan/home');
-      break;
-    case 'pump': //瓦斯抽采泵
-      router.push('/monitorChannel/gasPump-home');
-      break;
-    case 'nitrogen': //制氮
-      router.push('/nitrogen-home');
-      break;
+  //点击详情跳转
+  function onDetail(node) {
+    let str = listArr[0].children.filter((v) => v.id == node.pid)[0].itemValue;
+    let type = str.indexOf('&') != -1 ? str.substring(0, str.indexOf('&')) : '';
+    console.log(type, 'type--------');
+    switch (type) {
+      case 'pulping': //注浆
+        router.push('/grout-home');
+        break;
+      case 'window': //自动风窗
+        router.push('/monitorChannel/monitor-window?id=' + node.deviceID);
+        break;
+      case 'gate': //自动风门
+        router.push('/monitorChannel/monitor-gate?id=' + node.deviceID + '&deviceType=' + node.deviceType);
+        break;
+      case 'fanlocal': //局部风机
+        router.push('/monitorChannel/monitor-fanlocal?id=' + node.deviceID + '&deviceType=fanlocal');
+        break;
+      case 'fanmain': //主风机
+        router.push('/monitorChannel/monitor-fanmain?id=' + node.deviceID);
+        break;
+      case 'forcFan': //压风机
+        router.push('/forcFan/home');
+        break;
+      case 'pump': //瓦斯抽采泵
+        router.push('/monitorChannel/gasPump-home');
+        break;
+      case 'nitrogen': //制氮
+        router.push('/nitrogen-home');
+        break;
+    }
   }
   }
-}
 
 
-async function getVideoAddrs() {
-  clearCamera();
-  playerList.value = [];
-  let paramKind = searchParam.devKind.substring(0, searchParam.devKind.indexOf('&'));
-  let res = await list({ devKind: paramKind, strType: searchParam.strType, pageSize: pageSize.value, pageNo: current.value });
-  total.value = res['total'] || 0;
-  if (res.records.length != 0) {
-    const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
-    const cameras = res.records;
-    for (let i = 0; i < cameras.length; i++) {
-      const item = cameras[i];
-      if (item['devicekind'] === 'toHKRtsp' || item['devicekind'] === 'toHKHLs' || item['devicekind'] === 'HLL' || item['devicekind'] === 'YZG_URL') {
-        // 从海康平台接口获取视频流
-        const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
-        const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : ''
-        try {
-          const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
-          if (data && data['url']) {
-            cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+  async function getVideoAddrs() {
+    clearCamera();
+    playerList.value = [];
+    let paramKind = searchParam.devKind.substring(0, searchParam.devKind.indexOf('&'));
+    let res = await list({ devKind: paramKind, strType: searchParam.strType, pageSize: pageSize.value, pageNo: current.value });
+    total.value = res['total'] || 0;
+    if (res.records.length != 0) {
+      const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
+      const cameras = res.records;
+      for (let i = 0; i < cameras.length; i++) {
+        const item = cameras[i];
+        if (
+          item['devicekind'] === 'toHKRtsp' ||
+          item['devicekind'] === 'toHKHLs' ||
+          item['devicekind'] === 'HLL' ||
+          item['devicekind'] === 'YZG_URL'
+        ) {
+          // 从海康平台接口获取视频流
+          const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
+          const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : '';
+          try {
+            const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
+            if (data && data['url']) {
+              cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+            }
+            // cameraList.push({
+            //   name: item['name'],
+            //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
+            //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
+            // });
+          } catch (error) {}
+        } else {
+          if (item['addr'].includes('0.0.0.0')) {
+            item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
           }
           }
-          // cameraList.push({
-          //   name: item['name'],
-          //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
-          //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
-          // });
-        } catch (error) { }
-      } else {
-        if (item['addr'].includes('0.0.0.0')) {
-          item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
+          cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
         }
         }
-        cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
       }
       }
+      addrList.value = cameraList;
+      console.log(addrList.value, ' addrList.value-------------');
     }
     }
-    addrList.value = cameraList;
-    console.log(addrList.value, ' addrList.value-------------');
   }
   }
-}
 
 
-async function getVideoAddrsSon(Id) {
-  clearCamera();
-  playerList.value = [];
-  let res = await getVentanalyCamera({ deviceid: Id });
-  if (res.records.length != 0) {
-    const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
-    const cameras = res.records;
-    for (let i = 0; i < cameras.length; i++) {
-      const item = cameras[i];
+  async function getVideoAddrsSon(Id) {
+    clearCamera();
+    playerList.value = [];
+    let res = await getVentanalyCamera({ deviceid: Id });
+    if (res.records.length != 0) {
+      const cameraList = <{ name: string; addr: string; cameraRate: number; devicekind: string }[]>[];
+      const cameras = res.records;
+      for (let i = 0; i < cameras.length; i++) {
+        const item = cameras[i];
 
 
-      if (item['devicekind'] === 'toHKRtsp' || item['devicekind'] === 'toHKHLs' || item['devicekind'] === 'HLL' || item['devicekind'] === 'YZG_URL') {
-        // 从海康平台接口获取视频流
-        const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
-        const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : ''
-        try {
-          const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
-          if (data && data['url']) {
-            cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+        if (
+          item['devicekind'] === 'toHKRtsp' ||
+          item['devicekind'] === 'toHKHLs' ||
+          item['devicekind'] === 'HLL' ||
+          item['devicekind'] === 'YZG_URL'
+        ) {
+          // 从海康平台接口获取视频流
+          const videoType = item['devicekind'] === 'toHKRtsp' ? 'rtsp' : '';
+          const devicekindType = item['devicekind'] === 'YZG_URL' ? 'YZG_URL' : '';
+          try {
+            const data = await cameraAddr({ devicekind: devicekindType, cameraCode: item['addr'], videoType });
+            if (data && data['url']) {
+              cameraList.push({ name: item['name'], addr: data['url'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
+            }
+            // cameraList.push({
+            //   name: item['name'],
+            //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
+            //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
+            // });
+          } catch (error) {}
+        } else {
+          if (item['addr'].includes('0.0.0.0')) {
+            item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
           }
           }
-          // cameraList.push({
-          //   name: item['name'],
-          //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
-          //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
-          // });
-        } catch (error) { }
-      } else {
-        if (item['addr'].includes('0.0.0.0')) {
-          item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname);
+          cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
         }
         }
-        cameraList.push({ name: item['name'], addr: item['addr'], cameraRate: item['cameraRate'], devicekind: item['devicekind'] });
       }
       }
+      addrList.value = cameraList;
     }
     }
-    addrList.value = cameraList;
   }
   }
-}
 
 
-async function onChange(page) {
-  current.value = page;
-  await getVideoAddrs();
-  getVideo();
-}
+  async function onChange(page) {
+    current.value = page;
+    await getVideoAddrs();
+    getVideo();
+  }
 
 
-function getVideo() {
-  const ip = VUE_APP_URL.webRtcUrl;
-  for (let i = 0; i < addrList.value.length; i++) {
-    const item = addrList.value[i];
-    if (item.addr.startsWith('rtsp://')) {
-      const dom = document.getElementById('video' + i) as HTMLVideoElement;
-      dom.muted = true;
-      dom.volume = 0;
-      const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip);
-      webRtcServerList.push(webRtcServer);
-      webRtcServer.connect(item.addr);
-    } else {
-      setNoRtspVideo('player' + i, item.addr, item.cameraRate, item.devicekind);
+  function getVideo() {
+    const ip = VUE_APP_URL.webRtcUrl;
+    for (let i = 0; i < addrList.value.length; i++) {
+      const item = addrList.value[i];
+      if (item.addr.startsWith('rtsp://')) {
+        const dom = document.getElementById('video' + i) as HTMLVideoElement;
+        dom.muted = true;
+        dom.volume = 0;
+        const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip);
+        webRtcServerList.push(webRtcServer);
+        webRtcServer.connect(item.addr);
+      } else {
+        setNoRtspVideo('player' + i, item.addr, item.cameraRate, item.devicekind);
+      }
     }
     }
   }
   }
-}
 
 
-function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
-  const fileExtension = videoAddr.split('.').pop();
-  if (fileExtension === 'flv' || devicekind == 'flv') {
-    const player = new Player({
-      lang: 'zh',
-      id: id,
-      url: videoAddr,
-      width: 589,
-      height: 330,
-      poster: '/src/assets/images/vent/noSinge.png',
-      plugins: [FlvPlugin],
-      fluid: true,
-      autoplay: true,
-      isLive: true,
-      playsinline: true,
-      screenShot: true,
-      whitelist: [''],
-      ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
-      closeVideoClick: true,
-      customConfig: {
-        isClickPlayBack: false,
-      },
-      defaultPlaybackRate: cameraRate || 1,
-      controls: false,
-      flv: {
-        retryCount: 3, // 重试 3 次,默认值
-        retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-        loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-        fetchOptions: {
-          // 该参数会透传给 fetch,默认值为 undefined
-          mode: 'cors',
-        },
-        targetLatency: 10, // 直播目标延迟,默认 10 秒
-        maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-        disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-        maxJumpDistance: 10,
-      },
-    });
-    playerList.value.push(player);
-  }
-  if (fileExtension === 'm3u8' || devicekind == 'm3u8') {
-    let player;
-    if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
-      // 原生支持 hls 播放
-      player = new Player({
+  function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
+    const fileExtension = videoAddr.split('.').pop();
+    if (fileExtension === 'flv' || devicekind == 'flv') {
+      const player = new Player({
         lang: 'zh',
         lang: 'zh',
         id: id,
         id: id,
         url: videoAddr,
         url: videoAddr,
         width: 589,
         width: 589,
         height: 330,
         height: 330,
-        isLive: true,
-        autoplay: true,
-        autoplayMuted: true,
-        cors: true,
-        ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
         poster: '/src/assets/images/vent/noSinge.png',
         poster: '/src/assets/images/vent/noSinge.png',
-        defaultPlaybackRate: cameraRate || 1,
-        controls: false,
-        hls: {
-          retryCount: 3, // 重试 3 次,默认值
-          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-          fetchOptions: {
-            // 该参数会透传给 fetch,默认值为 undefined
-            mode: 'cors',
-          },
-          targetLatency: 10, // 直播目标延迟,默认 10 秒
-          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
-          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-          maxJumpDistance: 10,
-        },
-      });
-    } else if (HlsPlugin.isSupported()) {
-      // 第一步
-      player = new Player({
-        lang: 'zh',
-        id: id,
-        url: videoAddr,
-        width: 589,
-        height: 330,
-        isLive: true,
+        plugins: [FlvPlugin],
+        fluid: true,
         autoplay: true,
         autoplay: true,
-        autoplayMuted: true,
-        plugins: [HlsPlugin], // 第二步
-        poster: '/src/assets/images/vent/noSinge.png',
+        isLive: true,
+        playsinline: true,
+        screenShot: true,
+        whitelist: [''],
         ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
         ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+        closeVideoClick: true,
+        customConfig: {
+          isClickPlayBack: false,
+        },
         defaultPlaybackRate: cameraRate || 1,
         defaultPlaybackRate: cameraRate || 1,
         controls: false,
         controls: false,
-        hls: {
+        flv: {
           retryCount: 3, // 重试 3 次,默认值
           retryCount: 3, // 重试 3 次,默认值
           retryDelay: 1000, // 每次重试间隔 1 秒,默认值
           retryDelay: 1000, // 每次重试间隔 1 秒,默认值
           loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
           loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
@@ -383,164 +329,228 @@ function setNoRtspVideo(id, videoAddr, cameraRate, devicekind) {
           maxJumpDistance: 10,
           maxJumpDistance: 10,
         },
         },
       });
       });
+      playerList.value.push(player);
+    }
+    if (fileExtension === 'm3u8' || devicekind == 'm3u8') {
+      let player;
+      if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
+        // 原生支持 hls 播放
+        player = new Player({
+          lang: 'zh',
+          id: id,
+          url: videoAddr,
+          width: 589,
+          height: 330,
+          isLive: true,
+          autoplay: true,
+          autoplayMuted: true,
+          cors: true,
+          ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+          poster: '/src/assets/images/vent/noSinge.png',
+          defaultPlaybackRate: cameraRate || 1,
+          controls: false,
+          hls: {
+            retryCount: 3, // 重试 3 次,默认值
+            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+            fetchOptions: {
+              // 该参数会透传给 fetch,默认值为 undefined
+              mode: 'cors',
+            },
+            targetLatency: 10, // 直播目标延迟,默认 10 秒
+            maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+            maxJumpDistance: 10,
+          },
+        });
+      } else if (HlsPlugin.isSupported()) {
+        // 第一步
+        player = new Player({
+          lang: 'zh',
+          id: id,
+          url: videoAddr,
+          width: 589,
+          height: 330,
+          isLive: true,
+          autoplay: true,
+          autoplayMuted: true,
+          plugins: [HlsPlugin], // 第二步
+          poster: '/src/assets/images/vent/noSinge.png',
+          ignores: ['time', 'progress', 'play', 'i18n', 'volume', 'fullscreen', 'screenShot', 'playbackRate'],
+          defaultPlaybackRate: cameraRate || 1,
+          controls: false,
+          hls: {
+            retryCount: 3, // 重试 3 次,默认值
+            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+            fetchOptions: {
+              // 该参数会透传给 fetch,默认值为 undefined
+              mode: 'cors',
+            },
+            targetLatency: 10, // 直播目标延迟,默认 10 秒
+            maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+            maxJumpDistance: 10,
+          },
+        });
+      }
+      playerList.value.push(player);
     }
     }
-    playerList.value.push(player);
   }
   }
-}
 
 
-function goFullScreen(domId) {
-  const videoDom = document.getElementById(domId) as HTMLVideoElement;
-  if (videoDom.requestFullscreen) {
-    videoDom.requestFullscreen();
-    videoDom.play();
-  } else if (videoDom.mozRequestFullscreen) {
-    videoDom.mozRequestFullscreen();
-    videoDom.play();
-  } else if (videoDom.webkitRequestFullscreen) {
-    videoDom.webkitRequestFullscreen();
-    videoDom.play();
-  } else if (videoDom.msRequestFullscreen) {
-    videoDom.msRequestFullscreen();
-    videoDom.play();
+  function goFullScreen(domId) {
+    const videoDom = document.getElementById(domId) as HTMLVideoElement;
+    if (videoDom.requestFullscreen) {
+      videoDom.requestFullscreen();
+      videoDom.play();
+    } else if (videoDom.mozRequestFullscreen) {
+      videoDom.mozRequestFullscreen();
+      videoDom.play();
+    } else if (videoDom.webkitRequestFullscreen) {
+      videoDom.webkitRequestFullscreen();
+      videoDom.play();
+    } else if (videoDom.msRequestFullscreen) {
+      videoDom.msRequestFullscreen();
+      videoDom.play();
+    }
   }
   }
-}
 
 
-function clearCamera() {
-  const num = webRtcServerList.length;
-  for (let i = 0; i < num; i++) {
-    if (webRtcServerList[i]) {
-      webRtcServerList[i].disconnect();
-      webRtcServerList[i] = null;
+  function clearCamera() {
+    const num = webRtcServerList.length;
+    for (let i = 0; i < num; i++) {
+      if (webRtcServerList[i]) {
+        webRtcServerList[i].disconnect();
+        webRtcServerList[i] = null;
+      }
     }
     }
+    for (let i = 0; i < playerList.value.length; i++) {
+      const player = playerList.value[i];
+      if (player.destroy) player.destroy();
+    }
+    playerList.value = [];
   }
   }
-  for (let i = 0; i < playerList.value.length; i++) {
-    const player = playerList.value[i];
-    if (player.destroy) player.destroy();
-  }
-  playerList.value = [];
-}
 
 
-onMounted(async () => {
-  await getCameraDevKindList();
-  await getVideoAddrs();
-  getVideo();
-});
+  onMounted(async () => {
+    await getCameraDevKindList();
+    await getVideoAddrs();
+    getVideo();
+  });
 
 
-onUnmounted(() => {
-  clearCamera();
-});
+  onUnmounted(() => {
+    clearCamera();
+  });
 </script>
 </script>
 <style lang="less">
 <style lang="less">
-@import '/@/design/theme.less';
+  @import '/@/design/theme.less';
 
 
-@{theme-deepblue} {
-  .camera-container {
-    --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+  @{theme-deepblue} {
+    .camera-container {
+      --image-camera_bg: url('/@/assets/images/themify/deepblue/vent/camera_bg.png');
+    }
   }
   }
-}
 
 
-.camera-container {
-  --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
-  position: relative;
-  width: calc(100% - 30px);
-  height: calc(100% - 84px);
-  display: flex;
-  margin: 15px;
-  justify-content: space-between;
-  align-items: center;
+  .camera-container {
+    --image-camera_bg: url('/@/assets/images/vent/camera_bg.png');
+    position: relative;
+    width: calc(100% - 30px);
+    height: calc(100% - 84px);
+    display: flex;
+    margin: 15px;
+    justify-content: space-between;
+    align-items: center;
 
 
-  .left-area {
-    width: 15%;
-    height: 100%;
-    padding: 20px;
-    border: 1px solid #99e8ff66;
-    background: #27546e1a;
-    box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
-    -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
-    -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
-    box-sizing: border-box;
+    .left-area {
+      width: 15%;
+      height: 100%;
+      padding: 20px;
+      border: 1px solid #99e8ff66;
+      background: #27546e1a;
+      box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+      -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+      -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
+      box-sizing: border-box;
 
 
-    // lxh
-    .iconfont {
-      color: #fff;
-      font-size: 12px;
-      margin-left: 5px;
+      // lxh
+      .iconfont {
+        color: #fff;
+        font-size: 12px;
+        margin-left: 5px;
+      }
     }
     }
-  }
 
 
-  .right-area {
-    width: 85%;
-    height: 100%;
-    padding: 0px 0px 0px 15px;
-    box-sizing: border-box;
+    .right-area {
+      width: 85%;
+      height: 100%;
+      padding: 0px 0px 0px 15px;
+      box-sizing: border-box;
 
 
-    .camera-box {
-      width: 100%;
-      height: calc(100% - 60px);
-      display: flex;
-      justify-content: space-around;
-      align-items: flex-start;
-      flex-wrap: wrap;
-      overflow-y: auto;
-    }
+      .camera-box {
+        width: 100%;
+        height: calc(100% - 60px);
+        display: flex;
+        justify-content: space-around;
+        align-items: flex-start;
+        flex-wrap: wrap;
+        overflow-y: auto;
+      }
 
 
-    .camera-box1 {
-      width: 100%;
-      height: calc(100% - 60px);
-      display: flex;
-      justify-content: flex-start;
-      align-items: flex-start;
-      flex-wrap: wrap;
-      overflow-y: auto;
-    }
+      .camera-box1 {
+        width: 100%;
+        height: calc(100% - 60px);
+        display: flex;
+        justify-content: flex-start;
+        align-items: flex-start;
+        flex-wrap: wrap;
+        overflow-y: auto;
+      }
 
 
-    .player-box {
-      width: 626px;
-      height: 370px;
-      padding: 17px 18px;
-      background: var(--image-camera_bg);
-      background-size: 100% 100%;
-      position: relative;
-      margin: 10px;
+      .player-box {
+        width: 626px;
+        height: 370px;
+        padding: 17px 18px;
+        background: var(--image-camera_bg);
+        background-size: 100% 100%;
+        position: relative;
+        margin: 10px;
 
 
-      .player-name {
-        font-size: 14px;
-        position: absolute;
-        top: 35px;
-        right: 15px;
-        color: #fff;
-        background-color: hsla(0, 0%, 50%, 0.5);
-        border-radius: 2px;
-        padding: 1px 5px;
-        max-width: 120px;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-        z-index: 999;
+        .player-name {
+          font-size: 14px;
+          position: absolute;
+          top: 35px;
+          right: 15px;
+          color: #fff;
+          background-color: hsla(0, 0%, 50%, 0.5);
+          border-radius: 2px;
+          padding: 1px 5px;
+          max-width: 120px;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          z-index: 999;
+        }
+
+        .click-box {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          top: 0;
+          left: 0;
+        }
       }
       }
 
 
-      .click-box {
-        position: absolute;
+      .pagination {
         width: 100%;
         width: 100%;
-        height: 100%;
-        top: 0;
-        left: 0;
+        height: 60px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
       }
       }
     }
     }
-
-    .pagination {
-      width: 100%;
-      height: 60px;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-    }
   }
   }
-}
 
 
-:deep(video) {
-  width: 100% !important;
-  height: 100% !important;
-  object-fit: cover !important;
-}
+  :deep(video) {
+    width: 100% !important;
+    height: 100% !important;
+    object-fit: cover !important;
+  }
 </style>
 </style>

+ 2 - 0
src/views/vent/monitorManager/comment/comment.api.ts

@@ -1,6 +1,7 @@
 import { defHttp } from '/@/utils/http/axios';
 import { defHttp } from '/@/utils/http/axios';
 
 
 enum Api {
 enum Api {
+  getDevice = '/monitor/device', //实时数据
   edit = '/safety/ventanalyDeviceInfo/edit',
   edit = '/safety/ventanalyDeviceInfo/edit',
   list = '/safety/ventanalyDeviceInfo/list',
   list = '/safety/ventanalyDeviceInfo/list',
   input = '/safety/ventanalyDeviceInfo/input',
   input = '/safety/ventanalyDeviceInfo/input',
@@ -33,3 +34,4 @@ export const getAllFileList = (params) => defHttp.get({ url: Api.getFileList, pa
 export const getGasDeviceInfo = (params) => defHttp.post({ url: Api.getGasDeviceInfo, params });
 export const getGasDeviceInfo = (params) => defHttp.post({ url: Api.getGasDeviceInfo, params });
 export const getHistoryData = (params) => defHttp.post({ url: Api.getHistoryData, params });
 export const getHistoryData = (params) => defHttp.post({ url: Api.getHistoryData, params });
 export const listdays = (params) => defHttp.get({ url: Api.listdays, params });
 export const listdays = (params) => defHttp.get({ url: Api.listdays, params });
+export const getDevice = (params) => defHttp.post({ url: Api.getDevice, params });

+ 8 - 7
src/views/vent/monitorManager/fanLocalMonitor/index.vue

@@ -193,8 +193,8 @@
                                 selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                                 selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                                   ? '无状态'
                                   ? '无状态'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
-                                  ? '正常'
-                                  : '异常'
+                                    ? '正常'
+                                    : '异常'
                               }}</div>
                               }}</div>
                             </div>
                             </div>
                             <div class="signal-item" v-if="warningMonitorRowIndex == 1">
                             <div class="signal-item" v-if="warningMonitorRowIndex == 1">
@@ -212,8 +212,8 @@
                                 selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                                 selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                                   ? '无状态'
                                   ? '无状态'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
-                                  ? '正常'
-                                  : '异常'
+                                    ? '正常'
+                                    : '异常'
                               }}</div>
                               }}</div>
                             </div>
                             </div>
                           </div>
                           </div>
@@ -256,7 +256,7 @@
               ref="MonitorDataTable"
               ref="MonitorDataTable"
               :dataSource="dataSource"
               :dataSource="dataSource"
               :columnsType="`${selectData.deviceType}_monitor`"
               :columnsType="`${selectData.deviceType}_monitor`"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               :scroll="scroll"
               :scroll="scroll"
               :is-action="true"
               :is-action="true"
             >
             >
@@ -1952,9 +1952,10 @@
     justify-content: flex-end;
     justify-content: flex-end;
     padding-right: 380px;
     padding-right: 380px;
     pointer-events: none;
     pointer-events: none;
-
+    .liveVideo {
+      align-self: auto !important;
+    }
     .video-parent {
     .video-parent {
-      height: 208px;
       pointer-events: auto !important;
       pointer-events: auto !important;
     }
     }
   }
   }

+ 3 - 3
src/views/vent/monitorManager/fireDoorMonitor/index.vue

@@ -66,7 +66,7 @@
               :isShowActionColumn="true"
               :isShowActionColumn="true"
               :dataSource="dataSource"
               :dataSource="dataSource"
               design-scope="gate-monitor"
               design-scope="gate-monitor"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               :scroll="{ y: scroll.y - 40 }"
               :scroll="{ y: scroll.y - 40 }"
               title="风门监测"
               title="风门监测"
               :isShowPagination="true"
               :isShowPagination="true"
@@ -252,8 +252,8 @@
   const MonitorDataTable = ref();
   const MonitorDataTable = ref();
   let contrlValue = '';
   let contrlValue = '';
   const playerRef = ref();
   const playerRef = ref();
-  // const deviceType = ref('door');
-  const deviceType = ref('gate');
+  const deviceType = ref('door');
+  // const deviceType = ref('gate');
   // const deviceType = ref('firedoor');
   // const deviceType = ref('firedoor');
   const activeKey = ref('1'); // tab
   const activeKey = ref('1'); // tab
   const loading = ref(false);
   const loading = ref(false);

+ 353 - 0
src/views/vent/monitorManager/gateMonitor/dandaoFcBd3.threejs.ts

@@ -0,0 +1,353 @@
+import * as THREE from 'three';
+
+import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
+import gsap from 'gsap';
+
+class ddFc_7 {
+  model;
+  modelName = 'ddFcGroup';
+  group: THREE.Object3D = new THREE.Object3D();
+  animationTimer;
+  isLRAnimation = true;
+  direction = 1;
+  windowsActionArr = {
+    frontWindow: [],
+    backWindow: [],
+  };
+  player1;
+  player2;
+  playerStartClickTime1 = new Date().getTime();
+  mixers: THREE.AnimationMixer | undefined;
+  frontClipAction;
+  fmClock = new THREE.Clock();
+  constructor(model) {
+    this.model = model;
+    this.group.name = 'ddFcGroup';
+  }
+  addLight = () => {
+    if (!this.group || !this.group) return;
+
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
+    directionalLight.position.set(-437, 61, 559);
+    this.group.add(directionalLight);
+
+    const pointLight2 = new THREE.PointLight(0xffffff, 1, 150);
+    pointLight2.position.set(-101, 34, 16);
+    pointLight2.shadow.bias = 0.05;
+    this.group.add(pointLight2);
+
+    const pointLight3 = new THREE.PointLight(0xffffff, 1, 150);
+    pointLight3.position.set(19, 25, -7);
+    pointLight3.shadow.bias = 0.05;
+    this.group.add(pointLight3);
+
+    const pointLight6 = new THREE.PointLight(0xffffff, 1, 300);
+    pointLight6.position.set(51, 51, 9);
+    pointLight6.shadow.bias = 0.05;
+    this.group.add(pointLight6);
+  };
+
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-35, 25, 15);
+  }
+
+  addMonitorText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    const screenDownText = VENT_PARAM['modalText']
+      ? VENT_PARAM['modalText']
+      : History_Type['type'] == 'remote'
+        ? `国能神东煤炭集团监制`
+        : '煤科通安(北京)智控科技有限公司研制';
+
+    const screenDownTextX = 90 - (screenDownText.length - 10) * 7;
+    const textArr = [
+      {
+        text: `远程定量调节自动风门`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 100,
+        y: 95,
+      },
+      {
+        text: `${selectData.OpenDegree ? '开度值(°)' : selectData.forntArea ? '过风面积(㎡)' : '过风面积(㎡)'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 145,
+      },
+      {
+        text: selectData.OpenDegree
+          ? Number(`${selectData.OpenDegree}`).toFixed(2)
+          : selectData.forntArea
+            ? Number(`${selectData.forntArea}`).toFixed(2)
+            : '-',
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 145,
+      },
+      {
+        text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态:'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 200,
+      },
+      {
+        text: `${
+          selectData.frontRearDP
+            ? selectData.frontRearDP
+            : selectData.windSpeed
+              ? selectData.windSpeed
+              : selectData.netStatus == '0'
+                ? '断开'
+                : '连接'
+        }`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 200,
+      },
+      {
+        text: `${selectData.fWindowM3 ? '过风量(m³/min)' : '风窗道数'}: `,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 250,
+      },
+      {
+        text: `${selectData.fWindowM3 ? selectData.fWindowM3 : selectData.nwindownum}`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 250,
+      },
+      {
+        text: screenDownText,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: screenDownTextX,
+        y: 300,
+      },
+    ];
+    getTextCanvas(750, 546, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.DoubleSide, // 这里是双面渲染的意思
+      });
+      textMap.dispose();
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group?.getObjectByName('monitorText');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.003, 0.0034, 0.004);
+        planeMesh.position.set(4.25, 0.41, -0.27);
+        this.group?.add(planeMesh);
+      }
+    });
+  }
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    const modalGroup = this.group?.getObjectByName('ddFc-bd3');
+    // 初始化窗得动画
+    const meshArr01: THREE.Object3D[] = [];
+    const meshArr02: THREE.Object3D[] = [];
+    const fmGroup = modalGroup?.getObjectByName('FengMen_ShouDong_1')?.getObjectByName('FengMen_1');
+    const fcGroup1 = fmGroup?.getObjectByName('Men_1');
+    const fcGroup2 = fmGroup?.getObjectByName('Men_2');
+    if (fcGroup1 && fcGroup2) {
+      fcGroup1.getObjectByName('shanye_1')?.children.filter((obj) => {
+        if (obj.name.includes('shanye_1')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr01.push(obj);
+        }
+      });
+      fcGroup1.getObjectByName('shanye_2')?.children.filter((obj) => {
+        if (obj.name.includes('shanye_2')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr01.push(obj);
+        }
+      });
+      fcGroup2.children.filter((obj) => {
+        if (obj.name.includes('shanye_3')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr02.push(obj);
+        }
+      });
+      fcGroup2.getObjectByName('shanye_4')?.children.filter((obj) => {
+        if (obj.name.includes('shanye_4')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr02.push(obj);
+        }
+      });
+      this.windowsActionArr.frontWindow = [...meshArr01];
+      this.windowsActionArr.backWindow = [...meshArr02];
+    }
+    // 初始化门的动画
+    if (modalGroup && fmGroup) {
+      const tracks = modalGroup.animations[0].tracks;
+      const fontTracks: any[] = [];
+      for (let i = 0; i < tracks.length; i++) {
+        const track = tracks[i];
+        if (track.name.startsWith('Men')) {
+          fontTracks.push(track);
+        }
+      }
+      this.mixers = new THREE.AnimationMixer(fmGroup);
+      const frontDoor = new THREE.AnimationClip('frontDoor', 15, fontTracks);
+      this.frontClipAction = this.mixers.clipAction(frontDoor, fmGroup);
+      this.frontClipAction.clampWhenFinished = true;
+      // this.frontClipAction.reset();
+      // this.frontClipAction.time = 0;
+      // this.frontClipAction.timeScale = 1;
+      // this.frontClipAction.clampWhenFinished = true;
+      // this.frontClipAction.loop = THREE.LoopOnce;
+      // this.frontClipAction.play();
+    }
+  }
+
+  // 播放动画
+  play(handlerState, timeScale = 0.01) {
+    let handler = () => {};
+    if (this.frontClipAction) {
+      switch (handlerState) {
+        case 1: // 打开前门
+          handler = () => {
+            this.frontClipAction.paused = true;
+            this.frontClipAction.reset();
+            this.frontClipAction.time = 0;
+            this.frontClipAction.timeScale = timeScale;
+            this.frontClipAction.play();
+            this.fmClock.start();
+          };
+          break;
+        case 2: // 关闭前门
+          handler = () => {
+            this.frontClipAction.paused = true;
+            this.frontClipAction.reset(); //
+            this.frontClipAction.time = 2.5;
+            this.frontClipAction.timeScale = -timeScale;
+            this.frontClipAction.play();
+            this.fmClock.start();
+          };
+          break;
+        default:
+      }
+      handler();
+    }
+  }
+
+  playWindow(rotationParam, flag) {
+    if (!this.windowsActionArr.frontWindow) {
+      return;
+    }
+    if (flag === 1) {
+      // 前风窗动画
+      this.windowsActionArr.frontWindow.forEach((mesh: THREE.Mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.z),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 2) {
+      // 后风窗动画
+      this.windowsActionArr.backWindow.forEach((mesh: THREE.Mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(rotationParam.backDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.backDeg1 - mesh.rotation.z),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 0) {
+      ([...this.windowsActionArr.frontWindow] as THREE.Mesh[]).forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(90),
+          overwrite: true,
+        });
+      });
+    }
+  }
+
+  /* 点击风窗,风窗全屏 */
+  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
+    if (this.animationTimer) {
+      clearTimeout(this.animationTimer);
+      this.animationTimer = null;
+    }
+    // 判断是否点击到视频
+    intersects.find((intersect) => {
+      const mesh = intersect.object;
+      if (mesh.name === 'player1') {
+        if (new Date().getTime() - this.playerStartClickTime1 < 400) {
+          // 双击,视频放大
+          if (this.player1) {
+            this.player1.requestFullscreen();
+          }
+        }
+        this.playerStartClickTime1 = new Date().getTime();
+        return true;
+      }
+      return false;
+    });
+  }
+
+  mouseUpModel() {}
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.mixers && this.fmClock.running) {
+      this.mixers.update(2);
+    }
+  }
+
+  mountedThree() {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel(['ddFc-bd3'], this.group).then(() => {
+        console.log(this.group);
+        this.setModalPosition();
+        this.initAnimation();
+        resolve(null);
+      });
+    });
+  }
+
+  destroy() {
+    if (this.mixers) {
+      const modalGroup = this.group?.getObjectByName('ddFc-bd3');
+      const fmGroup = modalGroup?.getObjectByName('FengMen_ShouDong_1')?.getObjectByName('FengMen_1');
+      this.mixers.uncacheClip(this.frontClipAction.getClip());
+      this.mixers.uncacheAction(this.frontClipAction.getClip(), fmGroup);
+      if (fmGroup) this.mixers.uncacheRoot(fmGroup);
+      if (this.model.animations[0]) this.model.animations[0].tracks = [];
+    }
+    this.model.clearGroup(this.group);
+    this.frontClipAction = undefined;
+    this.model = null;
+    this.group = null;
+  }
+}
+export default ddFc_7;

+ 343 - 0
src/views/vent/monitorManager/gateMonitor/gate.threejs.tj.ssl.ts

@@ -0,0 +1,343 @@
+import * as THREE from 'three';
+
+import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
+import gsap from 'gsap';
+
+class ddFmSsl {
+  model;
+  modelName = 'fm-fc-ssl';
+  group: THREE.Object3D = new THREE.Object3D();
+  animationTimer;
+  isLRAnimation = true;
+  direction = 1;
+  windowsActionArr = {
+    frontWindow: [],
+    backWindow: [],
+  };
+  player1;
+  player2;
+  playerStartClickTime1 = new Date().getTime();
+  mixers: THREE.AnimationMixer | undefined;
+  frontClipAction;
+  fmClock = new THREE.Clock();
+  constructor(model) {
+    this.model = model;
+    this.group.name = 'fm-fc-ssl';
+  }
+  addLight = () => {
+    if (!this.group || !this.group) return;
+
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
+    directionalLight.position.set(-437, 61, 559);
+    this.group.add(directionalLight);
+
+    const pointLight2 = new THREE.PointLight(0xffffff, 1, 150);
+    pointLight2.position.set(-101, 34, 16);
+    pointLight2.shadow.bias = 0.05;
+    this.group.add(pointLight2);
+
+    const pointLight3 = new THREE.PointLight(0xffffff, 1, 150);
+    pointLight3.position.set(19, 25, -7);
+    pointLight3.shadow.bias = 0.05;
+    this.group.add(pointLight3);
+
+    const pointLight6 = new THREE.PointLight(0xffffff, 1, 300);
+    pointLight6.position.set(51, 51, 9);
+    pointLight6.shadow.bias = 0.05;
+    this.group.add(pointLight6);
+  };
+
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-35, 25, 15);
+  }
+
+  addMonitorText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    const screenDownText = VENT_PARAM['modalText']
+      ? VENT_PARAM['modalText']
+      : History_Type['type'] == 'remote'
+        ? `国能神东煤炭集团监制`
+        : '煤科通安(北京)智控科技有限公司研制';
+
+    const screenDownTextX = 90 - (screenDownText.length - 10) * 7;
+    const textArr = [
+      {
+        text: `远程定量调节自动风门`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 100,
+        y: 95,
+      },
+      {
+        text: `${selectData.OpenDegree ? '开度值(°)' : selectData.frontArea ? '过风面积(㎡)' : '过风面积(㎡)'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 145,
+      },
+      {
+        text: selectData.OpenDegree
+          ? Number(`${selectData.OpenDegree}`).toFixed(2)
+          : selectData.frontArea
+            ? Number(`${selectData.frontArea}`).toFixed(2)
+            : '-',
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 145,
+      },
+      {
+        text: `${selectData.frontRearDP ? '风窗压差(Pa)' : selectData.windSpeed ? '风速(m/s)' : '通信状态:'}:`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 200,
+      },
+      {
+        text: `${
+          selectData.frontRearDP
+            ? selectData.frontRearDP
+            : selectData.windSpeed
+              ? selectData.windSpeed
+              : selectData.netStatus == '0'
+                ? '断开'
+                : '连接'
+        }`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 200,
+      },
+      {
+        text: `${selectData.fWindowM3 ? '过风量(m³/min)' : '风窗道数'}: `,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 5,
+        y: 250,
+      },
+      {
+        text: `${selectData.fWindowM3 ? selectData.fWindowM3 : selectData.nwindownum}`,
+        font: 'normal 30px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: 330,
+        y: 250,
+      },
+      {
+        text: screenDownText,
+        font: 'normal 28px Arial',
+        color: '#009900',
+        strokeStyle: '#002200',
+        x: screenDownTextX,
+        y: 300,
+      },
+    ];
+    getTextCanvas(750, 546, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.DoubleSide, // 这里是双面渲染的意思
+      });
+      textMap.dispose();
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group?.getObjectByName('monitorText');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.003, 0.0034, 0.004);
+        planeMesh.position.set(4.25, 0.41, -0.27);
+        this.group?.add(planeMesh);
+      }
+    });
+  }
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    const modalGroup = this.group?.getObjectByName('fm-fc-ssl');
+    // 初始化窗得动画
+    const meshArr01: THREE.Object3D[] = [];
+    const meshArr02: THREE.Object3D[] = [];
+    const fmGroup = modalGroup?.getObjectByName('FengMen_DaiChuang');
+    const fcGroup1 = fmGroup?.getObjectByName('Men_1');
+    const fcGroup2 = fmGroup?.getObjectByName('Men_2');
+    if (fcGroup1 && fcGroup2) {
+      fcGroup1.getObjectByName('shanye_1')?.children.filter((obj) => {
+        if (obj.name.includes('shanye_1')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr01.push(obj);
+        }
+      });
+      fcGroup2.getObjectByName('shanye_3')?.children.filter((obj) => {
+        if (obj.name.includes('shanye_1')) {
+          obj.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0);
+          meshArr02.push(obj);
+        }
+      });
+
+      this.windowsActionArr.frontWindow = [...meshArr01];
+      this.windowsActionArr.backWindow = [...meshArr02];
+    }
+    // 初始化门的动画
+    if (modalGroup && fmGroup) {
+      const tracks = modalGroup.animations[0].tracks;
+      const fontTracks: any[] = [];
+      for (let i = 0; i < tracks.length; i++) {
+        const track = tracks[i];
+        if (track.name.startsWith('Men')) {
+          fontTracks.push(track);
+        }
+      }
+      this.mixers = new THREE.AnimationMixer(fmGroup);
+      const frontDoor = new THREE.AnimationClip('frontDoor', 15, fontTracks);
+      this.frontClipAction = this.mixers.clipAction(frontDoor, fmGroup);
+      this.frontClipAction.clampWhenFinished = true;
+      // this.frontClipAction.reset();
+      // this.frontClipAction.time = 0;
+      // this.frontClipAction.timeScale = 1;
+      // this.frontClipAction.clampWhenFinished = true;
+      // this.frontClipAction.loop = THREE.LoopOnce;
+      // this.frontClipAction.play();
+    }
+  }
+
+  // 播放动画
+  play(handlerState, timeScale = 0.01) {
+    let handler = () => {};
+    if (this.frontClipAction) {
+      switch (handlerState) {
+        case 1: // 打开前门
+          handler = () => {
+            this.frontClipAction.paused = true;
+            this.frontClipAction.reset();
+            this.frontClipAction.time = 0;
+            this.frontClipAction.timeScale = timeScale;
+            this.frontClipAction.play();
+            this.fmClock.start();
+          };
+          break;
+        case 2: // 关闭前门
+          handler = () => {
+            this.frontClipAction.paused = true;
+            this.frontClipAction.reset(); //
+            this.frontClipAction.time = 2.5;
+            this.frontClipAction.timeScale = -timeScale;
+            this.frontClipAction.play();
+            this.fmClock.start();
+          };
+          break;
+        default:
+      }
+      handler();
+    }
+  }
+
+  playWindow(rotationParam, flag) {
+    if (!this.windowsActionArr.frontWindow) {
+      return;
+    }
+    if (flag === 1) {
+      // 前风窗动画
+      this.windowsActionArr.frontWindow.forEach((mesh: THREE.Mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(rotationParam.frontDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.frontDeg1 - mesh.rotation.z),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 2) {
+      // 后风窗动画
+      this.windowsActionArr.backWindow.forEach((mesh: THREE.Mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(rotationParam.backDeg1),
+          duration: (1 / 9) * Math.abs(rotationParam.backDeg1 - mesh.rotation.z),
+          overwrite: true,
+        });
+      });
+    } else if (flag === 0) {
+      ([...this.windowsActionArr.frontWindow] as THREE.Mesh[]).forEach((mesh) => {
+        gsap.to(mesh.rotation, {
+          z: THREE.MathUtils.degToRad(90),
+          overwrite: true,
+        });
+      });
+    }
+  }
+
+  /* 点击风窗,风窗全屏 */
+  mousedownModel(intersects: THREE.Intersection<THREE.Object3D<THREE.Event>>[]) {
+    if (this.animationTimer) {
+      clearTimeout(this.animationTimer);
+      this.animationTimer = null;
+    }
+    // 判断是否点击到视频
+    intersects.find((intersect) => {
+      const mesh = intersect.object;
+      if (mesh.name === 'player1') {
+        if (new Date().getTime() - this.playerStartClickTime1 < 400) {
+          // 双击,视频放大
+          if (this.player1) {
+            this.player1.requestFullscreen();
+          }
+        }
+        this.playerStartClickTime1 = new Date().getTime();
+        return true;
+      }
+      return false;
+    });
+  }
+
+  mouseUpModel() {}
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+    if (this.mixers && this.fmClock.running) {
+      this.mixers.update(2);
+    }
+  }
+
+  mountedThree() {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel('fm-fc-ssl').then((gltf) => {
+        this.group = gltf[0];
+        this.group.name = 'fm-fc-ssl';
+        this.setModalPosition();
+        this.initAnimation();
+        resolve(null);
+      });
+    });
+  }
+
+  destroy() {
+    if (this.mixers) {
+      const modalGroup = this.group?.getObjectByName('fm-fc-ssl');
+      const fmGroup = modalGroup?.getObjectByName('FengMen_ShouDong_1')?.getObjectByName('FengMen_1');
+      this.mixers.uncacheClip(this.frontClipAction.getClip());
+      this.mixers.uncacheAction(this.frontClipAction.getClip(), fmGroup);
+      if (fmGroup) this.mixers.uncacheRoot(fmGroup);
+      if (this.model.animations[0]) this.model.animations[0].tracks = [];
+    }
+    this.model.clearGroup(this.group);
+    this.frontClipAction = undefined;
+    this.model = null;
+    this.group = null;
+  }
+}
+export default ddFmSsl;

+ 147 - 0
src/views/vent/monitorManager/gateMonitor/gate.threejs.ts

@@ -33,6 +33,9 @@ let model,
   fmYjXr, // 窑街拱形行人风门
   fmYjXr, // 窑街拱形行人风门
   fmYj, // 窑街拱形行车风门
   fmYj, // 窑街拱形行车风门
   fmSp1, // 沙坪一道风门
   fmSp1, // 沙坪一道风门
+  fmDcBd, // 带风窗
+  fmSsl, // 思山岭双道蓝色风门
+  fm_fc_ssl, // 带风窗
   group: THREE.Object3D,
   group: THREE.Object3D,
   fmType = '',
   fmType = '',
   windowType = 'singleWindow';
   windowType = 'singleWindow';
@@ -46,6 +49,10 @@ const rotationParam = {
   frontRightDeg1: 0, // 前门目标
   frontRightDeg1: 0, // 前门目标
   backRightDeg0: 0, // 后门初始
   backRightDeg0: 0, // 后门初始
   backRightDeg1: 0, // 后门目标
   backRightDeg1: 0, // 后门目标
+  frontDeg0: 0,
+  frontDeg1: 0,
+  backDeg0: 0,
+  backDeg1: 0,
 };
 };
 
 
 const { mouseDownFn } = useEvent();
 const { mouseDownFn } = useEvent();
@@ -84,6 +91,12 @@ const startAnimation = () => {
       fmYj.mouseUpModel();
       fmYj.mouseUpModel();
     } else if (fmType === 'fmSp1') {
     } else if (fmType === 'fmSp1') {
       fmSp1.mouseUpModel();
       fmSp1.mouseUpModel();
+    } else if (fmType === 'fm_fc_bd') {
+      fmDcBd.mouseUpModel();
+    } else if (fmType === 'fmSsl') {
+      fmSsl.mouseUpModel();
+    } else if (fmType === 'fm_fc_ssl') {
+      fm_fc_ssl.mouseUpModel();
     }
     }
   });
   });
 };
 };
@@ -119,6 +132,12 @@ const mouseEvent = (event) => {
         fmYj.mousedownModel(intersects);
         fmYj.mousedownModel(intersects);
       } else if (fmType === 'fmSp1') {
       } else if (fmType === 'fmSp1') {
         fmSp1.mousedownModel(intersects);
         fmSp1.mousedownModel(intersects);
+      } else if (fmType === 'fm_fc_bd' && fmDcBd) {
+        fmDcBd.mousedownModel(intersects);
+      } else if (fmType === 'fmSsl' && fmSsl) {
+        fmSsl.mousedownModel(intersects);
+      } else if (fmType === 'fm_fc_ssl' && fm_fc_ssl) {
+        fm_fc_ssl.mousedownModel(intersects);
       }
       }
     });
     });
     console.log('摄像头控制信息', model.orbitControls, model.camera);
     console.log('摄像头控制信息', model.orbitControls, model.camera);
@@ -153,6 +172,12 @@ export const addMonitorText = (selectData) => {
     fmYj.addMonitorText(selectData);
     fmYj.addMonitorText(selectData);
   } else if (fmType === 'fmSp1' && fmSp1) {
   } else if (fmType === 'fmSp1' && fmSp1) {
     fmSp1.addMonitorText(selectData);
     fmSp1.addMonitorText(selectData);
+  } else if (fmType === 'fm_fc_bd' && fmDcBd) {
+    return fmDcBd.addMonitorText(selectData);
+  } else if (fmType === 'fmSsl' && fmSsl) {
+    return fmSsl.addMonitorText.call(fmSsl, selectData);
+  } else if (fmType === 'fm_fc_ssl' && fm_fc_ssl) {
+    return fm_fc_ssl.addMonitorText.call(fm_fc_ssl, selectData);
   }
   }
 };
 };
 
 
@@ -197,6 +222,12 @@ export const play = (handlerState, flag?) => {
     return fmYj?.play.call(fmYj, handlerState, flag);
     return fmYj?.play.call(fmYj, handlerState, flag);
   } else if (fmType === 'fmSp1') {
   } else if (fmType === 'fmSp1') {
     return fmSp1?.play(handlerState, flag);
     return fmSp1?.play(handlerState, flag);
+  } else if (fmType === 'fm_fc_bd' && fmDcBd) {
+    return fmDcBd.play.call(fmDcBd, handlerState, flag);
+  } else if (fmType === 'fmSsl' && fmSsl) {
+    return fmSsl.play.call(fmSsl, handlerState, flag);
+  } else if (fmType === 'fm_fc_ssl' && fm_fc_ssl) {
+    return fm_fc_ssl.play.call(fm_fc_ssl, handlerState, flag);
   }
   }
 };
 };
 
 
@@ -257,6 +288,21 @@ export function computePlay(data, maxarea, isFirst = false) {
     fmWindow.playWindow(rotationParam, 2);
     fmWindow.playWindow(rotationParam, 2);
     fmWindow.playWindow(rotationParam, 3);
     fmWindow.playWindow(rotationParam, 3);
     fmWindow.playWindow(rotationParam, 4);
     fmWindow.playWindow(rotationParam, 4);
+  } else if (maxarea && fmType === 'fm_fc_bd') {
+    // maxarea = 90;
+    rotationParam.frontDeg0 = (90 / Number(maxarea)) * Number(isFirst ? 0 : data.frontArea);
+    rotationParam.backDeg0 = (90 / Number(maxarea)) * Number(isFirst ? 0 : data.rearArea);
+    rotationParam.frontDeg1 = (90 / Number(maxarea)) * Number(data.frontArea) || 0;
+    rotationParam.backDeg1 = (90 / Number(maxarea)) * Number(data.rearArea) || 0;
+    fmDcBd.playWindow(rotationParam, 1);
+    fmDcBd.playWindow(rotationParam, 2);
+  } else if (fmType === 'fm_fc_ssl') {
+    rotationParam.frontDeg0 = Number(isFirst ? 0 : data.frontPresentValue);
+    rotationParam.backDeg0 = Number(isFirst ? 0 : data.rearPresentValue);
+    rotationParam.frontDeg1 = Number(data.frontPresentValue) || 0;
+    rotationParam.backDeg1 = Number(data.rearPresentValue) || 0;
+    fm_fc_ssl.playWindow(rotationParam, 1);
+    fm_fc_ssl.playWindow(rotationParam, 2);
   }
   }
 }
 }
 
 
@@ -409,6 +455,56 @@ export const setModelType = (type) => {
         model.scene.add(fmWindowZhq.group);
         model.scene.add(fmWindowZhq.group);
         const position = { x: -2.28, y: -0.91, z: -5.68 };
         const position = { x: -2.28, y: -0.91, z: -5.68 };
 
 
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fm_fc_bd' && fmDcBd && fmDcBd.group) {
+      if (fmDcBd.frontClipAction) {
+        fmDcBd.frontClipAction.reset();
+        fmDcBd.frontClipAction.time = 0.5;
+        fmDcBd.frontClipAction.stop();
+      }
+
+      model.startAnimation = fmDcBd.render.bind(fmDcBd);
+      model.scene.remove(group);
+      group = fmDcBd.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmDcBd.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
+        await animateCamera(
+          oldCameraPosition,
+          { x: -2.27, y: -0.91, z: -5.67 },
+          { x: 66.257, y: 57.539, z: 94.313 },
+          { x: position.x, y: position.y, z: position.z },
+          model,
+          0.6
+        );
+      }, 300);
+    } else if (fmType === 'fm_fc_ssl' && fm_fc_ssl && fm_fc_ssl.group) {
+      if (fm_fc_ssl.frontClipAction) {
+        fm_fc_ssl.frontClipAction.reset();
+        fm_fc_ssl.frontClipAction.time = 0.5;
+        fm_fc_ssl.frontClipAction.stop();
+      }
+
+      model.startAnimation = fm_fc_ssl.render.bind(fm_fc_ssl);
+      model.scene.remove(group);
+      group = fm_fc_ssl.group;
+      const oldCameraPosition = { x: -761, y: 569, z: 871 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fm_fc_ssl.group);
+        const position = { x: -2.28, y: -0.91, z: -5.68 };
+
         await animateCamera(
         await animateCamera(
           oldCameraPosition,
           oldCameraPosition,
           { x: -2.27, y: -0.91, z: -5.67 },
           { x: -2.27, y: -0.91, z: -5.67 },
@@ -682,6 +778,35 @@ export const setModelType = (type) => {
           0.8
           0.8
         );
         );
       }, 300);
       }, 300);
+    } else if (fmType === 'fmSsl' && fmSsl && fmSsl.group) {
+      if (fmSsl.clipActionArr.frontDoor) {
+        fmSsl.clipActionArr.frontDoor.reset();
+        fmSsl.clipActionArr.frontDoor.time = 0.5;
+        fmSsl.clipActionArr.frontDoor.stop();
+      }
+      if (fmSsl.clipActionArr.backDoor) {
+        fmSsl.clipActionArr.backDoor.reset();
+        fmSsl.clipActionArr.backDoor.time = 0.5;
+        fmSsl.clipActionArr.backDoor.stop();
+      }
+      model.startAnimation = fmSsl.render.bind(fmSsl);
+      model.scene.remove(group);
+      group = fmSsl.group;
+      group.rotation.y = 0;
+
+      const oldCameraPosition = { x: -1000, y: 100, z: 500 };
+      setTimeout(async () => {
+        resolve(null);
+        model.scene.add(fmSsl.group);
+        await animateCamera(
+          oldCameraPosition,
+          { x: 0, y: 0, z: 0 },
+          { x: 50.99, y: 69.32, z: 93.61 },
+          { x: -10.04, y: -14.38, z: -31.4 },
+          model,
+          0.8
+        );
+      }, 300);
     }
     }
   });
   });
 };
 };
@@ -713,6 +838,9 @@ const loadModel = (code): Promise<any> => {
   if (code === 'FmYjXr') return import('./gate.threejs.two.yj').then((r) => r.default); // 姚街行人
   if (code === 'FmYjXr') return import('./gate.threejs.two.yj').then((r) => r.default); // 姚街行人
   if (code === 'FmYj') return import('./gate.threejs.yj').then((r) => r.default); // 姚街
   if (code === 'FmYj') return import('./gate.threejs.yj').then((r) => r.default); // 姚街
   if (code === 'FmSp1') return import('./gate.threejs.one.sp').then((r) => r.default);
   if (code === 'FmSp1') return import('./gate.threejs.one.sp').then((r) => r.default);
+  if (code === 'fmDcBd') return import('./dandaoFcBd3.threejs').then((r) => r.default);
+  if (code === 'fmSsl') return import('./gate.threejs.two.ssl').then((r) => r.default);
+  if (code === 'fm_fc_ssl') return import('./gate.threejs.tj.ssl').then((r) => r.default);
   return import('./gate.threejs.yy').then((r) => r.default);
   return import('./gate.threejs.yy').then((r) => r.default);
 };
 };
 
 
@@ -794,6 +922,21 @@ export const mountedThree = (playerDom) => {
             fmSp1 = new FmSp1(model);
             fmSp1 = new FmSp1(model);
             await fmSp1.mountedThree();
             await fmSp1.mountedThree();
             break;
             break;
+          case 'fm_fc_bd':
+            const FmDcBd = await loadModel('fmDcBd');
+            fmDcBd = new FmDcBd(model);
+            await fmDcBd.mountedThree();
+            break;
+          case 'fmSsl':
+            const FmSsl = await loadModel('fmSsl');
+            fmSsl = new FmSsl(model);
+            await fmSsl.mountedThree();
+            break;
+          case 'fm_fc_ssl':
+            const Fm_fc_ssl = await loadModel('fm_fc_ssl');
+            fm_fc_ssl = new Fm_fc_ssl(model);
+            await fm_fc_ssl.mountedThree();
+            break;
         }
         }
       }
       }
       resolve(null);
       resolve(null);
@@ -842,6 +985,8 @@ export const destroy = () => {
     if (fmYjXr) fmYjXr.destroy();
     if (fmYjXr) fmYjXr.destroy();
     if (fmYj) fmYj.destroy();
     if (fmYj) fmYj.destroy();
     if (fmSp1) fmSp1.destroy();
     if (fmSp1) fmSp1.destroy();
+    if (fmDcBd) fmDcBd.destroy();
+    if (fm_fc_ssl) fm_fc_ssl.destroy();
     fm1 = null;
     fm1 = null;
     fm2 = null;
     fm2 = null;
     fm3 = null;
     fm3 = null;
@@ -855,6 +1000,8 @@ export const destroy = () => {
     fmYjXr = null;
     fmYjXr = null;
     fmYj = null;
     fmYj = null;
     fmSp1 = null;
     fmSp1 = null;
+    fmDcBd = null;
+    fm_fc_ssl = null;
     group = null;
     group = null;
     model.mixers = [];
     model.mixers = [];
     model.destroy();
     model.destroy();

+ 353 - 0
src/views/vent/monitorManager/gateMonitor/gate.threejs.two.ssl.ts

@@ -0,0 +1,353 @@
+import * as THREE from 'three';
+import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
+import { getTextCanvas, renderVideo } from '/@/utils/threejs/util';
+import { drawHot } from '/@/utils/threejs/util';
+import { useAppStore } from '/@/store/modules/app';
+
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class fmSsl {
+  modelName = 'fmssl';
+  model; //
+  group;
+  isLRAnimation = true; // 是否开启左右摇摆动画
+  direction = 1; // 摇摆方向
+  animationTimer: NodeJS.Timeout | null = null; // 摇摆开启定时器
+  player1;
+  player2;
+  deviceDetailCSS3D;
+  playerStartClickTime1 = new Date().getTime();
+  playerStartClickTime2 = new Date().getTime();
+
+  fmClock = new THREE.Clock();
+  mixers: THREE.AnimationMixer | undefined;
+  appStore = useAppStore();
+
+  backDamperOpenMesh;
+  backDamperClosedMesh;
+  frontDamperOpenMesh;
+  frontDamperClosedMesh;
+
+  clipActionArr = {
+    frontDoor: null as unknown as THREE.AnimationAction,
+    backDoor: null as unknown as THREE.AnimationAction,
+  };
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
+    directionalLight.position.set(344, 690, 344);
+    this.group?.add(directionalLight);
+    directionalLight.target = this.group as THREE.Object3D;
+
+    const pointLight2 = new THREE.PointLight(0xffeeee, 1, 300);
+    pointLight2.position.set(-4, 10, 1.8);
+    pointLight2.shadow.bias = 0.05;
+    this.group?.add(pointLight2);
+
+    const pointLight3 = new THREE.PointLight(0xffeeee, 1, 200);
+    pointLight3.position.set(-0.5, -0.5, 0.75);
+    pointLight3.shadow.bias = 0.05;
+    this.group?.add(pointLight3);
+  }
+  // 重置摄像头
+  resetCamera() {
+    this.model.camera.far = 274;
+    this.model.orbitControls?.update();
+    this.model.camera.updateProjectionMatrix();
+  }
+  // 设置模型位置
+  setModalPosition() {
+    this.group?.scale.set(22, 22, 22);
+    this.group?.position.set(-20, 20, 9);
+  }
+
+  /* 添加监控数据 */
+  addMonitorText(selectData) {
+    if (!this.group) {
+      return;
+    }
+    const screenDownText = VENT_PARAM['modalText']
+      ? VENT_PARAM['modalText']
+      : History_Type['type'] == 'remote'
+        ? `国能神东煤炭集团监制`
+        : '煤科通安(北京)智控科技有限公司研制';
+
+    const screenDownTextX = 90 - (screenDownText.length - 11) * 6;
+    const textArr = [
+      {
+        text: `远程控制自动风门`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 120,
+        y: 100,
+      },
+      {
+        text: `净通行高度(m):`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 0,
+        y: 155,
+      },
+      {
+        text: `${selectData.fclearheight ? selectData.fclearheight : '-'}`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 330,
+        y: 155,
+      },
+      {
+        text: `净通行宽度(m): `,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 0,
+        y: 215,
+      },
+      {
+        text: ` ${selectData.fclearwidth ? selectData.fclearwidth : '-'}`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 320,
+        y: 215,
+      },
+      {
+        text: `故障诊断:`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 0,
+        y: 275,
+      },
+      {
+        text: `${selectData.warnLevel_str ? selectData.warnLevel_str : '-'}`,
+        font: 'normal 30px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: 320,
+        y: 275,
+      },
+      {
+        text: screenDownText,
+        font: 'normal 28px Arial',
+        color: '#00FF00',
+        strokeStyle: '#007400',
+        x: screenDownTextX,
+        y: 325,
+      },
+    ];
+
+    //
+    getTextCanvas(526, 346, textArr, '').then((canvas: HTMLCanvasElement) => {
+      const textMap = new THREE.CanvasTexture(canvas); // 关键一步
+      textMap.colorSpace = THREE.SRGBColorSpace;
+      const textMaterial = new THREE.MeshBasicMaterial({
+        // 关于材质并未讲解 实操即可熟悉                 这里是漫反射类似纸张的材质,对应的就有高光类似金属的材质.
+        map: textMap, // 设置纹理贴图
+        transparent: true,
+        side: THREE.FrontSide, // 这里是双面渲染的意思
+      });
+      textMaterial.blending = THREE.CustomBlending;
+      const monitorPlane = this.group.getObjectByName('monitorText');
+      if (monitorPlane) {
+        monitorPlane.material = textMaterial;
+      } else {
+        const planeGeometry = new THREE.PlaneGeometry(526, 346); // 平面3维几何体PlaneGeometry
+        const planeMesh = new THREE.Mesh(planeGeometry, textMaterial);
+        planeMesh.name = 'monitorText';
+        planeMesh.scale.set(0.002, 0.002, 0.002);
+        planeMesh.position.set(3.995, 0.67, -0.27);
+        this.group.add(planeMesh);
+      }
+      textMap.dispose();
+    });
+  }
+
+  /* 风门动画 */
+  render() {
+    if (!this.model) {
+      return;
+    }
+
+    if (this.mixers && this.fmClock.running) {
+      this.mixers.update(2);
+    }
+  }
+
+  mouseUpModel() {}
+
+  /* 提取风门序列帧,初始化前后门动画 */
+  initAnimation() {
+    debugger;
+    console.log('this.group', this.group);
+    const men1 = this.group.getObjectByName('FengMen_SiShanLing').getObjectByName('men1');
+    const men2 = this.group.getObjectByName('FengMen_SiShanLing').getObjectByName('men2');
+    const tracks = this.group.animations[0].tracks;
+    const fontTracks: any[] = [],
+      backTracks: any[] = [];
+    for (let i = 0; i < tracks.length; i++) {
+      const track = tracks[i];
+      if (track.name.startsWith('men1')) {
+        fontTracks.push(track);
+      } else if (track.name.startsWith('men2')) {
+        backTracks.push(track);
+      }
+    }
+
+    this.mixers = new THREE.AnimationMixer(this.group.getObjectByName('FengMen_SiShanLing'));
+
+    const frontDoor = new THREE.AnimationClip('frontDoor', 22, fontTracks);
+    const backDoor = new THREE.AnimationClip('backDoor', 22, backTracks);
+    const frontClipAction = this.mixers.clipAction(frontDoor, men1);
+    const backClipAction = this.mixers.clipAction(backDoor, men2);
+    frontClipAction.clampWhenFinished = true;
+    frontClipAction.loop = THREE.LoopOnce;
+    this.clipActionArr.frontDoor = frontClipAction;
+    backClipAction.clampWhenFinished = true;
+    backClipAction.loop = THREE.LoopOnce;
+    this.clipActionArr.backDoor = backClipAction;
+  }
+
+  // 播放动画
+  play(handlerState, timeScale = 0.05) {
+    if (this.clipActionArr.frontDoor) {
+      let handler = () => {};
+      switch (handlerState) {
+        case 1: // 打开前门
+          handler = () => {
+            this.clipActionArr.frontDoor.paused = true;
+            this.clipActionArr.frontDoor.reset();
+            this.clipActionArr.frontDoor.time = 0;
+            this.clipActionArr.frontDoor.timeScale = timeScale;
+            this.clipActionArr.frontDoor.play();
+            this.fmClock.start();
+          };
+          break;
+        case 2: // 关闭前门
+          handler = () => {
+            this.clipActionArr.frontDoor.paused = true;
+            this.clipActionArr.frontDoor.reset(); //
+            this.clipActionArr.frontDoor.time = 1.5;
+            this.clipActionArr.frontDoor.timeScale = -timeScale;
+            this.clipActionArr.frontDoor.play();
+            this.fmClock.start();
+          };
+          break;
+        case 3: // 打开后门
+          handler = () => {
+            this.clipActionArr.backDoor.paused = true;
+            this.clipActionArr.backDoor.reset();
+            this.clipActionArr.backDoor.time = 0;
+            this.clipActionArr.backDoor.timeScale = timeScale;
+            this.clipActionArr.backDoor.play();
+            this.fmClock.start();
+          };
+          break;
+        case 4: // 关闭后门
+          handler = () => {
+            this.clipActionArr.backDoor.paused = true;
+            this.clipActionArr.backDoor.reset(); //
+            this.clipActionArr.backDoor.time = 1.5;
+            this.clipActionArr.backDoor.timeScale = -timeScale;
+            this.clipActionArr.backDoor.play();
+            this.fmClock.start();
+          };
+        case 5: // 打开前后门
+          handler = () => {
+            this.clipActionArr.frontDoor.paused = true;
+            this.clipActionArr.frontDoor.reset();
+            this.clipActionArr.frontDoor.time = 0;
+            this.clipActionArr.frontDoor.timeScale = timeScale;
+            this.clipActionArr.frontDoor.play();
+            this.fmClock.start();
+            // 显示打开前门文字
+            if (this.frontDamperOpenMesh) this.frontDamperOpenMesh.visible = true;
+            if (this.frontDamperClosedMesh) this.frontDamperClosedMesh.visible = false;
+
+            this.clipActionArr.backDoor.paused = true;
+            this.clipActionArr.backDoor.reset();
+            this.clipActionArr.backDoor.time = 0;
+            this.clipActionArr.backDoor.timeScale = timeScale;
+            this.clipActionArr.backDoor.play();
+            this.fmClock.start();
+          };
+          break;
+        case 6: // 关闭前后门
+          handler = () => {
+            this.clipActionArr.frontDoor.paused = true;
+            this.clipActionArr.frontDoor.reset(); //
+            this.clipActionArr.frontDoor.time = 1.5;
+            this.clipActionArr.frontDoor.timeScale = -timeScale;
+            this.clipActionArr.frontDoor.play();
+            this.fmClock.start();
+
+            if (this.frontDamperOpenMesh) this.frontDamperOpenMesh.visible = false;
+            if (this.frontDamperClosedMesh) this.frontDamperClosedMesh.visible = true;
+
+            this.clipActionArr.backDoor.paused = true;
+            this.clipActionArr.backDoor.reset();
+            this.clipActionArr.backDoor.time = 1.5;
+            this.clipActionArr.backDoor.timeScale = -timeScale;
+            this.clipActionArr.backDoor.play();
+            this.fmClock.start();
+          };
+          break;
+        default:
+      }
+
+      handler();
+    }
+  }
+
+  mountedThree(playerDom) {
+    this.group = new THREE.Object3D();
+    this.group.name = this.modelName;
+
+    return new Promise((resolve) => {
+      if (!this.model) {
+        resolve(null);
+      }
+      this.model.setGLTFModel('fmssl').then((gltf) => {
+        this.group = gltf[0];
+        this.group.name = 'fmssl';
+        console.log('fmssl', this.group);
+        this.setModalPosition();
+        this.initAnimation();
+        this.addLight();
+        this.model.animate();
+        resolve(this.model);
+      });
+    });
+  }
+
+  destroy() {
+    if (this.model) {
+      if (this.mixers) {
+        this.mixers.uncacheClip(this.clipActionArr.frontDoor.getClip());
+        this.mixers.uncacheClip(this.clipActionArr.backDoor.getClip());
+        this.mixers.uncacheAction(this.clipActionArr.frontDoor.getClip(), this.group);
+        this.mixers.uncacheAction(this.clipActionArr.backDoor.getClip(), this.group);
+        this.mixers.uncacheRoot(this.group);
+
+        if (this.model.animations[0]) this.model.animations[0].tracks = [];
+      }
+      this.model.clearGroup(this.group);
+      this.clipActionArr.backDoor = undefined;
+      this.clipActionArr.frontDoor = undefined;
+
+      this.mixers = undefined;
+
+      // document.getElementById('damper3D').parentElement.remove(document.getElementById('damper3D'))
+    }
+  }
+}
+export default fmSsl;

+ 64 - 37
src/views/vent/monitorManager/gateMonitor/index.vue

@@ -38,10 +38,16 @@
           >同时关闭</div
           >同时关闭</div
         >
         >
         <template v-if="selectData['gateStyle'] && selectData['gateStyle'].includes('fm_fc')">
         <template v-if="selectData['gateStyle'] && selectData['gateStyle'].includes('fm_fc')">
-          <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(10)">A窗控制</div>
-          <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(11)">B窗控制</div>
-          <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(12)">C窗控制</div>
-          <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(13)">D窗控制</div>
+          <template v-if="selectData['nwindownum'] == 4">
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(10)">A窗控制</div>
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(11)">B窗控制</div>
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(12)">C窗控制</div>
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(13)">D窗控制</div>
+          </template>
+          <template v-if="selectData['nwindownum'] == 2">
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(14)">前窗控制</div>
+            <div v-if="hasPermission('btn:controlWindow')" class="button-box" @click="playAnimation(15)">后窗控制</div>
+          </template>
         </template>
         </template>
       </div>
       </div>
       <!-- 控制模式 -->
       <!-- 控制模式 -->
@@ -94,7 +100,7 @@
       </div>
       </div>
     </div>
     </div>
     <div class="title-text"> {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.strname }} </div>
     <div class="title-text"> {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.strname }} </div>
-    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 350, scroll)">
+    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 50, scroll)">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
         <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
         <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
           <a-tab-pane v-if="!hasPermission('show:noMonitor')" key="1" tab="实时监测">
           <a-tab-pane v-if="!hasPermission('show:noMonitor')" key="1" tab="实时监测">
@@ -106,7 +112,7 @@
               :isShowActionColumn="true"
               :isShowActionColumn="true"
               :dataSource="dataSource"
               :dataSource="dataSource"
               design-scope="gate-monitor"
               design-scope="gate-monitor"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               :scroll="{ y: scroll.y - 40 }"
               :scroll="{ y: scroll.y - 40 }"
               title="风门监测"
               title="风门监测"
               :isShowPagination="true"
               :isShowPagination="true"
@@ -326,9 +332,9 @@
       right: 0px;
       right: 0px;
       width: 100%;
       width: 100%;
       height: 800px;
       height: 800px;
-      overflow-y: auto;
       pointer-events: none;
       pointer-events: none;
-      margin-left: auto;
+      overflow-y: auto;
+      flex-direction: column;
     "
     "
   >
   >
   </div>
   </div>
@@ -379,10 +385,7 @@
   import { render } from '/@/utils/common/renderUtils';
   import { render } from '/@/utils/common/renderUtils';
   import { useGlobSetting } from '/@/hooks/setting';
   import { useGlobSetting } from '/@/hooks/setting';
   import { getDictItemsByCode } from '/@/utils/dict';
   import { getDictItemsByCode } from '/@/utils/dict';
-  import { defHttp } from '/@/utils/http/axios';
-  import BarAndLine from '/@/components/chart/BarAndLine.vue';
   import HistoryTableChart from '../comment/HistoryTableChart.vue';
   import HistoryTableChart from '../comment/HistoryTableChart.vue';
-  import { useSvgAnimation } from '/@/hooks/vent/useSvgAnimation';
 
 
   const { hasPermission } = usePermission();
   const { hasPermission } = usePermission();
   const { sysOrgCode } = useGlobSetting();
   const { sysOrgCode } = useGlobSetting();
@@ -672,34 +675,40 @@
         }
         }
         break;
         break;
       case 5: // 打开前后门
       case 5: // 打开前后门
-        if (
-          selectData.frontGateOpen == '0' &&
-          selectData.frontGateClose == '1' &&
-          selectData.rearGateOpen == '0' &&
-          selectData.rearGateClose == '1'
-        ) {
-          modalTitle.value = '打开前后门';
-          modalType.value = '5';
-          modalIsShow.value = true;
-        } else {
-          // message.warning('前后门已经打开或正在打开,请勿重新操作');
-          message.warning('没有监测到前门、后门关到位,无法进行指令下发操作');
-        }
+        // if (
+        //   selectData.frontGateOpen == '0' &&
+        //   selectData.frontGateClose == '1' &&
+        //   selectData.rearGateOpen == '0' &&
+        //   selectData.rearGateClose == '1'
+        // ) {
+        //   modalTitle.value = '打开前后门';
+        //   modalType.value = '5';
+        //   modalIsShow.value = true;
+        // } else {
+        //   // message.warning('前后门已经打开或正在打开,请勿重新操作');
+        //   message.warning('没有监测到前门、后门关到位,无法进行指令下发操作');
+        // }
+        modalTitle.value = '打开前后门';
+        modalType.value = '5';
+        modalIsShow.value = true;
         break;
         break;
       case 6: // 关闭前后门
       case 6: // 关闭前后门
-        if (
-          selectData.frontGateOpen == '1' &&
-          selectData.frontGateClose == '0' &&
-          selectData.rearGateOpen == '1' &&
-          selectData.rearGateClose == '0'
-        ) {
-          modalTitle.value = '关闭前后门';
-          modalType.value = '6';
-          modalIsShow.value = true;
-        } else {
-          // message.warning('前后门已经关闭或正在关闭,请勿重新操作');
-          message.warning('没有监测到前门、后门开到位,无法进行指令下发操作');
-        }
+        // if (
+        //   selectData.frontGateOpen == '1' &&
+        //   selectData.frontGateClose == '0' &&
+        //   selectData.rearGateOpen == '1' &&
+        //   selectData.rearGateClose == '0'
+        // ) {
+        //   modalTitle.value = '关闭前后门';
+        //   modalType.value = '6';
+        //   modalIsShow.value = true;
+        // } else {
+        //   // message.warning('前后门已经关闭或正在关闭,请勿重新操作');
+        //   message.warning('没有监测到前门、后门开到位,无法进行指令下发操作');
+        // }
+        modalTitle.value = '关闭前后门';
+        modalType.value = '6';
+        modalIsShow.value = true;
         break;
         break;
 
 
       case 7: // 控制模式切换
       case 7: // 控制模式切换
@@ -729,6 +738,16 @@
         modalType.value = '13';
         modalType.value = '13';
         modalIsShow.value = true;
         modalIsShow.value = true;
         break;
         break;
+      case 14: // 风窗控制
+        modalTitle.value = '前窗控制';
+        modalType.value = '14';
+        modalIsShow.value = true;
+        break;
+      case 15: // 风窗控制
+        modalTitle.value = '后窗控制';
+        modalType.value = '15';
+        modalIsShow.value = true;
+        break;
     }
     }
 
 
     if (globalConfig?.simulatedPassword) {
     if (globalConfig?.simulatedPassword) {
@@ -953,6 +972,14 @@
         data.paramcode = 'rearSetValue2';
         data.paramcode = 'rearSetValue2';
         data.value = value;
         data.value = value;
         break;
         break;
+      case '14': // 后(B)窗控制
+        data.paramcode = 'frontSetValue';
+        data.value = value;
+        break;
+      case '15': // 后(B)窗控制
+        data.paramcode = 'rearSetValue';
+        data.value = value;
+        break;
     }
     }
 
 
     if (data.paramcode) {
     if (data.paramcode) {

+ 2 - 6
src/views/vent/monitorManager/gateMonitor/modal.vue

@@ -5,7 +5,7 @@
         <ExclamationCircleFilled style="color: #ffb700; font-size: 30px" />
         <ExclamationCircleFilled style="color: #ffb700; font-size: 30px" />
         <div class="warning-text">您是否要进行{{ title }}操作?</div>
         <div class="warning-text">您是否要进行{{ title }}操作?</div>
       </div>
       </div>
-      <div v-if="type == '10' || type == '11' || type == '12' || type == '13'">
+      <div v-if="type == '10' || type == '11' || type == '12' || type == '13' || type == '14' || type == '15'">
         <div class="vent-flex-row input-box">
         <div class="vent-flex-row input-box">
           <div class="label">风窗开启角度:</div>
           <div class="label">风窗开启角度:</div>
           <a-input-number size="small" placeholder="0" :min="0" v-model:value="value" />
           <a-input-number size="small" placeholder="0" :min="0" v-model:value="value" />
@@ -60,11 +60,7 @@
 
 
   function handleOk() {
   function handleOk() {
     //
     //
-    if (type.value == '10') {
-      emit('handleOk', passWord.value, type.value, value.value);
-    } else {
-      emit('handleOk', passWord.value, type.value, value.value);
-    }
+    emit('handleOk', passWord.value, type.value, value.value);
   }
   }
   function handleCancel() {
   function handleCancel() {
     //
     //

+ 22 - 21
src/views/vent/monitorManager/mainFanMonitor/index.vue

@@ -76,22 +76,22 @@
                     selectData['Fan1StartStatus'] == '0' && globalConfig?.simulatedPassword
                     selectData['Fan1StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan1')]
                       : selectData[data.dataIndex.replace('Fan', 'Fan1')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan1')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan1')]
+                        : '-'
                   }}</div>
                   }}</div>
                   <div class="item-value" v-if="dataMonitorRowIndex == 1">{{
                   <div class="item-value" v-if="dataMonitorRowIndex == 1">{{
                     selectData['Fan2StartStatus'] == '0' && globalConfig?.simulatedPassword
                     selectData['Fan2StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan2')]
                       : selectData[data.dataIndex.replace('Fan', 'Fan2')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan2')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan2')]
+                        : '-'
                   }}</div>
                   }}</div>
                   <div class="item-value" v-if="dataMonitorRowIndex == 2">{{
                   <div class="item-value" v-if="dataMonitorRowIndex == 2">{{
                     selectData['Fan3StartStatus'] == '0' && globalConfig?.simulatedPassword
                     selectData['Fan3StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan3')]
                       : selectData[data.dataIndex.replace('Fan', 'Fan3')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan3')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan3')]
+                        : '-'
                   }}</div>
                   }}</div>
                 </div>
                 </div>
                 <div v-else>
                 <div v-else>
@@ -150,8 +150,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                           selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                             ? '无状态'
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
                             : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                         }}</div>
                       </template>
                       </template>
                     </div>
                     </div>
@@ -175,8 +175,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                           selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                             ? '无状态'
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
                             : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                         }}</div>
                       </template>
                       </template>
                     </div>
                     </div>
@@ -200,8 +200,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan3')] == undefined
                           selectData[state.dataIndex.replace('Fan', 'Fan3')] == undefined
                             ? '无状态'
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan3')] == '0'
                             : selectData[state.dataIndex.replace('Fan', 'Fan3')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                         }}</div>
                       </template>
                       </template>
                     </div>
                     </div>
@@ -311,7 +311,7 @@
               ref="MonitorDataTable"
               ref="MonitorDataTable"
               :dataSource="dataSource"
               :dataSource="dataSource"
               columnsType="fanmain_monitor"
               columnsType="fanmain_monitor"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               :scroll="{ y: scroll.y - (headElHeight - 56) }"
               :scroll="{ y: scroll.y - (headElHeight - 56) }"
               :is-action="true"
               :is-action="true"
             >
             >
@@ -1060,12 +1060,12 @@
         selectData['modalTyoe'] === 'xiejing'
         selectData['modalTyoe'] === 'xiejing'
           ? 'mainXjWindRect'
           ? 'mainXjWindRect'
           : selectData['modalTyoe'] === 'lijing1'
           : selectData['modalTyoe'] === 'lijing1'
-          ? 'mainLjWindRect'
-          : selectData['modalTyoe'] === 'lijing_3'
-          ? 'mainWindRect3'
-          : selectData['modalTyoe'] === 'lijing_1'
-          ? 'mainLjDtWindRect'
-          : 'mainWindRect';
+            ? 'mainLjWindRect'
+            : selectData['modalTyoe'] === 'lijing_3'
+              ? 'mainWindRect3'
+              : selectData['modalTyoe'] === 'lijing_1'
+                ? 'mainLjDtWindRect'
+                : 'mainWindRect';
 
 
       frontMonitorIsShow.value = false;
       frontMonitorIsShow.value = false;
       centerMonitorIsShow.value = false;
       centerMonitorIsShow.value = false;
@@ -2377,9 +2377,10 @@
     // padding-left: 380px;
     // padding-left: 380px;
     pointer-events: none;
     pointer-events: none;
     overflow-x: auto;
     overflow-x: auto;
-
+    .liveVideo {
+      align-self: auto !important;
+    }
     .video-parent {
     .video-parent {
-      // height: 100%;
       pointer-events: auto !important;
       pointer-events: auto !important;
     }
     }
   }
   }

+ 2 - 3
src/views/vent/monitorManager/safetyMonitor/index.vue

@@ -153,11 +153,11 @@
           </div>
           </div>
         </a-tab-pane>
         </a-tab-pane>
       </template>
       </template>
-      <template v-else>
+      <template v-else-if="deviceType != 'safetymonitor'">
         <a-tab-pane key="3" tab="报警历史">
         <a-tab-pane key="3" tab="报警历史">
           <div class="tab-item">
           <div class="tab-item">
             <AlarmHistoryCommentTable
             <AlarmHistoryCommentTable
-              v-if="activeKey == '3' && deviceType != 'safetymonitor'"
+              v-if="activeKey == '3'"
               columns-type="alarm"
               columns-type="alarm"
               :device-type="deviceType"
               :device-type="deviceType"
               :device-list-api="getDeviceList.bind(null, { devicekind: deviceType, pageSize: 10000 })"
               :device-list-api="getDeviceList.bind(null, { devicekind: deviceType, pageSize: 10000 })"
@@ -166,7 +166,6 @@
           </div>
           </div>
         </a-tab-pane>
         </a-tab-pane>
       </template>
       </template>
-
       <a-tab-pane key="4" tab="操作历史" v-if="deviceType !== 'safetymonitor' && deviceType !== 'wasichoufang'">
       <a-tab-pane key="4" tab="操作历史" v-if="deviceType !== 'safetymonitor' && deviceType !== 'wasichoufang'">
         <div class="tab-item">
         <div class="tab-item">
           <HandlerHistoryTable
           <HandlerHistoryTable

+ 2 - 2
src/views/vent/monitorManager/sensorMonitor/index.vue

@@ -45,7 +45,8 @@
               </MonitorTable>
               </MonitorTable>
             </div>
             </div>
             <!-- v-if="chartsColumns.length > 0" -->
             <!-- v-if="chartsColumns.length > 0" -->
-            <div class="charts-box" v-if="false">
+            <div class="charts-box" v-if="hasPermission('sensor:showMonitorChart') && chartsColumns && chartsColumns.length > 0">
+              ">
               <BarAndLine
               <BarAndLine
                 :chartsColumnsType="selectData.deviceType"
                 :chartsColumnsType="selectData.deviceType"
                 xAxisPropType="readTime"
                 xAxisPropType="readTime"
@@ -64,7 +65,6 @@
             <HistoryTable
             <HistoryTable
               :columns-type="deviceKind"
               :columns-type="deviceKind"
               :device-type="deviceKind"
               :device-type="deviceKind"
-              :device-list-api="list.bind(null, { devicetype: selectData.deviceType })"
               @change="historyDataSourceChange"
               @change="historyDataSourceChange"
               designScope="modelsensor-history"
               designScope="modelsensor-history"
               :scroll="{ y: 600 }"
               :scroll="{ y: 600 }"

+ 290 - 0
src/views/vent/monitorManager/sprayMonitor/component.vue

@@ -0,0 +1,290 @@
+<template>
+  <customHeader
+    :fieldNames="{ label: 'systemname', value: 'id', options: 'children' }"
+    :options="options"
+    :optionValue="optionValue"
+    @change="changeSelectRow"
+  >
+    {{ mainTitle }}
+  </customHeader>
+  <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden">
+    <a-spin :spinning="loading" />
+    <div id="model3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden"></div>
+    <slot name="monitorSlot3D"></slot>
+  </div>
+  <div class="scene-box">
+    <div class="center-container">
+      <template v-if="activeKey == 'monitor'">
+        <!-- <balancePressHome v-if="activeKey == 'monitor'" :deviceId="optionValue" /> -->
+      </template>
+      <div v-else class="history-group">
+        <div class="device-button-group" v-if="deviceList.length > 0">
+          <div
+            class="device-button"
+            :class="{ 'device-active': deviceActive == device.deviceType }"
+            v-for="(device, index) in deviceList"
+            :key="index"
+            @click="deviceChange(index)"
+            >{{ device.deviceName }}</div
+          >
+        </div>
+        <div class="history-container">
+          <HistoryTable
+            v-if="activeKey == 'monitor_history'"
+            class="vent-margin-t-20"
+            :columns-type="`${deviceType}`"
+            :device-type="deviceType"
+            :sysId="optionValue"
+            :scroll="{ y: 650 }"
+            :only-bouned-devices="monitorHistoryConfig.onlyBounedDevices"
+            :show-history-curve="monitorHistoryConfig.showHistoryCurve"
+          />
+          <HandlerHistoryTable
+            v-if="activeKey == 'handler_history'"
+            class="vent-margin-t-20"
+            columns-type="operator_history"
+            :deviceType="deviceType"
+            :device-list-api="getHandlerList"
+          />
+
+          <AlarmHistoryTable
+            v-if="activeKey == 'faultRecord'"
+            columns-type="alarm"
+            :device-type="deviceType"
+            :list="getAlarmList"
+            :sys-id="optionValue"
+            :device-list-api="workFaceDeviceList.bind(null, { id: optionValue })"
+            designScope="alarm-history"
+          />
+        </div>
+      </div>
+    </div>
+    <BottomMenu @change="changeActive" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import customHeader from '/@/components/vent/customHeader.vue';
+  import { ref, onMounted, onUnmounted } from 'vue';
+  import { getDevice, sysList } from '../comment/comment.api';
+  import BottomMenu from '/@/views/vent/comment/components/bottomMenu.vue';
+  // import balancePressHome from './components/balancePressHome.vue';
+  import HistoryTable from '../comment/HistoryTable.vue';
+  import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
+  import { useRouter } from 'vue-router';
+  import { Config } from '../../deviceManager/configurationTable/types';
+  import { defHttp } from '/@/utils/http/axios';
+  import { workFaceDeviceList } from '../../deviceManager/comment/warningTabel/warning.api';
+
+  type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
+
+  const props = withDefaults(
+    defineProps<{
+      mainTitle: string;
+      /** 获取各表格配置时依赖的设备类型 */
+      // deviceType: string;
+      /** 主要模块配置 */
+      mainConfig: {
+        configs: Config[];
+      };
+      /** 历史数据配置 */
+      monitorHistoryConfig: {
+        /** 请求历史数据时传入的类型字符 */
+        columnsType: string;
+        /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
+        onlyBounedDevices: boolean;
+        /** 显示历史数据曲线图 */
+        showHistoryCurve: boolean;
+      };
+      /** 操作历史配置 */
+      handlerHistoryConfig: {
+        /** 请求操作历史时传入的类型字符 */
+        columnsType: string;
+      };
+      /** 报警历史配置 */
+      alarmHistoryConfig: {
+        /** 请求报警历史时传入的类型字符 */
+        columnsType: string;
+      };
+      /** 请求场景数据传入的类型字符 */
+      strtype: string;
+      /** 请求场景数据传入的页面类型字符 */
+      pagetype: string;
+    }>(),
+    {
+      pagetype: 'normal',
+      onlyBounedDevices: false,
+      showHistoryCurve: false,
+    }
+  );
+
+  const { currentRoute } = useRouter();
+  const loading = ref(false);
+
+  const activeKey = ref('monitor');
+
+  function changeActive(activeValue) {
+    activeKey.value = activeValue;
+  }
+
+  const options = ref([]);
+  const optionValue = ref('');
+
+  async function getSysDataSource() {
+    const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype });
+    // 初始时选择第一条数据
+    options.value = res.records || [];
+    if (!optionValue.value) {
+      changeSelectRow(options.value[0]['id']);
+    }
+  }
+
+  // 切换检测数据
+  function changeSelectRow(deviceID) {
+    optionValue.value = deviceID;
+    getDeviceList();
+  }
+
+  //关联设备
+  const deviceList = ref<DeviceType[]>([]);
+  const deviceActive = ref('');
+  const deviceType = ref('');
+
+  function deviceChange(index) {
+    deviceType.value = deviceList.value[index].deviceType;
+    deviceActive.value = deviceList.value[index].deviceType;
+  }
+
+  // 查询关联设备列表
+  async function getDeviceList() {
+    const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value });
+
+    deviceList.value = msgTxt.reduce((arr, item) => {
+      const data = item.datalist.forEach((data: any) => {
+        return Object.assign(data, data.readData);
+      });
+      // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
+      if (item.type != 'sys') {
+        arr.unshift({
+          deviceType: item.type,
+          deviceName: item.typeName || item.datalist[0].typeName,
+          datalist: data,
+        });
+      }
+
+      return arr;
+    });
+    if (!deviceActive.value) {
+      deviceChange(0);
+    }
+  }
+
+  /** 获取操作历史 */
+  function getHandlerList() {
+    return sysList({ strtype: deviceType.value });
+  }
+
+  function getAlarmList() {
+    return (params) => defHttp.get({ url: '/safety/managesysAutoLog/list', params });
+  }
+
+  onMounted(() => {
+    if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
+      optionValue.value = currentRoute.value['query']['id'] as string;
+    }
+    getSysDataSource();
+  });
+
+  onUnmounted(() => {});
+</script>
+<style lang="less" scoped>
+  @import '/@/design/vent/modal.less';
+  @ventSpace: zxm;
+  .scene-box {
+    margin-top: 20px;
+    pointer-events: none;
+    .history-group {
+      padding: 0 20px;
+      .history-container {
+        pointer-events: auto;
+        position: relative;
+        background: #6195af1a;
+        width: calc(100% + 10px);
+        top: 0px;
+        left: -10px;
+        border: 1px solid #00fffd22;
+        padding: 10px 0;
+        box-shadow: 0 0 20px #44b4ff33 inset;
+      }
+    }
+    .device-button-group {
+      // margin: 0 20px;
+      display: flex;
+      pointer-events: auto;
+      position: relative;
+      margin-top: 90px;
+      &::after {
+        position: absolute;
+        content: '';
+        width: calc(100% + 10px);
+        height: 2px;
+        top: 30px;
+        left: -10px;
+        border-bottom: 1px solid #0efcff;
+      }
+      .device-button {
+        padding: 4px 15px;
+        position: relative;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 14px;
+
+        color: #fff;
+        cursor: pointer;
+        margin: 0 3px;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 0;
+          right: 0;
+          bottom: 0;
+          left: 0;
+          border: 1px solid #6176af;
+          transform: skewX(-38deg);
+          background-color: rgba(0, 77, 103, 85%);
+          z-index: -1;
+        }
+      }
+      .device-active {
+        // color: #0efcff;
+        &::before {
+          border-color: #0efcff;
+          box-shadow: 1px 1px 3px 1px #0efcff inset;
+        }
+      }
+    }
+  }
+  .center-container {
+    width: 100%;
+    height: calc(100% - 200px);
+  }
+
+  .input-box {
+    display: flex;
+    align-items: center;
+    padding-left: 10px;
+
+    .input-title {
+      color: #73e8fe;
+      width: auto;
+    }
+
+    .@{ventSpace}-input-number {
+      border-color: #ffffff88 !important;
+    }
+
+    margin-right: 10px;
+  }
+</style>

+ 6 - 0
src/views/vent/monitorManager/sprayMonitor/index.vue

@@ -0,0 +1,6 @@
+<template>
+  <div></div>
+</template>
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped></style>

+ 12 - 4
src/views/vent/monitorManager/windowMonitor/index.vue

@@ -8,8 +8,6 @@
           <div v-if="hasPermission('window:AreaControl')" class="button-box" @click="setArea(2)">设定后窗面积</div>
           <div v-if="hasPermission('window:AreaControl')" class="button-box" @click="setArea(2)">设定后窗面积</div>
           <div v-if="hasPermission('window:showAngle')" class="button-box" @click="setAngle(1)">设定前窗角度</div>
           <div v-if="hasPermission('window:showAngle')" class="button-box" @click="setAngle(1)">设定前窗角度</div>
           <div v-if="hasPermission('window:showAngle')" class="button-box" @click="setAngle(2)">设定后窗角度</div>
           <div v-if="hasPermission('window:showAngle')" class="button-box" @click="setAngle(2)">设定后窗角度</div>
-          <div v-if="hasPermission('window:gateControl')" class="button-box" @click="playAnimation(1)">打开前门</div>
-          <div v-if="hasPermission('window:gateControl')" class="button-box" @click="playAnimation(2)">关闭前门</div>
           <div v-if="hasPermission('window:twoAreaControl')" class="button-box" @click="setControl('1', '窗1面积设定')">窗1面积设定</div>
           <div v-if="hasPermission('window:twoAreaControl')" class="button-box" @click="setControl('1', '窗1面积设定')">窗1面积设定</div>
           <div v-if="hasPermission('window:twoAreaControl')" class="button-box" @click="setControl('2', '窗2面积设定')">窗2面积设定</div>
           <div v-if="hasPermission('window:twoAreaControl')" class="button-box" @click="setControl('2', '窗2面积设定')">窗2面积设定</div>
           <div v-if="hasPermission('window:twoAngleControl')" class="button-box" @click="setControl('1', '窗1角度设定')">窗1角度设定</div>
           <div v-if="hasPermission('window:twoAngleControl')" class="button-box" @click="setControl('1', '窗1角度设定')">窗1角度设定</div>
@@ -36,6 +34,16 @@
           <div v-if="hasPermission('window:showAddArea')" class="button-box" @click="setControl('addSetValue', '增加面积设置')">增加面积</div>
           <div v-if="hasPermission('window:showAddArea')" class="button-box" @click="setControl('addSetValue', '增加面积设置')">增加面积</div>
           <div v-if="hasPermission('window:showAddArea')" class="button-box" @click="setControl('deSetValue', '减少面积设置')">减少面积</div>
           <div v-if="hasPermission('window:showAddArea')" class="button-box" @click="setControl('deSetValue', '减少面积设置')">减少面积</div>
         </div>
         </div>
+        <div class="row" v-if="hasPermission('window:gateControl') && Number(selectData.ndoorcount) == 2">
+          <div class="button-box" @click="setControl('frontGateOpen_S', '打开前门')">打开前门</div>
+          <div class="button-box" @click="setControl('frontGateClose_S', '关闭前门')">关闭前门</div>
+          <div class="button-box" @click="setControl('rearGateOpen_S', '打开后门')">打开前门</div>
+          <div class="button-box" @click="setControl('rearGateClose_S', '关闭后门')">关闭前门</div>
+        </div>
+        <div class="row" v-if="hasPermission('window:gateControl') && Number(selectData.ndoorcount) == 1">
+          <div class="button-box" @click="setControl('frontGateOpen_S', '打开门')">打开门</div>
+          <div class="button-box" @click="setControl('frontGateClose_S', '关闭门')">关闭门</div>
+        </div>
         <div v-if="hasPermission('window:sameSet')" class="button-box" @click="setControl('sameSetValue', '风窗面积设置')">设定风窗面积</div>
         <div v-if="hasPermission('window:sameSet')" class="button-box" @click="setControl('sameSetValue', '风窗面积设置')">设定风窗面积</div>
         <div v-if="hasPermission('window:COTest')" class="button-box" @click="setControl('COTest', 'CO调控预测')">CO调控预测</div>
         <div v-if="hasPermission('window:COTest')" class="button-box" @click="setControl('COTest', 'CO调控预测')">CO调控预测</div>
       </div>
       </div>
@@ -80,7 +88,7 @@
     <div class="title-text">
     <div class="title-text">
       {{ selectData.strinstallpos || selectData.strname }}
       {{ selectData.strinstallpos || selectData.strname }}
     </div>
     </div>
-    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 350, scroll)">
+    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 50, scroll)">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
         <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
         <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
           <a-tab-pane key="1" tab="实时监测">
           <a-tab-pane key="1" tab="实时监测">
@@ -89,7 +97,7 @@
               ref="MonitorDataTable"
               ref="MonitorDataTable"
               :columnsType="deviceType"
               :columnsType="deviceType"
               :dataSource="dataSource"
               :dataSource="dataSource"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               design-scope="window-monitor"
               design-scope="window-monitor"
               :scroll="{ y: scroll.y - 40 }"
               :scroll="{ y: scroll.y - 40 }"
               title="风窗监测"
               title="风窗监测"

+ 4 - 5
src/views/vent/monitorManager/windrectMonitor/index.vue

@@ -40,7 +40,7 @@
     <div class="title-text">
     <div class="title-text">
       {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.strname }}
       {{ selectData.supplyAirAddr || selectData.strinstallpos || selectData.strname }}
     </div>
     </div>
-    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 350, scroll)">
+    <div class="bottom-tabs-box" @mousedown="setDivHeight($event, 50, scroll)">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
       <dv-border-box8 :dur="5" :style="`padding: 5px; height: ${scroll.y + 120}px`">
         <div class="tabs-button-group">
         <div class="tabs-button-group">
           <a-button v-if="hasPermission('windrect:handler')" class="tabs-button" type="primary" @click="openModel">一键测风</a-button>
           <a-button v-if="hasPermission('windrect:handler')" class="tabs-button" type="primary" @click="openModel">一键测风</a-button>
@@ -54,7 +54,7 @@
               :columnsType="deviceType"
               :columnsType="deviceType"
               :dataSource="dataSource"
               :dataSource="dataSource"
               design-scope="windrect-monitor"
               design-scope="windrect-monitor"
-              @selectRow="getSelectRow"
+              @select-row="getSelectRow"
               :scroll="{ y: scroll.y - 40 }"
               :scroll="{ y: scroll.y - 40 }"
               title="测风装置监测"
               title="测风装置监测"
               :isShowPagination="true"
               :isShowPagination="true"
@@ -87,13 +87,12 @@
           <a-tab-pane v-if="hasPermission('windrect:chartMonitor')" key="2" tab="监测曲线图" force-render>
           <a-tab-pane v-if="hasPermission('windrect:chartMonitor')" key="2" tab="监测曲线图" force-render>
             <div class="tab-item" style="height: 280px" v-if="activeKey === '2'">
             <div class="tab-item" style="height: 280px" v-if="activeKey === '2'">
               <DeviceEcharts
               <DeviceEcharts
-                chartsColumnsType="windrect_chart"
+                :chartsColumnsType="deviceType + '_chart'"
                 xAxisPropType="strname"
                 xAxisPropType="strname"
                 :dataSource="dataSource"
                 :dataSource="dataSource"
                 height="100%"
                 height="100%"
-                :chartsColumns="chartsColumnList"
                 :device-list-api="list.bind(null, { devicetype: 'windrect', pagetype: 'normal' })"
                 :device-list-api="list.bind(null, { devicetype: 'windrect', pagetype: 'normal' })"
-                device-type="windrect"
+                :device-type="deviceType"
               />
               />
             </div>
             </div>
           </a-tab-pane>
           </a-tab-pane>

+ 3 - 3
src/views/vent/safetyList/safetyList.api.ts

@@ -54,7 +54,7 @@ export const remove158Substation = (params) => defHttp.post({ url: Api.remove158
 //158分站操作记录
 //158分站操作记录
 export const get158SetLog = (params) => defHttp.post({ url: Api.get158SetLog, params }, { joinParamsToUrl: true });
 export const get158SetLog = (params) => defHttp.post({ url: Api.get158SetLog, params }, { joinParamsToUrl: true });
 //删除158分站传感器
 //删除158分站传感器
-export const remove158Device = (params) => defHttp.post({ url: Api.remove158Device, params },{ joinParamsToUrl: true });
+export const remove158Device = (params) => defHttp.post({ url: Api.remove158Device, params }, { joinParamsToUrl: true });
 //158监测详情读取列表
 //158监测详情读取列表
 export const set158StationDevicesRead = (params) => defHttp.post({ url: Api.set158StationDevicesRead, params }, { joinParamsToUrl: true });
 export const set158StationDevicesRead = (params) => defHttp.post({ url: Api.set158StationDevicesRead, params }, { joinParamsToUrl: true });
 //编辑设备名称
 //编辑设备名称
@@ -68,11 +68,11 @@ export const get130StationDevices = (params) => defHttp.post({ url: Api.get130St
 //读取分站设备数据
 //读取分站设备数据
 export const set130StationRead = (params) => defHttp.post({ url: Api.set130StationRead, params }, { joinParamsToUrl: true });
 export const set130StationRead = (params) => defHttp.post({ url: Api.set130StationRead, params }, { joinParamsToUrl: true });
 //删除130分站及其关联传感器
 //删除130分站及其关联传感器
-export const remove130Substation = (params) => defHttp.post({ url: Api.remove130Substation, params }, { joinParamsToUrl: true });
+export const remove130Substation = (params) => defHttp.post({ url: Api.remove158Substation, params }, { joinParamsToUrl: true });
 //130分站操作记录
 //130分站操作记录
 export const get130SetLog = (params) => defHttp.post({ url: Api.get130SetLog, params }, { joinParamsToUrl: true });
 export const get130SetLog = (params) => defHttp.post({ url: Api.get130SetLog, params }, { joinParamsToUrl: true });
 //删除130分站传感器
 //删除130分站传感器
-export const remove130Device = (params) => defHttp.post({ url: Api.remove130Device, params });
+export const remove130Device = (params) => defHttp.post({ url: Api.remove158Device, params }, { joinParamsToUrl: true });
 //130监测详情读取列表
 //130监测详情读取列表
 export const set130StationDevicesRead = (params) => defHttp.post({ url: Api.set130StationDevicesRead, params }, { joinParamsToUrl: true });
 export const set130StationDevicesRead = (params) => defHttp.post({ url: Api.set130StationDevicesRead, params }, { joinParamsToUrl: true });
 // 分站详细信息列表
 // 分站详细信息列表

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