Просмотр исходного кода

Merge branch 'master' of http://39.97.59.228:8013/hrx/mky-vent-base

lxh 5 месяцев назад
Родитель
Сommit
c5fab2badf
93 измененных файлов с 3440 добавлено и 3103 удалено
  1. 1 1
      .env
  2. 2 1
      .env.development
  3. 1 1
      package.json
  4. 1 1
      pnpm-lock.yaml
  5. BIN
      src/assets/images/home-container/configurable/middata_bg.png
  6. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-1.png
  7. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-2.png
  8. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-3.png
  9. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-4.png
  10. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-5.png
  11. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb1.png
  12. BIN
      src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb2.png
  13. BIN
      src/assets/images/sealedGoaf/configurable/table/table1-label.png
  14. BIN
      src/assets/images/sealedGoaf/views/home/module-title.png
  15. BIN
      src/assets/images/vent/home/2.5D.png
  16. BIN
      src/assets/images/vent/home/3D.png
  17. BIN
      src/assets/images/vent/loginDataCenter/dataCenterBg.png
  18. BIN
      src/assets/images/vent/loginDataCenter/loginForm.png
  19. 3 0
      src/assets/images/vent/loginDataCenter/password.svg
  20. 3 0
      src/assets/images/vent/loginDataCenter/user.svg
  21. BIN
      src/assets/images/vent/loginNeiye/1-1.png
  22. BIN
      src/assets/images/vent/loginNeiye/1-2.png
  23. BIN
      src/assets/images/vent/loginNeiye/bg.png
  24. BIN
      src/assets/images/vent/loginNeiye/btn1.png
  25. BIN
      src/assets/images/vent/loginNeiye/btn2.png
  26. 6 0
      src/assets/images/vent/loginNeiye/pass.svg
  27. 4 0
      src/assets/images/vent/loginNeiye/user.svg
  28. 1362 1344
      src/components/AIChat/MiniChat.vue
  29. 1 0
      src/components/Application/src/AppProvider.vue
  30. 3 3
      src/components/Container/src/Adaptive.vue
  31. 172 11
      src/components/vent/BasicMonitoring.vue
  32. 64 0
      src/components/vent/micro/createSubApp.vue
  33. 3 5
      src/components/vent/micro/needAir.vue
  34. 4 6
      src/components/vent/micro/ventDoc.vue
  35. 5 7
      src/components/vent/micro/ventModal.vue
  36. 7 8
      src/components/vent/micro/ventModal2D.vue
  37. 2 0
      src/enums/cacheEnum.ts
  38. 5 5
      src/layouts/default/header/components/VoiceBroadcast.vue
  39. 1 1
      src/layouts/default/header/components/VoiceBroadcastGsd.vue
  40. 19 1
      src/layouts/default/header/index.vue
  41. 1 1
      src/layouts/default/setting/handler.ts
  42. 5 16
      src/layouts/default/sider/bottomSideder.vue
  43. 1 0
      src/logics/theme/updateBackground.ts
  44. 4 2
      src/main.ts
  45. 1 0
      src/qiankun/apps.ts
  46. 14 42
      src/qiankun/index.ts
  47. 1 0
      src/qiankun/state.ts
  48. 10 6
      src/router/guard/index.ts
  49. 19 0
      src/router/guard/permissionGuard.ts
  50. 33 0
      src/router/routes/basic.ts
  51. 5 1
      src/router/routes/index.ts
  52. 17 1
      src/store/modules/app.ts
  53. 2 0
      src/utils/cache/persistent.ts
  54. 0 747
      src/views/sealedGoafSys/home/configurable.data.sealedGoaf.ts
  55. 0 138
      src/views/sealedGoafSys/home/sealedGoaf.vue
  56. 131 0
      src/views/sys/login/LoginDataCenter.vue
  57. 208 0
      src/views/sys/login/LoginFormDataCenter.vue
  58. 214 0
      src/views/sys/login/LoginFormNeiye.vue
  59. 132 0
      src/views/sys/login/LoginNeiye.vue
  60. 1 1
      src/views/vent/dataCenter/deviceCenter/index.vue
  61. 1 1
      src/views/vent/dataCenter/infoCenter/index.vue
  62. 11 2
      src/views/vent/dataCenter/infoCenter/infoCenter.data.ts
  63. 2 6
      src/views/vent/dataCenter/stationCenter/index.vue
  64. 9 6
      src/views/vent/deviceManager/configurationTable/types.ts
  65. 2 2
      src/views/vent/gas/gasHome/index.vue
  66. 241 235
      src/views/vent/home/clique/components/icon-light.vue
  67. 3 5
      src/views/vent/home/colliery/index.vue
  68. 18 10
      src/views/vent/home/configurable/components/MonitorBar.vue
  69. 19 3
      src/views/vent/home/configurable/components/detail/CustomChart.vue
  70. 0 37
      src/views/vent/home/configurable/components/detail/CustomTable.vue
  71. 0 149
      src/views/vent/home/configurable/components/detail/MiniBoard.vue
  72. 57 37
      src/views/vent/home/configurable/configurable.api.ts
  73. 6 7
      src/views/vent/home/configurable/configurable.data.tashan.ts
  74. 8 8
      src/views/vent/home/configurable/configurable.data.ts
  75. 87 26
      src/views/vent/home/configurable/vent182.vue
  76. 9 7
      src/views/vent/home/configurable/ventV5.vue
  77. 68 68
      src/views/vent/monitorManager/alarmMonitor/warn/waterLevel.vue
  78. 2 2
      src/views/vent/monitorManager/balancePressMonitor/index1.vue
  79. 121 31
      src/views/vent/monitorManager/deviceMonitor/components/device/index.vue
  80. 6 6
      src/views/vent/monitorManager/deviceMonitor/index.vue
  81. 10 6
      src/views/vent/monitorManager/fanLocalMonitor/components/entryThree.vue
  82. 5 4
      src/views/vent/monitorManager/fanLocalMonitor/index.vue
  83. 0 2
      src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.fire.redGate.ts
  84. 0 1
      src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ssl.ts
  85. 2 3
      src/views/vent/monitorManager/footageMonitor/index.vue
  86. 3 0
      src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue
  87. 131 0
      src/views/vent/monitorManager/mainFanMonitor/components/setValueSetting.vue
  88. 81 49
      src/views/vent/monitorManager/mainFanMonitor/index.vue
  89. 51 23
      src/views/vent/monitorManager/sprayMonitor/index.vue
  90. 0 0
      src/views/vent/monitorManager/sprayMonitor/spray.api.ts
  91. 2 11
      src/views/vent/monitorManager/sprayMonitor/spray.three.ts
  92. 17 5
      src/views/vent/monitorManager/sprayMonitor/spray.threejs.base.ts
  93. 0 1
      src/views/vent/monitorManager/windowMonitor/components/windowDualSVG.vue

+ 1 - 1
.env

@@ -1,5 +1,5 @@
 # port
-VITE_PORT = 3100
+VITE_PORT = 8062
 
 #  网站标题
 VITE_GLOB_APP_TITLE = 智能通风管控系统

+ 2 - 1
.env.development

@@ -31,5 +31,6 @@ VITE_GLOB_API_URL_PREFIX=
 #VITE_APP_SUB_APP = [["micro-need-air", "//10.10.150.72:8099/"], ["micro-vent-3dModal", "//localhost:8091/"], ["micro-fire-front", "//localhost:8090/"]]
 # VITE_APP_SUB_APP = [["micro-vent-3dModal", "//192.168.183.154:8091/", "micro-vent-3dModal"], ["micro-need-air", "//192.168.183.88:8093/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
 # VITE_APP_SUB_APP = [["micro-vent-3dModal", "//192.168.1.16:8091/", "micro-vent-3dModal"], ["micro-need-air", "//192.168.183.88:8093/", "micro-need-air"], ["micro-fire-front", "//localhost:8097/", "fire-Micro"]]
-VITE_APP_SUB_APP = [["micro-vent-3dModal", "//182.92.126.35:8091/", "micro-vent-3dModal"],["micro-vent-2dModal", "//localhost:8088/", "micro-vent-2dModal"],["micro-vent-doc", "//localhost:5173/", "micro-vent-doc"],["micro-need-air", "//182.92.126.35:8093/", "micro-need-air"], ["micro-fire-front", "//182.92.126.35:8097/", "fire-Micro"]]
+# VITE_APP_SUB_APP = [["micro-vent-3dModal", "//182.92.126.35:8091", "micro-vent-3dModal"],["micro-vent-2dModal", "//localhost:8088", "micro-vent-2dModal"],["micro-vent-doc", "//localhost:5173", "micro-vent-doc"],["micro-need-air", "//182.92.126.35:8093", "micro-need-air"], ["micro-fire-front", "//182.92.126.35:8097", "fire-Micro"]]
+VITE_APP_SUB_APP = [["micro-vent-3dModal", "//192.168.1.16:8091", "micro-vent-3dModal"],["micro-vent-2dModal", "//localhost:8088", "micro-vent-2dModal"],["micro-vent-doc", "//localhost:5173", "micro-vent-doc"],["micro-need-air", "//182.92.126.35:8093", "micro-need-air"], ["micro-fire-front", "//182.92.126.35:8097", "fire-Micro"]]
 # VITE_APP_SUB_APP = [["micro-vent-3dModal", "//localhost:8091/"], ["micro-need-air", "//localhost:8099/"], ["micro-fire-front", "//localhost:8090/"]]

+ 1 - 1
package.json

@@ -73,7 +73,7 @@
     "pinia": "2.1.6",
     "plyr": "^3.7.8",
     "print-js": "^1.6.0",
-    "qiankun": "^2.8.4",
+    "qiankun": "^2.10.16",
     "qrcode": "^1.5.3",
     "qrcodejs2": "0.0.2",
     "qs": "^6.11.2",

+ 1 - 1
pnpm-lock.yaml

@@ -162,7 +162,7 @@ importers:
         specifier: ^1.6.0
         version: 1.6.0
       qiankun:
-        specifier: ^2.8.4
+        specifier: ^2.10.16
         version: 2.10.16
       qrcode:
         specifier: ^1.5.3

BIN
src/assets/images/home-container/configurable/middata_bg.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-1.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-2.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-3.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-4.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-5.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb1.png


BIN
src/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb2.png


BIN
src/assets/images/sealedGoaf/configurable/table/table1-label.png


BIN
src/assets/images/sealedGoaf/views/home/module-title.png


BIN
src/assets/images/vent/home/2.5D.png


BIN
src/assets/images/vent/home/3D.png


BIN
src/assets/images/vent/loginDataCenter/dataCenterBg.png


BIN
src/assets/images/vent/loginDataCenter/loginForm.png


+ 3 - 0
src/assets/images/vent/loginDataCenter/password.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="19.702" height="25.838" viewBox="0 0 19.702 25.838">
+  <path id="路径_57147" data-name="路径 57147" d="M191.747,92.91h-17.7a1,1,0,0,1-1-1v-12.7a1,1,0,0,1,1-1h.7V76c-.015-.238-.03-.49-.03-.743a8.187,8.187,0,1,1,16.373,0V78.2h.669a1,1,0,0,1,1,1V91.885A1,1,0,0,1,191.747,92.91ZM182.4,86.343v2.3h1v-2.3a2.169,2.169,0,1,0-2.674-2.11A2.208,2.208,0,0,0,182.4,86.343Zm6.508-11.01v-.059a6.01,6.01,0,1,0-12.02,0v2.942h12.035Z" transform="translate(-173.056 -67.072)" fill="#fff"/>
+</svg>

+ 3 - 0
src/assets/images/vent/loginDataCenter/user.svg

@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="25.542" height="27.884" viewBox="0 0 25.542 27.884">
+  <path id="路径_57143" data-name="路径 57143" d="M112.367,79.9c-5.431,0-9.487,3.519-9.487,8.63v.551c0,2.671,4.346,2.671,9.849,2.671h5.844c5.285,0,9.849,0,9.849-2.671v-.551c0-5.111-4.056-8.63-9.487-8.63Zm2.992-1.345a7.357,7.357,0,1,0-7.8-7.345,7.592,7.592,0,0,0,7.8,7.345Z" transform="translate(-102.88 -63.87)" fill="#fff"/>
+</svg>

BIN
src/assets/images/vent/loginNeiye/1-1.png


BIN
src/assets/images/vent/loginNeiye/1-2.png


BIN
src/assets/images/vent/loginNeiye/bg.png


BIN
src/assets/images/vent/loginNeiye/btn1.png


BIN
src/assets/images/vent/loginNeiye/btn2.png


+ 6 - 0
src/assets/images/vent/loginNeiye/pass.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="13.08" height="14.807" viewBox="0 0 13.08 14.807">
+  <g id="组_15950" data-name="组 15950" transform="translate(-63.5 0.5)">
+    <path id="路径_57144" data-name="路径 57144" d="M74.813,5.177h-.458V4.314a4.253,4.253,0,0,0-.341-1.676,4.352,4.352,0,0,0-2.3-2.3,4.287,4.287,0,0,0-3.352,0,4.352,4.352,0,0,0-2.3,2.3,4.253,4.253,0,0,0-.341,1.676v.863h-.458A1.269,1.269,0,0,0,64,6.445v6.094a1.269,1.269,0,0,0,1.267,1.267h9.546a1.269,1.269,0,0,0,1.267-1.267V6.445A1.269,1.269,0,0,0,74.813,5.177Zm-8.278-.863a3.505,3.505,0,0,1,7.011,0v.863H66.535Zm8.737,8.224a.46.46,0,0,1-.458.458H65.267a.46.46,0,0,1-.458-.458V6.445a.46.46,0,0,1,.458-.458h9.546a.46.46,0,0,1,.458.458Z" fill="#fff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1"/>
+    <path id="路径_57145" data-name="路径 57145" d="M482.4,608a.406.406,0,0,0-.4.4v1.78a.4.4,0,1,0,.809,0V608.4A.406.406,0,0,0,482.4,608Z" transform="translate(-412.364 -599.803)" fill="#fff" stroke="rgba(0,0,0,0)" stroke-miterlimit="10" stroke-width="1"/>
+  </g>
+</svg>

+ 4 - 0
src/assets/images/vent/loginNeiye/user.svg

@@ -0,0 +1,4 @@
+<svg id="组_15951" data-name="组 15951" xmlns="http://www.w3.org/2000/svg" width="15.126" height="15.246" viewBox="0 0 15.126 15.246">
+  <path id="路径_57131" data-name="路径 57131" d="M270.129,11.018a3.111,3.111,0,1,1-3.111,3.111,3.121,3.121,0,0,1,3.111-3.111m0-.778a3.889,3.889,0,1,0,3.889,3.889A3.851,3.851,0,0,0,270.129,10.24Z" transform="translate(-262.385 -10.24)" fill="#fff"/>
+  <path id="路径_57132" data-name="路径 57132" d="M20.079,471.04v.778a17.075,17.075,0,0,1,2.567.233,2.475,2.475,0,0,1,.7.233c.156.078.233.078.389.156,2.489,1.167,3.345,3.189,3.034,4.667-.156.622-.544,1.4-1.322,1.478H14.634c-.7,0-1.089-.7-1.245-1.322-.311-1.478.467-3.578,3.111-4.823a10.47,10.47,0,0,1,3.578-.545v-.856m0,0a11.912,11.912,0,0,0-3.889.622c-5.134,2.411-4.2,7.623-1.556,7.623h10.89c2.722-.233,3.345-5.367-1.556-7.623-.078,0-.7-.233-1.167-.311A12.5,12.5,0,0,0,20.079,471.04Z" transform="translate(-12.49 -464.039)" fill="#fff"/>
+</svg>

+ 1362 - 1344
src/components/AIChat/MiniChat.vue

@@ -5,7 +5,7 @@
       <img src="@/assets/images/vent/home/wakeBtn.png" />
     </div>
   </div>
-  <div class="container">
+  <div class="mini-container">
     <div v-if="isShowChatBroad" class="mini-chat">
       <!-- 左侧折叠区域 -->
       <div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
@@ -157,7 +157,7 @@
           <div class="file-preview" v-else-if="uploadedFiles.name && uploadedFiles.size && uploadfileType !== 'image'">
             <div class="file-info">
               📄{{ uploadedFiles.name }} (大小:{{ (uploadedFiles.size / 1024).toFixed(2) }}KB)
-              <DeleteOutlined @click="deleteFile()"></DeleteOutlined>
+              <DeleteOutlined @click="deleteFile()" />
             </div>
           </div>
           <a-textarea
@@ -199,7 +199,7 @@
               :remove="handleRemove"
             >
               <a-button class="upload-btn">
-                <UploadOutlined></UploadOutlined>
+                <UploadOutlined />
                 从本地上传
               </a-button>
             </a-upload>
@@ -245,1486 +245,1504 @@
 </template>
 
 <script lang="ts" setup>
-import VueOfficePdf from '@vue-office/pdf/lib/v3/vue-office-pdf.mjs';
-import VueOfficeDocx from '@vue-office/docx/lib/v3/vue-office-docx.mjs';
-import VueOfficeExcel from '@vue-office/excel/lib/v3/vue-office-excel.mjs';
-import { ref, onMounted, nextTick, watch, onUnmounted } 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,
-  RightOutlined,
-  DownOutlined,
-  FastForwardFilled,
-  RedoOutlined,
-} from '@ant-design/icons-vue';
-import { createVNode } from 'vue';
-import { marked } from 'marked';
-import katex from 'katex';
-import 'katex/dist/katex.min.css';
-import * as echarts from 'echarts';
-const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
-const inputText = ref(''); // 输入框内容
-const refreshText = ref(''); //重新生成文本
-const sessionHistory = ref([]);
-const isShowChatBroad = ref(false);
-const editingId = ref<number | null>(null);
-const editText = ref('');
-const currentSessionID = ref('');
-const taskID = ref('');
-const messageID = ref('');
-const open = ref<boolean>(false);
-const isThinking = ref(false); //深度思考是否开启
-const Thinking = ref(false);
-const isShowDoc = ref(false);
-const fileType = ref('');
-const fileUrl = ref('');
-const APIKEY = ref('Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd');
-interface ListItem {
-  id: number;
-  name?: string;
-}
-interface Message {
-  id: string; // 唯一标识(可用时间戳生成)
-  type: 'user' | 'system' | 'response';
-  content: String; // 原始 Markdown 字符串(用于拼接)
-  parsedContent: String; // 解析后的 HTML(用于渲染)
-  contentR1: String; // 原始思考过程 Markdown
-  parsedContentR1: String; // 解析后的思考过程 HTML
-  timestamp: number; // 排序依据
-  isShowThink: boolean; //深度思考展示
-  message_files?: Array<[]>; //文件信息
-  echartsOption?: Object; //echart图表
-}
-// 定义消息历史数组类型
-const messageHistory = ref<Message[]>([]);
-const isFold = ref(true); // 是否折叠
-const userid = useUserStore().getUserInfo.id as string;
-const uploadedFiles = ref({}); //接口获取文件信息
-const askFile = ref({});
-const uploadfileType = ref(''); //提问框文件展示
-const askFileType = ref(''); //回答区域文件展示
-const message_files = ref([]);
-const fileList = ref([]);
-const suggestList = ref([]); //建议列表
-const loading = ref(false);
-// const chartRef = ref(null);
-const validEchartsOption = ref(null);
-const optionTextRef = ref(null);
-const echartsOption = ref({});
-let myChart: echarts.EChartsType | null = null;
-const chartDom = ref<HTMLDivElement | null>(null);
-// 初始化图表
-const initChart = () => {
-  const targetNode = document.getElementById('chart-target');
-  if (!targetNode) {
-    console.error('目标插入节点不存在!');
-    return;
+  import VueOfficePdf from '@vue-office/pdf/lib/v3/vue-office-pdf.mjs';
+  import VueOfficeDocx from '@vue-office/docx/lib/v3/vue-office-docx.mjs';
+  import VueOfficeExcel from '@vue-office/excel/lib/v3/vue-office-excel.mjs';
+  import { ref, onMounted, nextTick, watch, onUnmounted } 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,
+    RightOutlined,
+    DownOutlined,
+    FastForwardFilled,
+    RedoOutlined,
+  } from '@ant-design/icons-vue';
+  import { createVNode } from 'vue';
+  import { marked } from 'marked';
+  import katex from 'katex';
+  import 'katex/dist/katex.min.css';
+  import * as echarts from 'echarts';
+  const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
+  const inputText = ref(''); // 输入框内容
+  const refreshText = ref(''); //重新生成文本
+  const sessionHistory = ref([]);
+  const isShowChatBroad = ref(false);
+  const editingId = ref<number | null>(null);
+  const editText = ref('');
+  const currentSessionID = ref('');
+  const taskID = ref('');
+  const messageID = ref('');
+  const open = ref<boolean>(false);
+  const isThinking = ref(false); //深度思考是否开启
+  const Thinking = ref(false);
+  const isShowDoc = ref(false);
+  const fileType = ref('');
+  const fileUrl = ref('');
+  const APIKEY = ref('Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd');
+  interface ListItem {
+    id: number;
+    name?: string;
   }
+  interface Message {
+    id: string; // 唯一标识(可用时间戳生成)
+    type: 'user' | 'system' | 'response';
+    content: String; // 原始 Markdown 字符串(用于拼接)
+    parsedContent: String; // 解析后的 HTML(用于渲染)
+    contentR1: String; // 原始思考过程 Markdown
+    parsedContentR1: String; // 解析后的思考过程 HTML
+    timestamp: number; // 排序依据
+    isShowThink: boolean; //深度思考展示
+    message_files?: Array<[]>; //文件信息
+    echartsOption?: Object; //echart图表
+  }
+  // 定义消息历史数组类型
+  const messageHistory = ref<Message[]>([]);
+  const isFold = ref(true); // 是否折叠
+  const userid = useUserStore().getUserInfo.id as string;
+  const uploadedFiles = ref({}); //接口获取文件信息
+  const askFile = ref({});
+  const uploadfileType = ref(''); //提问框文件展示
+  const askFileType = ref(''); //回答区域文件展示
+  const message_files = ref([]);
+  const fileList = ref([]);
+  const suggestList = ref([]); //建议列表
+  const loading = ref(false);
+  // const chartRef = ref(null);
+  const validEchartsOption = ref(null);
+  const optionTextRef = ref(null);
+  const echartsOption = ref({});
+  let myChart: echarts.EChartsType | null = null;
+  const chartDom = ref<HTMLDivElement | null>(null);
+  // 初始化图表
+  const initChart = () => {
+    const targetNode = document.getElementById('chart-target');
+    if (!targetNode) {
+      console.error('目标插入节点不存在!');
+      return;
+    }
 
-  // 2. 创建 ECharts 容器(带唯一 id,方便后续清理)
-  const chartContainer = document.createElement('div');
-  chartContainer.id = 'dynamic-echarts-container';
-  // 必须设置宽高(继承目标节点宽高,或自定义)
-  chartContainer.style.width = '100%';
-  chartContainer.style.height = '100%';
-  chartContainer.style.border = '1px solid #eee';
+    // 2. 创建 ECharts 容器(带唯一 id,方便后续清理)
+    const chartContainer = document.createElement('div');
+    chartContainer.id = 'dynamic-echarts-container';
+    // 必须设置宽高(继承目标节点宽高,或自定义)
+    chartContainer.style.width = '100%';
+    chartContainer.style.height = '100%';
+    chartContainer.style.border = '1px solid #eee';
 
-  // 3. 初始化 ECharts 实例到新建容器
-  myChart = echarts.init(chartContainer);
-  myChart.setOption(echartsOption.value);
+    // 3. 初始化 ECharts 实例到新建容器
+    myChart = echarts.init(chartContainer);
+    myChart.setOption(echartsOption.value);
 
-  // 4. 将容器插入目标节点(清空原有内容,可选)
-  targetNode.innerHTML = ''; // 清空目标节点原有内容(如需保留可注释)
-  targetNode.appendChild(chartContainer);
+    // 4. 将容器插入目标节点(清空原有内容,可选)
+    targetNode.innerHTML = ''; // 清空目标节点原有内容(如需保留可注释)
+    targetNode.appendChild(chartContainer);
 
-  // 5. 监听窗口resize,自适应图表
-  window.addEventListener('resize', handleResize);
-};
-// 图表自适应方法
-const handleResize = () => {
-  myChart?.resize();
-};
-// 监听配置变化,确保DOM更新后初始化
-watch(
-  () => echartsOption.value,
-  async (newOption) => {
-    if (!newOption || Object.keys(newOption).length === 0) return;
-    await nextTick(); // 等待 DOM 渲染
-    initChart();
-  },
-  { deep: true, immediate: true }
-);
+    // 5. 监听窗口resize,自适应图表
+    window.addEventListener('resize', handleResize);
+  };
+  // 图表自适应方法
+  const handleResize = () => {
+    myChart?.resize();
+  };
+  // 监听配置变化,确保DOM更新后初始化
+  watch(
+    () => echartsOption.value,
+    async (newOption) => {
+      if (!newOption || Object.keys(newOption).length === 0) return;
+      await nextTick(); // 等待 DOM 渲染
+      initChart();
+    },
+    { deep: true, immediate: true }
+  );
 
-// 文件预览
-const handleRendered = () => {
-  loading.value = false;
-  console.log('文件渲染完成');
-};
-const handleError = (error) => {
-  loading.value = false;
-  console.error('文件预览错误:', error);
-};
-// function previewFile(data) {
-//   fileType.value = data.extension;
-//   fileUrl.value = data.source_url;
-// }
-//启用深度思考
-const toggleThinking = () => {
-  isThinking.value = !isThinking.value;
-  if (isThinking.value) {
-    APIKEY.value = 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi';
-  } else {
-    APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
-  }
-};
-// 折叠思考过程
-const isShow = (message) => {
-  message.isShowThink = !message.isShowThink;
-};
-const dialogAreaRef = ref(null);
-// 滚动到底部的方法
-const scrollToBottom = async () => {
-  // 等待 DOM 更新(如消息渲染完成)
-  await nextTick();
-  if (dialogAreaRef.value) {
-    const el = dialogAreaRef.value;
-    // 关键:scrollTop = scrollHeight(滚动内容总高度)
-    el.scrollTop = el.scrollHeight;
-  }
-};
+  // 文件预览
+  const handleRendered = () => {
+    loading.value = false;
+    console.log('文件渲染完成');
+  };
+  const handleError = (error) => {
+    loading.value = false;
+    console.error('文件预览错误:', error);
+  };
+  // function previewFile(data) {
+  //   fileType.value = data.extension;
+  //   fileUrl.value = data.source_url;
+  // }
+  //启用深度思考
+  const toggleThinking = () => {
+    isThinking.value = !isThinking.value;
+    if (isThinking.value) {
+      APIKEY.value = 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi';
+    } else {
+      APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
+    }
+  };
+  // 折叠思考过程
+  const isShow = (message) => {
+    message.isShowThink = !message.isShowThink;
+  };
+  const dialogAreaRef = ref(null);
+  // 滚动到底部的方法
+  const scrollToBottom = async () => {
+    // 等待 DOM 更新(如消息渲染完成)
+    await nextTick();
+    if (dialogAreaRef.value) {
+      const el = dialogAreaRef.value;
+      // 关键:scrollTop = scrollHeight(滚动内容总高度)
+      el.scrollTop = el.scrollHeight;
+    }
+  };
 
-// 点击建议项时的处理函数
-const handleSuggestClick = (text) => {
-  // 将选中的建议填充到文本框
-  inputText.value = text;
-};
-function showAIChat() {
-  isShowChatBroad.value = !isShowChatBroad.value;
-  if (isShowChatBroad) {
-    isShowDoc.value = false;
+  // 点击建议项时的处理函数
+  const handleSuggestClick = (text) => {
+    // 将选中的建议填充到文本框
+    inputText.value = text;
+  };
+  function showAIChat() {
+    isShowChatBroad.value = !isShowChatBroad.value;
+    if (isShowChatBroad.value) {
+      isShowDoc.value = false;
+    }
   }
-}
-//复制消息
-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('复制命令执行失败');
+  //复制消息
+  function copyToClipboard(text) {
+    if (!text || text.trim() === '') {
+      message.warn('没有可复制的内容');
+      return;
     }
-  } catch (err) {
-    console.error('复制失败:', err);
-    message.error('复制失败,请手动复制');
-  } finally {
-    document.body.removeChild(textarea);
-  }
-}
-const initMarked = () => {
-  marked.setOptions({
-    gfm: true, // 启用GitHub风格的Markdown,包含表格
-    breaks: false, // 禁用换行符转换
-  });
-};
-// LaTeX 公式渲染
-const renderLatexInHtml = (html) => {
-  // 匹配块级公式($$...$$)
-  const blockRegex = /\$\$(.*?)\$\$/gs;
-  // 匹配行内公式($...$)
-  const inlineRegex = /\$(.*?)\$/g;
-  // // 替换块级公式(居中显示)
-  html = html.replace(blockRegex, (match, formula) => {
-    return katex.renderToString(formula.trim(), {
-      displayMode: true,
-      throwOnError: false,
-      strict: false,
-      trust: true,
+    // 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);
+    }
+  }
+  const initMarked = () => {
+    marked.setOptions({
+      gfm: true, // 启用GitHub风格的Markdown,包含表格
+      breaks: false, // 禁用换行符转换
     });
-  });
-  // 替换行内公式(行内显示)
-  html = html.replace(inlineRegex, (match, formula) => {
-    return katex.renderToString(formula.trim(), {
-      displayMode: false,
-      throwOnError: false,
-      strict: false,
+  };
+  // LaTeX 公式渲染
+  const renderLatexInHtml = (html) => {
+    // 匹配块级公式($$...$$)
+    const blockRegex = /\$\$(.*?)\$\$/gs;
+    // 匹配行内公式($...$)
+    const inlineRegex = /\$(.*?)\$/g;
+    // // 替换块级公式(居中显示)
+    html = html.replace(blockRegex, (match, formula) => {
+      return katex.renderToString(formula.trim(), {
+        displayMode: true,
+        throwOnError: false,
+        strict: false,
+        trust: true,
+      });
+    });
+    // 替换行内公式(行内显示)
+    html = html.replace(inlineRegex, (match, formula) => {
+      return katex.renderToString(formula.trim(), {
+        displayMode: false,
+        throwOnError: false,
+        strict: false,
+      });
     });
-  });
-  return html;
-};
-// Markdown + LaTeX 解析
-const parseMarkdownWithLatex = (mdStr) => {
-  if (!mdStr) return '';
-  try {
-    let html = marked(mdStr); // Markdown → HTML
-    html = renderLatexInHtml(html); // 替换公式
     return html;
-  } catch (error) {
-    console.error('解析失败:', error);
-    return mdStr; // 降级显示原始字符串
-  }
-};
-// echart 数据解析
-const parseEchart = (data) => {
-  const reg = /```[\s\S]*?\n([\s\S]*?)```/;
-  const match = data.match(reg);
+  };
+  // Markdown + LaTeX 解析
+  const parseMarkdownWithLatex = (mdStr) => {
+    if (!mdStr) return '';
+    try {
+      let html = marked(mdStr); // Markdown → HTML
+      html = renderLatexInHtml(html); // 替换公式
+      return html;
+    } catch (error) {
+      console.error('解析失败:', error);
+      return mdStr; // 降级显示原始字符串
+    }
+  };
+  // echart 数据解析
+  const parseEchart = (data) => {
+    const reg = /```[\s\S]*?\n([\s\S]*?)```/;
+    const match = data.match(reg);
 
-  if (match && match[1]) {
-    // 提取匹配到的JSON部分并解析
-    echartsOption.value = JSON.parse(match[1].trim());
-  }
-  return match;
-};
-const triggerChart = async () => {
-  await nextTick();
-  initChart();
-};
-//重新生成
-const refresh = () => {
-  handleSend(refreshText.value);
-};
-//重新编辑
-const editAsk = (data) => {
-  inputText.value = data;
-};
-//获取消息列表
-async function handleSend(data, files?) {
-  refreshText.value = data;
-  inputText.value = '';
-  if (files) {
-    askFileType.value = files?.mime_type?.startsWith('image') ? 'image' : 'document';
-  }
-  // message_files.value.push(files);
-  const fileList = files ? [files] : [];
-  uploadfileType.value = '';
-  if (isThinking) {
-    const newMessage = {
-      id: `user_${Date.now()}`,
-      type: 'user',
-      content: '',
-      parsedContent: data,
-      contentR1: '',
-      parsedContentR1: '',
-      timestamp: Date.now(),
-      isShowThink: true,
-      message_files: fileList,
-    };
-    messageHistory.value.push(newMessage);
-    message_files.value.push(files);
-  } else {
-    const newMessage = {
-      id: `user_${Date.now()}`,
-      type: 'user',
-      content: '',
-      parsedContent: data,
-      contentR1: '',
-      parsedContentR1: '',
-      timestamp: Date.now(),
-      isShowThink: false,
-      message_files: fileList,
-    };
-    messageHistory.value.push(newMessage);
-    message_files.value.push(files);
-  }
-  try {
-    const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: APIKEY.value,
-      },
-      body: JSON.stringify({
-        conversation_id: currentSessionID.value ? currentSessionID.value : '',
-        query: data,
-        ...(askFile.value?.id
-          ? {
-              files: [
-                {
-                  type: askFileType.value == 'image' ? 'image' : 'document',
-                  transfer_method: 'local_file',
-                  upload_file_id: askFile.value.id,
-                  url: '',
-                },
-              ],
-            }
-          : {}),
-        response_mode: 'streaming',
-        user: userid,
-        inputs: {},
-      }),
-    });
-    if (!response.ok) {
-      throw new Error(`HTTP error! status: ${response.status}`);
+    if (match && match[1]) {
+      // 提取匹配到的JSON部分并解析
+      echartsOption.value = JSON.parse(match[1].trim());
     }
-    uploadedFiles.value = {};
-    const decoder = new TextDecoder('utf-8');
-    const reader = response.body.getReader();
-    let textBuffer = ''; // 使用字符串缓冲区来累积数据
-    // 在组件中定义
-    const currentProcessingMessage = ref(null);
-    if (isThinking) {
+    return match;
+  };
+  const triggerChart = async () => {
+    await nextTick();
+    initChart();
+  };
+  //重新生成
+  const refresh = () => {
+    handleSend(refreshText.value);
+  };
+  //重新编辑
+  const editAsk = (data) => {
+    inputText.value = data;
+  };
+  //获取消息列表
+  async function handleSend(data, files?) {
+    refreshText.value = data;
+    inputText.value = '';
+    if (files) {
+      askFileType.value = files?.mime_type?.startsWith('image') ? 'image' : 'document';
+    }
+    // message_files.value.push(files);
+    const fileList = files ? [files] : [];
+    uploadfileType.value = '';
+    if (isThinking.value) {
       const newMessage = {
-        id: `response_${Date.now()}`,
-        type: 'response',
+        id: `user_${Date.now()}`,
+        type: 'user',
         content: '',
-        parsedContent: '',
+        parsedContent: data,
         contentR1: '',
         parsedContentR1: '',
         timestamp: Date.now(),
         isShowThink: true,
+        message_files: fileList,
       };
       messageHistory.value.push(newMessage);
-      currentProcessingMessage.value = newMessage; // 保存引用
+      message_files.value.push(files);
     } else {
       const newMessage = {
-        id: `response_${Date.now()}`,
-        type: 'response',
+        id: `user_${Date.now()}`,
+        type: 'user',
         content: '',
-        parsedContent: '',
+        parsedContent: data,
         contentR1: '',
         parsedContentR1: '',
         timestamp: Date.now(),
         isShowThink: false,
+        message_files: fileList,
       };
       messageHistory.value.push(newMessage);
-      currentProcessingMessage.value = newMessage; // 保存引用
+      message_files.value.push(files);
     }
-    while (true) {
-      const { done, value } = await reader.read();
-      if (done) {
-        if (textBuffer) {
-          processLine(textBuffer);
-        }
-        break;
+    try {
+      const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: APIKEY.value,
+        },
+        body: JSON.stringify({
+          conversation_id: currentSessionID.value ? currentSessionID.value : '',
+          query: data,
+          ...(askFile.value?.id
+            ? {
+                files: [
+                  {
+                    type: askFileType.value == 'image' ? 'image' : 'document',
+                    transfer_method: 'local_file',
+                    upload_file_id: askFile.value.id,
+                    url: '',
+                  },
+                ],
+              }
+            : {}),
+          response_mode: 'streaming',
+          user: userid,
+          inputs: {},
+        }),
+      });
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      uploadedFiles.value = {};
+      const decoder = new TextDecoder('utf-8');
+      const reader = response.body.getReader();
+      let textBuffer = ''; // 使用字符串缓冲区来累积数据
+      // 在组件中定义
+      const currentProcessingMessage = ref(null);
+      if (isThinking.value) {
+        const newMessage = {
+          id: `response_${Date.now()}`,
+          type: 'response',
+          content: '',
+          parsedContent: '',
+          contentR1: '',
+          parsedContentR1: '',
+          timestamp: Date.now(),
+          isShowThink: true,
+        };
+        messageHistory.value.push(newMessage);
+        currentProcessingMessage.value = newMessage; // 保存引用
+      } else {
+        const newMessage = {
+          id: `response_${Date.now()}`,
+          type: 'response',
+          content: '',
+          parsedContent: '',
+          contentR1: '',
+          parsedContentR1: '',
+          timestamp: Date.now(),
+          isShowThink: false,
+        };
+        messageHistory.value.push(newMessage);
+        currentProcessingMessage.value = newMessage; // 保存引用
       }
-      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);
+      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);
+          if (line) {
+            processLine(line);
+          }
         }
       }
-    }
-    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 === currentProcessingMessage.value.id);
-                if (!targetMessage) break;
-                let currentChunk = data.answer;
+      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 === currentProcessingMessage.value.id);
+                  if (!targetMessage) break;
+                  let currentChunk = data.answer;
 
-                // 检查是否包含起始标签
-                const startIndex = currentChunk.indexOf('<think>');
-                if (startIndex !== -1) {
-                  // 找到起始标签:将标签前的内容作为正文
-                  if (startIndex > 0) {
-                    targetMessage.content += currentChunk.substring(0, startIndex);
-                    targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
-                  }
-                  // 进入思考模式,将标签后的内容追加到contentR1
-                  Thinking.value = true;
-                  const remainingContent = currentChunk.substring(startIndex + '<think>'.length);
-                  if (remainingContent) {
-                    targetMessage.contentR1 += remainingContent;
-                    targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
-                  }
-                } else if (Thinking.value) {
-                  // 结束标签
-                  const endIndex = currentChunk.indexOf('</think>');
-                  if (endIndex !== -1) {
-                    // 找到结束标签:标签前的内容追加到contentR1
-                    if (endIndex > 0) {
-                      targetMessage.contentR1 += currentChunk.substring(0, endIndex);
-                      targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
+                  // 检查是否包含起始标签
+                  const startIndex = currentChunk.indexOf('<think>');
+                  if (startIndex !== -1) {
+                    // 找到起始标签:将标签前的内容作为正文
+                    if (startIndex > 0) {
+                      targetMessage.content += currentChunk.substring(0, startIndex);
+                      targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
                     }
-                    // 将标签后的内容作为正文
-                    const remainingContent = currentChunk.substring(endIndex + '</think>'.length);
+                    // 进入思考模式,将标签后的内容追加到contentR1
+                    Thinking.value = true;
+                    const remainingContent = currentChunk.substring(startIndex + '<think>'.length);
                     if (remainingContent) {
-                      targetMessage.content += remainingContent;
-                      targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
+                      targetMessage.contentR1 += remainingContent;
+                      targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
+                    }
+                  } else if (Thinking.value) {
+                    // 结束标签
+                    const endIndex = currentChunk.indexOf('</think>');
+                    if (endIndex !== -1) {
+                      // 找到结束标签:标签前的内容追加到contentR1
+                      if (endIndex > 0) {
+                        targetMessage.contentR1 += currentChunk.substring(0, endIndex);
+                        targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
+                      }
+                      // 将标签后的内容作为正文
+                      const remainingContent = currentChunk.substring(endIndex + '</think>'.length);
+                      if (remainingContent) {
+                        targetMessage.content += remainingContent;
+                        targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
+                      }
+                      targetMessage.content += currentChunk;
+                      Thinking.value = false;
+                      targetMessage.isShowThink = false;
+                    } else {
+                      // 没有结束标签,继续追加到contentR1
+                      targetMessage.contentR1 += currentChunk;
+                      targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
                     }
-                    targetMessage.content += currentChunk;
-                    Thinking.value = false;
-                    targetMessage.isShowThink = false;
                   } else {
-                    // 没有结束标签,继续追加到contentR1
-                    targetMessage.contentR1 += currentChunk;
-                    targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
+                    targetMessage.content += currentChunk;
+                    targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
                   }
-                } else {
-                  targetMessage.content += currentChunk;
-                  targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
+                  scrollToBottom(); // 每次接收消息都滚动
                 }
-                scrollToBottom(); // 每次接收消息都滚动
-              }
-              if (data.task_id && !taskID.value) taskID.value = data.task_id;
-              if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
-              if (data.message_id && !messageID.value) {
-                messageID.value = data.message_id;
-              }
-              break;
+                if (data.task_id && !taskID.value) taskID.value = data.task_id;
+                if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
+                if (data.message_id && !messageID.value) {
+                  messageID.value = data.message_id;
+                }
+                break;
+            }
+          } catch (error) {
+            console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
           }
-        } catch (error) {
-          console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
         }
       }
+      getNextSuggest();
+    } catch (error) {
+      console.error('Error in handleSend:', error);
+      // 在 UI 上显示错误信息
+      messageHistory.value.push({
+        id: `system_${Date.now()}`,
+        type: 'system',
+        content: '',
+        parsedContent: '请求错误',
+        contentR1: '',
+        parsedContentR1: '',
+        timestamp: Date.now(),
+        isShowThink: false,
+      });
     }
-    getNextSuggest();
-  } catch (error) {
-    console.error('Error in handleSend:', error);
-    // 在 UI 上显示错误信息
-    messageHistory.value.push({
-      id: `system_${Date.now()}`,
-      type: 'system',
-      content: '',
-      parsedContent: '请求错误',
-      contentR1: '',
-      parsedContentR1: '',
-      timestamp: Date.now(),
-      isShowThink: false,
-    });
   }
-}
-//创建新对话
-async function addNew() {
-  messageHistory.value = [];
-  currentSessionID.value = '';
-  taskID.value = '';
-  messageID.value = '';
-}
-// 上传文件
-const showModal = (data) => {
-  open.value = data;
-};
-const close = () => {
-  isShowDoc.value = false;
-};
-const showPreview = (data) => {
-  fileType.value = data[0].extension ? data[0].extension : data[0].filename.split('.').pop();
-  fileUrl.value = data[0].source_url ? data[0].source_url : data[0].url;
-  isShowDoc.value = true;
-};
-//删除文件
-const deleteFile = () => {
-  uploadedFiles.value = {};
-};
-// 上传文件
-const handleBeforeUpload = async (file) => {
-  const formData = new FormData();
-  formData.append('file', file);
-  formData.append('user', userid);
+  //创建新对话
+  async function addNew() {
+    messageHistory.value = [];
+    currentSessionID.value = '';
+    taskID.value = '';
+    messageID.value = '';
+  }
+  // 上传文件
+  const showModal = (data) => {
+    open.value = data;
+  };
+  const close = () => {
+    isShowDoc.value = false;
+  };
+  const showPreview = (data) => {
+    fileType.value = data[0].extension ? data[0].extension : data[0].filename.split('.').pop();
+    fileUrl.value = data[0].source_url ? data[0].source_url : data[0].url;
+    isShowDoc.value = true;
+  };
+  //删除文件
+  const deleteFile = () => {
+    uploadedFiles.value = {};
+  };
+  // 上传文件
+  const handleBeforeUpload = async (file) => {
+    const formData = new FormData();
+    formData.append('file', file);
+    formData.append('user', userid);
 
-  try {
-    const response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
-      method: 'POST',
-      headers: {
-        Authorization: APIKEY.value,
-      },
-      body: formData,
-    });
+    try {
+      const response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
+        method: 'POST',
+        headers: {
+          Authorization: APIKEY.value,
+        },
+        body: formData,
+      });
 
-    const result = await response.json();
-    if (response.ok) {
-      uploadedFiles.value = result;
-      askFile.value = result;
-      uploadfileType.value = result.mime_type.startsWith('image') ? 'image' : 'document';
-      // previewFile(result);
-    } else {
-      message.error(`上传失败: ${result.message || '网络错误'}`);
+      const result = await response.json();
+      if (response.ok) {
+        uploadedFiles.value = result;
+        askFile.value = result;
+        uploadfileType.value = result.mime_type.startsWith('image') ? 'image' : 'document';
+        // previewFile(result);
+      } else {
+        message.error(`上传失败: ${result.message || '网络错误'}`);
+      }
+    } catch (error) {
+      console.error('保存失败:', error);
+      message.error('上传失败,请重试');
     }
-  } catch (error) {
-    console.error('保存失败:', error);
-    message.error('上传失败,请重试');
-  }
-  return false;
-};
+    return false;
+  };
 
-const handleRemove = (file) => {
-  const index = fileList.value.findIndex((item) => item.uid === file.uid);
-  if (index !== -1) {
-    fileList.value.splice(index, 1);
-  }
-};
-//停止响应
-async function stopReq() {
-  try {
-    let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID.value}/stop`, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: APIKEY.value,
-      },
-      body: JSON.stringify({
-        user: userid,
-      }),
-    });
-    if (!response) {
-      throw new Error('Network response was not ok');
+  const handleRemove = (file) => {
+    const index = fileList.value.findIndex((item) => item.uid === file.uid);
+    if (index !== -1) {
+      fileList.value.splice(index, 1);
     }
-  } catch (error) {
-    console.error('保存失败:', error);
-  }
-}
-//获取具体会话记录
-async function sessionsHistory(id: string, retryCount = 0) {
-  const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi'];
-  // 最大重试次数
-  const maxRetries = API_KEYS.length - 1;
-  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: API_KEYS[retryCount],
-      },
-    });
-    if (!response.ok) {
-      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+  };
+  //停止响应
+  async function stopReq() {
+    try {
+      let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID.value}/stop`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: APIKEY.value,
+        },
+        body: JSON.stringify({
+          user: userid,
+        }),
+      });
+      if (!response) {
+        throw new Error('Network response was not ok');
+      }
+    } catch (error) {
+      console.error('保存失败:', error);
     }
-    const data = await response.json();
-    if (data.data.length > 0) {
-      messageHistory.value = [];
-      data.data.forEach((item: any) => {
-        currentSessionID.value = item.conversation_id;
-        messageHistory.value.push({
-          id: `user_${Date.now()}`,
-          type: 'user',
-          content: '',
-          parsedContent: item.query,
-          contentR1: '',
-          parsedContentR1: '',
-          message_files: item.message_files,
-          timestamp: Date.now(),
-          isShowThink: false,
-        });
-        const answer = item.answer;
-        // 查找<think>
-        const startIndex = answer.indexOf('<think>');
-        const endIndex = answer.indexOf('</think>');
-        const isChart = answer.indexOf('echarts');
-        let content = '';
-        let contentR1 = '';
-        // 根据标签判断
-        if (startIndex !== -1 && endIndex !== -1) {
-          content = answer.substring(0, startIndex) + answer.substring(endIndex + '</think>'.length);
-          contentR1 = answer.substring(startIndex + '<think>'.length, endIndex);
-        } else if (startIndex !== -1) {
-          // 只有起始标签
-          content = answer.substring(0, startIndex);
-          contentR1 = answer.substring(startIndex + '<think>'.length);
-        } else if (endIndex !== -1) {
-          // 只有结束标签
-          content = answer.substring(endIndex + '</think>'.length);
-          contentR1 = answer.substring(0, endIndex);
-        } else {
-          // 没有标签
-          content = answer;
-        }
-        if (isChart != -1) {
-          // 添加到消息历史
-          messageHistory.value.push({
-            id: `system_${Date.now()}`,
-            type: 'system',
-            content: '',
-            parsedContent: '',
-            contentR1: '',
-            parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
-            timestamp: Date.now(),
-            isShowThink: contentR1.length > 0, // 如果有思考内容则显示
-            echartsOption: parseEchart(content.trim()),
-          });
-          triggerChart();
-        } else {
-          // 添加到消息历史
+  }
+  //获取具体会话记录
+  async function sessionsHistory(id: string, retryCount = 0) {
+    const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi'];
+    // 最大重试次数
+    const maxRetries = API_KEYS.length - 1;
+    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: API_KEYS[retryCount],
+        },
+      });
+      if (!response.ok) {
+        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+      }
+      const data = await response.json();
+      if (data.data.length > 0) {
+        messageHistory.value = [];
+        data.data.forEach((item: any) => {
+          currentSessionID.value = item.conversation_id;
           messageHistory.value.push({
-            id: `system_${Date.now()}`,
-            type: 'system',
+            id: `user_${Date.now()}`,
+            type: 'user',
             content: '',
-            parsedContent: parseMarkdownWithLatex(content.trim()),
+            parsedContent: item.query,
             contentR1: '',
-            parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
+            parsedContentR1: '',
+            message_files: item.message_files,
             timestamp: Date.now(),
-            isShowThink: contentR1.length > 0, // 如果有思考内容则显示
+            isShowThink: false,
           });
-        }
-      });
+          const answer = item.answer;
+          // 查找<think>
+          const startIndex = answer.indexOf('<think>');
+          const endIndex = answer.indexOf('</think>');
+          const isChart = answer.indexOf('echarts');
+          let content = '';
+          let contentR1 = '';
+          // 根据标签判断
+          if (startIndex !== -1 && endIndex !== -1) {
+            content = answer.substring(0, startIndex) + answer.substring(endIndex + '</think>'.length);
+            contentR1 = answer.substring(startIndex + '<think>'.length, endIndex);
+          } else if (startIndex !== -1) {
+            // 只有起始标签
+            content = answer.substring(0, startIndex);
+            contentR1 = answer.substring(startIndex + '<think>'.length);
+          } else if (endIndex !== -1) {
+            // 只有结束标签
+            content = answer.substring(endIndex + '</think>'.length);
+            contentR1 = answer.substring(0, endIndex);
+          } else {
+            // 没有标签
+            content = answer;
+          }
+          if (isChart != -1) {
+            // 添加到消息历史
+            messageHistory.value.push({
+              id: `system_${Date.now()}`,
+              type: 'system',
+              content: '',
+              parsedContent: '',
+              contentR1: '',
+              parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
+              timestamp: Date.now(),
+              isShowThink: contentR1.length > 0, // 如果有思考内容则显示
+              echartsOption: parseEchart(content.trim()),
+            });
+            triggerChart();
+          } else {
+            // 添加到消息历史
+            messageHistory.value.push({
+              id: `system_${Date.now()}`,
+              type: 'system',
+              content: '',
+              parsedContent: parseMarkdownWithLatex(content.trim()),
+              contentR1: '',
+              parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
+              timestamp: Date.now(),
+              isShowThink: contentR1.length > 0, // 如果有思考内容则显示
+            });
+          }
+        });
+      }
+    } catch (error) {
+      // 判断是否达到最大重试次数
+      if (retryCount < maxRetries) {
+        console.warn(`请求失败,正在尝试第 ${retryCount + 2} 个 API Key...`);
+        return sessionsHistory(id, retryCount + 1);
+      } else {
+        console.error('所有 API Key 均尝试失败:', error);
+        throw error;
+      }
     }
-  } catch (error) {
-    // 判断是否达到最大重试次数
-    if (retryCount < maxRetries) {
-      console.warn(`请求失败,正在尝试第 ${retryCount + 2} 个 API Key...`);
-      return sessionsHistory(id, retryCount + 1);
-    } else {
-      console.error('所有 API Key 均尝试失败:', error);
-      throw error;
+    editingId.value = null;
+  }
+  //获取下一轮建议问题列表
+  //停止响应
+  async function getNextSuggest() {
+    try {
+      let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
+        method: 'GET',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: APIKEY.value,
+        },
+      });
+      const data = await response.json();
+      suggestList.value = data.data;
+      if (!response) {
+        throw new Error('Network response was not ok');
+      }
+    } catch (error) {
+      console.error('保存失败:', error);
     }
   }
-  editingId.value = null;
-}
-//获取下一轮建议问题列表
-//停止响应
-async function getNextSuggest() {
-  try {
-    let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
-      method: 'GET',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: APIKEY.value,
-      },
-    });
-    const data = await response.json();
-    suggestList.value = data.data;
-    if (!response) {
-      throw new Error('Network response was not ok');
+  //编辑标题
+  const startEditing = (item: ListItem) => {
+    editingId.value = item.id;
+    editText.value = item.name || '';
+  };
+  // 保存修改
+  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: APIKEY.value,
+        },
+        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);
     }
-  } catch (error) {
-    console.error('保存失败:', error);
-  }
-}
-//编辑标题
-const startEditing = (item: ListItem) => {
-  editingId.value = item.id;
-  editText.value = item.name || '';
-};
-// 保存修改
-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: APIKEY.value,
+    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: APIKEY.value,
+            },
+            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: '删除会话时出现错误,请稍后重试。',
+          });
+        }
       },
-      body: JSON.stringify({
-        name: editText.value,
-        user: userid,
-      }),
     });
-    if (!response.ok) {
-      throw new Error('Network response was not ok');
+  };
+  const fold = () => {
+    isFold.value = !isFold.value;
+    if (!isFold.value) {
+      getHistoryList();
     }
-    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: APIKEY.value,
-          },
-          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() {
-  sessionHistory.value = [];
-  try {
-    const fetch1 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
-      method: 'get',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
-      },
-    })
-      .then((res) => {
-        return res.json();
+  };
+  // 获取历史会话列表
+  async function getHistoryList() {
+    sessionHistory.value = [];
+    try {
+      const fetch1 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
+        method: 'get',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
+        },
       })
-      .catch((err) => {
-        console.error('第一个请求错误:', err);
-        return { data: [] }; // 失败时返回空数组
-      });
+        .then((res) => {
+          return res.json();
+        })
+        .catch((err) => {
+          console.error('第一个请求错误:', err);
+          return { data: [] }; // 失败时返回空数组
+        });
 
-    const fetch2 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
-      method: 'get',
-      headers: {
-        'Content-Type': 'application/json',
-        Authorization: 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi',
-      },
-    })
-      .then((res) => {
-        return res.json();
+      const fetch2 = fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
+        method: 'get',
+        headers: {
+          'Content-Type': 'application/json',
+          Authorization: 'Bearer app-7iLQR9T77YtwDYUYGPk1PFbi',
+        },
       })
-      .catch((err) => {
-        console.error('第二个请求错误:', err);
-        return { data: [] }; // 失败时返回空数组
-      });
+        .then((res) => {
+          return res.json();
+        })
+        .catch((err) => {
+          console.error('第二个请求错误:', err);
+          return { data: [] }; // 失败时返回空数组
+        });
 
-    // 等待两个请求完成
-    const [data1, data2] = await Promise.all([fetch1, fetch2]);
-    // 合并两个数组(确保是数组)
-    const list1 = Array.isArray(data1.data) ? data1.data : [];
-    const list2 = Array.isArray(data2.data) ? data2.data : [];
-    // 合并数组并赋值给 sessionHistory
-    sessionHistory.value = [...list1, ...list2];
-    // 设置当前会话ID(如果有数据)
-    if (sessionHistory.value.length > 0) {
-      currentSessionID.value = sessionHistory.value[0].id;
+      // 等待两个请求完成
+      const [data1, data2] = await Promise.all([fetch1, fetch2]);
+      // 合并两个数组(确保是数组)
+      const list1 = Array.isArray(data1.data) ? data1.data : [];
+      const list2 = Array.isArray(data2.data) ? data2.data : [];
+      // 合并数组并赋值给 sessionHistory
+      sessionHistory.value = [...list1, ...list2];
+      // 设置当前会话ID(如果有数据)
+      if (sessionHistory.value.length > 0) {
+        currentSessionID.value = sessionHistory.value[0].id;
+      }
+    } catch (error) {
+      console.error('获取历史数据失败:', error);
+      // 错误处理(比如提示用户)
     }
-  } catch (error) {
-    console.error('获取历史数据失败:', error);
-    // 错误处理(比如提示用户)
-  }
-}
-//格式化消息
-const formatMessage = (text) => {
-  if (!text) return '';
-  let formatted = text
-    // 处理换行
-    .replace(/\n\n/g, '<br>')
-    .replace(/\n###/g, '<br>')
-    .replace(/###/g, '')
-    .replace(/---/g, '')
-    .replace(/^- /gm, '<br> - ')
-    // 处理粗体
-    .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
-    // 处理斜体
-    .replace(/\*(.*?)\*/g, '<em>$1</em>')
-    // 处理行内代码
-    .replace(/`([^`]+)`/g, '<code>$1</code>');
-  return formatted;
-};
-// 初始化按钮定位
-onMounted(() => {
-  getHistoryList();
-  // 初始化(仅执行一次)
-  initMarked();
-  triggerChart();
-});
-onUnmounted(() => {
-  // 卸载时销毁实例,清理 DOM
-  if (myChart) {
-    myChart.dispose();
-    myChart = null;
-  }
-  const chartContainer = document.getElementById('dynamic-echarts-container');
-  if (chartContainer && chartContainer.parentNode) {
-    chartContainer.parentNode.removeChild(chartContainer);
-  }
-  window.removeEventListener('resize', handleResize);
-});
+  }
+  //格式化消息
+  const formatMessage = (text) => {
+    if (!text) return '';
+    let formatted = text
+      // 处理换行
+      .replace(/\n\n/g, '<br>')
+      .replace(/\n###/g, '<br>')
+      .replace(/###/g, '')
+      .replace(/---/g, '')
+      .replace(/^- /gm, '<br> - ')
+      // 处理粗体
+      .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
+      // 处理斜体
+      .replace(/\*(.*?)\*/g, '<em>$1</em>')
+      // 处理行内代码
+      .replace(/`([^`]+)`/g, '<code>$1</code>');
+    return formatted;
+  };
+  // 初始化按钮定位
+  onMounted(() => {
+    getHistoryList();
+    // 初始化(仅执行一次)
+    initMarked();
+    triggerChart();
+  });
+  onUnmounted(() => {
+    // 卸载时销毁实例,清理 DOM
+    if (myChart) {
+      myChart.dispose();
+      myChart = null;
+    }
+    const chartContainer = document.getElementById('dynamic-echarts-container');
+    if (chartContainer && chartContainer.parentNode) {
+      chartContainer.parentNode.removeChild(chartContainer);
+    }
+    window.removeEventListener('resize', handleResize);
+  });
 </script>
 
 <style lang="less" scoped>
-.btn-header {
-  width: 40px;
-  height: 40px;
-  margin-right: 5px;
-  margin-bottom: 5px;
-}
-.container {
-  display: flex;
-  flex-direction: row;
-}
-.mini-chat {
-  display: flex;
-  min-width: none;
-  width: 950px;
-  height: 85%;
-  border-radius: 4px;
-  position: fixed;
-  top: 50px;
-  right: 20px;
-  padding: 10px;
-  background-color: #0a1a2f;
-  background-image: linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f),
-    linear-gradient(to top, #0b69b6, #0a1a2f), linear-gradient(to right, #0b69b6, #0a1a2f);
-  background-size: 100% 5px, 5px 100%, 100% 5px, 5px 100%;
-  background-position: top left, top right, bottom right, bottom left;
-  background-repeat: no-repeat;
-  z-index: 9999999;
-  color: #fff;
-}
-.doc {
-  display: flex;
-  flex-direction: column;
-  min-width: 600px;
-  height: 85%;
-  border-radius: 4px;
-  position: fixed;
-  top: 50px;
-  right: 51%;
-  padding: 10px;
-  background-color: #0a1a2f;
-  background-image: linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f),
-    linear-gradient(to top, #0b69b6, #0a1a2f), linear-gradient(to right, #0b69b6, #0a1a2f);
-  background-size: 100% 5px, 5px 100%, 100% 5px, 5px 100%;
-  background-position: top left, top right, bottom right, bottom left;
-  background-repeat: no-repeat;
-  z-index: 9999999;
-  color: #fff;
-}
-
-.close {
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  width: 100%;
-  padding: 1px;
-  border-radius: 8px;
-}
-
-.closeBtn {
-  background-color: #007bff;
-  color: white;
-  border: none;
-  border-radius: 4px;
-  cursor: pointer;
-  transition: all 0.3s ease;
-}
-
-.closeBtn:hover {
-  background-color: #0056b3;
-}
-.left-side {
-  background: #0c2842;
-  transition: width 0.5s ease; /* 平滑过渡动画 */
-  width: 140px; /* 展开时宽度 */
-  position: relative; /* 用于按钮定位 */
-}
-.left-side.collapsed {
-  width: 40px; /* 折叠时宽度 */
-}
-
-.addBtn {
-  height: 30px;
-  position: absolute;
-  background-size: 100% 100%;
-  background-position: center;
-  padding: 2px;
-  right: 10px;
-  bottom: 40px;
-  left: 10px;
-  align-items: center;
-  border-radius: 3px;
-  cursor: pointer;
-}
-.custom-list {
-  height: 650px;
-  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: 29px;
-}
-.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;
-  width: calc(100% - 134px) !important;
-  .title {
-    text-align: center;
-    font-size: 14px;
-    padding: 5px;
+  .btn-header {
+    width: 40px;
+    height: 40px;
+    margin-right: 5px;
+    margin-bottom: 5px;
   }
-  .dialog-area {
-    flex: 1;
-    gap: 30px;
-    overflow-y: auto;
-    padding: 5px;
+  .mini-container {
     display: flex;
-    flex-direction: column;
+    flex-direction: row;
+  }
+  .mini-chat {
+    display: flex;
+    min-width: none;
+    width: 950px;
+    height: 85%;
+    border-radius: 4px;
+    position: fixed;
+    top: 50px;
+    right: 20px;
+    padding: 10px;
+    background-color: #0a1a2f;
+    background-image:
+      linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f), linear-gradient(to top, #0b69b6, #0a1a2f),
+      linear-gradient(to right, #0b69b6, #0a1a2f);
+    background-size:
+      100% 5px,
+      5px 100%,
+      100% 5px,
+      5px 100%;
+    background-position:
+      top left,
+      top right,
+      bottom right,
+      bottom left;
+    background-repeat: no-repeat;
+    z-index: 9999999;
     color: #fff;
-
-    .ask-message {
-      padding: 10px;
-      border-radius: 5px;
-      background: #0c2842;
-      max-width: 300px;
-      min-width: 50px;
-      white-space: pre-wrap;
-      word-wrap: break-word;
-      overflow-wrap: break-word;
-      overflow: auto;
-      line-height: 1.5;
-    }
-    .answer-message {
-      padding: 10px;
-      border-radius: 5px;
-      background: #0c2842;
-      max-width: 90%;
-    }
   }
-
-  .input-area {
-    padding: 5px;
-    background-color: #043256;
-    border: 1px solid #2cb6ff;
-    border-radius: 5px;
+  .doc {
     display: flex;
     flex-direction: column;
-    justify-content: space-around;
-    gap: 5px;
-    min-height: 170px;
-    height: auto;
-    margin: 5px;
-    position: relative;
-  }
-  /* 文件展示区域 */
-  .img-preview {
-    position: relative;
-    width: 50px;
-    height: 50px;
-  }
-  .img-preview img {
-    width: 100%;
-    height: 100%;
-    object-fit: contain;
-    display: block;
-  }
-  .file-preview {
-    position: relative;
-    margin-bottom: 10px;
-    padding: 8px;
-    border: 1px solid #e5e7eb;
+    min-width: 600px;
+    height: 85%;
     border-radius: 4px;
-    background: #f9fafb;
-    box-sizing: border-box;
+    position: fixed;
+    top: 50px;
+    right: 51%;
+    padding: 10px;
+    background-color: #0a1a2f;
+    background-image:
+      linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f), linear-gradient(to top, #0b69b6, #0a1a2f),
+      linear-gradient(to right, #0b69b6, #0a1a2f);
+    background-size:
+      100% 5px,
+      5px 100%,
+      100% 5px,
+      5px 100%;
+    background-position:
+      top left,
+      top right,
+      bottom right,
+      bottom left;
+    background-repeat: no-repeat;
+    z-index: 9999999;
+    color: #fff;
   }
-  .file-info {
+
+  .close {
     display: flex;
-    justify-content: space-between;
+    justify-content: flex-end;
     align-items: center;
-    font-size: 14px;
-    color: #333;
+    width: 100%;
+    padding: 1px;
+    border-radius: 8px;
   }
-  .clear-btn {
-    border: none;
-    background: #ff4d4f;
+
+  .closeBtn {
+    background-color: #007bff;
     color: white;
-    border-radius: 50%;
-    width: 20px;
-    height: 20px;
-    cursor: pointer;
-    font-size: 12px;
-  }
-  .file-content {
-    margin-top: 6px;
-    font-size: 13px;
-    color: #666;
-    line-height: 1.4;
-  }
-  /* 文件列表容器 */
-  .uploaded-files {
-    padding: 8px;
-    background-color: #f5f5f5;
+    border: none;
     border-radius: 4px;
-    min-height: 40px;
+    cursor: pointer;
+    transition: all 0.3s ease;
   }
 
-  /* 单个文件项 */
-  .file-item {
-    display: inline-flex;
-    align-items: center;
-    padding: 4px 8px;
-    margin-right: 8px;
-    margin-bottom: 8px;
-    background-color: #fff;
-    border: 1px solid #e9e9e9;
-    border-radius: 4px;
+  .closeBtn:hover {
+    background-color: #0056b3;
   }
-
-  /* 文件名 */
-  .file-name {
-    margin-left: 8px;
-    margin-right: 8px;
-    max-width: 150px;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
+  .left-side {
+    background: #0c2842;
+    transition: width 0.5s ease; /* 平滑过渡动画 */
+    width: 140px; /* 展开时宽度 */
+    position: relative; /* 用于按钮定位 */
+  }
+  .left-side.collapsed {
+    width: 40px; /* 折叠时宽度 */
   }
 
-  .ant-input {
-    border: none;
-    background-color: rgba(255, 255, 255, 0) !important;
-    color: #fff;
+  .addBtn {
+    height: 30px;
+    position: absolute;
+    background-size: 100% 100%;
+    background-position: center;
+    padding: 2px;
+    right: 10px;
+    bottom: 40px;
+    left: 10px;
+    align-items: center;
+    border-radius: 3px;
+    cursor: pointer;
   }
-  .ant-input:focus {
-    border: none; /* 聚焦时无边框 */
-    outline: none; /* 聚焦时无轮廓 */
-    box-shadow: none; /* 移除可能存在的阴影效果 */
+  .custom-list {
+    height: 650px;
+    overflow-y: auto;
   }
-  .ctrl-btn {
+  .text-container {
     display: flex;
-    flex-direction: row;
     justify-content: space-between;
+    width: 100%;
+    overflow: hidden;
+  }
+  .btn-container {
+    display: flex;
   }
-  .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;
+  .jeecg-layout-header-action span[role='img'] {
+    padding: 0;
   }
-
-  .question-input::placeholder {
-    color: #64748b !important;
+  .text-ellipsis {
+    flex: 1;
   }
-
-  .control-btn {
-    height: 25px;
-    background-color: #043256;
-    border: 1px solid #2cb6ff;
+  .edit-text {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    width: 90px;
     color: #fff;
-    font-size: 10px;
-    margin-right: 10px;
+    font-size: 12px;
     cursor: pointer;
-    transition: background-color 0.2s ease; /* 平滑过渡效果 */
   }
-  /* 激活状态样式(点击后) */
-  .control-btn.active {
-    background-color: #2cb6ff; /* 蓝色背景(Ant Design 主色) */
-    color: white; /* 白色文字 */
+  .edit-icon {
+    flex-shrink: 0;
+    margin-left: auto;
+    line-height: 23px;
   }
-  .control-btn1 {
-    height: 20px;
-    background-color: #234a6b;
-    border: 1px solid #234a6b;
-    color: #fff;
-    font-size: 10px;
-    margin-right: 10px;
+  .delete-icon {
+    flex-shrink: 0;
+    margin-left: auto;
+    line-height: 23px;
+  }
+  .edit-icon:hover {
+    color: #1890ff !important;
     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;
+  .delete-icon:hover {
+    color: #1890ff !important;
     cursor: pointer;
-    transition: all 0.2s;
   }
-  /* 文件上传区 */
-  .file-upload {
+  .edit-input {
+    font-size: 10px;
+  }
+  .btn-text-bg {
+    width: 14px;
+    height: 14px;
     position: absolute;
-    right: 20px;
-    bottom: 70px;
-    width: 180px;
-    display: flex;
-    flex-direction: column;
-    gap: 10px;
-    border: 1px solid #2cb6ff;
-    background-color: #234a6b;
-    border-radius: 6px;
-    padding: 10px;
+    background-size: 100% 100%;
+    right: 10px;
+    top: 10px;
+    left: 10px;
+    bottom: 10px;
   }
-
-  .input-container {
-    position: relative;
-    display: flex;
-    align-items: center;
-    width: 100%;
+  .btn-text {
+    margin-left: 3px;
+    font-size: 12px;
+    color: #fff;
+    white-space: nowrap;
+    margin-left: 30px;
+    line-height: 29px;
   }
-
-  .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;
+  .historyBtn {
+    width: 20px;
+    height: 20px;
+    position: absolute;
+    background-size: 100% 100%;
+    background-position: center;
+    padding: 2px;
+    right: 10px;
+    top: 10px;
   }
-
-  .confirm-btn {
+  .historyBtn1 {
+    width: 20px;
+    height: 20px;
     position: absolute;
-    right: 5px;
-    background-color: #2cb6ff;
-    border: none;
-    color: #fff;
-    border-radius: 4px;
-    font-size: 12px;
-    padding: 4px 10px;
+    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;
-    transition: all 0.2s;
-    height: 28px;
   }
 
-  .confirm-btn:hover {
-    background-color: #2cb6ff;
-  }
+  .right-side {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    width: calc(100% - 134px) !important;
+    .title {
+      text-align: center;
+      font-size: 14px;
+      padding: 5px;
+    }
+    .dialog-area {
+      flex: 1;
+      gap: 30px;
+      overflow-y: auto;
+      padding: 5px;
+      display: flex;
+      flex-direction: column;
+      color: #fff;
 
-  .custom-upload {
-    width: 100%;
-    padding: 0 !important;
-  }
+      .ask-message {
+        padding: 10px;
+        border-radius: 5px;
+        background: #0c2842;
+        max-width: 300px;
+        min-width: 50px;
+        white-space: pre-wrap;
+        word-wrap: break-word;
+        overflow-wrap: break-word;
+        overflow: auto;
+        line-height: 1.5;
+      }
+      .answer-message {
+        padding: 10px;
+        border-radius: 5px;
+        background: #0c2842;
+        max-width: 90%;
+      }
+    }
 
-  .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;
+    .input-area {
+      padding: 5px;
+      background-color: #043256;
+      border: 1px solid #2cb6ff;
+      border-radius: 5px;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-around;
+      gap: 5px;
+      min-height: 170px;
+      height: auto;
+      margin: 5px;
+      position: relative;
+    }
+    /* 文件展示区域 */
+    .img-preview {
+      position: relative;
+      width: 50px;
+      height: 50px;
+    }
+    .img-preview img {
+      width: 100%;
+      height: 100%;
+      object-fit: contain;
+      display: block;
+    }
+    .file-preview {
+      position: relative;
+      margin-bottom: 10px;
+      padding: 8px;
+      border: 1px solid #e5e7eb;
+      border-radius: 4px;
+      background: #f9fafb;
+      box-sizing: border-box;
+    }
+    .file-info {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      font-size: 14px;
+      color: #333;
+    }
+    .clear-btn {
+      border: none;
+      background: #ff4d4f;
+      color: white;
+      border-radius: 50%;
+      width: 20px;
+      height: 20px;
+      cursor: pointer;
+      font-size: 12px;
+    }
+    .file-content {
+      margin-top: 6px;
+      font-size: 13px;
+      color: #666;
+      line-height: 1.4;
+    }
+    /* 文件列表容器 */
+    .uploaded-files {
+      padding: 8px;
+      background-color: #f5f5f5;
+      border-radius: 4px;
+      min-height: 40px;
+    }
+
+    /* 单个文件项 */
+    .file-item {
+      display: inline-flex;
+      align-items: center;
+      padding: 4px 8px;
+      margin-right: 8px;
+      margin-bottom: 8px;
+      background-color: #fff;
+      border: 1px solid #e9e9e9;
+      border-radius: 4px;
+    }
+
+    /* 文件名 */
+    .file-name {
+      margin-left: 8px;
+      margin-right: 8px;
+      max-width: 150px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .ant-input {
+      border: none;
+      background-color: rgba(255, 255, 255, 0) !important;
+      color: #fff;
+    }
+    .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: background-color 0.2s ease; /* 平滑过渡效果 */
+    }
+    /* 激活状态样式(点击后) */
+    .control-btn.active {
+      background-color: #2cb6ff; /* 蓝色背景(Ant Design 主色) */
+      color: white; /* 白色文字 */
+    }
+    .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;
+      flex-direction: column;
+      gap: 10px;
+      border: 1px solid #2cb6ff;
+      background-color: #234a6b;
+      border-radius: 6px;
+      padding: 10px;
+    }
+
+    .input-container {
+      position: relative;
+      display: flex;
+      align-items: center;
+      width: 100%;
+    }
+
+    .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;
+    }
+
+    .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;
+    }
+
+    .confirm-btn:hover {
+      background-color: #2cb6ff;
+    }
+
+    .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;
+    }
+    .message-wrapper.ai-message-wrapper {
+      display: flex;
+      align-items: flex-start;
+    }
+    .answerIcon {
+      flex: 0 0 45px;
+    }
+
+    .suggestion-item {
+      height: 30px;
+      margin-left: 45px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 10px 16px;
+      border: 1px solid #1890ff;
+      color: white;
+      border-radius: 4px;
+      cursor: pointer;
+      width: 33%;
+      font-size: 12px;
+    }
+    .thinking-section {
+      border-left: 3px solid #e5e7eb;
+      padding-left: 12px;
+      margin-bottom: 16px;
+    }
+
+    .thinking-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 8px 0;
+      cursor: pointer;
+      user-select: none;
+    }
+
+    .thinking-title {
+      font-size: 14px;
+      font-weight: 500;
+      color: #6b7280;
+    }
   }
-  .custom-upload .ant-upload-select:hover .ant-btn {
-    border-color: #1f84bd !important;
+</style>
+<style scoped>
+  .zxm-popover-inner-content {
+    padding: 1px;
   }
-  .message-wrapper.ai-message-wrapper {
+  .message-wrapper {
     display: flex;
     align-items: flex-start;
+    position: relative;
   }
-  .answerIcon {
-    flex: 0 0 45px;
+  .user-message-wrapper {
+    flex-direction: row-reverse;
   }
-
-  .suggestion-item {
-    height: 30px;
-    margin-left: 45px;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 10px 16px;
-    border: 1px solid #1890ff;
-    color: white;
-    border-radius: 4px;
-    cursor: pointer;
-    width: 33%;
-    font-size: 12px;
+  .ai-message-wrapper {
+    flex-direction: row;
   }
-  .thinking-section {
-    border-left: 3px solid #e5e7eb;
-    padding-left: 12px;
-    margin-bottom: 16px;
+  /* 鼠标滑过 .message-wrapper 时,显示图标 */
+  .ai-message-wrapper:hover .copy-icon-container {
+    opacity: 1;
+    visibility: visible;
   }
-
-  .thinking-header {
+  .user-message-wrapper:hover .copy-icon-container {
+    opacity: 1;
+    visibility: visible;
+  }
+  .copy-icon {
+    font-size: 16px;
+    color: #666;
+    cursor: pointer;
+  }
+  /* 默认隐藏,hover 时显示 */
+  .copy-icon-container {
+    position: absolute;
     display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 8px 0;
+    flex-direction: row;
+    bottom: -20px;
+    right: 15%;
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.3s ease;
+    z-index: 10;
+  }
+  /* 复制图标样式 */
+  .copy-icon {
+    font-size: 16px;
     cursor: pointer;
-    user-select: none;
+    margin-right: 20%;
+    float: right;
+    color: #fff;
   }
-
-  .thinking-title {
-    font-size: 14px;
-    font-weight: 500;
-    color: #6b7280;
+  .message-wrapper:hover .copy-icon {
+    opacity: 1;
+  }
+  .copy-icon:hover {
+    color: #1890ff;
+  }
+  ::v-deep table {
+    border-collapse: collapse;
+    width: 100%;
+    margin: 10px 0;
+    border: 1px solid #333;
+  }
+  ::v-deep th {
+    border: 1px solid #333;
+    background-color: #234a6b;
+    padding: 5px;
+    text-align: center;
+  }
+  ::v-deep td {
+    border: 1px solid #ddd;
+    padding: 8px 12px;
+    text-align: center;
   }
-}
-</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;
-}
-/* 鼠标滑过 .message-wrapper 时,显示图标 */
-.ai-message-wrapper:hover .copy-icon-container {
-  opacity: 1;
-  visibility: visible;
-}
-.user-message-wrapper:hover .copy-icon-container {
-  opacity: 1;
-  visibility: visible;
-}
-.copy-icon {
-  font-size: 16px;
-  color: #666;
-  cursor: pointer;
-}
-/* 默认隐藏,hover 时显示 */
-.copy-icon-container {
-  position: absolute;
-  display: flex;
-  flex-direction: row;
-  bottom: -20px;
-  right: 15%;
-  opacity: 0;
-  visibility: hidden;
-  transition: all 0.3s ease;
-  z-index: 10;
-}
-/* 复制图标样式 */
-.copy-icon {
-  font-size: 16px;
-  cursor: pointer;
-  margin-right: 20%;
-  float: right;
-  color: #fff;
-}
-.message-wrapper:hover .copy-icon {
-  opacity: 1;
-}
-.copy-icon:hover {
-  color: #1890ff;
-}
-::v-deep table {
-  border-collapse: collapse;
-  width: 100%;
-  margin: 10px 0;
-  border: 1px solid #333;
-}
-::v-deep th {
-  border: 1px solid #333;
-  background-color: #234a6b;
-  padding: 5px;
-  text-align: center;
-}
-::v-deep td {
-  border: 1px solid #ddd;
-  padding: 8px 12px;
-  text-align: center;
-}
 </style>
 <style scoped>
-/* 已上传文件列表 */
-.file-list {
-  margin-top: 20px;
-}
-.pre-container {
-  flex: 1;
-  width: 100%;
-  height: 100%;
-  overflow: auto;
-}
-.img-container {
-}
-.vue-office-excel {
-  background: #fff !important;
-}
+  /* 已上传文件列表 */
+  .file-list {
+    margin-top: 20px;
+  }
+  .pre-container {
+    flex: 1;
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+  }
+  .img-container {
+  }
+  .vue-office-excel {
+    background: #fff !important;
+  }
 
-.file-item {
-  display: flex;
-  align-items: center;
-  padding: 12px 15px;
-  background-color: #ddd;
-  border-radius: 6px;
-  margin-bottom: 10px;
-  transition: background-color 0.2s ease;
-}
+  .file-item {
+    display: flex;
+    align-items: center;
+    padding: 12px 15px;
+    background-color: #ddd;
+    border-radius: 6px;
+    margin-bottom: 10px;
+    transition: background-color 0.2s ease;
+  }
 
-.file-item:hover {
-  background-color: #f0f2f5;
-}
+  .file-item:hover {
+    background-color: #f0f2f5;
+  }
 
-.file-info {
-  flex: 1;
-  overflow: hidden;
-}
+  .file-info {
+    flex: 1;
+    overflow: hidden;
+  }
 
-.file-name {
-  font-size: 15px;
-  color: #1d2129;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  margin-bottom: 3px;
-}
+  .file-name {
+    font-size: 15px;
+    color: #1d2129;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    margin-bottom: 3px;
+  }
 
-.file-actions {
-  display: flex;
-  gap: 10px;
-}
+  .file-actions {
+    display: flex;
+    gap: 10px;
+  }
 
-.btn {
-  padding: 0px 15px;
-  margin-left: 10px;
-  border-radius: 4px;
-  border: none;
-  font-size: 14px;
-  cursor: pointer;
-  transition: all 0.2s ease;
-}
+  .btn {
+    padding: 0px 15px;
+    margin-left: 10px;
+    border-radius: 4px;
+    border: none;
+    font-size: 14px;
+    cursor: pointer;
+    transition: all 0.2s ease;
+  }
 
-.btn-preview {
-  background-color: #165dff;
-  color: white;
-}
+  .btn-preview {
+    background-color: #165dff;
+    color: white;
+  }
 
-.btn-preview:hover {
-  background-color: #0d47a1;
-}
+  .btn-preview:hover {
+    background-color: #0d47a1;
+  }
 
-.btn-delete {
-  background-color: #f2f3f5;
-  color: #4e5969;
-}
+  .btn-delete {
+    background-color: #f2f3f5;
+    color: #4e5969;
+  }
 
-.btn-delete:hover {
-  background-color: #e5e6eb;
-  color: #1d2129;
-}
+  .btn-delete:hover {
+    background-color: #e5e6eb;
+    color: #1d2129;
+  }
 </style>

+ 1 - 0
src/components/Application/src/AppProvider.vue

@@ -68,6 +68,7 @@
                 split: menuSplit,
               },
             });
+            // appStore.setModalType();
           }
         }
       }

+ 3 - 3
src/components/Container/src/Adaptive.vue

@@ -125,7 +125,7 @@
         observer = new MutationObserver(onResize);
         observer.observe(dom, {
           attributes: true,
-          attributeFilter: ['style'],
+          // attributeFilter: ['style'],
           attributeOldValue: true,
         });
       };
@@ -146,13 +146,13 @@
         setRem();
         cssSize();
         window.addEventListener('resize', debounce(100, onResize));
-        initMutationObserver();
+        // initMutationObserver();
         ready.value = true;
       });
 
       onUnmounted(() => {
         window.removeEventListener('resize', onResize);
-        removeMutationObserver();
+        // removeMutationObserver();
       });
 
       return {

+ 172 - 11
src/views/vent/monitorManager/sprayMonitor/component.vue → src/components/vent/BasicMonitoring.vue

@@ -1,4 +1,5 @@
 <template>
+  <!-- 场景选择下拉框 -->
   <customHeader
     :fieldNames="{ label: 'systemname', value: 'id', options: 'children' }"
     :options="options"
@@ -7,10 +8,21 @@
   >
     {{ mainTitle }}
   </customHeader>
+  <!-- 默认插槽,一般用来放模型 -->
   <slot :monitor-data="monitorData"></slot>
   <div class="scene-box">
     <div class="center-container">
+      <!-- 监测模块 -->
       <template v-if="activeKey == 'monitor'">
+        <slot name="opration">
+          <div class="button-wrapper">
+            <template v-for="item in mainConfig.operations" :key="item.key">
+              <template v-if="hasPermission(item.permission)">
+                <div :class="{ 'button-box': true, 'button-box-disable': item.disabled }" @click="handleOperate(item)">{{ item.label }} </div>
+              </template>
+            </template>
+          </div>
+        </slot>
         <slot name="monitor">
           <ModuleCommon
             v-for="cfg in mainConfig.configs"
@@ -25,6 +37,7 @@
         </slot>
       </template>
       <div v-else class="history-group">
+        <!-- 场景下关联的设备有很多,在这里选择 -->
         <div v-if="showDeviceList && deviceList.length > 0" class="device-button-group">
           <div
             v-for="(device, index) in deviceList"
@@ -70,28 +83,46 @@
     </div>
     <BottomMenu @change="changeActive" />
   </div>
+  <PasswordModal
+    :modal-is-show="passwordModalShown"
+    :modal-title="operatingTarget?.label"
+    @handle-ok="handlePasswordOK"
+    @handle-cancel="passwordModalShown = false"
+  />
 </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 { getDevice, sysList } from '/@/views/vent/monitorManager/comment/comment.api';
   import BottomMenu from '/@/views/vent/comment/components/bottomMenu.vue';
-  import ModuleCommon from '../../home/configurable/components/ModuleCommon.vue';
-  import HistoryTable from '../comment/HistoryTable.vue';
-  import HandlerHistoryTable from '../comment/HandlerHistoryTable.vue';
-  import AlarmHistoryTable from '../comment/AlarmHistoryTable.vue';
+  import ModuleCommon from '/@/views/vent/home/configurable/components/ModuleCommon.vue';
+  import HistoryTable from '/@/views/vent/monitorManager/comment/HistoryTable.vue';
+  import HandlerHistoryTable from '/@/views/vent/monitorManager/comment/HandlerHistoryTable.vue';
+  import AlarmHistoryTable from '/@/views/vent/monitorManager/comment/AlarmHistoryTable.vue';
   import { useRouter } from 'vue-router';
-  import { Config } from '../../deviceManager/configurationTable/types';
-  import { message } from 'ant-design-vue';
+  import { Config } from '/@/views/vent/deviceManager/configurationTable/types';
+  import { message, Modal } from 'ant-design-vue';
   import _ from 'lodash';
+  import PasswordModal from '/@/views/vent/monitorManager/comment/components/PasswordModal.vue';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { deviceControlApi } from '/@/api/vent';
 
   type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
+  type Operation = {
+    label: string;
+    key: string;
+    value: string;
+    disabled?: boolean;
+    showPassword?: boolean;
+    permission?: string;
+    onOk?: (key: string, value: string, pwd?: string) => Promise<void>;
+  };
 
   const props = withDefaults(
     defineProps<{
       mainTitle: string;
-      /** 是否显示历史数据上方的设备列表 */
+      /** 是否显示历史数据上方的设备类型选择器 */
       showDeviceList?: boolean;
       /** 请求场景数据传入的类型字符 */
       strtype: string;
@@ -101,6 +132,7 @@
       // deviceType: string;
       /** 主要模块配置 */
       mainConfig: {
+        operations: Operation[];
         configs: Config[];
         /** 获取该场景所含设备及其监测信息的API */
         monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
@@ -141,6 +173,7 @@
     }>(),
     {
       mainConfig: () => ({
+        operations: [],
         configs: [],
       }),
       monitorHistoryConfig: () => ({}),
@@ -152,6 +185,7 @@
   );
 
   const { currentRoute } = useRouter();
+  const { hasPermission } = usePermission();
 
   const activeKey = ref('monitor');
 
@@ -159,7 +193,9 @@
     activeKey.value = activeValue;
   }
 
+  /** 场景选项 */
   const options = ref([]);
+  /** 已选择了的场景的id */
   const optionValue = ref('');
 
   /** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
@@ -180,17 +216,18 @@
     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 }).catch(() => {
       message.error('获取已绑定设备时发生了错误');
@@ -217,6 +254,7 @@
   }
 
   let timer: NodeJS.Timeout;
+  /** 场景的监测数据 */
   const monitorData = ref<any>({});
   /** 获取本场景下所绑定的设备,将监测数据赋值 */
   async function getMonitor() {
@@ -241,6 +279,59 @@
     }
   }
 
+  /** 密码提示框是否显示 */
+  const passwordModalShown = ref(false);
+  /** 下发操作时的目标配置 */
+  const operatingTarget = ref<Operation>();
+
+  /** 操作按钮点击后根据配置弹出确认框,初始化数据 */
+  function handleOperate(item: Operation) {
+    if (item.disabled) return;
+    operatingTarget.value = item;
+
+    if (item.showPassword) {
+      passwordModalShown.value = true;
+    } else {
+      Modal.confirm({
+        title: '操作确认',
+        content: `确定要进行${operatingTarget.value.label}操作吗?`,
+        iconType: 'info',
+        onOk: () => handlePasswordOK(),
+      });
+    }
+  }
+
+  /** 密码输入后确认的回调 */
+  function handlePasswordOK(pwd?: string) {
+    if (!operatingTarget.value) return message.error('操作目标不存在');
+    const { onOk = deviceControl, key, value } = operatingTarget.value;
+
+    return onOk(key, value, pwd)
+      .then(() => {
+        passwordModalShown.value = false;
+      })
+      .finally(() => {
+        operatingTarget.value = undefined;
+      });
+  }
+
+  function deviceControl(key: string, value: string, pwd?: string) {
+    return deviceControlApi({
+      deviceid: optionValue.value,
+      devicetype: deviceType.value,
+      password: pwd,
+      paramcode: key,
+      value: value,
+    })
+      .then((r) => {
+        if (!r.success) throw r.message || '操作失败';
+        message.success('指令下发成功');
+      })
+      .catch((e) => {
+        message.error(typeof e === 'string' ? e : '指令下发失败');
+      });
+  }
+
   onMounted(async () => {
     if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
       optionValue.value = currentRoute.value['query']['id'] as string;
@@ -262,7 +353,9 @@
 <style lang="less" scoped>
   @import '/@/design/vent/modal.less';
   @ventSpace: zxm;
+
   .scene-box {
+    --image-tab-group-bg: url('/@/assets/images/vent/tab-group-bg.png');
     margin-top: 40px;
     pointer-events: none;
     .history-group {
@@ -342,9 +435,77 @@
     }
 
     .@{ventSpace}-input-number {
-      border-color: #ffffff88 !important;
+      border-color: #ffffff88;
     }
 
     margin-right: 10px;
   }
+
+  .button-wrapper {
+    position: relative;
+    top: 30px;
+    left: 500px;
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    margin-top: 10px;
+    width: 1165px !important;
+    height: 75px;
+    background: var(--image-tab-group-bg) no-repeat;
+    background-image: 100% 100%;
+    padding: 0 30px;
+    z-index: 10;
+
+    .button-box {
+      margin: 0 10px;
+    }
+  }
+
+  .button-box {
+    position: relative;
+    padding: 5px;
+    border-radius: 5px;
+    width: auto;
+    height: 34px;
+    border: 1px solid var(--vent-base-border);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--vent-font-color);
+    padding: 0 10px;
+    cursor: pointer;
+    pointer-events: all;
+
+    &:hover {
+      background: var(--vent-device-manager-control-btn-hover);
+    }
+
+    &::before {
+      width: calc(100% - 6px);
+      height: 26px;
+      content: '';
+      position: absolute;
+      top: 3px;
+      right: 0;
+      left: 3px;
+      bottom: 0;
+      z-index: -1;
+      border-radius: inherit;
+      /*important*/
+      background: var(--vent-device-manager-control-btn);
+    }
+  }
+
+  .button-box-disable {
+    cursor: not-allowed;
+    border: 1px solid var(--vent-base-border);
+
+    &:hover {
+      background: none;
+    }
+
+    &:before {
+      background: linear-gradient(#5897c299, #4a92a899);
+    }
+  }
 </style>

+ 64 - 0
src/components/vent/micro/createSubApp.vue

@@ -0,0 +1,64 @@
+<template>
+  <component :is="component" />
+</template>
+
+<script setup lang="ts">
+  import { ref, onBeforeUnmount, onBeforeMount, watch, nextTick } from 'vue';
+  import { useRoute } from 'vue-router';
+  import { getActions } from '/@/qiankun/state';
+  import { activeApps } from '/@/qiankun/index';
+
+  const route = useRoute();
+
+  const component = ref<any>(null);
+
+  async function resetComponent() {
+    component.value = null;
+
+    return nextTick(() => {
+      return setTimeout(async () => {
+        const appType = route.path.split('/')[1];
+        if (appType === 'micro-need-air') {
+          component.value = (await import('./needAir.vue')).default;
+        } else if (appType === 'micro-vent-doc') {
+          component.value = (await import('./ventDoc.vue')).default;
+        } else if (appType === 'micro-vent-3dModal' || appType === 'micro-vent-2dModal') {
+          if (route.query['isNoReverse'] != '1') {
+            if (VENT_PARAM['is2DModel']) {
+              component.value = (await import('./ventModal2D.vue')).default;
+            } else {
+              component.value = (await import('./ventModal.vue')).default;
+            }
+          } else if (appType === 'micro-vent-3dModal') {
+            component.value = (await import('./ventModal.vue')).default;
+          } else if (appType === 'micro-vent-2dModal') {
+            component.value = (await import('./ventModal2D.vue')).default;
+          }
+        }
+      }, 0);
+    });
+  }
+
+  watch(
+    () => [route.query, route.path],
+    async ([newQuery, newPath], [oldQuery, oldPath]) => {
+      // 这里判断
+      if (newPath !== oldPath) {
+        await resetComponent();
+      } else if (newQuery !== oldQuery) {
+        const actions = getActions();
+        const appType = route.path.split('/')[1];
+        activeApps[appType].mountPromise.then(() => {
+          actions.setGlobalState({ currentRouter: route });
+        });
+      }
+    }
+  );
+
+  onBeforeMount(async () => {
+    await resetComponent();
+  });
+  onBeforeUnmount(() => {
+    component.value = null;
+  });
+</script>

+ 3 - 5
src/components/vent/micro/needAir.vue

@@ -8,21 +8,19 @@
   import { onMounted, onBeforeUnmount, defineComponent, ref, unref } from 'vue';
   import { unmountMicroApps, mountMicroApp } from '/@/qiankun';
   import { resetMicroContentWH } from '/@/utils/domUtils';
-  import { useRouter } from 'vue-router';
   export default defineComponent({
     name: 'NeedAir',
     setup() {
       const loading = ref(true);
-      const { currentRoute } = useRouter();
       onMounted(() => {
-        mountMicroApp(unref(currentRoute).fullPath);
+        mountMicroApp('micro-need-air');
         resetMicroContentWH('micro-need-air', () => {
           loading.value = false;
         });
       });
 
-      onBeforeUnmount(() => {
-        unmountMicroApps(currentRoute);
+      onBeforeUnmount(async () => {
+        await unmountMicroApps(['micro-need-air']);
       });
 
       return { loading };

+ 4 - 6
src/components/vent/micro/ventDoc.vue

@@ -8,23 +8,21 @@
   import { ref, onMounted, onBeforeUnmount, defineComponent, unref } from 'vue';
   import { unmountMicroApps, mountMicroApp, activeApps } from '/@/qiankun';
   import { resetMicroContentWH } from '/@/utils/domUtils';
-  import { useRouter } from 'vue-router';
 
   export default defineComponent({
-    name: 'VentModel',
+    name: 'VentDoc',
     setup() {
-      const { currentRoute } = useRouter();
       const loading = ref(true);
       onMounted(() => {
         // 判断子应用是否存在,不存在注册
-        mountMicroApp(unref(currentRoute).fullPath);
+        mountMicroApp('micro-vent-doc');
         resetMicroContentWH('micro-vent-doc', () => {
           loading.value = false;
         });
       });
 
-      onBeforeUnmount(() => {
-        unmountMicroApps(['/micro-vent-doc']);
+      onBeforeUnmount(async () => {
+        await unmountMicroApps(['micro-vent-doc']);
       });
 
       return { loading };

+ 5 - 7
src/components/vent/micro/ventModal.vue

@@ -5,26 +5,24 @@
   <div id="micro-vent-3dModal"></div>
 </template>
 <script lang="ts">
-  import { ref, onMounted, onBeforeUnmount, defineComponent, unref } from 'vue';
-  import { unmountMicroApps, mountMicroApp, activeApps } from '/@/qiankun';
+  import { ref, onMounted, onBeforeUnmount, defineComponent } from 'vue';
+  import { unmountMicroApps, mountMicroApp } from '/@/qiankun';
   import { resetMicroContentWH } from '/@/utils/domUtils';
-  import { useRouter } from 'vue-router';
 
   export default defineComponent({
     name: 'VentModel',
     setup() {
-      const { currentRoute } = useRouter();
       const loading = ref(true);
       onMounted(() => {
         // 判断子应用是否存在,不存在注册
-        mountMicroApp(unref(currentRoute).fullPath);
+        mountMicroApp('micro-vent-3dModal', true);
         resetMicroContentWH('micro-vent-3dModal', () => {
           loading.value = false;
         });
       });
 
-      onBeforeUnmount(() => {
-        unmountMicroApps(['/micro-vent-3dModal']);
+      onBeforeUnmount(async () => {
+        await unmountMicroApps(['micro-vent-3dModal']);
       });
 
       return { loading };

+ 7 - 8
src/components/vent/micro/ventModal2D.vue

@@ -5,24 +5,22 @@
   <div id="micro-vent-2dModal"></div>
 </template>
 <script lang="ts">
-  import { onMounted, onBeforeUnmount, defineComponent, ref, unref } from 'vue';
+  import { onMounted, onBeforeUnmount, defineComponent, ref, unref, nextTick } from 'vue';
   import { unmountMicroApps, mountMicroApp } from '/@/qiankun';
   import { resetMicroContentWH } from '/@/utils/domUtils';
-  import { useRouter } from 'vue-router';
   export default defineComponent({
     name: 'Vent2dModal',
     setup() {
       const loading = ref(true);
-      const { currentRoute } = useRouter();
       onMounted(() => {
-        mountMicroApp(unref(currentRoute).fullPath);
+        mountMicroApp('micro-vent-2dModal');
         resetMicroContentWH('micro-vent-2dModal', () => {
           loading.value = false;
         });
       });
 
-      onBeforeUnmount(() => {
-        unmountMicroApps(currentRoute);
+      onBeforeUnmount(async () => {
+        await unmountMicroApps(['micro-vent-2dModal']);
       });
 
       return { loading };
@@ -39,7 +37,8 @@
     height: 100%;
   }
   #micro-vent-2dModal {
-    width: 100vw;
-    height: 100vh;
+    width: 100%;
+    height: 100%;
+    pointer-events: auto;
   }
 </style>

+ 2 - 0
src/enums/cacheEnum.ts

@@ -43,3 +43,5 @@ export enum CacheTypeEnum {
   SESSION,
   LOCAL,
 }
+
+export const MODAL_TYPE = 'MODAL__TYPE';

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

@@ -123,11 +123,11 @@
         // let wsClientId = md5(token);
         // let userId = unref(userStore.getUserInfo).id + '_' + wsClientId;
         let userId = unref(userStore.getUserInfo).id + '?token=' + token;
-        console.log(userStore.getUserInfo,'userId---')
+        console.log(userStore.getUserInfo, 'userId---');
         // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
         // let url = glob.wsUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
         let url = 'http://' + window.location.hostname + ':9999'?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
-        console.log(url,'url------')
+        console.log(url, 'url------');
         connectWebSocket(url);
         onWebSocket(onWebSocketMessage);
       }
@@ -183,7 +183,7 @@
 <style lang="less" scoped>
   .btn {
     line-height: 30px;
-    margin-right: 20px;
+    margin-right: 10px;
     cursor: pointer;
     display: flex;
   }
@@ -213,8 +213,8 @@
     height: 350px;
     border-radius: 4px;
     position: fixed;
-    top: 50px;
-    right: 20px;
+    top: 57px;
+    right: 2px;
     background-color: rgb(255, 255, 255);
     background: url('../../../../assets/images/warn-dialog-bg.png') no-repeat center;
     background-size: 100% 100%;

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

@@ -153,7 +153,7 @@
 <style lang="less" scoped>
   .btn {
     line-height: 30px;
-    margin-right: 20px;
+    margin-right: 10px;
     cursor: pointer;
     display: flex;
   }

+ 19 - 1
src/layouts/default/header/index.vue

@@ -58,9 +58,13 @@
   <div :class="`${prefixCls}-action`" style="position: fixed; top: 30px; right: 20px; z-index: 999999">
     <div class="right-position">
       <!-- 公司端不显示语音播报功能 weatherBroadcast.vue-->
+      <a-radio-group v-if="hasPermission('show:modalChange')" v-model:value="programModalType" @change="changeModalType" style="display: flex">
+        <a-radio :value="true">2.5D</a-radio>
+        <a-radio :value="false">3D</a-radio>
+      </a-radio-group>
       <WeatherBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && isShowQy && portValue != '8062'" />
       <VoiceBroadcast v-if="sysOrgCode != 'sdmtjtgsd' && portValue != '8062'" />
-      <AIChat v-if="hasPermission('show:AIChat')"></AIChat>
+      <AIChat v-if="hasPermission('show:AIChat')" />
       <VoiceBroadcastGsd v-if="sysOrgCode == 'sdmtjtgsd'" />
       <UserDropDown v-if="showUserDropdown" :theme="getHeaderTheme" />
       <LoginSelect ref="loginSelectRef" @success="loginSelectOk" />
@@ -93,6 +97,7 @@
 
   import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
   import { useLocale } from '/@/locales/useLocale';
+  import { Persistent } from '/@/utils/cache/persistent';
 
   import WeatherBroadcast from './components/weatherBroadcast.vue';
   import VoiceBroadcast from './components/VoiceBroadcast.vue';
@@ -105,6 +110,8 @@
 
   import { noHeadeLink } from '../layout.data';
   import { usePermission } from '/@/hooks/web/usePermission';
+  import { useAppStore } from '/@/store/modules/app';
+  import { router } from '/@/router';
 
   export default defineComponent({
     name: 'LayoutHeader',
@@ -206,6 +213,9 @@
         return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
       });
 
+      const appStore = useAppStore();
+      const programModalType = ref(appStore.getProgramModalType);
+
       // /**
       //  * 首页多租户部门弹窗逻辑
       //  */
@@ -232,6 +242,12 @@
         return window.self === window.top;
       });
 
+      const changeModalType = (e) => {
+        appStore.setModalType(e.target['value']);
+        programModalType.value = e.target['value'];
+        window.location.reload();
+      };
+
       onMounted(() => {
         showLoginSelect();
       });
@@ -272,6 +288,8 @@
         homePath,
         hasPermission,
         isShowQy,
+        programModalType,
+        changeModalType,
       };
     },
   });

+ 1 - 1
src/layouts/default/setting/handler.ts

@@ -21,7 +21,7 @@ export function baseHandler(event: HandlerEnum, value: any) {
 
 export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConfig> {
   const appStore = useAppStore();
-
+  // appStore.setModalType();
   const { getThemeColor, getDarkMode } = useRootSetting();
   switch (event) {
     case HandlerEnum.CHANGE_LAYOUT:

+ 5 - 16
src/layouts/default/sider/bottomSideder.vue

@@ -70,6 +70,7 @@
   import { unmountMicroApps } from '/@/qiankun';
   import { useUserStoreWithOut } from '/@/store/modules/user';
   import { useAppStore } from '/@/store/modules/app';
+  import { router } from '/@/router';
 
   export default defineComponent({
     name: 'BottomSider',
@@ -86,7 +87,7 @@
       const userStore = useUserStoreWithOut();
       // const appStore = useAppStore();
       // const themeIcon = appStore.getDarkMode;
-        const themeIcon=ref('styleTwo')
+      const themeIcon = ref('styleTwo');
 
       function selectMenu(e: Event, programMenu) {
         e.stopPropagation();
@@ -115,8 +116,9 @@
           return;
         }
         // if (currentRoute.value.path.startsWith('/micro')) {
-        //   go(path.path);
-        //   // window.history.pushState({}, '', path.path);
+        //   router.replace(path.path);
+        //   // go(path.path);
+        //   window.history.pushState({}, '', path.path);
         // } else {
         //   go(path.path);
         // }
@@ -130,19 +132,6 @@
           go(userStore.getUserInfo.homePath);
         } else if (currentRoute.value.path.startsWith('/micro-need-air')) {
           window.history.pushState({}, '', glob.homePath || PageEnum.BASE_HOME);
-          // if (currentRoute.value.path.startsWith('/micro-vent-3dModal/dashboard/analysis')) {
-          //   if (glob.homePath == '/micro-vent-3dModal/dashboard/analysis' || PageEnum.BASE_HOME == '/micro-vent-3dModal/dashboard/analysis') {
-          //     actions.setGlobalState({ pageObj: { pageType: 'home' } });
-          //     go(glob.homePath || PageEnum.BASE_HOME);
-          //   }
-          // } else {
-          //   if (glob.homePath == '/model3D/home' || PageEnum.BASE_HOME == '/model3D/home') {
-          //     go(glob.homePath || PageEnum.BASE_HOME);
-          //     // location.reload()
-          //   }
-
-          //   go(glob.homePath || PageEnum.BASE_HOME);
-          // }
         } else {
           go(glob.homePath || PageEnum.BASE_HOME);
         }

+ 1 - 0
src/logics/theme/updateBackground.ts

@@ -25,6 +25,7 @@ export function updateHeaderBgColor(color?: string) {
       theme: appStore.getDarkMode as ThemeEnum,
     },
   });
+  appStore.setModalType();
 
   // if theme is not default, set the colors in /design/colors or /design/themify/
   if (!color) {

+ 4 - 2
src/main.ts

@@ -1,9 +1,9 @@
 import 'uno.css';
 import '/@/design/index.less';
-import animate from 'animate.css';
 //lxh
 import './assets/iconfong/iconfont.css';
 import './assets/iconfong/iconfont.js';
+import animate from 'animate.css';
 
 // 注册图标
 import 'virtual:svg-icons-register';
@@ -23,6 +23,7 @@ import { registerThirdComp } from '/@/settings/registerThirdComp';
 // import { registerPackages } from '/@/utils/monorepo/registerPackages';
 import { initModalWorker, initTHREE } from '/@/utils/threejs/main.worker';
 import GlobalConfig from './components/config/GlobalConfig.vue';
+import { MODAL_TYPE } from '/@/enums/cacheEnum';
 
 // 在本地开发中引入的,以提高浏览器响应速度
 if (import.meta.env.DEV) {
@@ -74,13 +75,14 @@ async function bootstrap() {
   initTHREE();
 
   app.component('GlobalConfig', GlobalConfig);
+  debugger;
   app.provide('globalConfig', {
     // 你的全局配置
     simulatedPassword: VENT_PARAM['simulatedPassword'],
     History_Type: History_Type['type'],
     pageType: '',
     showReport: VENT_PARAM['showReport'],
-    is2DModel: VENT_PARAM['is2DModel'],
+    is2DModel: localStorage.getItem(MODAL_TYPE) == 'false' ? false : true,
   });
 
   // 挂载应用

+ 1 - 0
src/qiankun/apps.ts

@@ -30,6 +30,7 @@ for (const key in import.meta.env) {
         container: `#${app[0]}`,
         activeRule: app[0],
       };
+      // debugger;
 
       _apps.push(obj);
     });

+ 14 - 42
src/qiankun/index.ts

@@ -1,7 +1,7 @@
 /**
  * qiankun配置
  */
-import { loadMicroApp } from 'qiankun';
+import { loadMicroApp, start } from 'qiankun';
 import { apps } from './apps';
 import { getProps } from './state';
 
@@ -13,7 +13,6 @@ function filterApps() {
   apps.forEach((item) => {
     //主应用需要传递给微应用的数据。
     item['props'] = getProps();
-    console.log('主应用给子应用传的数据', item.props);
 
     //微应用触发的路由规则
     // @ts-ignore
@@ -22,56 +21,29 @@ function filterApps() {
   return apps;
 }
 
-const mountMicroApp = (path, toPath?) => {
+const mountMicroApp = (name, isReFresh = false) => {
   const microApps = filterApps();
-  const app = microApps.find((item) => path.startsWith(item['activeRule']));
+  const app = microApps.find((item) => item.name === name);
   if (app) {
-    if (activeApps[app['activeRule']]) {
-      // return;
-      activeApps[app['activeRule']].unmount();
-      activeApps[app['activeRule']] = null;
-      delete activeApps[app['activeRule']];
-    }
-    // debugger;
-    const instance = activeApps[app['activeRule']];
-    console.log('子应用实例--------------->', instance);
-    if (instance) {
-      instance.update();
+    const instance = activeApps[app.name];
+    if (instance && instance.getStatus() === 'NOT_MOUNTED' && !isReFresh) {
+      instance.mount(app);
     } else {
-      if (toPath) {
-        app['props']['data']['publicPath'] = toPath;
-      }
-      activeApps[app['activeRule']] = loadMicroApp(app, {
-        fetch(url, ...args) {
-          // 给指定的微应用 entry 开启跨域请求
-          if (url === 'http://1.1.1.3:89/cookie/flash.js') {
-            return window.fetch(url, {
-              ...args,
-              headers: {
-                'Access-Control-Allow-Origin': '*',
-                'Content-Type': 'text/plain',
-              },
-              mode: 'cors',
-              credentials: 'include',
-            });
-          }
-          return window.fetch(url, ...args);
-        },
-      }); // 手动加载子应用
+      delete activeApps[app.name];
+      activeApps[app.name] = loadMicroApp(app, { autoStart: false, sandbox: { strictStyleIsolation: false } }); // 手动加载子应用'
     }
   }
 };
 
 // 卸载app的方法
-const unmountMicroApps = (multipleApp) => {
+const unmountMicroApps = async (multipleApp) => {
   if (JSON.stringify(activeApps) !== '{}' && multipleApp.some) {
     for (const key in activeApps) {
-      const isExist = multipleApp.some((name) => name == key);
-      if (isExist) {
-        activeApps[key].unmount();
-        activeApps[key] = null;
-        delete activeApps[key];
-      }
+      multipleApp.filter(async (name) => {
+        if (name.includes(key)) {
+          await activeApps[key].unmount();
+        }
+      });
     }
   }
 };

+ 1 - 0
src/qiankun/state.ts

@@ -18,6 +18,7 @@ export function getProps() {
       router,
       isMounted: false,
     },
+    actions: getActions(),
   };
 }
 

+ 10 - 6
src/router/guard/index.ts

@@ -39,20 +39,24 @@ RootRoute.redirect = glob.homePath || PageEnum.BASE_HOME;
 function createPageGuard(router: Router) {
   const loadedPageMap = new Map<string, boolean>();
   router.beforeEach(async (to, from) => {
-    if (from.path.startsWith('/micro-') && !to.path.startsWith('/micro-')) {
-      unmountMicroApps(['/' + from.path.split('/')[1]]);
-    } else if (from.path.startsWith('/micro-') && to.path.startsWith('/micro-') && from.path.split('/')[1] != to.path.split('/')[1]) {
-      unmountMicroApps(['/' + from.path.split('/')[1]]);
-    }
+    // if (from.path.startsWith('/micro-') && !to.path.startsWith('/micro-')) {
+    //   await unmountMicroApps([from.path.split('/')[1]]);
+    // } else if (from.path.startsWith('/micro-') && to.path.startsWith('/micro-') && from.path.split('/')[1] != to.path.split('/')[1]) {
+    //   await unmountMicroApps([from.path.split('/')[1]]);
+    // }
     // The page has already been loaded, it will be faster to open it again, you don’t need to do loading and other processing
     to.meta.loaded = !!loadedPageMap.get(to.path);
+
     // Notify routing changes
     setRouteChange(to);
 
     return true;
   });
 
-  router.afterEach((to) => {
+  router.afterEach(async (to, from) => {
+    // debugger;
+    // history.replaceState({ ...history.state, current: to.path }, '', '');
+
     loadedPageMap.set(to.path, true);
   });
 }

+ 19 - 0
src/router/guard/permissionGuard.ts

@@ -19,6 +19,7 @@ import { MOCK_LOGIN_URL_QUERY, SKIP_SSO_URL_QUERY } from '../constant';
 import { useSso } from '/@/hooks/web/useSso';
 import { useAutoLogin } from '/@/hooks/vent/useAutoLogin';
 import { addBrowseLog } from '@/router/helper/menuHelper';
+import { string } from 'vue-types';
 
 const LOGIN_PATH = PageEnum.BASE_LOGIN;
 //auth2登录路由
@@ -45,6 +46,21 @@ export function createPermissionGuard(router: Router) {
   const { doAutoLogin, doTokenLogin, validateRoute, tokenValidateRoute } = useAutoLogin();
   router.beforeEach(async (to, from, next) => {
     RootRoute.redirect = glob.homePath || PageEnum.BASE_HOME;
+    if (to.query['isNoReverse'] != '1') {
+      if (VENT_PARAM['is2DModel']) {
+        for (const key in to) {
+          if (typeof to[key] == 'string' && to[key].includes('micro-vent-3dModal')) {
+            to[key] = to[key].replace('micro-vent-3dModal', 'micro-vent-2dModal');
+          }
+        }
+      } else {
+        for (const key in to) {
+          if (typeof to[key] == 'string' && to[key].includes('micro-vent-2dModal')) {
+            to[key] = to[key].replace('micro-vent-2dModal', 'micro-vent-3dModal');
+          }
+        }
+      }
+    }
 
     if (_.isEmpty(history.state.current)) {
       _.assign(history.state, { current: from.fullPath });
@@ -278,6 +294,9 @@ export function createPermissionGuard(router: Router) {
 
   router.afterEach((to, from) => {
     addBrowseLog(to, from, [...whitePathList, PAGE_NOT_FOUND_ROUTE.path]);
+    if (to.path.startsWith('micro-')) {
+      window.history.pushState({}, '', to.fullPath);
+    }
   });
 
   window.addEventListener(

+ 33 - 0
src/router/routes/basic.ts

@@ -75,6 +75,17 @@ export const QIANKUN_ROUTE: AppRouteRecordRaw = {
       },
       ver: '1',
     },
+    {
+      path: '/micro-vent-2dModal/gas/home',
+      name: '2dModal-gas-home',
+      component: () => import('/@/views/vent/gas/gasHome/index.vue'),
+      meta: {
+        title: '抽采综合管控',
+        hideBreadcrumb: true,
+        hideMenu: true,
+      },
+      ver: '1',
+    },
     {
       path: '/micro-vent-3dModal/modelchannel/model3D/home',
       name: 'micro-vent-3dModal-modelchannel-model3D-home',
@@ -97,6 +108,17 @@ export const QIANKUN_ROUTE: AppRouteRecordRaw = {
       },
       ver: '1',
     },
+    {
+      path: '/micro-vent-2dModal/gas-pipe-net/home',
+      name: '2dModal-gas-pipe-net-home',
+      component: () => import('/@/views/vent/gas/gasPipeNet/index.vue'),
+      meta: {
+        title: '瓦斯管网监测',
+        hideBreadcrumb: true,
+        hideMenu: true,
+      },
+      ver: '1',
+    },
     {
       path: '/micro-vent-3dModal/dashboard/analysis',
       name: 'micro-vent-3dModal-dashboard-analysis',
@@ -108,6 +130,17 @@ export const QIANKUN_ROUTE: AppRouteRecordRaw = {
       },
       ver: '1',
     },
+    {
+      path: '/micro-vent-2dModal/dashboard/analysis',
+      name: 'micro-vent-2dModal-dashboard-analysis',
+      component: () => import('/@/views/vent/monitorManager/deviceMonitor/index.vue'),
+      meta: {
+        title: '通防综合管控',
+        hideBreadcrumb: true,
+        hideMenu: true,
+      },
+      ver: '1',
+    },
     {
       path: '/micro-need-air/:path(.*)*',
       name: 'micro-need-air',

+ 5 - 1
src/router/routes/index.ts

@@ -38,7 +38,11 @@ export const LoginRoute: AppRouteRecordRaw = {
   path: '/login',
   name: 'Login',
   //新版后台登录,如果想要使用旧版登录放开即可
-  component: () => import('/@/views/sys/login/Login.vue'),
+  // component: () => import('/@/views/sys/login/Login.vue'),
+  //数据中心登录
+  // component: () => import('/@/views/sys/login/LoginDataCenter.vue'),
+  // 内页登录
+  component: () => import('/@/views/sys/login/LoginNeiye.vue'),
   // component: () => import('/@/views/system/loginmini/MiniLogin.vue'),
   meta: {
     title: t('routes.basic.login'),

+ 17 - 1
src/store/modules/app.ts

@@ -5,7 +5,7 @@ import { defineStore } from 'pinia';
 import { store } from '/@/store';
 
 import { ThemeEnum } from '/@/enums/appEnum';
-import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY } from '/@/enums/cacheEnum';
+import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY, MODAL_TYPE } from '/@/enums/cacheEnum';
 import { Persistent } from '/@/utils/cache/persistent';
 import { darkMode } from '/@/settings/designSetting';
 import { resetRouter } from '/@/router';
@@ -75,6 +75,10 @@ export const useAppStore = defineStore({
     getHeightScale(): number {
       return this.heightScale;
     },
+    getProgramModalType(): boolean | null {
+      VENT_PARAM['is2DModel'] = localStorage.getItem(MODAL_TYPE) == 'false' ? false : true;
+      return VENT_PARAM['is2DModel'];
+    },
   },
   actions: {
     setPageLoading(loading: boolean): void {
@@ -95,6 +99,18 @@ export const useAppStore = defineStore({
       Persistent.setLocal(PROJ_CFG_KEY, this.projectConfig);
     },
 
+    setModalType(is2DModel?): void {
+      if (is2DModel === undefined) {
+        if (localStorage.getItem(MODAL_TYPE)) {
+          VENT_PARAM['is2DModel'] = localStorage.getItem(MODAL_TYPE) == 'false' ? false : true;
+        } else {
+          localStorage.setItem(MODAL_TYPE, VENT_PARAM['is2DModel']);
+        }
+      } else {
+        localStorage.setItem(MODAL_TYPE, is2DModel);
+      }
+    },
+
     async resetAllState() {
       resetRouter();
       Persistent.clearAll();

+ 2 - 0
src/utils/cache/persistent.ts

@@ -18,6 +18,7 @@ import {
   LOGIN_INFO_KEY,
   OAUTH2_THIRD_LOGIN_TENANT_ID,
   PWD_KEY,
+  MODAL_TYPE,
 } from '/@/enums/cacheEnum';
 import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
 import { toRaw } from 'vue';
@@ -35,6 +36,7 @@ interface BasicStore {
   [TENANT_ID]: string;
   [LOGIN_INFO_KEY]: LoginInfo;
   [OAUTH2_THIRD_LOGIN_TENANT_ID]: string;
+  [MODAL_TYPE]: string;
 }
 
 type LocalStore = BasicStore;

+ 0 - 747
src/views/sealedGoafSys/home/configurable.data.sealedGoaf.ts

@@ -1,747 +0,0 @@
-import type { Config } from '@/views/vent/deviceManager/configurationTable/types';
-
-export const testConfigSealedGoaf: Config[] = [
-  // 1. 矿井状况
-  {
-    deviceType: 'goafsInfo',
-    moduleName: '矿井状况',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: { show: false, value: '' },
-        slot: { show: false, value: '' },
-      },
-      background: { show: false, type: 'video', link: '' },
-      layout: {
-        direction: 'row',
-        items: [{ name: 'board', basis: '100%' }],
-      },
-      list: [],
-      board: [
-        {
-          type: 'CKQMB',
-          readFrom: '',
-          layout: 'label-top',
-          items: [
-            {
-              label: '生产矿井',
-              value: '219',
-            },
-            {
-              label: '建设矿井',
-              value: '219',
-            },
-            {
-              label: '停产停建',
-              value: '17',
-            },
-            {
-              label: '长期停产停建',
-              value: '17',
-            },
-            {
-              label: '实施关闭',
-              value: '2',
-            },
-            {
-              label: '情况变化',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [],
-      complex_list: [],
-      preset: [],
-    },
-    showStyle: {
-      size: 'width:440px;height:200px;',
-      version: '原版',
-      position: 'top:30px;left:10px;',
-      headerPosition: 'centerBottom',
-    },
-  },
-  // 2. 当日生产状态
-  {
-    deviceType: 'spray_auto',
-    moduleName: '当日生产状态',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: { show: false, value: '' },
-        slot: { show: false, value: '' },
-      },
-      background: { show: false, type: 'video', link: '' },
-      layout: {
-        direction: 'row',
-        items: [{ name: 'board', basis: '100%' }],
-      },
-      list: [],
-      board: [
-        {
-          type: 'CKQMB',
-          readFrom: '',
-          layout: 'label-top',
-          items: [
-            {
-              label: '正在生产',
-              value: '210',
-            },
-            {
-              label: '正在建设',
-              value: '27',
-            },
-            {
-              label: '停产',
-              value: '17',
-            },
-            {
-              label: '停工',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [],
-      complex_list: [],
-      preset: [],
-    },
-    showStyle: {
-      size: 'width:440px;height:150px;',
-      version: '原版',
-      position: 'top:225px;left:10px;',
-    },
-  },
-  // 3. 煤层自燃倾向性
-  {
-    deviceType: 'deviceInfo',
-    moduleName: '煤层自燃倾向性',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: {
-          show: false,
-          value: '',
-        },
-        slot: {
-          show: false,
-          value: '',
-        },
-      },
-      background: {
-        show: false,
-        type: 'video',
-        link: '',
-      },
-      layout: {
-        direction: 'column',
-        items: [
-          {
-            name: 'chart',
-            basis: '100%',
-          },
-        ],
-      },
-      chart: [
-        {
-          type: 'pie_drag',
-          readFrom: '',
-          legend: { show: false, formatter: '{b}:{c}\n{d}%' },
-          xAxis: [{ show: false }],
-          yAxis: [{ show: false, name: '风量', position: 'left' }],
-          series: [{ readFrom: 'piechart', xprop: 'pos', yprop: 'val', label: '' }],
-        },
-      ],
-      gallery: [],
-      gallery_list: [],
-      table: [],
-      list: [],
-      complex_list: [],
-      preset: [],
-      mock: {
-        piechart: [
-          { pos: 'Ⅰ类容易自燃', val: 50 },
-          { pos: 'Ⅱ类自燃', val: 80 },
-          { pos: 'Ⅲ类不易自燃', val: 40 },
-        ],
-      },
-    },
-    showStyle: {
-      size: 'width:440px;height:210px;',
-      version: '原版',
-      position: 'top:370px;left:10px;',
-      headerPosition: 'centerBottom',
-    },
-  },
-  // 4. 联网状态
-  {
-    deviceType: 'coalFireFeature',
-    moduleName: '联网状态',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: {
-          show: true,
-          value: '',
-        },
-        slot: {
-          show: false,
-          value: '',
-        },
-      },
-      background: {
-        show: false,
-        type: 'video',
-        link: '',
-      },
-      layout: {
-        direction: 'column',
-        items: [
-          {
-            name: 'board',
-            basis: '40%',
-            overflow: true,
-          },
-          {
-            name: 'table',
-            basis: '60%',
-            overflow: true,
-          },
-        ],
-      },
-      board: [
-        {
-          type: 'CKQMB0',
-          readFrom: '',
-          layout: 'val-top',
-          items: [
-            {
-              label: '应接入',
-              value: '210',
-            },
-            {
-              label: '在线',
-              value: '27',
-            },
-            {
-              label: '中断',
-              value: '17',
-            },
-            {
-              label: '未接入',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [
-        {
-          type: 'CKQMB',
-          // parser: 'json',
-          readFrom: 'tableArray',
-          columns: [
-            {
-              name: ' ',
-              prop: 'index',
-            },
-            {
-              name: '应接入',
-              prop: 'yjr',
-            },
-            {
-              name: '在线',
-              prop: 'zx',
-            },
-            {
-              name: '中断',
-              prop: 'zd',
-            },
-            {
-              name: '未接入',
-              prop: 'wjr',
-            },
-          ],
-        },
-      ],
-      list: [],
-      complex_list: [],
-      preset: [],
-      mock: {
-        boardArray: [
-          {
-            label: '低风险',
-            value: '210',
-          },
-          {
-            label: '一般风险',
-            value: '27',
-          },
-          {
-            label: '较高风险',
-            value: '17',
-          },
-          {
-            label: '高风险',
-            value: '1',
-          },
-        ],
-        tableArray: [
-          {
-            index: '执法一处',
-            yjr: '73',
-            zx: '73',
-            zd: '0',
-            wjr: '0',
-          },
-          {
-            index: '执法二处',
-            yjr: '73',
-            zx: '73',
-            zd: '0',
-            wjr: '0',
-          },
-          {
-            index: '执法三处',
-            yjr: '73',
-            zx: '73',
-            zd: '0',
-            wjr: '0',
-          },
-          {
-            index: '执法四处',
-            yjr: '73',
-            zx: '73',
-            zd: '0',
-            wjr: '0',
-          },
-        ],
-      },
-    },
-    showStyle: {
-      size: 'width:440px;height:290px;',
-      version: '原版',
-      position: 'top:570px;left:10px;',
-    },
-  },
-  // 5. 密闭情况总览
-  {
-    deviceType: '',
-    moduleName: '密闭情况总览',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: {
-          show: true,
-          value: '',
-        },
-        slot: {
-          show: false,
-          value: '',
-        },
-      },
-      background: {
-        show: false,
-        type: 'video',
-        link: '',
-      },
-      layout: {
-        direction: 'column',
-        items: [
-          {
-            name: 'board',
-            basis: '30%',
-            overflow: true,
-          },
-          {
-            name: 'table',
-            basis: '70%',
-            overflow: true,
-          },
-        ],
-      },
-      board: [
-        {
-          type: 'CKQMB1',
-          readFrom: '',
-          layout: 'val-top',
-          items: [
-            {
-              label: '低风险',
-              value: '210',
-            },
-            {
-              label: '一般风险',
-              value: '27',
-            },
-            {
-              label: '较高风险',
-              value: '17',
-            },
-            {
-              label: '高风险',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [
-        {
-          type: 'CKQMB',
-          // parser: 'json',
-          readFrom: 'tableArray',
-          columns: [
-            {
-              name: ' ',
-              prop: 'index',
-            },
-            {
-              name: '低风险',
-              prop: 'dfx',
-            },
-            {
-              name: '一般风险',
-              prop: 'ybfx',
-            },
-            {
-              name: '较高风险',
-              prop: 'jgfx',
-            },
-            {
-              name: '高风险',
-              prop: 'gfx',
-            },
-          ],
-        },
-      ],
-      list: [],
-      complex_list: [],
-      preset: [],
-      mock: {
-        boardArray: [
-          {
-            label: '低风险',
-            value: '210',
-          },
-          {
-            label: '一般风险',
-            value: '27',
-          },
-          {
-            label: '较高风险',
-            value: '17',
-          },
-          {
-            label: '高风险',
-            value: '1',
-          },
-        ],
-        tableArray: [
-          {
-            index: '执法一处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法二处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法三处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法四处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法五处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法六处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-        ],
-      },
-    },
-    showStyle: {
-      size: 'width:440px;height:355px;',
-      version: '原版',
-      position: 'top:10px;right:10px;',
-    },
-  },
-  // 6. 采空区密闭预警分析
-  {
-    deviceType: 'goafMonitoring',
-    moduleName: '采空区密闭预警分析',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: {
-          show: true,
-          value: '',
-        },
-        slot: {
-          show: false,
-          value: '',
-        },
-      },
-      background: {
-        show: false,
-        type: 'video',
-        link: '',
-      },
-      layout: {
-        direction: 'column',
-        items: [
-          {
-            name: 'board',
-            basis: '40%',
-            overflow: true,
-          },
-          {
-            name: 'table',
-            basis: '60%',
-            overflow: true,
-          },
-        ],
-      },
-      board: [
-        {
-          type: 'CKQMB1',
-          readFrom: '',
-          layout: 'val-top',
-          items: [
-            {
-              label: '低风险',
-              value: '210',
-            },
-            {
-              label: '一般风险',
-              value: '27',
-            },
-            {
-              label: '较高风险',
-              value: '17',
-            },
-            {
-              label: '高风险',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [
-        {
-          type: 'CKQMB',
-          // parser: 'json',
-          readFrom: 'tableArray',
-          columns: [
-            {
-              name: ' ',
-              prop: 'index',
-            },
-            {
-              name: '低风险',
-              prop: 'dfx',
-            },
-            {
-              name: '一般风险',
-              prop: 'ybfx',
-            },
-            {
-              name: '较高风险',
-              prop: 'jgfx',
-            },
-            {
-              name: '高风险',
-              prop: 'gfx',
-            },
-          ],
-        },
-      ],
-      list: [],
-      complex_list: [],
-      preset: [],
-      mock: {
-        boardArray: [
-          {
-            label: '低风险',
-            value: '210',
-          },
-          {
-            label: '一般风险',
-            value: '27',
-          },
-          {
-            label: '较高风险',
-            value: '17',
-          },
-          {
-            label: '高风险',
-            value: '1',
-          },
-        ],
-        tableArray: [
-          {
-            index: '执法一处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法二处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-          {
-            index: '执法三处',
-            dfx: '50',
-            ybfx: '20',
-            jgfx: '0',
-            gfx: '0',
-          },
-        ],
-      },
-    },
-    showStyle: {
-      size: 'width:440px;height:300px;',
-      version: '原版',
-      position: 'top:365px;right:10px;',
-    },
-  },
-  // 7. 当日报警情况统计
-  {
-    deviceType: 'RealtimeMonitor',
-    moduleName: '当日报警情况统计',
-    pageType: 'sealed_goaf',
-    moduleData: {
-      header: {
-        show: false,
-        readFrom: '',
-        selector: {
-          show: false,
-          value: '',
-        },
-        slot: {
-          show: false,
-          value: '',
-        },
-      },
-      background: {
-        show: false,
-        type: 'video',
-        link: '',
-      },
-      layout: {
-        direction: 'row',
-        items: [
-          {
-            name: 'board',
-            basis: '100%',
-          },
-        ],
-      },
-      board: [
-        {
-          type: 'CKQMB2',
-          readFrom: '',
-          layout: 'val-top',
-          items: [
-            {
-              label: 'CO超限',
-              value: '210',
-            },
-            {
-              label: 'CH4超限',
-              value: '27',
-            },
-            {
-              label: 'C2H4超限',
-              value: '17',
-            },
-            {
-              label: 'C2H2超限',
-              value: '1',
-            },
-            {
-              label: 'O2超限',
-              value: '210',
-            },
-            {
-              label: '温度超限',
-              value: '27',
-            },
-            {
-              label: '压差超限',
-              value: '17',
-            },
-            {
-              label: '设备断连',
-              value: '1',
-            },
-          ],
-        },
-      ],
-      chart: [],
-      gallery: [],
-      gallery_list: [],
-      table: [],
-      list: [],
-      complex_list: [],
-      preset: [],
-    },
-    showStyle: {
-      size: 'width:440px;height:210px;',
-      version: '原版',
-      position: 'top:660px;right:10px;',
-    },
-  },
-];

+ 0 - 138
src/views/sealedGoafSys/home/sealedGoaf.vue

@@ -1,138 +0,0 @@
-<!-- eslint-disable vue/multi-word-component-names -->
-<template>
-  <div class="company-home">
-    <div class="title-select-area">
-      <a-select
-        class="title-select"
-        ref="select"
-        v-model:value="deviceId"
-        @change="handleDeviceChange"
-        popupClassName="drop"
-        :field-names="fieldNames"
-        :options="selectorOptions"
-        :dropdownStyle="{
-          width: '380px',
-          background: 'transparent',
-          borderBottom: '1px solid #ececec66',
-          backdropFilter: 'blur(50px)',
-          color: '#fff',
-        }"
-      />
-    </div>
-    <!-- 渲染所有模块 -->
-    <ModuleCommon
-      v-for="cfg in cfgs"
-      :key="cfg.deviceType + cfg.moduleName"
-      :show-style="cfg.showStyle"
-      :module-data="cfg.moduleData"
-      :module-name="cfg.moduleName"
-      :device-type="cfg.deviceType"
-      :data="data"
-      :visible="true"
-    />
-    <!-- <Three3D :modal-name="modalName" /> -->
-  </div>
-</template>
-<script lang="ts" setup>
-  import { computed, onMounted, onUnmounted, ref } from 'vue';
-  import { useInitConfigs, useInitPage } from '@/views/vent/home/configurable/hooks/useInit';
-  import { testConfigSealedGoaf } from './configurable.data.sealedGoaf';
-  import ModuleCommon from '@/views/vent/home/configurable/components/ModuleCommon.vue';
-  import { useGlobSetting } from '/@/hooks/setting';
-
-  const { title = '省局采空区密闭监测与分析系统' } = useGlobSetting();
-  const { data, updateData, mainTitle } = useInitPage(title);
-
-  const cfgs = computed(() => configs.value);
-  const { configs, fetchConfigs } = useInitConfigs();
-
-  const deviceId = ref('0'); // 当前选中设备ID
-  const fieldNames = { label: 'name', value: 'id' }; // 下拉框字段映射
-  const selectorOptions = ref([
-    { name: '采空区密闭分析', id: '0' },
-    { name: '采空区密闭监测', id: '1' },
-  ]);
-  // 切换设备事件
-  function handleDeviceChange(param) {
-    console.log('切换下拉框选项');
-  }
-  onMounted(() => {
-    fetchConfigs('sealed_goaf').then(() => {
-      configs.value = testConfigSealedGoaf;
-    });
-  });
-
-  // 数据处理函数
-  onUnmounted(() => {});
-</script>
-<style lang="less" scoped>
-  @import '/@/design/theme.less';
-
-  @font-face {
-    font-family: 'douyuFont';
-    src: url('/@/assets/font/douyuFont.otf');
-  }
-  @select-single: ~'@{namespace}-select-single';
-  @select-selector: ~'@{namespace}-select-selector';
-  @selection-item: ~'@{namespace}--select-selection-item';
-  .company-home {
-    --image-module-title: url('/@/assets/images/sealedGoaf/views/home/module-title.png');
-
-    width: 100%;
-    height: 100%;
-    color: @white;
-    position: relative;
-    background: #d7effe;
-    :deep(.vent-box1-bg) {
-      .box1-top {
-        background: unset;
-        height: 45px;
-        padding-top: 10px;
-        .title {
-          height: 25px;
-          background: var(--image-module-title) no-repeat;
-          color: #234e8e;
-          justify-content: left;
-          background-size: cover;
-          font-size: 20px;
-          padding-left: 10px;
-          div {
-            margin-top: -12px;
-          }
-        }
-      }
-      .box-container {
-        background-color: unset;
-        &::before {
-          background-image: none;
-        }
-        &::after {
-          background-image: none;
-        }
-      }
-      .box1-bottom {
-        background: none;
-      }
-    }
-    .title-select {
-      :deep(.@{select-selector}) {
-        width: 300px;
-        background-color: #2b6ff0;
-        box-shadow: none !important;
-        border: 1px solid #b3d8ff;
-        border-radius: 4px; /* 圆角 */
-        padding: 8px 12px; /* 内边距 */
-        font-size: 14px;
-        color: #333; /* 文字颜色 */
-
-        /* 交互样式 */
-        cursor: pointer;
-        width: auto;
-        :deep(.@{selection-item}) {
-          color: #fff !important;
-          font-size: 20px;
-        }
-      }
-    }
-  }
-</style>

+ 131 - 0
src/views/sys/login/LoginDataCenter.vue

@@ -0,0 +1,131 @@
+<template>
+  <div class="login-container">
+    <!-- 标题 -->
+    <h1 class="title">数据管理中心</h1>
+
+    <!-- 登录框(切图外框) -->
+    <div class="login-frame">
+      <div class="flex center">
+        <LoginForm />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import LoginForm from './LoginFormDataCenter.vue';
+// 响应式数据
+const username = ref('');
+const password = ref('');
+
+// 登录方法
+const handleLogin = () => {
+  if (!username.value) {
+    alert('请输入用户名');
+    return;
+  }
+  if (!password.value) {
+    alert('请输入密码');
+    return;
+  }
+  // 这里可对接接口,示例仅打印
+  console.log('登录信息:', { username: username.value, password: password.value });
+};
+
+// 忘记密码
+const handleForgotPwd = () => {
+  alert('跳转到忘记密码页面');
+};
+</script>
+
+<style scoped>
+.login-container {
+  width: 100%;
+  height: 100%;
+  background: url('@/assets/images/vent/loginDataCenter/dataCenterBg.png') no-repeat center;
+  background-size: 100% 100%;
+  overflow: hidden;
+  position: relative;
+}
+
+/* 标题样式 */
+.title {
+  position: absolute;
+  top: 24vh;
+  left: 20%;
+  transform: translateX(-50%);
+  color: #e2e8f0;
+  font-size: 24px;
+  font-weight: 800;
+  font-style: italic;
+  letter-spacing: 5px;
+  margin: 0;
+}
+
+/* 登录框外框(切图) */
+.login-frame {
+  position: absolute;
+  top: 55%;
+  left: 20%;
+  transform: translate(-50%, -50%);
+  width: 38%;
+  height: 40%;
+  background: url('@/assets/images/vent/loginDataCenter/loginForm.png') no-repeat center;
+  background-size: 100% 100%;
+}
+
+/* 登录表单 */
+.login-form {
+  width: 80%;
+  display: flex;
+  flex-direction: column;
+}
+
+.input-item {
+  width: 100%;
+}
+
+.input-item:focus {
+  border-color: #3b82f6;
+  box-shadow: 0 0 8px rgba(59, 130, 246, 0.5);
+}
+
+.input-item::placeholder {
+  color: #94a3b8;
+}
+
+.forgot-pwd {
+  text-align: right;
+}
+
+.forgot-pwd a {
+  color: #94a3b8;
+  font-size: 0.8rem;
+  text-decoration: none;
+  transition: color 0.3s;
+}
+
+.forgot-pwd a:hover {
+  color: #3b82f6;
+}
+
+.login-btn {
+  width: 100%;
+  padding: 0.8rem;
+  background: #3b82f6;
+  border: none;
+  border-radius: 4px;
+  color: #fff;
+  font-size: 1rem;
+  font-weight: bold;
+  cursor: pointer;
+  transition: all 0.3s;
+  box-shadow: 0 0 10px rgba(59, 130, 246, 0.4);
+}
+
+.login-btn:hover {
+  background: #2563eb;
+  box-shadow: 0 0 15px rgba(59, 130, 246, 0.6);
+}
+</style>

+ 208 - 0
src/views/sys/login/LoginFormDataCenter.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="login-box">
+    <Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow" @keypress.enter="handleLogin" autocomplete="off">
+      <FormItem name="account" class="enter-x">
+        <div class="input-box">
+          <div class="img-user" />
+          <Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" class="fix-auto-fill" />
+        </div>
+      </FormItem>
+      <FormItem name="password" class="enter-x">
+        <div class="input-box">
+          <div class="img-pass" />
+          <InputPassword size="large" visibilityToggle v-model:value="formData.password" :placeholder="t('sys.login.password')" />
+        </div>
+      </FormItem>
+    </Form>
+    <div class="btn-box">
+      <div class="btn" @click="handleLogin"> {{ t('sys.login.loginButton') }}</div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { reactive, ref, toRaw, unref, computed, onMounted } from 'vue';
+
+import { Checkbox, Form, Input, Row, Col, Button } from 'ant-design-vue';
+import { createFromIconfontCN } from '@ant-design/icons-vue';
+import { useI18n } from '/@/hooks/web/useI18n';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+import { useUserStore } from '/@/store/modules/user';
+import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
+import { useDesign } from '/@/hooks/web/useDesign';
+import { getCodeInfo } from '/@/api/sys/user';
+//import { onKeyStroke } from '@vueuse/core';
+
+const ACol = Col;
+const ARow = Row;
+const FormItem = Form.Item;
+const InputPassword = Input.Password;
+const IconFont = createFromIconfontCN({
+  scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
+});
+const { t } = useI18n();
+const { notification, createErrorModal } = useMessage();
+const { prefixCls } = useDesign('login');
+const userStore = useUserStore();
+
+const { setLoginState, getLoginState } = useLoginState();
+const { getFormRules } = useFormRules();
+
+const formRef = ref();
+const thirdModalRef = ref();
+const loading = ref(false);
+const rememberMe = ref(false);
+
+const formData = reactive({
+  account: '',
+  password: '',
+  inputCode: '',
+});
+const randCodeData = reactive({
+  randCodeImage: '',
+  requestCodeSuccess: false,
+  checkKey: null,
+});
+
+const { validForm } = useFormValid(formRef);
+
+//onKeyStroke('Enter', handleLogin);
+
+const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
+
+async function handleLogin() {
+  const data = await validForm();
+  if (!data) return;
+  try {
+    loading.value = true;
+    const { userInfo } = await userStore.login(
+      toRaw({
+        password: data.password,
+        username: data.account,
+        captcha: data.inputCode,
+        checkKey: randCodeData.checkKey,
+        mode: 'none', //不要默认的错误提示
+      })
+    );
+    if (userInfo) {
+      notification.success({
+        message: t('sys.login.loginSuccessTitle'),
+        description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
+        duration: 3,
+      });
+    }
+  } catch (error) {
+    // notification.error({
+    //   message: t('sys.api.errorTip'),
+    //   description: error.message || t('sys.api.networkExceptionMsg'),
+    //   duration: 3,
+    // });
+    loading.value = false;
+
+    //update-begin-author:taoyan date:2022-5-3 for: issues/41 登录页面,当输入验证码错误时,验证码图片要刷新一下,而不是保持旧的验证码图片不变
+    handleChangeCheckCode();
+    //update-end-author:taoyan date:2022-5-3 for: issues/41 登录页面,当输入验证码错误时,验证码图片要刷新一下,而不是保持旧的验证码图片不变
+  }
+}
+function handleChangeCheckCode() {
+  formData.inputCode = '';
+  //TODO 兼容mock和接口,暂时这样处理
+  randCodeData.checkKey = 1629428467008; //new Date().getTime();
+  getCodeInfo(randCodeData.checkKey).then((res) => {
+    randCodeData.randCodeImage = res;
+    randCodeData.requestCodeSuccess = true;
+  });
+}
+
+/**
+ * 第三方登录
+ * @param type
+ */
+function onThirdLogin(type) {
+  thirdModalRef.value.onThirdLogin(type);
+}
+//初始化验证码
+onMounted(() => {
+  handleChangeCheckCode();
+});
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+@ventSpace: zxm;
+
+.login-box {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  gap: 20px;
+  padding: 20px;
+  margin-top: 8%;
+  margin-left: 28%;
+  .input-box {
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+    background: #165691;
+    border: 1px solid #1d7fbd;
+    border-radius: 4px;
+    font-size: 0.9rem;
+    transition: all 0.3s;
+    input {
+      background: #165691 !important;
+      border: none;
+      color: #fff;
+    }
+    .img-user {
+      width: 24px;
+      height: 24px;
+      margin: 0 12px;
+      background: url('@/assets/images/vent/loginDataCenter/user.svg') no-repeat center;
+      background-size: contain;
+      align-self: center;
+    }
+    .img-pass {
+      width: 24px;
+      height: 24px;
+      margin: 0 12px;
+      background: url('@/assets/images/vent/loginDataCenter/password.svg') no-repeat center;
+      background-size: contain;
+      align-self: center;
+    }
+    :deep(.zxm-input .zxm-input-lg) {
+      background: #165691 !important;
+      border: none;
+      color: #fff;
+    }
+    :deep(.zxm-input-affix-wrapper > input.zxm-input) {
+      background: #165691 !important;
+      border: none;
+      color: #fff;
+    }
+    :deep(.zxm-form-item-has-error :not(.zxm-input-disabled):not(.zxm-input-borderless).zxm-input) {
+      background: #165691 !important;
+    }
+    :deep(.zxm-input-affix-wrapper) {
+      background: #165691 !important;
+      border: none;
+    }
+  }
+  .btn-box {
+    width: 276px;
+    height: 40px;
+    top: 40px;
+    left: 30%;
+    cursor: pointer;
+    background: #2390fd;
+    border-radius: 4px;
+    .btn {
+      color: #fff;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 14px;
+      height: 40px;
+    }
+  }
+}
+</style>

+ 214 - 0
src/views/sys/login/LoginFormNeiye.vue

@@ -0,0 +1,214 @@
+<template>
+  <div class="login-box">
+    <Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow" @keypress.enter="handleLogin" autocomplete="off">
+      <FormItem name="account" class="enter-x">
+        <div class="input-box">
+          <div class="img-user" />
+          <Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" class="fix-auto-fill" />
+        </div>
+      </FormItem>
+      <FormItem name="password" class="enter-x">
+        <div class="input-box">
+          <div class="img-pass" />
+          <InputPassword size="large" visibilityToggle v-model:value="formData.password" :placeholder="t('sys.login.password')" />
+        </div>
+      </FormItem>
+    </Form>
+    <div class="btn-box">
+      <div class="btn" @click="handleLogin"> {{ t('sys.login.loginButton') }}</div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { reactive, ref, toRaw, unref, computed, onMounted } from 'vue';
+
+import { Checkbox, Form, Input, Row, Col, Button } from 'ant-design-vue';
+import { createFromIconfontCN } from '@ant-design/icons-vue';
+import { useI18n } from '/@/hooks/web/useI18n';
+import { useMessage } from '/@/hooks/web/useMessage';
+
+import { useUserStore } from '/@/store/modules/user';
+import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
+import { useDesign } from '/@/hooks/web/useDesign';
+import { getCodeInfo } from '/@/api/sys/user';
+//import { onKeyStroke } from '@vueuse/core';
+
+const ACol = Col;
+const ARow = Row;
+const FormItem = Form.Item;
+const InputPassword = Input.Password;
+const IconFont = createFromIconfontCN({
+  scriptUrl: '//at.alicdn.com/t/font_2316098_umqusozousr.js',
+});
+const { t } = useI18n();
+const { notification, createErrorModal } = useMessage();
+const { prefixCls } = useDesign('login');
+const userStore = useUserStore();
+
+const { setLoginState, getLoginState } = useLoginState();
+const { getFormRules } = useFormRules();
+
+const formRef = ref();
+const thirdModalRef = ref();
+const loading = ref(false);
+const rememberMe = ref(false);
+
+const formData = reactive({
+  account: '',
+  password: '',
+  inputCode: '',
+});
+const randCodeData = reactive({
+  randCodeImage: '',
+  requestCodeSuccess: false,
+  checkKey: null,
+});
+
+const { validForm } = useFormValid(formRef);
+
+//onKeyStroke('Enter', handleLogin);
+
+const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
+
+async function handleLogin() {
+  const data = await validForm();
+  if (!data) return;
+  try {
+    loading.value = true;
+    const { userInfo } = await userStore.login(
+      toRaw({
+        password: data.password,
+        username: data.account,
+        captcha: data.inputCode,
+        checkKey: randCodeData.checkKey,
+        mode: 'none', //不要默认的错误提示
+      })
+    );
+    if (userInfo) {
+      notification.success({
+        message: t('sys.login.loginSuccessTitle'),
+        description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realname}`,
+        duration: 3,
+      });
+    }
+  } catch (error) {
+    // notification.error({
+    //   message: t('sys.api.errorTip'),
+    //   description: error.message || t('sys.api.networkExceptionMsg'),
+    //   duration: 3,
+    // });
+    loading.value = false;
+
+    //update-begin-author:taoyan date:2022-5-3 for: issues/41 登录页面,当输入验证码错误时,验证码图片要刷新一下,而不是保持旧的验证码图片不变
+    handleChangeCheckCode();
+    //update-end-author:taoyan date:2022-5-3 for: issues/41 登录页面,当输入验证码错误时,验证码图片要刷新一下,而不是保持旧的验证码图片不变
+  }
+}
+function handleChangeCheckCode() {
+  formData.inputCode = '';
+  //TODO 兼容mock和接口,暂时这样处理
+  randCodeData.checkKey = 1629428467008; //new Date().getTime();
+  getCodeInfo(randCodeData.checkKey).then((res) => {
+    randCodeData.randCodeImage = res;
+    randCodeData.requestCodeSuccess = true;
+  });
+}
+
+/**
+ * 第三方登录
+ * @param type
+ */
+function onThirdLogin(type) {
+  thirdModalRef.value.onThirdLogin(type);
+}
+//初始化验证码
+onMounted(() => {
+  handleChangeCheckCode();
+});
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+@ventSpace: zxm;
+
+.login-box {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  gap: 20px;
+  padding: 20px;
+  margin-top: 17%;
+  margin-left: 25%;
+  .input-box {
+    display: flex;
+    flex-direction: row;
+    width: 300px;
+    background-image: url('@/assets/images/vent/loginNeiye/1-1.png');
+    background-size: 100% 100%;
+    background-repeat: no-repeat;
+    height: 40px;
+    border-radius: 4px;
+    font-size: 0.9rem;
+    transition: all 0.3s;
+    color: #fff;
+    input {
+      background: transparent !important;
+      border: none;
+      color: #fff;
+    }
+    .img-user {
+      width: 24px;
+      height: 24px;
+      margin: 0 12px;
+      margin-left: 30px;
+      background: url('@/assets/images/vent/loginNeiye/user.svg') no-repeat center;
+      background-size: contain;
+      align-self: center;
+    }
+    .img-pass {
+      width: 24px;
+      height: 24px;
+      margin: 0 12px;
+      margin-left: 30px;
+      background: url('@/assets/images/vent/loginNeiye/pass.svg') no-repeat center;
+      background-size: contain;
+      align-self: center;
+    }
+    :deep(.zxm-input .zxm-input-lg) {
+      background: transparent !important;
+      border: none;
+      color: #fff;
+    }
+    :deep(.zxm-input-affix-wrapper > input.zxm-input) {
+      background: transparent !important;
+      border: none;
+      color: #fff;
+    }
+    :deep(.zxm-form-item-has-error :not(.zxm-input-disabled):not(.zxm-input-borderless).zxm-input) {
+      background: transparent !important;
+    }
+    :deep(.zxm-input-affix-wrapper) {
+      background: transparent !important;
+      border: none;
+    }
+  }
+  .btn-box {
+    width: 100%;
+    height: 40px;
+    top: 40px;
+    left: 30%;
+    cursor: pointer;
+    background: url('@/assets/images/vent/loginNeiye/btn1.png') no-repeat center;
+    background-size: 100% 100%;
+    border-radius: 4px;
+    .btn {
+      color: #fff;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 16px;
+      height: 40px;
+    }
+  }
+}
+</style>

+ 132 - 0
src/views/sys/login/LoginNeiye.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="login-container">
+    <!-- 标题 -->
+    <h1 class="title">内页管理系统</h1>
+
+    <!-- 登录框(切图外框) -->
+    <div class="login-frame">
+      <div class="flex center">
+        <LoginForm />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import LoginForm from './LoginFormNeiye.vue';
+// 响应式数据
+const username = ref('');
+const password = ref('');
+
+// 登录方法
+const handleLogin = () => {
+  if (!username.value) {
+    alert('请输入用户名');
+    return;
+  }
+  if (!password.value) {
+    alert('请输入密码');
+    return;
+  }
+  // 这里可对接接口,示例仅打印
+  console.log('登录信息:', { username: username.value, password: password.value });
+};
+
+// 忘记密码
+const handleForgotPwd = () => {
+  alert('跳转到忘记密码页面');
+};
+</script>
+
+<style scoped>
+.login-container {
+  width: 100%;
+  height: 100%;
+  background: url('@/assets/images/vent/loginNeiye/bg.png') no-repeat center;
+  background-size: 100% 100%;
+  overflow: hidden;
+  position: relative;
+}
+
+/* 标题样式 */
+.title {
+  position: absolute;
+  top: 13.5vh;
+  left: 50%;
+  transform: translateX(-50%);
+  color: #e2e8f0;
+  font-size: 24px;
+  font-weight: 800;
+  font-style: italic;
+  letter-spacing: 5px;
+  margin: 0;
+  z-index: 1;
+}
+
+/* 登录框外框(切图) */
+.login-frame {
+  position: absolute;
+  top: 43%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 38%;
+  height: 50%;
+  background: url('@/assets/images/vent/loginNeiye/1-2.png') no-repeat center;
+  background-size: 100% 100%;
+}
+
+/* 登录表单 */
+.login-form {
+  width: 80%;
+  display: flex;
+  flex-direction: column;
+}
+
+.input-item {
+  width: 100%;
+}
+
+.input-item:focus {
+  border-color: #3b82f6;
+  box-shadow: 0 0 8px rgba(59, 130, 246, 0.5);
+}
+
+.input-item::placeholder {
+  color: #94a3b8;
+}
+
+.forgot-pwd {
+  text-align: right;
+}
+
+.forgot-pwd a {
+  color: #94a3b8;
+  font-size: 0.8rem;
+  text-decoration: none;
+  transition: color 0.3s;
+}
+
+.forgot-pwd a:hover {
+  color: #3b82f6;
+}
+
+.login-btn {
+  width: 100%;
+  padding: 0.8rem;
+  background: #3b82f6;
+  border: none;
+  border-radius: 4px;
+  color: #fff;
+  font-size: 1rem;
+  font-weight: bold;
+  cursor: pointer;
+  transition: all 0.3s;
+  box-shadow: 0 0 10px rgba(59, 130, 246, 0.4);
+}
+
+.login-btn:hover {
+  background: #2563eb;
+  box-shadow: 0 0 15px rgba(59, 130, 246, 0.6);
+}
+</style>

+ 1 - 1
src/views/vent/dataCenter/deviceCenter/index.vue

@@ -350,7 +350,7 @@ async function refreshData(deviceId: string) {
   // 这里实现具体的请求逻辑
   const device = deviceList.value.find((d) => d.id === deviceId);
   const result = await getDevMonitorListById({ devId: deviceId.toString() });
-  monitorList.value = Object.values(result.readData);
+  monitorList.value[deviceId] = Object.values(result.readData);
 }
 
 onMounted(() => {

+ 1 - 1
src/views/vent/dataCenter/infoCenter/index.vue

@@ -259,7 +259,7 @@
         }
         .infoBox2 {
           :deep(.box-container) {
-            padding: 5px 0px 5px 20px !important;
+            padding: 5px 0px 5px 5px !important;
           }
         }
       }

+ 11 - 2
src/views/vent/dataCenter/infoCenter/infoCenter.data.ts

@@ -130,6 +130,9 @@ export const dailyNumOption: ModuleDataChart = {
   type: 'bar',
   readFrom: '',
   legend: { show: false },
+  grid: {
+    left: 100,
+  },
   xAxis: [
     {
       show: true,
@@ -139,14 +142,20 @@ export const dailyNumOption: ModuleDataChart = {
       },
     },
   ],
-  yAxis: [{ show: true, name: '', position: 'left' }],
+  yAxis: [
+    {
+      show: true,
+      name: '',
+      position: 'left',
+    },
+  ],
   series: [{ readFrom: 'collectDataByDayList', xprop: 'day', yprop: 'total_count', label: '' }],
   dataZoom: [
     {
       type: 'slider' as any,
       show: true,
       xAxisIndex: 0,
-      height: 12,
+      height: 10,
       bottom: 10,
       start: 0,
       handleStyle: { color: '#66ffff' },

+ 2 - 6
src/views/vent/dataCenter/stationCenter/index.vue

@@ -106,7 +106,7 @@ const cardList = ref<any[]>(); //分站列表
 const deviceList = ref<any[]>([]);
 const openNum = ref(0);
 const clsoeNum = ref(0);
-const monitorList = ref<any[]>([]); // 监测数据列表
+const monitorList = ref<Record<string, any[]>>({}); // 监测数据列表
 const expandedRowKeys = ref([]);
 const selectedIndex = ref(0);
 // // 分页参数
@@ -295,10 +295,6 @@ async function loadMonitoringData(deviceId: string) {
     clearInterval(timer);
     timer = null;
   }
-
-  // 立即请求一次数据
-  // 这里可以加上初始加载,避免等待1秒的间隔
-  // 定时器会在1秒后开始,所以先手动加载一次
   refreshData(deviceId);
 
   // 设置新的定时器
@@ -310,7 +306,7 @@ async function refreshData(deviceId: string) {
   // 这里实现具体的请求逻辑
   const device = deviceList.value.find((d) => d.id === deviceId);
   const result = await getDevMonitorListById({ devId: deviceId.toString() });
-  monitorList.value = Object.values(result.readData);
+  monitorList.value[deviceId] = Object.values(result.readData);
 }
 onMounted(async () => {
   await getSubStationList();

+ 9 - 6
src/views/vent/deviceManager/configurationTable/types.ts

@@ -179,11 +179,7 @@ export interface ModuleDataBoard extends ReadFrom {
     | 'R'
     | 'S'
     | 'T'
-    | 'U'
-    | 'CKQMB'
-    | 'CKQMB0'
-    | 'CKQMB1'
-    | 'CKQMB2';
+    | 'U';
   /** 展示牌布局,决定是 label 部分在上方或是 value 在上方 */
   layout: 'val-top' | 'label-top' | 'new-top' | 'new1-top' | 'new2-top' | 'new3-top';
   /** 核心配置,每个展示牌对应一项 */
@@ -309,6 +305,13 @@ export interface ModuleDataChart extends ReadFrom {
     /** y轴的对齐方式 */
     position: 'left' | 'right';
   }[];
+  grid?: {
+    top?: number;
+    bottom?: number;
+    left?: number;
+    right?: number;
+    [key: string]: any;
+  };
   /**
    * 核心配置
    *
@@ -343,7 +346,7 @@ export interface ModuleDataChart extends ReadFrom {
 
 export interface ModuleDataTable extends ReadFrom {
   /** 表格的预设样式 */
-  type: 'A' | 'B' | 'C' | 'D' | 'CKQMB';
+  type: 'A' | 'B' | 'C' | 'D';
   /** 是否自动滚动 */
   autoScroll?: boolean;
   /** 核心配置,每个表格列对应一项 */

+ 2 - 2
src/views/vent/gas/gasHome/index.vue

@@ -89,8 +89,8 @@
     dataSource.value = await gasSystem();
   });
   onMounted(() => {});
-  onBeforeUnmount(() => {
-    unmountMicroApps(['/micro-vent-3dModal']);
+  onBeforeUnmount(async () => {
+    await unmountMicroApps(['/micro-vent-3dModal']);
   });
 </script>
 

+ 241 - 235
src/views/vent/home/clique/components/icon-light.vue

@@ -1,178 +1,194 @@
 <template>
   <div class="icon-light">
     <template v-for="(item, index) in pointList" :key="index">
-      <div class="icon-point" @click="openModal(item.code, item.label, item.leftV, item.topV)"
-        :style="{ left: item.leftV, top: item.topV }">
+      <div class="icon-point" @click="openModal(item.code, item.label, item.leftV, item.topV)" :style="{ left: item.leftV, top: item.topV }">
         <img :src="item.imgSrc" alt="" />
         <span
-          :class="{ 'icon-text-c': item.align == 'center', 'icon-text-l': item.align == 'left', 'icon-text-r': item.align == 'right', 'icon-text-b': item.align == 'bottom' }"
-          :style="{ color: item.textColor }">{{ item.label }}</span>
+          :class="{
+            'icon-text-c': item.align == 'center',
+            'icon-text-l': item.align == 'left',
+            'icon-text-r': item.align == 'right',
+            'icon-text-b': item.align == 'bottom',
+          }"
+          :style="{ color: item.textColor }"
+          >{{ item.label }}</span
+        >
       </div>
     </template>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, defineEmits, inject, watch, nextTick } from 'vue';
-import { getAssetURL } from '/@/utils/ui';
+  import { ref, defineEmits, inject, watch, nextTick } from 'vue';
+  import { getAssetURL } from '/@/utils/ui';
 
-const emit = defineEmits(['showDetail']);
-const globalConfig = inject('globalConfig');
-let props = defineProps({
-  warningList: {
-    type: Array,
-    default: () => {
-      return [];
-    },
-  },
-});
-// const iconWarningList = ref([]);
-const warningLevel1 = getAssetURL('company/home/point1.png'); // 低风险
-const warningLevel2 = getAssetURL('company/home/point2.png'); // 中风险
-const warningLevel3 = getAssetURL('company/home/point3.png'); // 高风险
-const warningLevel4 = getAssetURL('company/home/point4.png'); // 报警
-let pointList = ref<any[]>(
-  globalConfig.History_Type == ''
-    ? [
-      { code: 'sdmtjtltmk', imgSrc: warningLevel1, label: 'lt', leftV: '327px', topV: '40px', textColor: '#fff', align: 'left' },
-      { code: 'sdmtjtcctrk', imgSrc: warningLevel1, label: 'cce', leftV: '291px', topV: '69px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtbetmk',
-        imgSrc: warningLevel1,
-        label: 'bet',
-        leftV: '286px',
-        topV: '97px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtwlmlmk', imgSrc: warningLevel1, label: 'wlml', leftV: '327px', topV: '115px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtcctmk',
-        imgSrc: warningLevel1,
-        label: 'cc',
-        leftV: '346px',
-        topV: '132px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtsgtmk', imgSrc: warningLevel1, label: 'skt', leftV: '373px', topV: '149px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtbltmk',
-        imgSrc: warningLevel1,
-        label: 'blt',
-        leftV: '408px',
-        topV: '184px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjthlgmk', imgSrc: warningLevel1, label: 'hlg', leftV: '445px', topV: '214px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtswmk',
-        imgSrc: warningLevel1,
-        label: 'sw',
-        leftV: '439px',
-        topV: '244px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtdltmkhjtj', imgSrc: warningLevel1, label: 'hjt', leftV: '398px', topV: '265px', textColor: '#fff', align: 'left' },
-      { code: 'sdmtjtdltmk', imgSrc: warningLevel1, label: 'dlt', leftV: '492px', topV: '260px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtjjmk',
-        imgSrc: warningLevel1,
-        label: 'jj',
-        leftV: '565px',
-        topV: '413px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtyjlmk', imgSrc: warningLevel1, label: 'yjl', leftV: '669px', topV: '308px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtbdmk',
-        imgSrc: warningLevel1,
-        label: 'bd',
-        leftV: '862px',
-        topV: '340px',
-        textColor: '#fff',
-        align: 'left',
-      },
-    ]
-    : [
-      { code: 'sdmtjtltmk', imgSrc: warningLevel1, label: '柳塔矿', leftV: '405px', topV: '165px', textColor: '#fff', align: 'right' },
-      { code: 'sdmtjtcctrk', imgSrc: warningLevel1, label: '寸草塔二矿', leftV: '269px', topV: '116px', textColor: '#fff', align: 'left' },
-      {
-        code: 'sdmtjtbetmk',
-        imgSrc: warningLevel1,
-        label: '布尔台矿',
-        leftV: '329px',
-        topV: '177px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtwlmlmk', imgSrc: warningLevel4, label: '乌兰木伦矿', leftV: '462px', topV: '210px', textColor: '#fff', align: 'right' },
-      {
-        code: 'sdmtjtcctmk',
-        imgSrc: warningLevel1,
-        label: '寸草塔矿',
-        leftV: '396px',
-        topV: '224px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtsgtmk', imgSrc: warningLevel1, label: '石圪台矿', leftV: '514px', topV: '250px', textColor: '#fff', align: 'bottom' },
-      {
-        code: 'sdmtjtbltmk',
-        imgSrc: warningLevel1,
-        label: '补连塔矿',
-        leftV: '432px',
-        topV: '282px',
-        textColor: '#fff',
-        align: 'left',
+  const emit = defineEmits(['showDetail']);
+  const globalConfig = inject('globalConfig');
+  let props = defineProps({
+    warningList: {
+      type: Array,
+      default: () => {
+        return [];
       },
-      { code: 'sdmtjthlgmk', imgSrc: warningLevel1, label: '哈拉沟矿', leftV: '641px', topV: '258px', textColor: '#fff', align: 'bottom' },
-      {
-        code: 'sdmtjtswmk',
-        imgSrc: warningLevel3,
-        label: '上湾矿',
-        leftV: '381px',
-        topV: '334px',
-        textColor: '#fff',
-        align: 'left',
-      },
-      { code: 'sdmtjtdltmkhjtj', imgSrc: warningLevel1, label: '活鸡兔井', leftV: '598px', topV: '368px', textColor: '#fff', align: 'bottom' },
-      { code: 'sdmtjtdltmk', imgSrc: warningLevel2, label: '大柳塔井', leftV: '598px', topV: '308px', textColor: '#fff', align: 'bottom' },
-      {
-        code: 'sdmtjtjjmk',
-        imgSrc: warningLevel1,
-        label: '锦界矿',
-        leftV: '575px',
-        topV: '460px',
-        textColor: '#fff',
-        align: 'bottom',
-      },
-      { code: 'sdmtjtyjlmk', imgSrc: warningLevel1, label: '榆家梁矿', leftV: '748px', topV: '370px', textColor: '#fff', align: 'center' },
-      {
-        code: 'sdmtjtbdmk',
-        imgSrc: warningLevel1,
-        label: '保德矿',
-        leftV: '892px',
-        topV: '200px',
-        textColor: '#fff',
-        align: 'center',
-      },
-    ]
-);
-function openModal(code, label, leftV, topV) {
-  emit('showDetail', code, label, leftV, topV);
-}
-watch(
-  () => props.warningList,
-  (newVal: any, oldVal: any) => {
-    let newPointList;
-    if (newVal && oldVal && newVal.length == oldVal.length) {
-      for (let i = 0; i < newVal.length; i++) {
-        if (newVal[i].isWarning !== oldVal[i].isWarning) {
-          if (!newPointList) newPointList = [...pointList.value];
+    },
+  });
+  // const iconWarningList = ref([]);
+  const warningLevel1 = getAssetURL('company/home/point1.png'); // 低风险
+  const warningLevel2 = getAssetURL('company/home/point2.png'); // 中风险
+  const warningLevel3 = getAssetURL('company/home/point3.png'); // 高风险
+  const warningLevel4 = getAssetURL('company/home/point4.png'); // 报警
+  let pointList = ref<any[]>(
+    globalConfig.History_Type == ''
+      ? [
+          { code: 'sdmtjtltmk', imgSrc: warningLevel1, label: 'lt', leftV: '327px', topV: '40px', textColor: '#fff', align: 'left' },
+          { code: 'sdmtjtcctrk', imgSrc: warningLevel1, label: 'cce', leftV: '291px', topV: '69px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtbetmk',
+            imgSrc: warningLevel1,
+            label: 'bet',
+            leftV: '286px',
+            topV: '97px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtwlmlmk', imgSrc: warningLevel1, label: 'wlml', leftV: '327px', topV: '115px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtcctmk',
+            imgSrc: warningLevel1,
+            label: 'cc',
+            leftV: '346px',
+            topV: '132px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtsgtmk', imgSrc: warningLevel1, label: 'skt', leftV: '373px', topV: '149px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtbltmk',
+            imgSrc: warningLevel1,
+            label: 'blt',
+            leftV: '408px',
+            topV: '184px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjthlgmk', imgSrc: warningLevel1, label: 'hlg', leftV: '445px', topV: '214px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtswmk',
+            imgSrc: warningLevel1,
+            label: 'sw',
+            leftV: '439px',
+            topV: '244px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtdltmkhjtj', imgSrc: warningLevel1, label: 'hjt', leftV: '398px', topV: '265px', textColor: '#fff', align: 'left' },
+          { code: 'sdmtjtdltmk', imgSrc: warningLevel1, label: 'dlt', leftV: '492px', topV: '260px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtjjmk',
+            imgSrc: warningLevel1,
+            label: 'jj',
+            leftV: '565px',
+            topV: '413px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtyjlmk', imgSrc: warningLevel1, label: 'yjl', leftV: '669px', topV: '308px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtbdmk',
+            imgSrc: warningLevel1,
+            label: 'bd',
+            leftV: '862px',
+            topV: '340px',
+            textColor: '#fff',
+            align: 'left',
+          },
+        ]
+      : [
+          { code: 'sdmtjtltmk', imgSrc: warningLevel1, label: '柳塔矿', leftV: '405px', topV: '165px', textColor: '#fff', align: 'right' },
+          { code: 'sdmtjtcctrk', imgSrc: warningLevel1, label: '寸草塔二矿', leftV: '269px', topV: '116px', textColor: '#fff', align: 'left' },
+          {
+            code: 'sdmtjtbetmk',
+            imgSrc: warningLevel1,
+            label: '布尔台矿',
+            leftV: '329px',
+            topV: '177px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtwlmlmk', imgSrc: warningLevel4, label: '乌兰木伦矿', leftV: '462px', topV: '210px', textColor: '#fff', align: 'right' },
+          {
+            code: 'sdmtjtcctmk',
+            imgSrc: warningLevel1,
+            label: '寸草塔矿',
+            leftV: '396px',
+            topV: '224px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtsgtmk', imgSrc: warningLevel1, label: '石圪台矿', leftV: '514px', topV: '250px', textColor: '#fff', align: 'bottom' },
+          {
+            code: 'sdmtjtbltmk',
+            imgSrc: warningLevel1,
+            label: '补连塔矿',
+            leftV: '432px',
+            topV: '282px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjthlgmk', imgSrc: warningLevel1, label: '哈拉沟矿', leftV: '641px', topV: '258px', textColor: '#fff', align: 'bottom' },
+          {
+            code: 'sdmtjtswmk',
+            imgSrc: warningLevel3,
+            label: '上湾矿',
+            leftV: '381px',
+            topV: '334px',
+            textColor: '#fff',
+            align: 'left',
+          },
+          { code: 'sdmtjtdltmkhjtj', imgSrc: warningLevel1, label: '活鸡兔井', leftV: '598px', topV: '368px', textColor: '#fff', align: 'bottom' },
+          { code: 'sdmtjtdltmk', imgSrc: warningLevel2, label: '大柳塔井', leftV: '598px', topV: '308px', textColor: '#fff', align: 'bottom' },
+          {
+            code: 'sdmtjtjjmk',
+            imgSrc: warningLevel1,
+            label: '锦界矿',
+            leftV: '575px',
+            topV: '460px',
+            textColor: '#fff',
+            align: 'bottom',
+          },
+          { code: 'sdmtjtyjlmk', imgSrc: warningLevel1, label: '榆家梁矿', leftV: '748px', topV: '370px', textColor: '#fff', align: 'center' },
+          {
+            code: 'sdmtjtbdmk',
+            imgSrc: warningLevel1,
+            label: '保德矿',
+            leftV: '892px',
+            topV: '200px',
+            textColor: '#fff',
+            align: 'center',
+          },
+        ]
+  );
+  function openModal(code, label, leftV, topV) {
+    emit('showDetail', code, label, leftV, topV);
+  }
+  watch(
+    () => props.warningList,
+    (newVal: any, oldVal: any) => {
+      let newPointList;
+      if (newVal && oldVal && newVal.length == oldVal.length) {
+        for (let i = 0; i < newVal.length; i++) {
+          if (newVal[i].isWarning !== oldVal[i].isWarning) {
+            if (!newPointList) newPointList = [...pointList.value];
+            const index = (newPointList as []).findIndex((item) => item['code'] === newVal[i]['orgcode']);
+            // newPointList[index]['imgSrc'] = newVal[i].isWarning ? warningLevel4 : warningLevel1;
+            newPointList[index]['imgSrc'] = warningLevel1;
+            // newPointList[index]['textColor'] = newVal[i].isWarning ? '#ff5e00' : '#fff';
+            newPointList[index]['textColor'] = '#fff';
+          }
+        }
+      } else {
+        newPointList = [...pointList.value];
+        for (let i = 0; i < newVal.length; i++) {
           const index = (newPointList as []).findIndex((item) => item['code'] === newVal[i]['orgcode']);
           // newPointList[index]['imgSrc'] = newVal[i].isWarning ? warningLevel4 : warningLevel1;
           newPointList[index]['imgSrc'] = warningLevel1;
@@ -180,91 +196,81 @@ watch(
           newPointList[index]['textColor'] = '#fff';
         }
       }
-    } else {
-      newPointList = [...pointList.value];
-      for (let i = 0; i < newVal.length; i++) {
-        const index = (newPointList as []).findIndex((item) => item['code'] === newVal[i]['orgcode']);
-        // newPointList[index]['imgSrc'] = newVal[i].isWarning ? warningLevel4 : warningLevel1;
-        newPointList[index]['imgSrc'] = warningLevel1;
-        // newPointList[index]['textColor'] = newVal[i].isWarning ? '#ff5e00' : '#fff';
-        newPointList[index]['textColor'] = '#fff';
+      if (newPointList) {
+        pointList.value = newPointList;
       }
     }
-    if (newPointList) {
-      pointList.value = newPointList;
-    }
-  }
-);
+  );
 </script>
 
 <style lang="less" scoped>
-.icon-light {
-  position: relative;
-  width: 100%;
-  height: 100%;
+  .icon-light {
+    position: relative;
+    width: 100%;
+    height: 100%;
 
-  .icon-point {
-    display: flex;
-    align-items: center;
-    position: absolute;
+    .icon-point {
+      display: flex;
+      align-items: center;
+      position: absolute;
 
-    img {
-      width: 35px;
-      height: 35px;
-      cursor: pointer;
-    }
+      img {
+        width: 35px;
+        height: 35px;
+        cursor: pointer;
+      }
 
-    span {
-      font-size: 12px;
-      padding: 0px 5px;
-      color: #ffffff;
-      // background-color: rgba(12, 13, 13);
-    }
+      span {
+        font-size: 12px;
+        padding: 0px 5px;
+        color: #ffffff;
+        // background-color: rgba(12, 13, 13);
+      }
 
-    .icon-text-l {
-      position: absolute;
-      font-size: 12px;
-      padding: 0px 5px;
-      // background-color: rgba(12, 13, 13);
-      display: block;
-      width: 100px;
-      text-align: center;
-      // top: -20px;
-      left: -75px;
-    }
+      .icon-text-l {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        // background-color: rgba(12, 13, 13);
+        display: block;
+        width: 100px;
+        text-align: center;
+        // top: -20px;
+        left: -75px;
+      }
 
-    .icon-text-r {
-      position: absolute;
-      font-size: 12px;
-      padding: 0px 5px;
-      display: block;
-      width: 100px;
-      text-align: center;
-      // top: -20px;
-      left: 12px;
-    }
+      .icon-text-r {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        display: block;
+        width: 100px;
+        text-align: center;
+        // top: -20px;
+        left: 12px;
+      }
 
-    .icon-text-c {
-      position: absolute;
-      font-size: 12px;
-      padding: 0px 5px;
-      display: block;
-      width: 60px;
-      text-align: center;
-      top: -20px;
-      left: -12px;
-    }
+      .icon-text-c {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        display: block;
+        width: 60px;
+        text-align: center;
+        top: -20px;
+        left: -12px;
+      }
 
-    .icon-text-b {
-      position: absolute;
-      font-size: 12px;
-      padding: 0px 5px;
-      display: block;
-      width: 100px;
-      text-align: center;
-      left: -28px;
-      bottom: -20px;
+      .icon-text-b {
+        position: absolute;
+        font-size: 12px;
+        padding: 0px 5px;
+        display: block;
+        width: 100px;
+        text-align: center;
+        left: -28px;
+        bottom: -20px;
+      }
     }
   }
-}
 </style>

+ 3 - 5
src/views/vent/home/colliery/index.vue

@@ -49,10 +49,7 @@
               <!-- <div class="btn-icon" @click="goModalDetail"></div> -->
               <!-- 展会不显示按钮 -->
               <div v-if="sysOrgCode !== 'mkyzhpt'" class="btn-icon" @click="goModalDetail"></div>
-              <VentModal
-                ref="centerModalRef"
-                style="width: calc(100% - 30px); height: calc(100% - 30px); position: absolute; background-color: #fff"
-              />
+              <SubApp ref="centerModalRef" style="width: calc(100% - 30px); height: calc(100% - 40px); position: absolute; background-color: #fff" />
             </div>
           </div>
           <!-- 风量监测 -->
@@ -110,7 +107,7 @@
   import { getActions } from '/@/qiankun/state';
   import { unmountMicroApps, mountMicroApp } from '/@/qiankun';
   import { getDate } from './clique.data';
-  import VentModal from '/@/components/vent/micro/ventModal.vue';
+  import SubApp from '/@/components/vent/micro/createSubApp.vue';
   import { usePermission } from '/@/hooks/web/usePermission';
 
   const { currentRoute } = useRouter();
@@ -290,6 +287,7 @@
   }
 
   const changePageType = (pageType) => {
+    debugger;
     actions.setGlobalState({ pageObj: { pageType: pageType } });
   };
 

+ 18 - 10
src/views/vent/home/configurable/components/MonitorBar.vue

@@ -1,10 +1,8 @@
 <template>
   <a-row class="midback-info" :gutter="5">
-    <div v-for="item in computedConfig" :key="item.class" style="width: 17%; margin-left: 1%; margin-right: 2%; float: left">
-      <div class="middata" :class="item.class">
-        <div class="midnumberval">{{ item.value }}</div>
-        <div class="infotext1">{{ item.label }}</div>
-      </div>
+    <div v-for="item in computedConfig" :key="item.class" class="middata" :class="item.class">
+      <div class="midnumberval">{{ item.value }}</div>
+      <div class="infotext1">{{ item.label }}</div>
     </div>
     <!-- <div style="width: 17%; margin-left: 1%; margin-right: 2%; float: left">
       <div class="middata middata2">
@@ -123,14 +121,17 @@
   });
 </script>
 <style lang="less" scoped>
+  .midback-info {
+    pointer-events: none;
+  }
   .middata {
     // margin-top: 7px;
     padding: 5px 0px 5px 60px;
-    width: calc(100% - 10px);
-    height: 65px;
-    margin-top: 4px;
-    margin-left: 10px;
-    margin-bottom: 5px;
+    width: 175px;
+    height: 64px;
+    // margin-top: 4px;
+    // margin-left: 10px;
+    // margin-bottom: 5px;
     background-size: 100% 100%;
   }
   .middata0 {
@@ -163,4 +164,11 @@
     font-family: 'Microsoft YaHei', Arial;
     // font-family: 'UnidreamLED';
   }
+  .midback-info {
+    background-image: url('/@/assets/images/home-container/configurable/middata_bg.png');
+    background-size: 100% 100%;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+  }
 </style>

+ 19 - 3
src/views/vent/home/configurable/components/detail/CustomChart.vue

@@ -33,7 +33,7 @@
     const inst = getInstance();
     const domWidth = inst ? inst.getWidth() : 500;
     // 依据每一个图表配置生成图表选项
-    const { yAxis = [], xAxis = [], legend, order, type, sortBy, series, dataZoom = [] } = props.chartConfig;
+    const { yAxis = [], xAxis = [], legend, order, type, sortBy, series, dataZoom = [], grid } = props.chartConfig;
     const textStyle = {
       color: '#fff',
     };
@@ -81,6 +81,7 @@
         // show: get(baseSeries, '[0].data.length', 1) > e.maxAxisLength,
         xAxisIndex: i,
         end: e.end,
+        height: 20,
       };
     });
 
@@ -105,6 +106,7 @@
           top: 60,
           right: 37,
           bottom: 30,
+          ...grid,
         },
         dataZoom: [
           {
@@ -386,7 +388,12 @@
               show: true,
               position: 'outside',
               color: '#ddd',
-              formatter: get(legend, 'formatter', '{b}\n{c}pa'),
+              formatter: get(legend, 'formatter', '{b}\n{c}'),
+              rich: {
+                primarytext: {
+                  color: '#00c8d9',
+                },
+              },
             },
             labelLine: {
               length: 30,
@@ -503,9 +510,10 @@
         textStyle,
         grid: {
           top: 50,
-          bottom: dataZoom.length ? 70 : 30,
+          bottom: dataZoom.length ? 80 : 30,
           left: 50, // 缩小左侧边距,增加图表宽度
           right: 50, // 缩小右侧边距
+          ...grid,
         },
         legend: {
           textStyle,
@@ -597,6 +605,7 @@
           top: 40,
           right: 60,
           bottom: dataZoom.length ? 70 : 30,
+          ...grid,
         },
         dataZoom: baseDataZoom,
         xAxis: xAxis.map((e) => {
@@ -645,6 +654,7 @@
           top: 40,
           right: 60,
           bottom: dataZoom.length ? 70 : 30,
+          ...grid,
         },
         dataZoom: baseDataZoom,
         xAxis: xAxis.map((e) => {
@@ -695,6 +705,7 @@
           left: 60,
           right: 60,
           bottom: dataZoom.length ? 70 : 30,
+          ...grid,
         },
         dataZoom: baseDataZoom,
         xAxis: xAxis.map((e) => {
@@ -745,6 +756,7 @@
           top: 50,
           right: 50,
           bottom: dataZoom.length ? 70 : 30,
+          ...grid,
         },
         dataZoom: baseDataZoom,
         xAxis: xAxis.map((e) => {
@@ -811,6 +823,7 @@
           right: 40,
           bottom: dataZoom.length ? 70 : 30,
           show: false,
+          ...grid,
         },
         dataZoom: baseDataZoom,
         xAxis: xAxis.map((e) => {
@@ -848,6 +861,7 @@
         grid: {
           top: 50,
           bottom: 30,
+          ...grid,
         },
         legend: {
           textStyle,
@@ -901,6 +915,7 @@
         grid: {
           top: 40,
           bottom: 30,
+          ...grid,
         },
         legend: {
           textStyle,
@@ -1003,6 +1018,7 @@
         grid: {
           top: 40,
           bottom: 30,
+          ...grid,
         },
         legend: {
           textStyle,

+ 0 - 37
src/views/vent/home/configurable/components/detail/CustomTable.vue

@@ -85,7 +85,6 @@
     --image-list-head: url('/@/assets/images/home-container/configurable/firehome/list-head.png');
     --image-content-label-d: url(/@/assets/images/home-container/configurable/minehome/content-label.png);
     --image-content-body-d: url('/@/assets/images/home-container/configurable/minehome/content-body.png');
-    --image-content-label-ckqmb: url('/@/assets/images/sealedGoaf/configurable/table/table1-label.png');
     height: 100%;
     box-sizing: border-box;
     display: flex;
@@ -197,41 +196,5 @@
         background: #092b3a;
       }
     }
-    .table__content_label_CKQMB {
-      background-image: var(--image-content-label-ckqmb);
-      background-size: 100% 100%;
-      background-repeat: no-repeat;
-      color: #000000;
-    }
-    .table__content_list_CKQMB {
-      font-weight: bold;
-      font-size: 18px;
-    }
-    /* 第一个子元素:黑色 */
-    .table__content_list_row .table__content__list_item_CKQMB:nth-child(1) {
-      font-size: 12px;
-      text-align: left;
-      color: #000000;
-    }
-
-    /* 第二个子元素:蓝色 */
-    .table__content_list_row .table__content__list_item_CKQMB:nth-child(2) {
-      color: #1890ff;
-    }
-
-    /* 第三个子元素:黄色 */
-    .table__content_list_row .table__content__list_item_CKQMB:nth-child(3) {
-      color: #ffc107;
-    }
-
-    /* 第四个子元素:橙色 */
-    .table__content_list_row .table__content__list_item_CKQMB:nth-child(4) {
-      color: #ff7a45;
-    }
-
-    /* 第五个子元素:红色 */
-    .table__content_list_row .table__content__list_item_CKQMB:nth-child(5) {
-      color: #ff4d4f;
-    }
   }
 </style>

+ 0 - 149
src/views/vent/home/configurable/components/detail/MiniBoard.vue

@@ -216,13 +216,6 @@
     --image-board_bg_8: url('/@/assets/images/home-container/configurable/board_bg_8.png');
     --image-board_bg_9: url('/@/assets/images/home-container/configurable/board_bg_9.png');
     --image-board_bg_10: url('/@/assets/images/home-container/configurable/board_bg_10.png');
-    --image-board-bg-ckqmb-1: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-1.png');
-    --image-board-bg-ckqmb-2: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-2.png');
-    --image-board-bg-ckqmb-3: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-3.png');
-    --image-board-bg-ckqmb-4: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-4.png');
-    --image-board-bg-ckqmb-5: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb-5.png');
-    --image-board-bg-ckqmb1: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb1.png');
-    --image-board-bg-ckqmb2: url('/@/assets/images/sealedGoaf/configurable/miniBoard/board-bg-ckqmb2.png');
 
     --image-hycd: url(/@/assets/images/home-container/configurable/dusthome/hycd.png);
     --image-dyfl: url(/@/assets/images/home-container/configurable/dusthome/dyfl.png);
@@ -484,148 +477,6 @@
     background-image: var(--image-board-bg-o);
     background-size: 100% 100%;
   }
-  .mini-board_CKQMB {
-    width: 220px;
-    height: 50px;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 0 20px 0 20px;
-    background-image: var(--image-board-bg-ckqmb-1);
-    background-size: 100% 100%;
-  }
-  .mini-board_CKQMB:nth-child(2) {
-    background-image: var(--image-board-bg-ckqmb-2);
-  }
-  .mini-board_CKQMB:nth-child(3) {
-    background-image: var(--image-board-bg-ckqmb-3);
-  }
-  .mini-board_CKQMB:nth-child(4) {
-    background-image: var(--image-board-bg-ckqmb-4);
-  }
-  .mini-board_CKQMB:nth-child(5) {
-    background-image: var(--image-board-bg-ckqmb-5);
-  }
-  .mini-board_CKQMB:nth-child(6) {
-    background-image: var(--image-board-bg-ckqmb-1);
-  }
-  .mini-board__label_CKQMB {
-    color: #000000;
-    font-size: 13px;
-    font-family: 'Microsoft YaHei';
-    font-weight: bold;
-    padding-bottom: 6px;
-  }
-  .mini-board__value_CKQMB {
-    width: 55px;
-    color: #000000;
-    font-size: 25px;
-    padding-bottom: 13px;
-    font-family: 'Microsoft YaHei';
-  }
-  .mini-board_CKQMB0 {
-    width: 100px;
-    height: 90px;
-    background-image: var(--image-board-bg-ckqmb1);
-    background-size: 100% 100%;
-    padding-top: 28px;
-  }
-
-  .mini-board__label_CKQMB0 {
-    color: #000000;
-    font-size: 14px;
-    font-family: 'Microsoft YaHei';
-  }
-  .mini-board__value_CKQMB0 {
-    color: #000000;
-    font-size: 22px;
-    font-family: 'Microsoft YaHei';
-    padding-bottom: 10px;
-  }
-  /* 1. 第一个容器:label和value为蓝色 */
-  .mini-board_CKQMB0:nth-child(1) .mini-board__label_CKQMB0,
-  .mini-board_CKQMB0:nth-child(1) .mini-board__value_CKQMB0 {
-    color: #1890ff;
-  }
-
-  /* 2. 第二个容器:label和value为黄色 */
-  .mini-board_CKQMB0:nth-child(2) .mini-board__label_CKQMB0,
-  .mini-board_CKQMB0:nth-child(2) .mini-board__value_CKQMB0 {
-    color: #36a64d;
-  }
-
-  /* 3. 第三个容器:label和value为橙色 */
-  .mini-board_CKQMB0:nth-child(3) .mini-board__label_CKQMB0,
-  .mini-board_CKQMB0:nth-child(3) .mini-board__value_CKQMB0 {
-    color: #a97523;
-  }
-
-  /* 4. 第四个容器:label和value为红色 */
-  .mini-board_CKQMB0:nth-child(4) .mini-board__label_CKQMB0,
-  .mini-board_CKQMB0:nth-child(4) .mini-board__value_CKQMB0 {
-    color: #d6666c;
-  }
-  .mini-board_CKQMB1 {
-    width: 100px;
-    height: 90px;
-    background-image: var(--image-board-bg-ckqmb1);
-    background-size: 100% 100%;
-    padding-top: 28px;
-  }
-
-  .mini-board__label_CKQMB1 {
-    color: #000000;
-    font-size: 14px;
-    font-family: 'Microsoft YaHei';
-  }
-  .mini-board__value_CKQMB1 {
-    color: #000000;
-    font-size: 22px;
-    font-family: 'Microsoft YaHei';
-    padding-bottom: 10px;
-  }
-  /* 1. 第一个容器:label和value为蓝色 */
-  .mini-board_CKQMB1:nth-child(1) .mini-board__label_CKQMB1,
-  .mini-board_CKQMB1:nth-child(1) .mini-board__value_CKQMB1 {
-    color: #1890ff;
-  }
-
-  /* 2. 第二个容器:label和value为黄色 */
-  .mini-board_CKQMB1:nth-child(2) .mini-board__label_CKQMB1,
-  .mini-board_CKQMB1:nth-child(2) .mini-board__value_CKQMB1 {
-    color: #ffc107;
-  }
-
-  /* 3. 第三个容器:label和value为橙色 */
-  .mini-board_CKQMB1:nth-child(3) .mini-board__label_CKQMB1,
-  .mini-board_CKQMB1:nth-child(3) .mini-board__value_CKQMB1 {
-    color: #ff7a45;
-  }
-
-  /* 4. 第四个容器:label和value为红色 */
-  .mini-board_CKQMB1:nth-child(4) .mini-board__label_CKQMB1,
-  .mini-board_CKQMB1:nth-child(4) .mini-board__value_CKQMB1 {
-    color: #ff4d4f;
-  }
-  .mini-board_CKQMB2 {
-    width: 96px;
-    height: 71px;
-    background-image: var(--image-board-bg-ckqmb2);
-    background-size: 100% 100%;
-    padding-top: 8px;
-  }
-
-  .mini-board__label_CKQMB2 {
-    color: #000000;
-    font-size: 14px;
-    font-family: 'Microsoft YaHei';
-  }
-  .mini-board__value_CKQMB2 {
-    color: #2b6ff0;
-    font-size: 22px;
-    font-family: 'Microsoft YaHei';
-    padding-bottom: 6px;
-  }
 
   .mini-board_P {
     width: 97px;

+ 57 - 37
src/views/vent/home/configurable/configurable.api.ts

@@ -2,8 +2,8 @@ import { floor, isArray, random, slice } from 'lodash-es';
 import { defHttp } from '/@/utils/http/axios';
 import { get } from '../billboard/utils';
 import { useGlobSetting } from '/@/hooks/setting';
-import { ref, reactive } from 'vue'
-
+import { reactive } from 'vue';
+import _ from 'lodash';
 
 enum Api {
   list = '/safety/ventanalyDevice/homedata2',
@@ -16,7 +16,7 @@ enum Api {
   getTotal = '/safety/ventanalyAlarmLog/total',
   sysTypeWarnList = '/safety/ventanalyAlarmLog/sysTypeWarn',
   getDisasterProportion = '/safety/ventanalyAlarmLog/getDisasterProportion',
-  system = '/ventanaly-device/monitor/device'
+  system = '/ventanaly-device/monitor/device',
 }
 
 // 搞这个缓存是由于:目前代码上的设计是多个模块发出多次请求,每个模块自己负责消费前者的响应。
@@ -83,9 +83,9 @@ export const list = (params) => {
         // const m3_merge = parseInt(retM3_merge.value);
 
         e.piechart = [
-          { val: drag_1, valMock: floor((drag_1 / drag_total) * drag_merge), label: '进风区(Pa)' },
-          { val: drag_2, valMock: floor((drag_2 / drag_total) * drag_merge), label: '用风区(Pa)' },
-          { val: drag_3, valMock: floor((drag_3 / drag_total) * drag_merge), label: '回风区(Pa)' },
+          { val: drag_1, valMock: floor((drag_1 / drag_total) * drag_merge), label: '进风区' },
+          { val: drag_2, valMock: floor((drag_2 / drag_total) * drag_merge), label: '用风区' },
+          { val: drag_3, valMock: floor((drag_3 / drag_total) * drag_merge), label: '回风区' },
         ];
         e.readData.dengjikong_merge = get(res, 'midinfo[0].sysinfo.equalarea');
         e.readData.fy_merge_int = drag_merge;
@@ -99,7 +99,7 @@ export const list = (params) => {
         }
         if (isArray(e.history_report)) {
           e.history_report = slice(e.history_report, e.history_report.length - 30, e.history_report.length);
-          console.log(e, '999000')
+          console.log(e, '999000');
         }
       });
     }
@@ -453,7 +453,7 @@ export const getTotal = (params) => {
   return (cache.get(key) as Promise<any>).then(async (res) => {
     console.log(res, '多灾融合预警数据');
 
-    let dataVent = await getLevelNum()
+    let dataVent = await getLevelNum();
     const levelsList = [
       {
         name: '报警',
@@ -472,8 +472,8 @@ export const getTotal = (params) => {
         value: 0,
       },
       {
-        name: "低风险",
-        value: dataVent.vent || 0
+        name: '低风险',
+        value: dataVent.vent || 0,
       },
     ];
     //通风监测预警数据
@@ -539,13 +539,13 @@ export const getTotal = (params) => {
       return {
         label: el.systemname,
         value: el.gasNumber,
-        value1: el.pumpNumber
-      }
-    })
+        value1: el.pumpNumber,
+      };
+    });
 
     //火灾监测预警数据
-    res.fireInfos = reactive({})
-    res.fireInfos.dataOn = []
+    res.fireInfos = reactive({});
+    res.fireInfos.dataOn = [];
 
     if (res.bundletubeInfo && res.bundletubeInfo.msgTxt.length != 0 && res.bundletubeInfo.msgTxt[0].datalist.length != 0) {
       res.bundletubeInfo.msgTxt[0].datalist.forEach((el) => {
@@ -587,28 +587,27 @@ export const getTotal = (params) => {
     //粉尘监测预警数据
     res.dustInfo = [
       {
-        name: "报警",
-        value: res.info.sysInfo.dustS.levels.alarm
+        name: '报警',
+        value: res.info.sysInfo.dustS.levels.alarm,
       },
       {
-        name: "重大风险",
-        value: res.info.sysInfo.dustS.levels.red
+        name: '重大风险',
+        value: res.info.sysInfo.dustS.levels.red,
       },
       {
-        name: "较大风险",
-        value: res.info.sysInfo.dustS.levels.orange
+        name: '较大风险',
+        value: res.info.sysInfo.dustS.levels.orange,
       },
       {
-        name: "一般风险",
-        value: res.info.sysInfo.dustS.levels.yellow
+        name: '一般风险',
+        value: res.info.sysInfo.dustS.levels.yellow,
       },
       {
-        name: "低风险",
-        value: dataVent.dust ? dataVent.dust : res.info.sysInfo.dustS.levels.blue
+        name: '低风险',
+        value: dataVent.dust ? dataVent.dust : res.info.sysInfo.dustS.levels.blue,
       },
     ];
 
-
     return res;
   });
 };
@@ -625,28 +624,49 @@ export const getElectroData = (params) => {
     );
   }
   return (cache.get(key) as Promise<any>).then(async (res) => {
-    let data = res.msgTxt.find(el => el.type == 'ballvalve_auto')
+    let data = res.msgTxt.find((el) => el.type == 'ballvalve_auto');
     data.tempData = data?.datalist?.map((el, index) => {
       return {
         areaName: el.readData.areaName,
         tempStart: el.readData.tempStart,
         tempStop: el.readData.tempStop,
-        CORealtime: el.readData.CORealtime
-      }
-    })
+        CORealtime: el.readData.CORealtime,
+      };
+    });
     if (data?.datalist) {
-      data.datalist.forEach(el => {
+      data.datalist.forEach((el) => {
         el.cardData = {
-          title: el.strinstallpos, areaText: '区域', areaVal: el.readData.areaName, moduleText: '模式', moduleVal: el.readData.smokePattern, statusText: '烟雾传感器状态', statusVal: el.readData.smokeSensorStatus == 'False' ? '正常-低电平' : '异常', phoneText: '机号', phoneVal: el.readData.deviceName, tempNowText: '实时测温', tempNowVal: el.readData.tempRealtime, tempOpenText: '开启温度', tempOpenVal: el.readData.tempStart, timeText: '延时t1', timeVal: 0, tempMaxText: '最高温度', tempMaxVal: el.readData.tempMax, tempCloseText: '关闭温度', tempCloseVal: el.readData.tempMin, time3Text: '延时t3', time3Val: 0, deviceSTAT: el.readData.deviceSTAT == '1' ? true : false
-        }
-      })
-      data.chartData = data.datalist.map(el => {
+          title: el.strinstallpos,
+          areaText: '区域',
+          areaVal: el.readData.areaName,
+          moduleText: '模式',
+          moduleVal: el.readData.smokePattern,
+          statusText: '烟雾传感器状态',
+          statusVal: el.readData.smokeSensorStatus == 'False' ? '正常-低电平' : '异常',
+          phoneText: '机号',
+          phoneVal: el.readData.deviceName,
+          tempNowText: '实时测温',
+          tempNowVal: el.readData.tempRealtime,
+          tempOpenText: '开启温度',
+          tempOpenVal: el.readData.tempStart,
+          timeText: '延时t1',
+          timeVal: 0,
+          tempMaxText: '最高温度',
+          tempMaxVal: el.readData.tempMax,
+          tempCloseText: '关闭温度',
+          tempCloseVal: el.readData.tempMin,
+          time3Text: '延时t3',
+          time3Val: 0,
+          deviceSTAT: el.readData.deviceSTAT == '1' ? true : false,
+        };
+      });
+      data.chartData = data.datalist.map((el) => {
         return {
           time: el.readData.areaName,
           coRealTime: el.readData.CORealtime,
           coWarn: el.readData.COWarn,
-        }
-      })
+        };
+      });
     }
     return data;
   });

+ 6 - 7
src/views/vent/home/configurable/configurable.data.tashan.ts

@@ -68,7 +68,7 @@ export const testConfigTSFire: Config[] = [
   },
   // 2. 矿用自动喷洒系统(左中)
   {
-    deviceType: 'spray_auto',
+    deviceType: 'sprayInfo',
     moduleName: '矿用自动喷洒系统',
     pageType: 'ts_fire',
     moduleData: {
@@ -105,24 +105,24 @@ export const testConfigTSFire: Config[] = [
       board: [
         {
           type: 'M',
-          readFrom: '',
+          readFrom: 'deviceInfo',
           layout: 'val-top',
           items: [
             {
               label: '流量传感器#1',
-              value: '${deviceInfo.spray.warnInfo.alarmNum}',
+              value: '${spray.datalist[0].deviceType}',
             },
             {
               label: '流量传感器#2',
-              value: 'xxxxxx',
+              value: '${spray.datalist[0].fsectarea}',
             },
             {
               label: '压力传感器',
-              value: 'xxxxxx',
+              value: '${spray.datalist[0].readData.sign}',
             },
             {
               label: '液位计',
-              value: '液位超限',
+              value: '${spray.datalist[0].readData.sprayval}',
             },
           ],
         },
@@ -144,7 +144,6 @@ export const testConfigTSFire: Config[] = [
           },
         },
       ],
-      mock: {},
     },
     showStyle: {
       size: 'width:440px;height:310px;',

+ 8 - 8
src/views/vent/home/configurable/configurable.data.ts

@@ -6142,7 +6142,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'fanmain',
     moduleName: '主通风机系统',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: true,
@@ -6213,7 +6213,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'fanlocal',
     moduleName: '局部通风机系统',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: true,
@@ -6288,7 +6288,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: '',
     moduleName: '通风设施远程控制',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: false,
@@ -6330,7 +6330,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'sys_wind',
     moduleName: '风量监测',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: false,
@@ -6398,7 +6398,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'sys_majorpath',
     moduleName: '关键通风路线',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: true,
@@ -6459,7 +6459,7 @@ export const testConfigVent182: Config[] = [
         {
           type: 'pie_halo',
           readFrom: '',
-          legend: { show: false, formatter: '{b}:{c}\n{d}%' },
+          legend: { show: false, formatter: '{b}\n{primarytext|{c}(Pa)\n{d}%}' },
           xAxis: [{ show: false }],
           yAxis: [{ show: false, name: '风量', position: 'left' }],
           series: [{ readFrom: 'piechart', xprop: 'label', yprop: 'valMock', label: '' }],
@@ -6478,7 +6478,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'sys_surface_caimei',
     moduleName: '工作面智能监控',
-    pageType: 'vent',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: true,
@@ -6561,7 +6561,7 @@ export const testConfigVent182: Config[] = [
   {
     deviceType: 'warn',
     moduleName: '预警监测',
-    pageType: 'vent_realtime',
+    pageType: 'vent_182',
     moduleData: {
       header: {
         show: true,

+ 87 - 26
src/views/vent/home/configurable/vent182.vue

@@ -2,7 +2,7 @@
 <template>
   <div class="company-home">
     <div style="width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 0">
-      <VentModal />
+      <SubApp />
     </div>
     <!-- 如果是有 deviceType、type 等 query,认为是详情页,不需要展示普通模块,只需要模型 -->
     <template v-if="!route.query.deviceType">
@@ -71,12 +71,14 @@
         class="switch-button report-mode right-525px"
         @click="switchDataMode"
       ></div>
-      <div class="switch-button icon-goto right-475px" @click="goMicroApp()"></div>
+      <div class="switch-button icon-goto2D right-455px" @click="goMicroApp('2.5')"></div>
+      <div class="switch-button icon-goto3D right-515px" @click="goMicroApp('3')"></div>
     </template>
   </div>
 </template>
 <script lang="ts" setup>
   // 用来代替该菜单项	vent/monitorManager/deviceMonitor/index	/micro-vent-3dModal/dashboard/analysis
+  // 同时需要更改模型配置,旧配置为 dashboard/Analysis	/dashboard/analysis
   import { onMounted, onUnmounted, ref, watch } from 'vue';
   // import { CaretDownOutlined } from '@ant-design/icons-vue';
   import MonitorBar from './components/MonitorBar.vue';
@@ -85,7 +87,7 @@
   import ModuleOriginal from './components/ModuleOriginal.vue';
   import ModuleCommon from './components/ModuleCommon.vue';
   // import { useRoute } from 'vue-router';
-  import VentModal from '/@/components/vent/micro/ventModal.vue';
+  import SubApp from '/@/components/vent/micro/createSubApp.vue';
   import { list } from './configurable.api';
   import { useRoute, useRouter } from 'vue-router';
   import { useGlobSetting } from '/@/hooks/setting';
@@ -122,15 +124,28 @@
     }, 60000);
   }
 
-  function goMicroApp() {
-    router.push({
-      path: route.path,
-      query: {
-        ...route.query,
-        type: 'model3D',
-        deviceType: 'model3D',
-      },
-    });
+  function goMicroApp(flag: string) {
+    if (flag === '3') {
+      router.push({
+        path: route.path.includes('micro-vent-3dModal') ? route.path : route.path.replace('micro-vent-2dModal', 'micro-vent-3dModal'),
+        query: {
+          ...route.query,
+          type: 'model3D',
+          deviceType: 'model3D',
+          isNoReverse: '1',
+        },
+      });
+    } else {
+      router.push({
+        path: route.path.includes('micro-vent-2dModal') ? route.path : route.path.replace('micro-vent-3dModal', 'micro-vent-2dModal'),
+        query: {
+          ...route.query,
+          type: 'model3D',
+          deviceType: 'model3D',
+          isNoReverse: '1',
+        },
+      });
+    }
   }
 
   watch(
@@ -174,7 +189,8 @@
     --image-modal-top: url('/@/assets/images/vent/home/modal-top.png');
     --image-monitor-realtime: url('/@/assets/images/company/monitor-realtime.png');
     --image-monitor-doc: url('/@/assets/images/company/monitor-doc.png');
-    --image-monitor-goto: url('/@/assets/images/company/monitor-goto.png');
+    --image-monitor-goto2: url('/@/assets/images/vent/home/2.5D.png');
+    --image-monitor-goto3: url('/@/assets/images/vent/home/3D.png');
 
     width: 100%;
     height: 100%;
@@ -223,7 +239,7 @@
       color: @vent-font-color;
       position: absolute;
       top: 60px;
-      right: 480px;
+      right: 430px;
     }
     .module-dropdown-original {
       padding: 10px;
@@ -242,31 +258,76 @@
     }
 
     .switch-button {
-      width: 34px;
-      height: 34px;
+      width: 50px;
+      height: 50px;
+      border-radius: 15%;
+      padding: 8px;
+      backdrop-filter: blur(10px);
+      background-color: rgba(30, 58, 117, 0.418);
       position: absolute;
       // right: 5px;
       bottom: 300px;
       z-index: 5;
-      background-repeat: no-repeat;
-      background-size: 100% 100%;
     }
     .report-mode {
-      background-image: var(--image-monitor-doc);
+      &::after {
+        width: 34px;
+        height: 34px;
+        content: '';
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        background-image: var(--image-monitor-doc);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
     }
     .realtime-mode {
-      background-image: var(--image-monitor-realtime);
+      &::after {
+        width: 34px;
+        height: 34px;
+        content: '';
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        background-image: var(--image-monitor-realtime);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
     }
-    .icon-goto {
-      background-image: var(--image-monitor-goto);
+    .icon-goto2D {
+      &::after {
+        width: 34px;
+        height: 34px;
+        content: '';
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        background-image: var(--image-monitor-goto2);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+    .icon-goto3D {
+      &::after {
+        width: 34px;
+        height: 34px;
+        content: '';
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        background-image: var(--image-monitor-goto3);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+      }
     }
 
     .module-monitor-bar {
       position: absolute;
-      top: 100px;
-      width: 1000px;
-      height: 200px;
-      left: calc(50% - 500px);
+      top: 90px;
+      width: 1080px;
+      height: 74px;
+      left: calc(50% - 540px);
     }
   }
   :deep(.loading-box) {

+ 9 - 7
src/views/vent/home/configurable/ventV5.vue

@@ -2,7 +2,8 @@
 <template>
   <div class="company-home">
     <div style="width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 0">
-      <VentModal />
+      <SubApp />
+      <!-- <VentModel /> -->
     </div>
     <!-- 如果是有 deviceType、type 等 query,认为是详情页,不需要展示普通模块,只需要模型 -->
     <template v-if="!route.query.deviceType">
@@ -76,7 +77,7 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { onMounted, onUnmounted, ref, watch } from 'vue';
+  import { onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue';
   // import { CaretDownOutlined } from '@ant-design/icons-vue';
   import MonitorBar from './components/MonitorBar.vue';
   import { useInitConfigs, useInitPage } from './hooks/useInit';
@@ -84,12 +85,13 @@
   import ModuleOriginal from './components/ModuleOriginal.vue';
   import ModuleCommon from './components/ModuleCommon.vue';
   // import { useRoute } from 'vue-router';
-  import VentModal from '/@/components/vent/micro/ventModal.vue';
+  import SubApp from '/@/components/vent/micro/createSubApp.vue';
+  // import VentModel from '/@/components/vent/micro/ventModal.vue';
   import { list } from './configurable.api';
   import { useRoute, useRouter } from 'vue-router';
   import { useGlobSetting } from '/@/hooks/setting';
   // import { testConfigVent, testConfigVentRealtime } from './configurable.data';
-
+  const sunAppType = ref('');
   const { sysDataType = 'monitor', title = '智能通风管控系统' } = useGlobSetting();
   const { configs, isOriginal, isCommon, fetchConfigs } = useInitConfigs();
   const { mainTitle, enhancedConfigs, hiddenList, data, updateData, updateEnhancedConfigs } = useInitPage(title);
@@ -261,9 +263,9 @@
     .module-monitor-bar {
       position: absolute;
       top: 100px;
-      width: 1000px;
-      height: 200px;
-      left: calc(50% - 500px);
+      width: 970px;
+      // height: 200px;
+      left: calc(50% - 485px);
     }
   }
   :deep(.loading-box) {

+ 68 - 68
src/views/vent/monitorManager/alarmMonitor/warn/waterLevel.vue

@@ -21,89 +21,89 @@
         class="tableW"
         @change="pageChange"
         :scroll="{ y: 700 }"
-      ></a-table>
+      />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
-import CustomHeader from '/@/components/vent/customHeader.vue';
-import { getHydrology } from '../common.api';
-import { usePermission } from '/@/hooks/web/usePermission';
-import { useRouter } from 'vue-router';
-import { columns } from '../common.data';
-const { hasPermission } = usePermission();
-const router = useRouter();
-const tableData = ref([]);
+  import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
+  import CustomHeader from '/@/components/vent/customHeader.vue';
+  import { getHydrology } from '../common.api';
+  import { usePermission } from '/@/hooks/web/usePermission';
+  import { useRouter } from 'vue-router';
+  import { columns } from '../common.data';
+  const { hasPermission } = usePermission();
+  const router = useRouter();
+  const tableData = ref([]);
 
-//分页切换
-const pageChange = (val) => {
-  console.log(val, 'val');
-  pagination.current = val.current;
-  pagination.pageSize = val.pageSize;
-  getListData();
-};
-const pagination = reactive({
-  current: 1, // 当前页码
-  pageSize: 20, // 每页显示条数
-  total: 0, // 总条目数,后端返回
-  showTotal: (total) => `共 ${total} 条`, // 分页右下角显示信息
-});
-//返回首页
-function getBack() {
-  router.push('/monitorChannel/monitor-alarm-home');
-}
-async function getListData() {
-  var params = {
-    pageIndex: pagination.current,
-    pageSize: pagination.pageSize,
+  //分页切换
+  const pageChange = (val) => {
+    console.log(val, 'val');
+    pagination.current = val.current;
+    pagination.pageSize = val.pageSize;
+    getListData();
   };
-  const res = await getHydrology({ ...params });
-  tableData.value = res.data;
-  pagination.total = res.count;
-}
-onMounted(() => {
-  getListData();
-});
-onUnmounted(() => {});
+  const pagination = reactive({
+    current: 1, // 当前页码
+    pageSize: 20, // 每页显示条数
+    total: 0, // 总条目数,后端返回
+    showTotal: (total) => `共 ${total} 条`, // 分页右下角显示信息
+  });
+  //返回首页
+  function getBack() {
+    router.push('/monitorChannel/monitor-alarm-home');
+  }
+  async function getListData() {
+    var params = {
+      pageIndex: pagination.current,
+      pageSize: pagination.pageSize,
+    };
+    const res = await getHydrology({ ...params });
+    tableData.value = res.data;
+    pagination.total = res.count;
+  }
+  onMounted(() => {
+    getListData();
+  });
+  onUnmounted(() => {});
 </script>
 
 <style lang="less" scoped>
-@import '/@/design/theme.less';
+  @import '/@/design/theme.less';
 
-@{theme-deepblue} {
-  .gasWarn {
-    --image-no-choice: url('/@/assets/images/themify/deepblue/fire/no-choice.png');
-    --image-choice: url('/@/assets/images/themify/deepblue/fire/choice.png');
-    --image-border: url('/@/assets/images/themify/deepblue/fire/border.png');
-    --image-bj1: url('/@/assets/images/themify/deepblue/fire/bj1.png');
-    --image-top-area: url('/@/assets/images/themify/deepblue/fire/top-area.png');
+  @{theme-deepblue} {
+    .gasWarn {
+      --image-no-choice: url('/@/assets/images/themify/deepblue/fire/no-choice.png');
+      --image-choice: url('/@/assets/images/themify/deepblue/fire/choice.png');
+      --image-border: url('/@/assets/images/themify/deepblue/fire/border.png');
+      --image-bj1: url('/@/assets/images/themify/deepblue/fire/bj1.png');
+      --image-top-area: url('/@/assets/images/themify/deepblue/fire/top-area.png');
+    }
   }
-}
 
-.gasWarn {
-  --image-no-choice: url('/@/assets/images/fire/no-choice.png');
-  --image-choice: url('/@/assets/images/fire/choice.png');
-  --image-border: url('/@/assets/images/fire/border.png');
-  --image-bj1: url('/@/assets/images/fire/bj1.png');
-  --image-top-area: url('/@/assets/images/fire/top-area.png');
-  width: 100%;
-  height: 100%;
-  padding: 80px 10px 15px 10px;
-  box-sizing: border-box;
-  display: flex;
-  justify-content: space-between;
-
-  .gas-content {
-    position: relative;
+  .gasWarn {
+    --image-no-choice: url('/@/assets/images/fire/no-choice.png');
+    --image-choice: url('/@/assets/images/fire/choice.png');
+    --image-border: url('/@/assets/images/fire/border.png');
+    --image-bj1: url('/@/assets/images/fire/bj1.png');
+    --image-top-area: url('/@/assets/images/fire/top-area.png');
     width: 100%;
     height: 100%;
-    margin-left: 10px;
-    padding: 15px;
-    background: var(--image-border) no-repeat;
-    background-size: 100% 100%;
+    padding: 80px 10px 15px 10px;
     box-sizing: border-box;
+    display: flex;
+    justify-content: space-between;
+
+    .gas-content {
+      position: relative;
+      width: 100%;
+      height: 100%;
+      margin-left: 10px;
+      padding: 15px;
+      background: var(--image-border) no-repeat;
+      background-size: 100% 100%;
+      box-sizing: border-box;
+    }
   }
-}
 </style>

+ 2 - 2
src/views/vent/monitorManager/balancePressMonitor/index1.vue

@@ -171,8 +171,8 @@
   });
 
   onUnmounted(() => {});
-  onBeforeUnmount(() => {
-    unmountMicroApps(['/micro-vent-2dModal']);
+  onBeforeUnmount(async () => {
+    await unmountMicroApps(['micro-vent-2dModal']);
   });
 </script>
 <style lang="less" scoped>

+ 121 - 31
src/views/vent/monitorManager/deviceMonitor/components/device/index.vue

@@ -56,7 +56,7 @@
       </div>
     </div>
     <div
-      class="tabs-box bottom-tabs-box"
+      class="bottom-tabs-box"
       :class="{ 'table-hide': !tableShow, 'table-show': tableShow }"
       style="height: 290px"
       @mousedown="setDivHeight($event, 230, scroll, 0)"
@@ -64,7 +64,9 @@
     >
       <div :style="`padding: 5px; height: ${scroll.y + 100}px`">
         <div class="to-small">
-          <div class="to-home" @click="toHome"></div>
+          <div class="to-home" @click="toHome()"></div>
+          <div class="to-2D" @click="toHome('2D')"></div>
+          <div class="to-3D" @click="toHome('3D')"></div>
           <FullscreenOutlined v-if="!tableShow" class="table-show-icon" @click="toHide" />
         </div>
         <div class="device-button-group" v-if="deviceList.length > 0">
@@ -164,7 +166,7 @@
             <Input style="width: 200px" v-model:value="locationForm.stationname" />
           </div>
         </div>
-        <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange" id="tabsBox">
+        <a-tabs v-model:activeKey="activeKey" @change="tabChange" id="tabsBox">
           <a-tab-pane
             key="monitor"
             :tab="
@@ -689,6 +691,8 @@
   import { useGo } from '/@/hooks/web/usePage';
   import { useGlobSetting } from '/@/hooks/setting';
   import { usePermission } from '/@/hooks/web/usePermission';
+  import { PageEnum } from '/@/enums/pageEnum';
+  import { useUserStore } from '/@/store/modules/user';
 
   type DeviceType = { deviceType: string; deviceName: string; datalist: any[] };
   const glob = useGlobSetting();
@@ -773,6 +777,7 @@
     insType: '2',
     class: 'night',
   });
+
   //树形菜单选择事件
   const onSelect: TreeProps['onSelect'] = (keys, e) => {
     deviceType.value = '';
@@ -784,11 +789,11 @@
       if (deviceType.value != e.node.parent.node.type) deviceType.value = e.node.parent.node.type;
       systemID.value = e.node.type;
       // 传递工作面id信息,用于定位
-      go(`/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=${deviceType.value}&deviceid=${systemID.value}`);
+      go(`${route.path}?type=tunMonitor&deviceType=${deviceType.value}&deviceid=${systemID.value}`);
     } else {
       systemType.value = e.node.type;
       if (deviceType.value != e.node.type) deviceType.value = e.node.type;
-      go(`/micro-vent-3dModal/dashboard/analysis?type=tunMonitor&deviceType=${deviceType.value}&deviceid=`);
+      go(`${route.path}?type=tunMonitor&deviceType=${deviceType.value}&deviceid=`);
     }
     clearTimeout(timer);
     timer = undefined;
@@ -1115,24 +1120,23 @@
   }
 
   function goLocation(record) {
-    // debugger;
     if (record['deviceType'] == 'person_bd' || record['deviceType'] == 'car_bd') {
-      actions.setGlobalState({ locationId: record.devNum, locationObj: null, pageObj: null, type: record['deviceType'].split('_')[0] });
+      actions.setGlobalState({ locationId: record.devNum, locationObj: record, pageObj: null, type: record['deviceType'].split('_')[0] });
     } else if (deviceType.value == 'bundleSpyDayReport' || deviceType.value == 'dustDayReport' || deviceType.value == 'bundleDayReport') {
-      actions.setGlobalState({ locationName: record.jcdd, locationObj: null, pageObj: null, type: record['deviceType'] });
+      actions.setGlobalState({ locationName: record.jcdd, locationObj: record, pageObj: null, type: record['deviceType'] });
     } else if (deviceType.value.startsWith('gasDay')) {
-      actions.setGlobalState({ locationName: record.strInstallPos, locationObj: null, pageObj: null, type: record['deviceType'] });
+      actions.setGlobalState({ locationName: record.strInstallPos, locationObj: record, pageObj: null, type: record['deviceType'] });
     } else if (deviceType.value == 'gasDayReport') {
-      actions.setGlobalState({ locationName: record.jcdd, locationObj: null, pageObj: null, type: record['deviceType'] });
+      actions.setGlobalState({ locationName: record.jcdd, locationObj: record, pageObj: null, type: record['deviceType'] });
     } else if (deviceType.value.startsWith('substation')) {
-      actions.setGlobalState({ locationId: record.id, locationObj: null, pageObj: null, type: record['deviceType'] });
+      actions.setGlobalState({ locationId: record.id, locationObj: record, pageObj: null, type: record['deviceType'] });
     } else {
       if (deviceType.value.startsWith('location')) {
-        actions.setGlobalState({ locationId: record.deviceID, locationObj: null, pageObj: null, type: 'person' });
+        actions.setGlobalState({ locationId: record.deviceID, locationObj: record, pageObj: null, type: 'person' });
       } else if (deviceType.value.startsWith('vehicle')) {
-        actions.setGlobalState({ locationId: record.deviceID, locationObj: null, pageObj: null, type: 'car' });
+        actions.setGlobalState({ locationId: record.deviceID, locationObj: record, pageObj: null, type: 'car' });
       } else {
-        actions.setGlobalState({ locationId: record.deviceID, locationObj: null, pageObj: null });
+        actions.setGlobalState({ locationId: record.deviceID, locationObj: record, pageObj: null });
       }
     }
   }
@@ -1274,12 +1278,31 @@
     handleExportXls('瓦斯巡检记录', getExportUrl, { devicetype: deviceType.value });
   }
 
-  function toHome() {
-    deviceList.value = [];
-    if (timer) clearTimeout(timer);
-    timer = undefined;
-    deviceType.value = '';
-    go(glob.homePath);
+  function toHome(flag?: string) {
+    if (flag == '2D') {
+      router.push({
+        path: route.path.includes('micro-vent-2dModal') ? route.path : route.path.replace('micro-vent-3dModal', 'micro-vent-2dModal'),
+        query: {
+          ...route.query,
+          isNoReverse: '1',
+        },
+      });
+    } else if (flag == '3D') {
+      router.push({
+        path: route.path.includes('micro-vent-3dModal') ? route.path : route.path.replace('micro-vent-2dModal', 'micro-vent-3dModal'),
+        query: {
+          ...route.query,
+          isNoReverse: '1',
+        },
+      });
+    } else {
+      deviceList.value = [];
+      if (timer) clearTimeout(timer);
+      timer = undefined;
+      deviceType.value = '';
+      const { userInfo } = useUserStore();
+      go((userInfo && userInfo.homePath) || glob.homePath || PageEnum.BASE_HOME);
+    }
   }
 
   function toHide() {
@@ -1491,11 +1514,16 @@
     --image-tree-icon-cover-bg: url('/@/assets/images/vent/home/tree-icon-cover-bg.png');
     --image-tree-icon-cover-hover-bg: url('/@/assets/images/vent/home/tree-icon-cover-hover-bg.png');
     --image-tohome: url('/@/assets/images/vent/home/tohome.png');
+    --image-to2D: url('/@/assets/images/vent/home/2.5D.png');
+    --image-to3D: url('/@/assets/images/vent/home/3D.png');
     --tree-node-select: #00b1c8;
     --tree-node-hover: #00b1c855;
     --location-bottom-bg: #00709955;
     --location-bottom-border: #aef3ff4d;
   }
+  .scene-box {
+    pointer-events: none;
+  }
 
   .top-header {
     position: fixed;
@@ -1833,12 +1861,14 @@
 
   .bottom-tabs-box {
     position: relative;
-
-    .tabs-box {
-      width: calc(100% - 12px) !important;
-      bottom: 3px !important;
-      background-color: red;
-    }
+    background-color: var(--vent-configurable-module-border-bd);
+    backdrop-filter: blur(18px);
+    border: 1px solid #ffffff55;
+    border-radius: 8px;
+    // .tabs-box {
+    //   width: calc(100% - 12px) !important;
+    //   bottom: 3px !important;
+    // }
 
     .to-small {
       position: absolute;
@@ -1849,12 +1879,72 @@
       justify-content: center;
 
       .to-home {
-        width: 60px;
-        height: 60px;
-        background: var(--image-tohome) no-repeat center;
-        background-size: auto;
+        width: 50px;
+        height: 50px;
+        border-radius: 15%;
         padding: 8px;
-
+        backdrop-filter: blur(10px);
+        background-color: rgba(30, 58, 117, 0.418);
+        margin-right: 10px;
+        cursor: pointer;
+        &::after {
+          width: 34px;
+          height: 34px;
+          content: '';
+          position: absolute;
+          top: 8px;
+          left: 8px;
+          background: var(--image-tohome) no-repeat center;
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
+        &:hover {
+          background-color: rgba(79, 104, 134, 0.418);
+        }
+      }
+      .to-2D {
+        width: 50px;
+        height: 50px;
+        border-radius: 15%;
+        padding: 8px;
+        backdrop-filter: blur(10px);
+        background-color: rgba(30, 58, 117, 0.418);
+        margin-right: 10px;
+        cursor: pointer;
+        &::after {
+          width: 34px;
+          height: 34px;
+          content: '';
+          position: absolute;
+          top: 8px;
+          left: 8px;
+          background: var(--image-to2D) no-repeat center;
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
+        &:hover {
+          background-color: rgba(79, 104, 134, 0.418);
+        }
+      }
+      .to-3D {
+        width: 50px;
+        height: 50px;
+        border-radius: 15%;
+        padding: 8px;
+        backdrop-filter: blur(10px);
+        background-color: rgba(30, 58, 117, 0.418);
+        cursor: pointer;
+        &::after {
+          width: 34px;
+          height: 34px;
+          content: '';
+          position: absolute;
+          top: 8px;
+          left: 8px;
+          background: var(--image-to3D) no-repeat center;
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
         &:hover {
           background-color: rgba(79, 104, 134, 0.418);
         }

+ 6 - 6
src/views/vent/monitorManager/deviceMonitor/index.vue

@@ -5,8 +5,9 @@
       <DeviceVue ref="DeviceRef" v-else :pageData="pageData" /> -->
       <DeviceVue ref="DeviceRef" :pageData="pageData" />
     </template>
-    <Network ref="NetworkRef" v-if="routerParam === 'timesolution'" :pageResult="pageResult" @changePageType="changePageType" />
-    <VentModal style="width: 100%; height: 100%; position: absolute" />
+    <Network ref="NetworkRef" v-if="routerParam === 'timesolution'" :pageResult="pageResult" @change-page-type="changePageType" />
+    <!-- <VentModal style="width: 100%; height: 100%; position: absolute" /> -->
+    <SubApp style="width: 100%; height: 100%; position: absolute" />
   </div>
 </template>
 
@@ -18,7 +19,8 @@
   import { unmountMicroApps } from '/@/qiankun';
   import { getActions } from '/@/qiankun/state';
   import { useRoute } from 'vue-router';
-  import VentModal from '/@/components/vent/micro/ventModal.vue';
+  // import VentModal from '/@/components/vent/micro/ventModal.vue';
+  import SubApp from '/@/components/vent/micro/createSubApp.vue';
 
   const route = useRoute();
   const actions = getActions();
@@ -91,9 +93,7 @@
       }
     });
   });
-  onBeforeUnmount(() => {
-    unmountMicroApps(['/micro-vent-3dModal']);
-  });
+  onBeforeUnmount(() => {});
   onUnmounted(() => {
     isShow.value = false;
   });

+ 10 - 6
src/views/vent/monitorManager/fanLocalMonitor/components/entryThree.vue

@@ -4,7 +4,7 @@
     <div id="fanLocal3D" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> </div>
     <div id="fanLocal3DCSS" class="threejs-Object-CSS" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none">
       <div style="z-index: -1; position: relative" v-if="hasPermission('show:sensorMonitor')">
-        <div class="elementTag" id="inputBox0">
+        <div class="elementTag" id="inputBox0" v-if="!hasPermission('show:sensorMonitorBox1')">
           <div class="elementContent" v-if="selectData.windInputSpeed1 || selectData.windInputSpeed_merge">
             <p v-if="selectData.windInputSpeed1 || selectData.windInputSpeed_merge">
               局扇工作面风速:
@@ -24,7 +24,8 @@
             </p>
           </div>
         </div>
-        <div class="elementTag" id="inputBox1">
+        <!-- inputBox0 与 inputBox1 互斥 -->
+        <div class="elementTag" id="inputBox1" v-if="hasPermission('show:sensorMonitorBox1')">
           <div class="elementContent" v-if="selectData.windSpeed1 || selectData.gas3 || selectData.windQuantity1 || selectData.inletAirVolume_merge">
             <p v-if="selectData.windSpeed1">
               风筒入口风速:
@@ -57,10 +58,10 @@
                 selectData.windQuantity2
                   ? selectData.windQuantity2
                   : selectData.m3
-                    ? selectData.m3
-                    : selectData.ductOutletAirVolume_merge
-                      ? selectData.ductOutletAirVolume_merge
-                      : '-'
+                  ? selectData.m3
+                  : selectData.ductOutletAirVolume_merge
+                  ? selectData.ductOutletAirVolume_merge
+                  : '-'
               }}</span>
               <span class="unit"> m³/min</span>
             </p>
@@ -231,3 +232,6 @@
   defineEmits(['goDetailDevice']);
   const { hasPermission } = usePermission();
 </script>
+<style scoped lang="less">
+  @import '/@/design/vent/modal.less';
+</style>

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

@@ -193,8 +193,8 @@
                                 selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                                   ? '无状态'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
-                                    ? '正常'
-                                    : '异常'
+                                  ? '正常'
+                                  : '异常'
                               }}</div>
                             </div>
                             <div class="signal-item" v-if="warningMonitorRowIndex == 1">
@@ -212,8 +212,8 @@
                                 selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                                   ? '无状态'
                                   : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
-                                    ? '正常'
-                                    : '异常'
+                                  ? '正常'
+                                  : '异常'
                               }}</div>
                             </div>
                           </div>
@@ -1152,6 +1152,7 @@
         }
       }
       setModelType(mainModelType.value, modalType.value, fanDualArray.value);
+      addCssText();
     });
     await getCamera(id, playerRef, renderPlayer);
     return;

+ 0 - 2
src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.fire.redGate.ts

@@ -1,5 +1,4 @@
 import * as THREE from 'three';
-import { useAppStore } from '/@/store/modules/app';
 
 // import * as dat from 'dat.gui';
 // const gui = new dat.GUI();
@@ -20,7 +19,6 @@ class FireDoor {
 
   fmClock = new THREE.Clock();
   mixers: THREE.AnimationMixer | undefined;
-  appStore = useAppStore();
   damperOpenMesh;
   damperClosedMesh;
 

+ 0 - 1
src/views/vent/monitorManager/fireDoorMonitor/fireDoor.threejs.ssl.ts

@@ -132,7 +132,6 @@ class FireDoorSsl {
    * @param {number} [timeScale=0.01] - 动画时间缩放因子,控制动画播放速度
    */
   play(handlerState, timeScale = 0.01) {
-    console.log('debug rrr', handlerState);
     let handler = () => {};
     switch (handlerState) {
       case 1: // 打开门

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

@@ -58,9 +58,8 @@
   onMounted(async () => {
     await getSysDataSource();
   });
-  onBeforeUnmount(() => {
-    unmountMicroApps(['/micro-vent-3dModal']);
-  });
+  // onBeforeUnmount(async () => {
+  // });
 </script>
 <style lang="less" scoped>
   @import '/@/design/theme.less';

+ 3 - 0
src/views/vent/monitorManager/mainFanMonitor/components/entryThree.vue

@@ -247,3 +247,6 @@
   }>();
   const { hasPermission } = usePermission();
 </script>
+<style scoped lang="less">
+  @import '/@/design/vent/modal.less';
+</style>

+ 131 - 0
src/views/vent/monitorManager/mainFanMonitor/components/setValueSetting.vue

@@ -0,0 +1,131 @@
+<template>
+  <BasicModal
+    @register="register"
+    title="报警点位限值设置"
+    width="1400px"
+    v-bind="$attrs"
+    @ok="onSubmit"
+    @cancel="onSubmit"
+    :defaultFullscreen="false"
+  >
+    <div class="setting-box">
+      <div class="set-box" v-if="pointListFan1.length">
+        <div class="set-box-item" v-for="(point, key) in pointListFan1" :key="key">
+          <div class="button-box" @click="showModal(point)">{{ point['valuename'] }}</div>
+        </div>
+      </div>
+      <div class="set-box" v-if="pointListFan2.length">
+        <div class="set-box-item" v-for="(point, key) in pointListFan2" :key="key">
+          <div class="button-box" @click="showModal(point)">{{ point['valuename'] }}</div>
+        </div>
+      </div>
+      <div class="set-box" v-if="pointListFan3.length">
+        <div class="set-box-item" v-for="(point, key) in pointListFan3" :key="key">
+          <div class="button-box" @click="showModal(point)">{{ point['valuename'] }}</div>
+        </div>
+      </div>
+    </div>
+  </BasicModal>
+</template>
+<script lang="ts" setup>
+  import { onMounted, ref, onUnmounted } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { list } from '/@/views/vent/deviceManager/comment/pointTabel/point.api';
+
+  const emit = defineEmits(['close', 'register', 'showSetModal']);
+  const props = defineProps({
+    deviceType: {
+      type: String,
+      default: '',
+    },
+  });
+  const pointListFan1 = ref<{ valuename: string; valuecode: string }[]>([]);
+  const pointListFan2 = ref<{ valuename: string; valuecode: string }[]>([]);
+  const pointListFan3 = ref<{ valuename: string; valuecode: string }[]>([]);
+
+  // 注册 modal
+  const [register, { closeModal }] = useModalInner();
+
+  async function onSubmit() {
+    emit('close');
+    closeModal();
+  }
+
+  function showModal(item) {
+    emit('showSetModal', item);
+  }
+
+  onMounted(async () => {
+    const result = await list({ devicetype: props.deviceType, size: 500, valuecode: '*setVal' }); //setVal
+    const dataList = result.records.sort((a, b) => a.valuename.localeCompare(b.valuename));
+    pointListFan1.value = dataList.filter((item) => item.valuecode.startsWith('fan1') || item.valuecode.startsWith('Fan1'));
+    pointListFan2.value = dataList.filter((item) => item.valuecode.startsWith('fan2') || item.valuecode.startsWith('Fan2'));
+    pointListFan3.value = dataList.filter((item) => item.valuecode.startsWith('fan3') || item.valuecode.startsWith('Fan3'));
+  });
+  onUnmounted(() => {});
+</script>
+<style scoped lang="less">
+  @import '/@/design/theme.less';
+  @import '/@/design/vent/modal.less';
+  .setting-box {
+    flex: 1;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+  }
+  .set-box {
+    flex: 1;
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    margin: 0 10px;
+    justify-content: center;
+    &:not(:last-child) {
+      margin-right: 10px;
+      border-right: 1px solid var(--vent-base-border);
+    }
+  }
+  .set-box-item {
+    width: 200px;
+    height: auto;
+    display: flex;
+    flex-direction: row;
+    // flex-wrap: wrap;
+    justify-content: flex-start;
+    margin-bottom: 10px;
+  }
+  .button-box {
+    position: relative;
+    padding: 5px;
+    border: 1px transparent solid;
+    border-radius: 5px;
+    width: auto;
+    height: 34px;
+    border: 1px solid var(--vent-base-border);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--vent-font-color);
+    padding: 0 10px;
+    cursor: pointer;
+
+    &:hover {
+      background: linear-gradient(#3eb2ff55, #00c3ff55);
+    }
+
+    &::before {
+      width: calc(100% - 6px);
+      height: 26px;
+      content: '';
+      position: absolute;
+      top: 3px;
+      right: 0;
+      left: 3px;
+      bottom: 0;
+      z-index: -1;
+      border-radius: inherit;
+      /*important*/
+      background: linear-gradient(#014978, #2ec2ca);
+    }
+  }
+</style>

+ 81 - 49
src/views/vent/monitorManager/mainFanMonitor/index.vue

@@ -76,22 +76,22 @@
                     selectData['Fan1StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan1')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan1')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan1')]
+                        : '-'
                   }}</div>
                   <div class="item-value" v-if="dataMonitorRowIndex == 1">{{
                     selectData['Fan2StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan2')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan2')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan2')]
+                        : '-'
                   }}</div>
                   <div class="item-value" v-if="dataMonitorRowIndex == 2">{{
                     selectData['Fan3StartStatus'] == '0' && globalConfig?.simulatedPassword
                       ? '-'
                       : selectData[data.dataIndex.replace('Fan', 'Fan3')]
-                      ? selectData[data.dataIndex.replace('Fan', 'Fan3')]
-                      : '-'
+                        ? selectData[data.dataIndex.replace('Fan', 'Fan3')]
+                        : '-'
                   }}</div>
                 </div>
                 <div v-else>
@@ -150,8 +150,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan1')] === undefined
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan1')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                       </template>
                     </div>
@@ -175,8 +175,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan2')] == undefined
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan2')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                       </template>
                     </div>
@@ -200,8 +200,8 @@
                           selectData[state.dataIndex.replace('Fan', 'Fan3')] == undefined
                             ? '无状态'
                             : selectData[state.dataIndex.replace('Fan', 'Fan3')] == '0'
-                            ? '正常'
-                            : '异常'
+                              ? '正常'
+                              : '异常'
                         }}</div>
                       </template>
                     </div>
@@ -423,9 +423,7 @@
       </dv-border-box8>
     </div>
   </div>
-  <div style="z-index: -1; position: absolute; top: -100px; right: 10px; width: 300px; height: 280px; margin: auto" class="palyer">
-    <LivePlayer id="main-player1" ref="player1" :videoUrl="flvURL1()" muted loop loading controls />
-  </div>
+
   <div
     v-if="renderPlayer"
     ref="playerRef"
@@ -579,6 +577,10 @@
           > -->
         </div>
       </div>
+      <div class="vent-flex-row input-box" v-if="modalType == 'setVal' && setValPoint">
+        <div class="label">{{ setValPoint['valuename'] }}:</div>
+        <a-input-number v-model:value="setValPoint['value']" style="width: 150px" />
+      </div>
     </div>
   </a-modal>
   <a-modal
@@ -617,16 +619,21 @@
   </div>
 
   <!-- 摄像头显示隐藏图标 -->
-  <VideoCameraOutlined class="video-icon" :class="{ 'no-play': !showPlay }" @click="changePlay" />
-  <!-- 主风机故障诊断显示隐藏图标 -->
-  <div v-if="hasPermission('mainFan:isOpenAlarm') && sysOrgCode !== 'wljttlbl'" class="warn-icon" @click="showModalWarn"></div>
+  <div class="icon-box">
+    <VideoCameraOutlined class="video-icon" :class="{ 'no-play': !showPlay }" @click="changePlay" />
+    <FormOutlined @click="openModalDeviceSet" />
+    <!-- 主风机故障诊断显示隐藏图标 -->
+    <div v-if="hasPermission('mainFan:isOpenAlarm') && sysOrgCode !== 'wljttlbl'" class="warn-icon" @click="showModalWarn"></div>
+  </div>
+
   <!-- 工况辅助决策 -->
   <ConditionAssistance @register="registerModal" :deviceType="deviceType" :selectData="currentData" />
   <DeviceBaseInfo @register="registerModalDeviceEdit" :device-type="selectData['deviceType']" />
+  <setValueSetting @register="registerModalDeviceSet" :deviceType="deviceType" @show-set-modal="showSetModal" />
 </template>
 
 <script setup lang="ts">
-  import { ExclamationCircleFilled, VideoCameraOutlined } from '@ant-design/icons-vue';
+  import { ExclamationCircleFilled, VideoCameraOutlined, FormOutlined } from '@ant-design/icons-vue';
   import FanDeviceEcharts from '../comment/FanDeviceEcharts.vue';
   import BarAndLine from '../../../../components/chart/BarAndLine.vue';
   import FanEchrats from '/@/views/vent/monitorManager/mainFanMonitor/fanEchrats.vue';
@@ -653,7 +660,6 @@
   import { modalTypeArr, fbmControlData, faultDeviceHeader, PointMonitorType, getModelComponent } from './main.data';
   import { deviceControlApi } from '/@/api/vent/index';
   import { mountedThree, destroy, addText, setModelType, playAnimate, resetEcharts } from './main.threejs';
-  import LivePlayer from '@liveqing/liveplayer-v3';
   import { SvgIcon } from '/@/components/Icon';
   import { list, pointMonitor, getFaultDiagnosis } from './main.api';
   import { list as baseList } from '../../deviceManager/fanTabel/fan.api';
@@ -664,6 +670,7 @@
   import { BorderBox8 as DvBorderBox8 } from '@kjgl77/datav-vue3';
   import { useRouter } from 'vue-router';
   import ConditionAssistance from './components/conditionAssistance.vue';
+  import setValueSetting from './components/setValueSetting.vue';
   import AnalysisTable from './components/AnalysisTable.vue';
   import { useModal } from '/@/components/Modal';
   import type { BasicColumn } from '/@/components/Table/src/types/table';
@@ -673,8 +680,6 @@
   import { usePermission } from '/@/hooks/web/usePermission';
   import { cloneDeep } from 'lodash-es';
   import { getDictItems } from '/@/api/common/api';
-  import { warn } from 'node:console';
-  import { merge } from 'lodash';
 
   const globalConfig = inject('globalConfig');
   const HistoryTable =
@@ -686,6 +691,7 @@
 
   const [registerModal, { openModal, closeModal }] = useModal();
   const [registerModalDeviceEdit, { openModal: openModalDeviceEdit }] = useModal();
+  const [registerModalDeviceSet, { openModal: openModalDeviceSet }] = useModal();
   const { currentRoute } = useRouter();
   const { createMessage, createConfirm } = useMessage();
   const { sysOrgCode } = useGlobSetting();
@@ -705,7 +711,6 @@
   const MonitorDataTable = ref();
   const showPlay = ref(hasPermission('mainFan:showCamera') ? true : false);
   const player1 = ref(null);
-  const player2 = ref(null);
   const activeKey = ref('1');
   const loading = ref(false);
   const renderPlayer = ref(true);
@@ -728,6 +733,7 @@
   const warningMonitorRowIndex = ref(0);
   const faultData = ref<PointMonitorType[]>([]); // 存放故障点位监测数据(查的点表)
   const explosionDoorData = ref<PointMonitorType[]>([]); // 存放防爆门点位监测数据(查的点表)
+  const setValPoint = ref(null);
   const faultShow = ref(false);
   const IdList = ref<any[]>([]); // 存放风机设备id
   let headElHeight = ref(0);
@@ -807,6 +813,8 @@
   const echartsDataList = ref<any[]>([]);
   const requestLock = ref(hasPermission('mainFan:isOpenAlarm')); // 是否允许请求数据
   const warnList = ref<any[]>([]); // 存放故障诊断数据
+
+  /*** 获取图表列 */
   const remoteChartsColumns = getTableHeaderColumns('fanmain_chart');
   const chartsColumns = remoteChartsColumns && remoteChartsColumns.length > 0 ? remoteChartsColumns : [];
   let chartsColumnsFan1 = [],
@@ -820,7 +828,6 @@
 
       if (itemColumn['dataIndex']) {
         const dataIndex = itemColumn['dataIndex'] as string;
-
         dataIndexFan1 = dataIndex.startsWith('Fan') ? dataIndex.replace('Fan', 'Fan1') : dataIndex.replace('fan', 'fan1');
         dataIndexFan2 = dataIndex.startsWith('Fan') ? dataIndex.replace('Fan', 'Fan2') : dataIndex.replace('fan', 'fan2');
         chartsColumnsFan1.push({ ...itemColumn, dataIndex: dataIndexFan1, legend: '主机' + itemColumn['legend'] });
@@ -828,6 +835,11 @@
       }
     }
   }
+
+  /*** 获取设置列 */
+  let setValueColumnsFan1 = [],
+    setValueColumnsFan2 = [];
+
   const echatsOption = {
     legend: {
       top: 8,
@@ -874,15 +886,6 @@
     }
   });
 
-  const flvURL1 = () => {
-    // return `https://muiplayer.js.org/media/media.m3u8`;
-    return `/video/mainWind.mp4`;
-  };
-  const flvURL2 = () => {
-    return `/video/mainWind.mp4`;
-    // return `https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv`;
-  };
-
   const dataSource = ref([]);
 
   const propTypeArr = new Map([
@@ -902,6 +905,12 @@
     // MonitorDataTable.value.setSelectedRowKeys(selectData.deviceID);
   };
 
+  const showSetModal = (point) => {
+    modalIsShow.value = true;
+    modalType.value = 'setVal';
+    setValPoint.value = point;
+  };
+
   // 设备数据
   const controlType = ref(1);
 
@@ -1042,6 +1051,7 @@
       deviceBaseList.value = res.records;
     });
   };
+
   // 切换检测数据
   const getSelectRow = async (id) => {
     removeCamera(playerRef);
@@ -1077,12 +1087,12 @@
         selectData['modalTyoe'] === 'xiejing'
           ? 'mainXjWindRect'
           : 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;
       centerMonitorIsShow.value = false;
@@ -1140,6 +1150,7 @@
   const changePlay = () => {
     showPlay.value = !showPlay.value;
   };
+
   // 打开并设置modal的标题
   const showModal = (obj) => {
     // 神东要求主扇的控制功能改回跳转至生产管控平台
@@ -1574,6 +1585,15 @@
           .catch((err) => {
             createMessage.error(err.message);
           });
+      } else if (modalType.value == 'setVal' && setValPoint.value) {
+        start(setValPoint.value['valuecode'], setValPoint.value['value'])
+          .then(() => {
+            createMessage.success('指令下发成功');
+            setValPoint.value = null;
+          })
+          .catch((err) => {
+            createMessage.error(err.message);
+          });
       }
       modalIsShow.value = false;
     };
@@ -1592,13 +1612,17 @@
       }
     }
     modalIsShow.value = false;
+    setValPoint.value = null;
   };
+
   const handlefaultCancel = () => {
     modalWarnIsShow.value = false;
   };
+
   const handlefaultOk = () => {
     modalWarnIsShow.value = false;
   };
+
   function showModalWarn() {
     if (warnList.value.length > 0) {
       modalWarnIsShow.value = true;
@@ -1606,6 +1630,7 @@
       createMessage.info('当前无告警信息');
     }
   }
+
   function deviceEdit(e: Event, type: string, record) {
     e.stopPropagation();
     openModalDeviceEdit(true, {
@@ -2180,8 +2205,8 @@
     }
   }
 
-  .video-icon {
-    width: 30px;
+  .icon-box {
+    width: 140px;
     height: 38px;
     position: absolute;
     top: 600px;
@@ -2189,17 +2214,13 @@
     color: var(--vent-font-color);
     z-index: 1;
     font-size: 28px;
+    display: flex;
+    justify-content: space-between;
   }
-  .warn-icon {
+  .video-icon {
     width: 30px;
     height: 38px;
-    position: absolute;
-    top: 600px;
-    right: 460px;
-    background: url('/@/assets/images/vent/normal-mainfan.png') no-repeat;
-    background-size: contain;
-    z-index: 1;
-    font-size: 28px;
+    position: relative;
   }
   .no-play {
     &::after {
@@ -2219,6 +2240,17 @@
       );
     }
   }
+  .warn-icon {
+    width: 30px;
+    height: 38px;
+    // position: absolute;
+    // top: 600px;
+    // right: 460px;
+    background: url('/@/assets/images/vent/normal-mainfan.png') no-repeat;
+    background-size: contain;
+    z-index: 1;
+    font-size: 28px;
+  }
 
   .threejs-Object-CSS {
     .elementTag {

+ 51 - 23
src/views/vent/monitorManager/sprayMonitor/index.vue

@@ -1,9 +1,10 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
   <div class="spray-wrapper">
-    <MonitorComponent
+    <BasicMonitoring
       main-title="凝胶防灭火材料自动喷洒系统"
       :main-config="{
+        operations: operations,
         configs: configs,
       }"
       :monitor-history-config="{}"
@@ -11,35 +12,38 @@
       :alarm-history-config="{}"
       strtype="sys_surface_juejin"
     >
-      <!-- <template #default="{ monitorData }">
-        <div id="spray3D" class="w-full h-full">
+      <template #default="{ monitorData }">
+        <div id="spray3D" class="w-full h-full flex justify-center items-center overflow-hidden">
           <a-spin :spinning="loading" />
-          <div id="sprayCSS3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden">
+          <div id="sprayCSS3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden; pointer-events: none">
             <FourBorderBg id="sprayCSS3DEnvA">
               <div>送料电机</div>
-              <div>电压US:{{ monitorData.slus }}</div>
-              <div>电流LA:{{ monitorData.slla }}</div>
-              <div>电流LB:{{ monitorData.sllb }}</div>
-              <div>电流LC:{{ monitorData.sllc }}</div>
+              <div>电压US:{{ get(monitorData, 'spray_auto[0].readData.SlDjdy', '-') }}</div>
+              <div>电流LA:{{ get(monitorData, 'spray_auto[0].readData.SlDjAdl', '-') }}</div>
+              <div>电流LB:{{ get(monitorData, 'spray_auto[0].readData.SlDjBdl', '-') }}</div>
+              <div>电流LC:{{ get(monitorData, 'spray_auto[0].readData.SlDjCdl', '-') }}</div>
             </FourBorderBg>
             <FourBorderBg id="sprayCSS3DEnvB">
-              <div>输送电机</div>
-              <div>电压US:{{ monitorData.ssus }}</div>
-              <div>电流LA:{{ monitorData.ssla }}</div>
-              <div>电流LB:{{ monitorData.sslb }}</div>
-              <div>电流LC:{{ monitorData.sslc }}</div>
+              <div>注浆电机</div>
+              <div>电压US:{{ get(monitorData, 'spray_auto[0].readData.ZjDjdy', '-') }}</div>
+              <div>电流LA:{{ get(monitorData, 'spray_auto[0].readData.ZjDjAdl', '-') }}</div>
+              <div>电流LB:{{ get(monitorData, 'spray_auto[0].readData.ZjDjBdl', '-') }}</div>
+              <div>电流LC:{{ get(monitorData, 'spray_auto[0].readData.ZjDjCdl', '-') }}</div>
             </FourBorderBg>
           </div>
         </div>
-      </template> -->
-    </MonitorComponent>
+      </template>
+    </BasicMonitoring>
   </div>
 </template>
 <script setup lang="ts">
-  import { onMounted } from 'vue';
-  import MonitorComponent from './component.vue';
+  import { onMounted, ref } from 'vue';
+  import BasicMonitoring from '/@/components/vent/BasicMonitoring.vue';
   import { Config } from '../../deviceManager/configurationTable/types';
   import { useInitConfigs } from '../../home/configurable/hooks/useInit';
+  import { mountedThree, setModelType } from './spray.three';
+  import FourBorderBg from '/@/components/vent/fourBorderBg.vue';
+  import { get } from 'lodash-es';
 
   const defaultConfigs: Config[] = [
     {
@@ -273,15 +277,35 @@
   ];
 
   const { configs, fetchConfigs } = useInitConfigs();
+  const loading = ref(false);
+
+  const operations = ref([
+    {
+      label: '开启控制箱',
+      key: 'run_auto_sign',
+      value: '1',
+      disabled: false,
+      showPassword: true,
+      // permission: 'spray:run_auto_sign',
+    },
+    {
+      label: '关闭控制箱',
+      key: 'run_auto_sign',
+      value: '0',
+      disabled: false,
+      showPassword: true,
+      // permission: 'spray:run_auto_sign',
+    },
+  ]);
 
   onMounted(() => {
     fetchConfigs('spray');
-    // loading.value = true;
-    // mountedThree('#spray3D', ['#sprayCSS3D', '#sprayCSS3DEnvA', '#sprayCSS3DEnvB']).then(() => {
-    //   setModelType('spray').finally(() => {
-    //     loading.value = false;
-    //   });
-    // });
+    loading.value = true;
+    mountedThree('#spray3D', ['#sprayCSS3D', '#sprayCSS3DEnvA', '#sprayCSS3DEnvB']).then(() => {
+      setModelType('spray').finally(() => {
+        loading.value = false;
+      });
+    });
   });
 </script>
 
@@ -291,6 +315,10 @@
     height: 100%;
   }
 
+  #spray3D {
+    pointer-events: all;
+  }
+
   .spray-wrapper :deep(.list-item_L .list-item__icon_L) {
     background-image: url('/@/assets/images/home-container/configurable/minehome/list-icon-file.png');
   }

+ 0 - 0
src/views/vent/monitorManager/sprayMonitor/spray.api.ts


+ 2 - 11
src/views/vent/monitorManager/sprayMonitor/spray.three.ts

@@ -28,7 +28,7 @@ function dispatchMouseEvent(event) {
 function initEventListender() {
   if (!model) return;
   model.canvasContainer?.addEventListener('mousedown', (e) => dispatchMouseEvent(e));
-  // model.orbitControls?.addEventListener('change', () => render());
+  // model.orbitControls?.addEventListener('change', () => {});
 }
 
 /** 渲染并更新总模型 */
@@ -63,16 +63,7 @@ export function setModelType(modelType: 'spray') {
           if (!model.scene?.getObjectByName(group.name) && group) {
             model.scene?.add(group);
           }
-          const oldCameraPosition = { x: -693, y: 474, z: 398 };
-          const position = { x: 14.826074594663222, y: 16.901762713393836, z: 36.459944037951004 };
-          await animateCamera(
-            oldCameraPosition,
-            { x: 0, y: 0, z: 0 },
-            { x: position.x, y: position.y, z: position.z },
-            { x: 0, y: 0, z: 0 },
-            model,
-            0.8
-          );
+          await animateCamera({ x: -693, y: 474, z: 398 }, { x: 0, y: 0, z: 0 }, context.cameraPostion, context.orbitTarget, model, 0.8);
           resolve(null);
         }, 400);
       }

+ 17 - 5
src/views/vent/monitorManager/sprayMonitor/spray.threejs.base.ts

@@ -8,20 +8,32 @@ import { setModalCenter } from '/@/utils/threejs/util';
 
 class ModelContext {
   model;
-  modelName = 'tunFace';
+  modelName = 'spray-ts';
   // modelName = 'dedust';
   group: THREE.Object3D | null = null;
   /** 本模型内支持的锚点位置数组 */
   anchors: [number, number, number][] = [
-    [-46.1, -12.8, 8],
+    [-0.6, 0.3, -0.05],
     // [0, 0, 0],
     // 右侧传感器
-    [11.7, -14, 8],
+    [-0.3, 0.3, -0.05],
     // [-25.847, 8.783, 3.267],
     // [31.142, 8.783, 3.267],
   ];
   /** 本模型显示的css详情元素数组 */
   cssSprites: CSS3DSprite[] = [];
+  /** 初始化时用到的摄像头位置参数,可以供外部访问或初始化时使用 */
+  cameraPostion = {
+    x: 0.05342932338990988,
+    y: 0.08324360118266554,
+    z: 1.2186385420339856,
+  };
+  /** 初始化时用到的滑轨目标参数,可以供外部访问或初始化时使用,配合摄像头可以实现更精确的控制 */
+  orbitTarget = {
+    x: 0,
+    y: 0,
+    z: 0,
+  };
 
   constructor(model) {
     this.model = model;
@@ -47,7 +59,7 @@ class ModelContext {
         const css3D = new CSS3DSprite(element);
         this.cssSprites.push(css3D);
         css3D.name = selector;
-        css3D.scale.set(0.05, 0.05, 0.05);
+        css3D.scale.set(0.002, 0.002, 0.002);
         // const ff = gui.addFolder(`css元素${index}`);
         // ff.add(css3D.position, 'x', -100, 100);
         // ff.add(css3D.position, 'y', -100, 100);
@@ -78,7 +90,7 @@ class ModelContext {
         if (this.group) {
           setModalCenter(this.group);
           resolve(null);
-          this.addLight();
+          // this.addLight();
         }
       });
     });

+ 0 - 1
src/views/vent/monitorManager/windowMonitor/components/windowDualSVG.vue

@@ -2277,7 +2277,6 @@
 
   /** 根据SVG的使用场景播放动画 */
   function animate(data: any) {
-    console.log('debug data', data.OpenDegree, data.OpenDegree1, data.OpenDegree2);
     // 在SVG图片中,找到需要动起来的元素(类似<use xlink:href="#RE_L_0_Layer0_0_FILL"></use>),并填入id即可控制该元素的动画(如有)
     // 开度 / 最大开度 = 动画进度
     if (data.OpenDegree) {