Ver Fonte

# Fix 修复了微前端子应用组件多次加载导致的内存溢出问题、修改了模式切换样式

hongrunxia há 2 meses atrás
pai
commit
9510653a0f
33 ficheiros alterados com 2983 adições e 1772 exclusões
  1. BIN
      src/assets/images/home-container/configurable/ZhiDan_Ts.png
  2. BIN
      src/assets/images/home-container/configurable/blueBtn.png
  3. 96 0
      src/assets/images/home-container/configurable/device-group-paramer.svg
  4. BIN
      src/assets/images/home-container/configurable/greenBtn.png
  5. BIN
      src/assets/images/home-container/configurable/nitrogenBtnBj.png
  6. 377 374
      src/components/vent/BasicMonitoring.vue
  7. 117 117
      src/components/vent/customHeader.vue
  8. 2 5
      src/components/vent/micro/createSubApp.vue
  9. 7 6
      src/hooks/setting/index.ts
  10. 235 250
      src/layouts/default/header/components/weatherBroadcast.vue
  11. 6 1
      src/layouts/default/header/index.vue
  12. 1 1
      src/router/routes/index.ts
  13. 2 2
      src/views/vent/dataCenter/user/index.vue
  14. 38 0
      src/views/vent/dataCenter/user/user.data.ts
  15. 2 1
      src/views/vent/deviceManager/configurationTable/types.ts
  16. 30 24
      src/views/vent/home/colliery/components/wind-device.vue
  17. 153 128
      src/views/vent/home/configurable/components/MonitorBar.vue
  18. 353 324
      src/views/vent/home/configurable/components/content.vue
  19. 504 507
      src/views/vent/home/configurable/components/detail/ComplexList.vue
  20. 8 5
      src/views/vent/home/configurable/components/detail/TimelineList.vue
  21. 167 0
      src/views/vent/home/configurable/components/preset/nitrogenBtnList.vue
  22. 1 1
      src/views/vent/home/configurable/configurable.data.bd.ts
  23. 3 1
      src/views/vent/home/configurable/dustBD.vue
  24. 3 1
      src/views/vent/home/configurable/fireBD.vue
  25. 26 12
      src/views/vent/home/configurable/vent182.vue
  26. 1 0
      src/views/vent/monitorManager/deviceMonitor/index.vue
  27. 32 5
      src/views/vent/monitorManager/gateMonitor/components/CarDamageTable.vue
  28. 2 7
      src/views/vent/monitorManager/gateMonitor/gate.data.ts
  29. 90 0
      src/views/vent/monitorManager/nitrogenMonitor/index.vue
  30. 279 0
      src/views/vent/monitorManager/nitrogenMonitor/model-tip.vue
  31. 15 0
      src/views/vent/monitorManager/nitrogenMonitor/nitorgen.api.ts
  32. 325 0
      src/views/vent/monitorManager/nitrogenMonitor/nitrogen.data.ts
  33. 108 0
      src/views/vent/monitorManager/nitrogenMonitor/nitrogen.threejs.base.ts

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


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


+ 96 - 0
src/assets/images/home-container/configurable/device-group-paramer.svg

@@ -0,0 +1,96 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="37.093" height="37.503" viewBox="0 0 37.093 37.503">
+  <defs>
+    <filter id="路径_55417" x="1.122" y="0" width="26.174" height="32.35" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55418" x="1.122" y="10.235" width="26.174" height="27.267" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-2"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-2"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55419" x="12.761" y="3" width="20.252" height="26.292" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="3" result="blur-3"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-3"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55420" x="9.761" y="10.274" width="26.252" height="27.229" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-4"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-4"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55421" x="5.386" y="5.155" width="26.322" height="32.347" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-5"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-5"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55422" x="0" y="7.196" width="28.415" height="28.415" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-6"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-6"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55424" x="8.682" y="7.198" width="28.412" height="28.412" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-7"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-7"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_55425" x="5.386" y="0" width="26.322" height="26.968" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-8"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-8"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+    <filter id="路径_57691" x="4.414" y="1.905" width="28.267" height="28.267" filterUnits="userSpaceOnUse">
+      <feOffset input="SourceAlpha"/>
+      <feGaussianBlur stdDeviation="4" result="blur-9"/>
+      <feFlood flood-color="#006eff"/>
+      <feComposite operator="in" in2="blur-9"/>
+      <feComposite in="SourceGraphic"/>
+    </filter>
+  </defs>
+  <g id="组_16413" data-name="组 16413" transform="translate(-42.337 12)">
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55417)">
+      <path id="路径_55417-2" data-name="路径 55417" d="M128,0m.442,0h1.29q.442,0,.442.294V8.056q0,.294-.442.294h-1.29q-.442,0-.442-.294V.294Q128,0,128.442,0Z" transform="translate(-114.88 12)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55418)">
+      <path id="路径_55418-2" data-name="路径 55418" d="M128,776.229m.442,0h1.29q.442,0,.442.294V779.2q0,.294-.442.294h-1.29q-.442,0-.442-.294v-2.679Q128,776.229,128.442,776.229Z" transform="translate(-114.88 -753.99)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55419)">
+      <path id="路径_55419-2" data-name="路径 55419" d="M786.286,0m.458,0h1.336q.458,0,.458.294V8q0,.294-.458.294h-1.336q-.458,0-.458-.294V.294q0-.294.458-.294Z" transform="translate(-764.52 12)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55420)">
+      <path id="路径_55420-2" data-name="路径 55420" d="M786.286,779.154m.458,0h1.336q.458,0,.458.294v2.64q0,.294-.458.294h-1.336q-.458,0-.458-.294v-2.64Q786.286,779.154,786.744,779.154Z" transform="translate(-764.52 -756.88)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55421)">
+      <path id="路径_55421-2" data-name="路径 55421" d="M457.143,390.949m.472,0h1.378q.472,0,.472.294V399q0,.294-.472.294h-1.378q-.472,0-.472-.294v-7.759Q457.143,390.949,457.615,390.949Z" transform="translate(-439.76 -373.79)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55422)">
+      <path id="路径_55422-2" data-name="路径 55422" d="M57.064,587.35m-2.207,0a2.207,2.207,0,1,0,2.207-2.207A2.207,2.207,0,0,0,54.857,587.35Z" transform="translate(-42.86 -565.95)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55424)">
+      <path id="路径_55424-2" data-name="路径 55424" d="M715.349,587.349m-2.206,0a2.206,2.206,0,1,0,2.206-2.206A2.206,2.206,0,0,0,713.143,587.349Z" transform="translate(-692.46 -565.95)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_55425)">
+      <path id="路径_55425-2" data-name="路径 55425" d="M457.143,0m.472,0h1.378q.472,0,.472.294v2.38q0,.294-.472.294h-1.378q-.472,0-.472-.294V.294q0-.294.472-.294Z" transform="translate(-439.76 12)" fill="#b0d7ff"/>
+    </g>
+    <g transform="matrix(1, 0, 0, 1, 42.34, -12)" filter="url(#路径_57691)">
+      <path id="路径_57691-2" data-name="路径 57691" d="M386.134,180.42m-2.134,0a2.134,2.134,0,1,0,2.134-2.134A2.134,2.134,0,0,0,384,180.42Z" transform="translate(-367.59 -164.38)" fill="#b0d7ff"/>
+    </g>
+  </g>
+</svg>

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


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


+ 377 - 374
src/components/vent/BasicMonitoring.vue

@@ -15,7 +15,7 @@
       <!-- 监测模块 -->
       <template v-if="activeKey == 'monitor'">
         <slot name="opration">
-          <div class="button-wrapper">
+          <div class="button-wrapper" v-if="monitorType !== 'nitrogenMonitor'">
             <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>
@@ -92,420 +92,423 @@
 </template>
 
 <script setup lang="ts">
-  import customHeader from '/@/components/vent/customHeader.vue';
-  import { ref, onMounted, onUnmounted } from 'vue';
-  import { getDevice, sysList } from '/@/views/vent/monitorManager/comment/comment.api';
-  import BottomMenu from '/@/views/vent/comment/components/bottomMenu.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 '/@/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;
-      /** 请求场景数据传入的页面类型字符 */
-      pagetype?: string;
-      /** 获取各表格配置时依赖的设备类型 */
-      // deviceType: string;
-      /** 主要模块配置 */
-      mainConfig: {
-        operations: Operation[];
-        configs: Config[];
-        /** 获取该场景所含设备及其监测信息的API */
-        monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
-        /** 定时获取监测信息的配置,单位为毫秒,不传入即默认,传0即停用 */
-        timer?: number;
-      };
-      /** 历史数据配置 */
-      monitorHistoryConfig: {
-        /** 请求历史数据时传入的类型字符 */
-        columnsType?: string;
-        /** 如果默认的设备类型不适用,可以传递固定的类型 */
-        deviceType?: string;
-        /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
-        onlyBounedDevices?: boolean;
-        /** 显示历史数据曲线图 */
-        showHistoryCurve?: boolean;
-      };
-      /** 操作历史配置 */
-      handlerHistoryConfig: {
-        /** 请求操作历史时传入的类型字符 */
-        columnsType?: string;
-        /** 如果默认的设备类型不适用,可以传递固定的类型 */
-        deviceType?: string;
-        /** 获取操作历史的API,可以不提供以使用默认的请求 */
-        deviceListApi?: (params: any) => Promise<any[]>;
-      };
-      /** 报警历史配置 */
-      alarmHistoryConfig: {
-        /** 请求报警历史时传入的类型字符 */
-        columnsType?: string;
-        /** 如果默认的设备类型不适用,可以传递固定的类型 */
-        deviceType?: string;
-        /** 获取报警历史的API,可以不提供以使用默认的请求 */
-        list?: (params: any) => Promise<any[]>;
-        /** 获取设备以供报警历史过滤的API,可以不提供以使用默认的请求 */
-        deviceListApi?: (params: any) => Promise<any[]>;
-      };
-    }>(),
-    {
-      mainConfig: () => ({
-        operations: [],
-        configs: [],
-      }),
-      monitorHistoryConfig: () => ({}),
-      handlerHistoryConfig: () => ({}),
-      alarmHistoryConfig: () => ({}),
-      pagetype: 'normal',
-      showDeviceList: true,
-    }
-  );
-
-  const { currentRoute } = useRouter();
-  const { hasPermission } = usePermission();
-
-  const activeKey = ref('monitor');
-
-  function changeActive(activeValue) {
-    activeKey.value = activeValue;
+import customHeader from '/@/components/vent/customHeader.vue';
+import { ref, onMounted, onUnmounted } from 'vue';
+import { getDevice, sysList } from '/@/views/vent/monitorManager/comment/comment.api';
+import BottomMenu from '/@/views/vent/comment/components/bottomMenu.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 '/@/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;
+    /** 请求场景数据传入的页面类型字符 */
+    pagetype?: string;
+    /** 获取各表格配置时依赖的设备类型 */
+    monitorType?: string;
+    /** 监测页面类型*/
+    // deviceType: string;
+    /** 主要模块配置 */
+    mainConfig: {
+      operations: Operation[];
+      configs: Config[];
+      /** 获取该场景所含设备及其监测信息的API */
+      monitorApi?: (params: { deviceType: string; deviceId: number | string }) => Promise<any>;
+      /** 定时获取监测信息的配置,单位为毫秒,不传入即默认,传0即停用 */
+      timer?: number;
+    };
+    /** 历史数据配置 */
+    monitorHistoryConfig: {
+      /** 请求历史数据时传入的类型字符 */
+      columnsType?: string;
+      /** 如果默认的设备类型不适用,可以传递固定的类型 */
+      deviceType?: string;
+      /** 仅展示已绑定设备,选择是则从系统中获取sysId下已绑定设备。仅能查询到已绑定设备的历史数据 */
+      onlyBounedDevices?: boolean;
+      /** 显示历史数据曲线图 */
+      showHistoryCurve?: boolean;
+    };
+    /** 操作历史配置 */
+    handlerHistoryConfig: {
+      /** 请求操作历史时传入的类型字符 */
+      columnsType?: string;
+      /** 如果默认的设备类型不适用,可以传递固定的类型 */
+      deviceType?: string;
+      /** 获取操作历史的API,可以不提供以使用默认的请求 */
+      deviceListApi?: (params: any) => Promise<any[]>;
+    };
+    /** 报警历史配置 */
+    alarmHistoryConfig: {
+      /** 请求报警历史时传入的类型字符 */
+      columnsType?: string;
+      /** 如果默认的设备类型不适用,可以传递固定的类型 */
+      deviceType?: string;
+      /** 获取报警历史的API,可以不提供以使用默认的请求 */
+      list?: (params: any) => Promise<any[]>;
+      /** 获取设备以供报警历史过滤的API,可以不提供以使用默认的请求 */
+      deviceListApi?: (params: any) => Promise<any[]>;
+    };
+  }>(),
+  {
+    mainConfig: () => ({
+      operations: [],
+      configs: [],
+    }),
+    monitorHistoryConfig: () => ({}),
+    handlerHistoryConfig: () => ({}),
+    alarmHistoryConfig: () => ({}),
+    pagetype: 'normal',
+    showDeviceList: true,
   }
+);
 
-  /** 场景选项 */
-  const options = ref([]);
-  /** 已选择了的场景的id */
-  const optionValue = ref('');
+const { currentRoute } = useRouter();
+const { hasPermission } = usePermission();
 
-  /** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
-  async function getSysDataSource() {
-    const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype }).catch(() => {
-      message.error('获取场景数据时发生了错误');
-    });
-    // 初始时选择第一条数据
-    options.value = res.records || [];
-    if (!optionValue.value) {
-      changeSelectRow(options.value[0]['id']);
-    }
-  }
+const activeKey = ref('monitor');
 
-  // 切换检测数据
-  function changeSelectRow(deviceID) {
-    optionValue.value = deviceID;
-    getDeviceList();
-  }
+function changeActive(activeValue) {
+  activeKey.value = activeValue;
+}
 
-  /** 当前场景所关联设备 */
-  const deviceList = ref<DeviceType[]>([]);
-  const deviceActive = ref('');
-  const deviceType = ref('');
+/** 场景选项 */
+const options = ref([]);
+/** 已选择了的场景的id */
+const optionValue = ref('');
 
-  /** 选择设备 */
-  function deviceChange(index) {
-    deviceType.value = deviceList.value[index]?.deviceType || '';
-    deviceActive.value = deviceList.value[index]?.deviceType || '';
+/** 获取左上角场景选择框数据的方法,如果此时初始场景未赋值则选择首项做初始化 */
+async function getSysDataSource() {
+  const res = await sysList({ strtype: props.strtype, pagetype: props.pagetype }).catch(() => {
+    message.error('获取场景数据时发生了错误');
+  });
+  // 初始时选择第一条数据
+  options.value = res.records || [];
+  if (!optionValue.value) {
+    changeSelectRow(options.value[0]['id']);
   }
+}
+
+// 切换检测数据
+function changeSelectRow(deviceID) {
+  optionValue.value = deviceID;
+  // if (props.monitorType !== 'nitrogenMonitor') {
+  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('获取已绑定设备时发生了错误');
+  });
 
-  /** 查询当前场景所关联设备列表 */
-  async function getDeviceList() {
-    const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
-      message.error('获取已绑定设备时发生了错误');
+  deviceList.value = msgTxt.reduce((arr, item) => {
+    const data = item.datalist.map((data: any) => {
+      return Object.assign(data, data.readData);
     });
-
-    deviceList.value = msgTxt.reduce((arr, item) => {
-      const data = item.datalist.map((data: any) => {
-        return Object.assign(data, data.readData);
+    // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
+    if (item.type != 'sys') {
+      arr.unshift({
+        deviceType: item.type,
+        deviceName: item.typeName || item.datalist[0].typeName,
+        datalist: data,
       });
-      // sys代表场景本身,应该过滤掉去处理该场景下的关联设备
-      if (item.type != 'sys') {
-        arr.unshift({
-          deviceType: item.type,
-          deviceName: item.typeName || item.datalist[0].typeName,
-          datalist: data,
-        });
-      }
-
-      return arr;
-    }, []);
-    if (!deviceActive.value) {
-      deviceChange(0);
     }
+    return arr;
+  }, []);
+  if (!deviceActive.value) {
+    deviceChange(0);
   }
-
-  let timer: NodeJS.Timeout;
-  /** 场景的监测数据 */
-  const monitorData = ref<any>({});
-  /** 获取本场景下所绑定的设备,将监测数据赋值 */
-  async function getMonitor() {
-    if (props.mainConfig.monitorApi) {
-      monitorData.value = await props.mainConfig
-        .monitorApi({
-          deviceType: deviceType.value,
-          deviceId: optionValue.value,
-        })
-        .catch(() => {
-          message.error('获取已绑定设备时发生了错误');
-        });
-    } else if (optionValue.value) {
-      const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
+}
+
+let timer: NodeJS.Timeout;
+/** 场景的监测数据 */
+const monitorData = ref<any>({});
+/** 获取本场景下所绑定的设备,将监测数据赋值 */
+async function getMonitor() {
+  if (props.mainConfig.monitorApi) {
+    monitorData.value = await props.mainConfig
+      .monitorApi({
+        deviceType: deviceType.value,
+        deviceId: optionValue.value,
+      })
+      .catch(() => {
         message.error('获取已绑定设备时发生了错误');
       });
-      const temp = {};
-      msgTxt.forEach((item) => {
-        _.set(temp, item.type, item.datalist);
-      });
-      monitorData.value = temp;
-    }
+  } else if (optionValue.value) {
+    const { msgTxt = [] } = await getDevice({ devicetype: 'sys', systemID: optionValue.value }).catch(() => {
+      message.error('获取已绑定设备时发生了错误');
+    });
+    const temp = {};
+    msgTxt.forEach((item) => {
+      _.set(temp, item.type, item.datalist);
+    });
+    monitorData.value = temp;
   }
-
-  /** 密码提示框是否显示 */
-  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(),
-      });
-    }
+}
+
+/** 密码提示框是否显示 */
+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;
+/** 密码输入后确认的回调 */
+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,
+  return onOk(key, value, pwd)
+    .then(() => {
+      passwordModalShown.value = false;
     })
-      .then((r) => {
-        if (!r.success) throw r.message || '操作失败';
-        message.success('指令下发成功');
-      })
-      .catch((e) => {
-        message.error(typeof e === 'string' ? e : '指令下发失败');
-      });
-  }
+    .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;
-    }
-    await getSysDataSource();
-    if (props.mainConfig.timer !== 0) {
-      timer = setInterval(() => {
-        getMonitor();
-      }, props.mainConfig.timer || 5000);
-    } else {
+onMounted(async () => {
+  if (currentRoute.value && currentRoute.value['query'] && currentRoute.value['query']['id']) {
+    optionValue.value = currentRoute.value['query']['id'] as string;
+  }
+  await getSysDataSource();
+  if (props.mainConfig.timer !== 0) {
+    timer = setInterval(() => {
       getMonitor();
-    }
-  });
+    }, props.mainConfig.timer || 5000);
+  } else {
+    getMonitor();
+  }
+});
 
-  onUnmounted(() => {
-    clearInterval(timer);
-  });
+onUnmounted(() => {
+  clearInterval(timer);
+});
 </script>
 <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 {
-      margin-top: 80px;
-      padding: 0 10px;
-      .history-container {
-        pointer-events: auto;
-        background: #6195af1a;
-        // width: 100%;
-        border: 1px solid #00fffd22;
-        padding: 10px 0;
-        box-shadow: 0 0 20px #44b4ff33 inset;
-      }
-    }
-    .device-button-group {
-      // margin: 0 20px;
-      padding: 0 10px;
-      display: flex;
+@import '/@/design/vent/modal.less';
+@ventSpace: zxm;
+
+.scene-box {
+  --image-tab-group-bg: url('/@/assets/images/vent/tab-group-bg.png');
+  margin-top: 30px;
+  pointer-events: none;
+  .history-group {
+    margin-top: 80px;
+    padding: 0 10px;
+    .history-container {
       pointer-events: auto;
+      background: #6195af1a;
+      // width: 100%;
+      border: 1px solid #00fffd22;
+      padding: 10px 0;
+      box-shadow: 0 0 20px #44b4ff33 inset;
+    }
+  }
+  .device-button-group {
+    // margin: 0 20px;
+    padding: 0 10px;
+    display: flex;
+    pointer-events: auto;
+    position: relative;
+    &::after {
+      position: absolute;
+      content: '';
+      width: 100%;
+      height: 2px;
+      top: 30px;
+      left: -1px;
+      border-bottom: 1px solid #0efcff;
+    }
+    .device-button {
+      padding: 4px 15px;
       position: relative;
-      &::after {
-        position: absolute;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 14px;
+
+      color: #fff;
+      cursor: pointer;
+      margin: 0 3px;
+
+      &::before {
         content: '';
-        width: 100%;
-        height: 2px;
-        top: 30px;
-        left: -1px;
-        border-bottom: 1px solid #0efcff;
-      }
-      .device-button {
-        padding: 4px 15px;
-        position: relative;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        font-size: 14px;
-
-        color: #fff;
-        cursor: pointer;
-        margin: 0 3px;
-
-        &::before {
-          content: '';
-          position: absolute;
-          top: 0;
-          right: 0;
-          bottom: 0;
-          left: 0;
-          border: 1px solid #6176af;
-          transform: skewX(-38deg);
-          background-color: rgba(0, 77, 103, 85%);
-          z-index: -1;
-        }
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+        border: 1px solid #6176af;
+        transform: skewX(-38deg);
+        background-color: rgba(0, 77, 103, 85%);
+        z-index: -1;
       }
-      .device-active {
-        // color: #0efcff;
-        &::before {
-          border-color: #0efcff;
-          box-shadow: 1px 1px 3px 1px #0efcff inset;
-        }
+    }
+    .device-active {
+      // color: #0efcff;
+      &::before {
+        border-color: #0efcff;
+        box-shadow: 1px 1px 3px 1px #0efcff inset;
       }
     }
   }
-  .center-container {
-    width: 100%;
-    height: calc(100% - 150px);
+}
+.center-container {
+  width: 100%;
+  height: calc(100% - 150px);
+}
+
+.input-box {
+  display: flex;
+  align-items: center;
+  padding-left: 10px;
+
+  .input-title {
+    color: #73e8fe;
+    width: auto;
   }
 
-  .input-box {
-    display: flex;
-    align-items: center;
-    padding-left: 10px;
-
-    .input-title {
-      color: #73e8fe;
-      width: auto;
-    }
-
-    .@{ventSpace}-input-number {
-      border-color: #ffffff88;
-    }
-
-    margin-right: 10px;
+  .@{ventSpace}-input-number {
+    border-color: #ffffff88;
   }
 
-  .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;
-    }
-  }
+  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 {
-    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);
-    }
+    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);
-    }
+  &::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);
+.button-box-disable {
+  cursor: not-allowed;
+  border: 1px solid var(--vent-base-border);
 
-    &:hover {
-      background: none;
-    }
+  &:hover {
+    background: none;
+  }
 
-    &:before {
-      background: linear-gradient(#5897c299, #4a92a899);
-    }
+  &:before {
+    background: linear-gradient(#5897c299, #4a92a899);
   }
+}
 </style>

+ 117 - 117
src/components/vent/customHeader.vue

@@ -23,148 +23,148 @@
   </div>
 </template>
 <script lang="ts">
-  import { defineComponent, computed } from 'vue';
-  import { Decoration5 } from '@kjgl77/datav-vue3';
-  export default defineComponent({
-    name: 'CustomHeader',
-    components: { Decoration5 },
-    props: {
-      optionValue: {
-        type: String,
-      },
-      fieldNames: {
-        type: Object,
-        default: () => {},
-      },
-      options: {
-        type: Array,
-        default: () => [],
-      },
+import { defineComponent, computed } from 'vue';
+import { Decoration5 } from '@kjgl77/datav-vue3';
+export default defineComponent({
+  name: 'CustomHeader',
+  components: { Decoration5 },
+  props: {
+    optionValue: {
+      type: String,
     },
-    emits: ['change'],
-    setup(props, { emit }) {
-      const currentTitleValue = computed(() => props.optionValue);
-      // 标题选择
-      function handleTitleChange(value) {
-        emit('change', value);
-      }
-      return {
-        currentTitleValue,
-        handleTitleChange,
-      };
+    fieldNames: {
+      type: Object,
+      default: () => {},
     },
-  });
+    options: {
+      type: Array,
+      default: () => [],
+    },
+  },
+  emits: ['change'],
+  setup(props, { emit }) {
+    const currentTitleValue = computed(() => props.optionValue);
+    // 标题选择
+    function handleTitleChange(value) {
+      emit('change', value);
+    }
+    return {
+      currentTitleValue,
+      handleTitleChange,
+    };
+  },
+});
 </script>
 <style lang="less">
-  @import '/@/design/vent/modal.less';
+@import '/@/design/vent/modal.less';
 
-  .@{ventSpace}-select-dropdown.drop {
-    background-color: transparent !important;
+.@{ventSpace}-select-dropdown.drop {
+  background-color: transparent !important;
 
-    .@{ventSpace}-select-item-option-selected,
-    .@{ventSpace}-select-item-option-active {
+  .@{ventSpace}-select-item-option-selected,
+  .@{ventSpace}-select-item-option-active {
+    background-color: #ffffff33 !important;
+  }
+  .@{ventSpace}-select-item {
+    color: inherit;
+    &:hover {
       background-color: #ffffff33 !important;
     }
-    .@{ventSpace}-select-item {
+  }
+  .@{ventSpace}-select-tree {
+    .@{ventSpace}-select-tree-treenode {
       color: inherit;
-      &:hover {
-        background-color: #ffffff33 !important;
-      }
     }
-    .@{ventSpace}-select-tree {
-      .@{ventSpace}-select-tree-treenode {
-        color: inherit;
-      }
-      .@{ventSpace}-select-tree-switcher-icon {
-        color: inherit;
-      }
+    .@{ventSpace}-select-tree-switcher-icon {
+      color: inherit;
     }
   }
+}
 </style>
 <style lang="less" scoped>
-  @import '/@/design/theme.less';
-  @ventSpace: zxm;
+@import '/@/design/theme.less';
+@ventSpace: zxm;
 
-  @{theme-green} {
-    .vent-custom-header {
-      --image-vent-header1: url('/@/assets/images/themify/green/vent/vent-header1.png');
-      --image-select-bg: url('/@/assets/images/themify/deepblue/vent/home/select-bg.png');
-    }
+@{theme-green} {
+  .vent-custom-header {
+    --image-vent-header1: url('/@/assets/images/themify/green/vent/vent-header1.png');
+    --image-select-bg: url('/@/assets/images/themify/deepblue/vent/home/select-bg.png');
   }
-  @{theme-deepblue} {
-    .vent-custom-header {
-      --image-vent-header1: url('/@/assets/images/themify/deepblue/vent/vent-header1.png');
-      --image-select-bg: url('/@/assets/images/themify/deepblue/vent/home/select-bg.png');
-    }
+}
+@{theme-deepblue} {
+  .vent-custom-header {
+    --image-vent-header1: url('/@/assets/images/themify/deepblue/vent/vent-header1.png');
+    --image-select-bg: url('/@/assets/images/themify/deepblue/vent/home/select-bg.png');
   }
+}
 
-  .vent-custom-header {
-    --image-vent-header1: url('/@/assets/images/vent/vent-header1.png');
-    --image-select-bg: url('/@/assets/images/vent/home/select-bg.png');
+.vent-custom-header {
+  --image-vent-header1: url('/@/assets/images/vent/vent-header1.png');
+  --image-select-bg: url('/@/assets/images/vent/home/select-bg.png');
+  width: 100%;
+  position: relative;
+  z-index: 9999;
+  pointer-events: none;
+  .vent-home-header {
     width: 100%;
-    position: relative;
-    z-index: 9999;
-    pointer-events: none;
-    .vent-home-header {
-      width: 100%;
+    position: fixed;
+    top: 0;
+    // background: url('/@/assets/images/vent/new-home/header-bg.png') no-repeat;
+    // height: 100px;
+    background: var(--image-vent-header1) no-repeat;
+    height: 89px;
+    background-size: contain;
+    display: flex;
+    justify-content: center;
+    .header-icon {
+      margin-top: 45px;
+    }
+    .header-text {
       position: fixed;
-      top: 0;
-      // background: url('/@/assets/images/vent/new-home/header-bg.png') no-repeat;
-      // height: 100px;
-      background: var(--image-vent-header1) no-repeat;
-      height: 89px;
-      background-size: contain;
-      display: flex;
-      justify-content: center;
-      .header-icon {
-        margin-top: 45px;
-      }
-      .header-text {
-        position: fixed;
-        top: 5px;
-        color: #fff;
-        font-size: 32px;
-        font-family: 'ysbtFont';
-        background-image: linear-gradient(#ffffff 50%, @vent-base-light-bg);
-        -webkit-background-clip: text;
-        color: transparent;
-      }
+      top: 5px;
+      color: #fff;
+      font-size: 32px;
+      font-family: 'ysbtFont';
+      background-image: linear-gradient(#ffffff 50%, @vent-base-light-bg);
+      -webkit-background-clip: text;
+      color: transparent;
     }
-    .container-title {
-      width: 380px;
-      height: 34px;
-      top: 60px;
-      // background: url('/@/assets/images/vent/new-home/container-title-bg.png') no-repeat;
-      background: var(--image-select-bg) no-repeat;
-      background-size: contain;
-      padding: 0 0 0 40px;
-      // padding: 0 0 0 180px;
-      font-size: 20px;
-      pointer-events: auto;
-      position: relative;
-      z-index: 9999;
-      .title-select {
-        width: 340px;
-        position: absolute;
+  }
+  .container-title {
+    width: 380px;
+    height: 34px;
+    top: 20px;
+    // background: url('/@/assets/images/vent/new-home/container-title-bg.png') no-repeat;
+    background: var(--image-select-bg) no-repeat;
+    background-size: contain;
+    padding: 0 0 0 40px;
+    // padding: 0 0 0 180px;
+    font-size: 20px;
+    pointer-events: auto;
+    position: relative;
+    z-index: 9999;
+    .title-select {
+      width: 340px;
+      position: absolute;
 
-        top: 0;
-        // left: 160px;
-      }
+      top: 0;
+      // left: 160px;
     }
   }
-  :deep(.zxm-select) {
-    width: 300px;
-    .@{ventSpace}-select-selector {
-      background: transparent !important;
-      border: none !important;
-      box-shadow: none !important;
-      .zxm-select-selection-item {
-        color: #fff !important;
-        font-size: 20px;
-      }
-    }
-    .@{ventSpace}-select-arrow {
+}
+:deep(.zxm-select) {
+  width: 300px;
+  .@{ventSpace}-select-selector {
+    background: transparent !important;
+    border: none !important;
+    box-shadow: none !important;
+    .zxm-select-selection-item {
       color: #fff !important;
+      font-size: 20px;
     }
   }
+  .@{ventSpace}-select-arrow {
+    color: #fff !important;
+  }
+}
 </style>

+ 2 - 5
src/components/vent/micro/createSubApp.vue

@@ -45,12 +45,9 @@
       // 这里判断
       if (newPath !== oldPath) {
         await resetComponent();
-      } else if (newQuery !== oldQuery) {
+      } else if (newQuery !== oldQuery && (Object.keys(oldQuery).length == 0 || Object.keys(newQuery).length == 0)) {
         const actions = getActions();
-        const appType = route.path.split('/')[1];
-        activeApps[appType].mountPromise.then(() => {
-          actions.setGlobalState({ currentRouter: route });
-        });
+        actions.setGlobalState({ currentRouter: route });
       }
     }
   );

+ 7 - 6
src/hooks/setting/index.ts

@@ -30,7 +30,8 @@ const getUrl = () => {
             // getHomePath是以前的代码,留下做兼容,获取到一个默认的首页路径
             // 然后正常按配置走,默认使用上面的首页路径
             const homePathKey = data.result['defaultTheme'] ? data.result['defaultTheme'] : '';
-            homePath = data.result['systemHome'] ? data.result['systemHome'] : getHomePath(homePathKey);
+            const legacyHomePath = getHomePath(homePathKey);
+            homePath = data.result['systemHome'] ? data.result['systemHome'] : legacyHomePath;
             const faviconIcon = document.getElementById('faviconIcon');
             if (faviconIcon) {
               faviconIcon.setAttribute('href', `${VUE_APP_URL.baseUrl}/sys/common/static/${logoUrl}`);
@@ -47,11 +48,11 @@ const getUrl = () => {
 };
 
 export async function getRemoteSetting() {
-  if (!sysOrgCode) {
-    try {
-      await getUrl();
-    } catch (error) {}
-  }
+  // if (!sysOrgCode) {
+  try {
+    await getUrl();
+  } catch (error) {}
+  // }
 }
 
 export const useGlobSetting = (): Readonly<GlobConfig> => {

+ 235 - 250
src/layouts/default/header/components/weatherBroadcast.vue

@@ -5,10 +5,10 @@
     <a-badge :count="10">
       <a href="#" class="head-example"></a>
     </a-badge> -->
-      <div style="display: flex; flex-direction: row" class="btn-header">
+      <div style="display: flex; flex-direction: row;align-items: center;" class="btn-header">
         <img :src="parseWeatherData(weatherObj.text)" class="weather-icon" />
         <span class="unit">{{ weatherObj.pressure }}&nbsp;hPa</span>
-        <FileSearchOutlined style="font-size: 18px; color: #fff; line-height: 50px" />
+        <FileSearchOutlined style="font-size: 20px; color: #fff;" />
       </div>
     </div>
     <div v-if="isShowWeatherBroad" class="broadcast" ref="VoiceBroadcastRef" id="VoiceBroadcast">
@@ -17,36 +17,21 @@
       </div>
       <div class="broadcast-context">
         <div class="context-tab">
-          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 0 }" @click="toSelectList(0)"> 温度</div>
-          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 1 }" @click="toSelectList(1)"> 气压</div>
-          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 2 }" @click="toSelectList(2)"> 风力</div>
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 0 }" @click="toSelectList(0)">
+            温度</div>
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 1 }" @click="toSelectList(1)">
+            气压</div>
+          <div class="context-tab-item" :class="{ 'context-tab-item-active': activeKey == 2 }" @click="toSelectList(2)">
+            风力</div>
         </div>
         <div class="context-box">
           <div class="echarts-box">
-            <BarAndLine
-              v-if="activeKey == 0"
-              xAxisPropType="fxTime"
-              height="240px"
-              :dataSource="monitorData"
-              :chartsColumns="ChartsColumnsWD"
-              :option="Option"
-            />
-            <BarAndLine
-              v-if="activeKey == 1"
-              xAxisPropType="fxTime"
-              height="240px"
-              :dataSource="monitorData"
-              :chartsColumns="ChartsColumnsQY"
-              :option="Option"
-            />
-            <BarAndLine
-              v-if="activeKey == 2"
-              xAxisPropType="fxTime"
-              height="240px"
-              :dataSource="monitorData"
-              :chartsColumns="ChartsColumnsFL"
-              :option="Option"
-            />
+            <BarAndLine v-if="activeKey == 0" xAxisPropType="fxTime" height="240px" :dataSource="monitorData"
+              :chartsColumns="ChartsColumnsWD" :option="Option" />
+            <BarAndLine v-if="activeKey == 1" xAxisPropType="fxTime" height="240px" :dataSource="monitorData"
+              :chartsColumns="ChartsColumnsQY" :option="Option" />
+            <BarAndLine v-if="activeKey == 2" xAxisPropType="fxTime" height="240px" :dataSource="monitorData"
+              :chartsColumns="ChartsColumnsFL" :option="Option" />
           </div>
         </div>
       </div>
@@ -54,247 +39,247 @@
   </div>
 </template>
 <script lang="ts">
-  import BarAndLine from '/@/components/chart/BarAndLine.vue';
-  import { Tooltip, Badge } from 'ant-design-vue';
-  import { SoundOutlined, ClearOutlined, FileSearchOutlined, WarningOutlined } from '@ant-design/icons-vue';
-  import Icon from '/@/components/Icon';
-  import { defineComponent, ref, onMounted, nextTick } from 'vue';
-  import { defHttp } from '/@/utils/http/axios';
-  import { useDrag } from '@/hooks/event/useDrag';
+import BarAndLine from '/@/components/chart/BarAndLine.vue';
+import { Tooltip, Badge } from 'ant-design-vue';
+import { SoundOutlined, ClearOutlined, FileSearchOutlined, WarningOutlined } from '@ant-design/icons-vue';
+import Icon from '/@/components/Icon';
+import { defineComponent, ref, onMounted, nextTick } from 'vue';
+import { defHttp } from '/@/utils/http/axios';
+import { useDrag } from '@/hooks/event/useDrag';
 
-  export default defineComponent({
-    name: 'VoiceBroadcast',
-    components: { Icon, Tooltip, Badge, SoundOutlined, ClearOutlined, FileSearchOutlined, WarningOutlined, BarAndLine },
+export default defineComponent({
+  name: 'VoiceBroadcast',
+  components: { Icon, Tooltip, Badge, SoundOutlined, ClearOutlined, FileSearchOutlined, WarningOutlined, BarAndLine },
 
-    setup() {
-      const getWeather = () => defHttp.post({ url: '/safety/ventanalyDevice/getWeatherData' });
-      const activeKey = ref(0);
-      const isShowWeatherBroad = ref(false);
-      const monitorData = ref<any[]>([]);
+  setup() {
+    const getWeather = () => defHttp.post({ url: '/safety/ventanalyDevice/getWeatherData' });
+    const activeKey = ref(0);
+    const isShowWeatherBroad = ref(false);
+    const monitorData = ref<any[]>([]);
 
-      const ChartsColumnsWD = [
-        {
-          legend: '温度',
-          seriesName: '(℃)',
-          ymax: 0.8,
-          yname: '℃',
-          linetype: 'line',
-          yaxispos: 'left',
-          color: '#00FFA8',
-          sort: 1,
-          xRotate: 0,
-          dataIndex: 'temp',
-        },
-      ];
-      const ChartsColumnsQY = [
-        {
-          legend: '气压',
-          seriesName: '(hPa)',
-          ymax: 0.8,
-          yname: 'hPa',
-          linetype: 'line',
-          yaxispos: 'left',
-          color: '#00FFA8',
-          sort: 1,
-          xRotate: 0,
-          dataIndex: 'pressure',
-        },
-      ];
-      const ChartsColumnsFL = [
-        {
-          legend: '风力',
-          seriesName: '(等级)',
-          ymax: 0.8,
-          yname: '等级',
-          linetype: 'line',
-          yaxispos: 'left',
-          color: '#00FFA8',
-          sort: 1,
-          xRotate: 0,
-          dataIndex: 'widnScale',
-        },
-      ];
+    const ChartsColumnsWD = [
+      {
+        legend: '温度',
+        seriesName: '(℃)',
+        ymax: 0.8,
+        yname: '℃',
+        linetype: 'line',
+        yaxispos: 'left',
+        color: '#00FFA8',
+        sort: 1,
+        xRotate: 0,
+        dataIndex: 'temp',
+      },
+    ];
+    const ChartsColumnsQY = [
+      {
+        legend: '气压',
+        seriesName: '(hPa)',
+        ymax: 0.8,
+        yname: 'hPa',
+        linetype: 'line',
+        yaxispos: 'left',
+        color: '#00FFA8',
+        sort: 1,
+        xRotate: 0,
+        dataIndex: 'pressure',
+      },
+    ];
+    const ChartsColumnsFL = [
+      {
+        legend: '风力',
+        seriesName: '(等级)',
+        ymax: 0.8,
+        yname: '等级',
+        linetype: 'line',
+        yaxispos: 'left',
+        color: '#00FFA8',
+        sort: 1,
+        xRotate: 0,
+        dataIndex: 'widnScale',
+      },
+    ];
 
-      const Option = {
-        grid: {
-          top: '20%',
-          left: '5%',
-          right: '5%',
-          bottom: '3%',
-          containLabel: true,
-        },
-        toolbox: {
-          feature: null,
-        },
-      };
-      const weatherObj = ref({
-        cloud: '',
-        dew: '',
-        fxTime: '',
-        humidity: '',
-        icon: '',
-        pop: '',
-        precip: '',
-        pressure: '',
-        temp: '',
-        text: '',
-        wind360: '',
-        windDir: '',
-        windScale: '',
-        windSpeed: '',
-      });
+    const Option = {
+      grid: {
+        top: '20%',
+        left: '5%',
+        right: '5%',
+        bottom: '3%',
+        containLabel: true,
+      },
+      toolbox: {
+        feature: null,
+      },
+    };
+    const weatherObj = ref({
+      cloud: '',
+      dew: '',
+      fxTime: '',
+      humidity: '',
+      icon: '',
+      pop: '',
+      precip: '',
+      pressure: '',
+      temp: '',
+      text: '',
+      wind360: '',
+      windDir: '',
+      windScale: '',
+      windSpeed: '',
+    });
 
-      const iconMap = {
-        晴: new URL('/src/assets/icons/sun.svg', import.meta.url).href,
-        雨: new URL('/src/assets/icons/rain.svg', import.meta.url).href,
-        雪: new URL('/src/assets/icons/snow.svg', import.meta.url).href,
-        多云: new URL('/src/assets/icons/clound.svg', import.meta.url).href,
-        风: new URL('/src/assets/icons/wind.svg', import.meta.url).href,
-      };
+    const iconMap = {
+      晴: new URL('/src/assets/icons/sun.svg', import.meta.url).href,
+      雨: new URL('/src/assets/icons/rain.svg', import.meta.url).href,
+      雪: new URL('/src/assets/icons/snow.svg', import.meta.url).href,
+      多云: new URL('/src/assets/icons/clound.svg', import.meta.url).href,
+      风: new URL('/src/assets/icons/wind.svg', import.meta.url).href,
+    };
 
-      function parseWeatherData(res) {
-        return iconMap[res] || new URL('/src/assets/icons/sun.svg', import.meta.url).href;
-      }
-      function showWarningBroad() {
-        isShowWeatherBroad.value = !isShowWeatherBroad.value;
-        if (isShowWeatherBroad.value) {
-          toSelectList(0);
+    function parseWeatherData(res) {
+      return iconMap[res] || new URL('/src/assets/icons/sun.svg', import.meta.url).href;
+    }
+    function showWarningBroad() {
+      isShowWeatherBroad.value = !isShowWeatherBroad.value;
+      if (isShowWeatherBroad.value) {
+        toSelectList(0);
 
-          nextTick(() => {
-            const dom = document.getElementById('VoiceBroadcast');
-            if (dom) useDrag(dom);
-          });
-        }
-      }
-      async function toSelectList(key) {
-        activeKey.value = key;
+        nextTick(() => {
+          const dom = document.getElementById('VoiceBroadcast');
+          if (dom) useDrag(dom);
+        });
       }
-      async function getWeatherInfo() {
-        const res = await getWeather();
+    }
+    async function toSelectList(key) {
+      activeKey.value = key;
+    }
+    async function getWeatherInfo() {
+      const res = await getWeather();
 
-        weatherObj.value = JSON.parse(res.weatherDataNow);
-        monitorData.value = JSON.parse(res.weatherData);
-      }
-      onMounted(() => {
-        nextTick(async () => {
-          await getWeatherInfo();
-        });
+      weatherObj.value = JSON.parse(res.weatherDataNow);
+      monitorData.value = JSON.parse(res.weatherData);
+    }
+    onMounted(() => {
+      nextTick(async () => {
+        await getWeatherInfo();
       });
+    });
 
-      return {
-        showWarningBroad,
-        isShowWeatherBroad,
-        activeKey,
-        weatherObj,
-        parseWeatherData,
-        toSelectList,
-        monitorData,
-        ChartsColumnsWD,
-        ChartsColumnsQY,
-        ChartsColumnsFL,
-        Option,
-      };
-    },
-  });
+    return {
+      showWarningBroad,
+      isShowWeatherBroad,
+      activeKey,
+      weatherObj,
+      parseWeatherData,
+      toSelectList,
+      monitorData,
+      ChartsColumnsWD,
+      ChartsColumnsQY,
+      ChartsColumnsFL,
+      Option,
+    };
+  },
+});
 </script>
 <style lang="less" scoped>
-  .unit {
-    font-size: 15px;
-    line-height: 47px;
-    color: #fff;
-    padding-right: 10px;
-  }
-  .btn {
-    line-height: 30px;
-    cursor: pointer;
+.unit {
+  font-size: 14px;
+  // line-height: 47px;
+  color: #fff;
+  padding-right: 10px;
+}
+
+.btn {
+  // line-height: 30px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+}
+
+.btn-header {
+  height: 48px;
+  margin-right: 20px;
+}
+
+.weather-icon {
+  width: 20px;
+  height: 20px;
+  // margin-top: 14px;
+  margin-right: 10px;
+}
+
+.broadcast {
+  width: 500px;
+  height: 350px;
+  border-radius: 4px;
+  position: fixed;
+  top: 50px;
+  right: 20px;
+  background-color: rgb(255, 255, 255);
+  background: url('../../../../assets/images/warn-dialog-bg.png') no-repeat center;
+  background-size: 100% 100%;
+  z-index: 9999999;
+  color: #fff;
+
+  .title {
+    text-align: center;
+    padding: 0 20px;
+
+    :deep(.ant-badge:not(.ant-badge-status)) {
+      margin-right: 40px !important;
+    }
+
     display: flex;
-  }
-  .btn-header {
-    height: 50px;
-    margin-right: 20px;
-  }
-  .weather-icon {
-    width: 20px;
-    height: 20px;
-    margin-top: 14px;
-    margin-right: 10px;
-  }
-  .broadcast {
-    width: 500px;
-    height: 350px;
-    border-radius: 4px;
-    position: fixed;
-    top: 50px;
-    right: 20px;
-    background-color: rgb(255, 255, 255);
-    background: url('../../../../assets/images/warn-dialog-bg.png') no-repeat center;
-    background-size: 100% 100%;
-    z-index: 9999999;
-    color: #fff;
+    align-items: center;
+    padding-top: 10px;
 
-    .title {
+    .badge-title {
+      display: inline-block;
+      width: 62px;
+      line-height: 32px;
+      background-color: #2174f0;
+      border-radius: 26px;
       text-align: center;
-      padding: 0 20px;
+      color: #fff;
+      padding-bottom: 2px;
+    }
+  }
+}
 
-      :deep(.ant-badge:not(.ant-badge-status)) {
-        margin-right: 40px !important;
-      }
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      .message-title {
-        font-size: 16px;
-        color: #fff;
-        font-weight: 600;
-        line-height: 50px;
-        flex: 1;
-      }
-      .badge-box {
-        display: flex;
-        align-items: center;
-        padding-top: 10px;
-        .badge-title {
-          display: inline-block;
-          width: 62px;
-          line-height: 32px;
-          background-color: #2174f0;
-          border-radius: 26px;
-          text-align: center;
-          color: #fff;
-          padding-bottom: 2px;
-        }
-      }
+.broadcast-context {
+  .context-tab {
+    display: flex;
+
+    .context-tab-item {
+      line-height: 24px;
+      background: url('../../../../assets/images/tab-1.png') no-repeat center;
+      border-radius: 24px;
+      text-align: center;
+      color: #fff;
+      cursor: pointer;
+      font-size: 14px;
+      width: 80px;
     }
 
-    .broadcast-context {
-      .context-tab {
-        display: flex;
-        .context-tab-item {
-          line-height: 24px;
-          background: url('../../../../assets/images/tab-1.png') no-repeat center;
-          border-radius: 24px;
-          text-align: center;
-          color: #fff;
-          cursor: pointer;
-          font-size: 14px;
-          width: 80px;
-        }
-        .context-tab-item-active {
-          background: url('../../../../assets/images/tab-2.png') no-repeat center;
-        }
-      }
-      .context-box {
-        width: 100%;
-        height: calc(100% - 32px);
-        .echarts-box {
-          width: 100%;
-          height: 500px;
-        }
-      }
+    .context-tab-item-active {
+      background: url('../../../../assets/images/tab-2.png') no-repeat center;
     }
   }
-  :deep(.zxm-badge-count) {
-    width: 0px;
-    height: 0px;
+
+  .context-box {
+    width: 100%;
+    height: calc(100% - 32px);
+
+    .echarts-box {
+      width: 100%;
+      height: 500px;
+    }
   }
+}
+
+:deep(.zxm-badge-count) {
+  width: 0px;
+  height: 0px;
+}
 </style>

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

@@ -55,7 +55,7 @@
       <LoginSelect ref="loginSelectRef" @success="loginSelectOk" /> -->
     </div>
   </Header>
-  <div :class="`${prefixCls}-action`" style="position: fixed; top: 30px; right: 20px; z-index: 999999">
+  <div :class="`${prefixCls}-action`" style="position: fixed; top: 25px; 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">
@@ -363,4 +363,9 @@
     color: transparent;
     font-weight: 600;
   }
+  .right-position{
+    display: flex;
+    align-items: center;
+    height: 48px;
+  }
 </style>

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

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

+ 2 - 2
src/views/vent/dataCenter/user/index.vue

@@ -36,7 +36,7 @@
     <PasswordModal @register="registerPasswordModal" @success="reload" />
     <!--用户代理-->
     <UserAgentModal @register="registerAgentModal" @success="reload" />
-    <!--回收站-->
+    <!--授权弹窗-->
     <UserPermModal @register="registerPermModal" @success="reload" />
   </div>
 </template>
@@ -210,7 +210,7 @@ function getTableAction(record): ActionItem[] {
     },
     {
       label: '授权',
-      onClick: handlePermissionModel.bind(null, record.id),
+      onClick: handlePermissionModel.bind(null, record.username),
       // ifShow: () => hasPermission('system:user:edit'),
     },
   ];

+ 38 - 0
src/views/vent/dataCenter/user/user.data.ts

@@ -152,6 +152,44 @@ export const formSchema: FormSchema[] = [
     field: 'thirdType',
     component: 'Input',
     defaultValue: '_thirdPartyUser',
+    show: false,
+  },
+  // {
+  //   label: '角色',
+  //   field: 'selectedroles',
+  //   component: 'ApiSelect',
+  //   componentProps: {
+  //     mode: 'multiple',
+  //     api: getAllRolesList,
+  //     labelField: 'roleName',
+  //     valueField: 'id',
+  //   },
+  // },
+  {
+    label: '所属部门',
+    field: 'selecteddeparts',
+    component: 'JSelectDept',
+    required: true,
+    componentProps: ({ formActionType, formModel }) => {
+      return {
+        sync: false,
+        checkStrictly: true,
+        defaultExpandLevel: 2,
+
+        onSelect: (options, values) => {
+          const { updateSchema } = formActionType;
+          //所属部门修改后更新负责部门下拉框数据
+          updateSchema([
+            {
+              field: 'departIds',
+              componentProps: { options },
+            },
+          ]);
+          //所属部门修改后更新负责部门数据
+          formModel.departIds && (formModel.departIds = formModel.departIds.filter((item) => values.value.indexOf(item) > -1));
+        },
+      };
+    },
   },
 ];
 

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

@@ -100,6 +100,7 @@ export interface ModuleData {
         | 'button_list'
         | 'card_list'
         | 'generalist';
+        | 'nitrogenBtnList';
       /** 分区大小 */
       basis: string;
       overflow?: boolean;
@@ -365,7 +366,7 @@ export interface ModuleDataPreset extends ReadFrom {
 
 export interface ModuleDataComplexList extends ReadFrom {
   /** 列表预设的背景类型 */
-  type: 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H';
+  type: 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I';
   /** 是否需要根据数据来决定所展示的项目数量,需要确保 items 至少有一项,且 readFrom 指向数组 */
   mapFromData?: boolean;
   /** 核心配置,每个列表项对应一项 */

+ 30 - 24
src/views/vent/home/colliery/components/wind-device.vue

@@ -10,10 +10,10 @@
             </div>
             <div></div>
             <img :src="item.url" :alt="item.text" />
-            <div class="level-text">
-              <div class="all-count">
+            <div class="level-text" :class="item.allCount ? 'allText' : 'unallText'">
+              <div class="all-count" v-if="item.allCount">
                 <span>{{ `${item.allText}&nbsp:&nbsp` }}</span>
-                <span class="num-count">{{ item.allCount || 0 }}</span>
+                <span class="num-count">{{ item.allCount }}</span>
               </div>
               <div class="warn-count">
                 <span>{{ `${item.warnText}&nbsp:&nbsp` }}</span>
@@ -283,21 +283,8 @@ watch(
     overflow-y: auto;
 
     .icons-box {
-      // display: flex; lxh
-      // flex-wrap: wrap; lxh
-      // justify-content: space-around;
-      // justify-content: space-between; lxh
       height: 100%;
 
-      // overflow-y: hidden;
-      // align-items: start ;
-      // &:hover {
-      //   overflow-y: auto;
-      //   & > .icon-item {
-      //     // animation-play-state: paused; lxh
-      //     // animation: move1 2s linear;lxh
-      //   }
-      // }
       .icon-item {
         position: relative;
         display: flex;
@@ -308,14 +295,7 @@ watch(
 
         .level-text {
           width: 220px;
-          display: flex;
-          justify-content: space-between;
           position: absolute;
-          // top: 42px;lxh
-          // right: 25px;lxh
-          // color: #fff;lxh
-          // font-family: 'douyuFont';lxh
-          // font-size: 12px;lxh
           top: 47px;
           left: 185px;
           color: #ffffffe0;
@@ -323,20 +303,46 @@ watch(
           text-align: center;
           letter-spacing: 1px;
 
+          .all-count {
+            margin-right: 10px;
+          }
+
           .num-count,
           .num-count1 {
             font-family: 'douyuFont';
             font-size: 12px;
           }
 
-          .warn-count,.close-count {
+          .warn-count {
+            margin-right: 10px;
             .num-count {
               color: #ffffffe0;
             }
+
             .num-count1 {
               color: #ff0000;
             }
           }
+
+          .close-count {
+            .num-count {
+              color: #ffffffe0;
+            }
+
+            .num-count1 {
+              color: #ff0000;
+            }
+          }
+        }
+
+        .allText {
+          display: flex;
+          justify-content: flex-start;
+        }
+
+        .unallText {
+          display: flex;
+          justify-content: flex-start;
         }
 
         img {

+ 153 - 128
src/views/vent/home/configurable/components/MonitorBar.vue

@@ -31,144 +31,169 @@
   </a-row>
 </template>
 <script lang="ts" setup>
-  import { computed } from 'vue';
-  import { getFormattedText } from '../hooks/helper';
-  import { useGlobSetting } from '/@/hooks/setting';
+import { computed } from 'vue';
+import { getFormattedText } from '../hooks/helper';
+import { useGlobSetting } from '/@/hooks/setting';
 
-  const props = defineProps<{
-    isDataRealTime: boolean;
-    data: any;
-  }>();
+const props = defineProps<{
+  isDataRealTime: boolean;
+  data: any;
+}>();
 
-  const { sysOrgCode } = useGlobSetting();
+const { sysOrgCode } = useGlobSetting();
 
-  const config = computed(() => {
-    if (sysOrgCode === 'huainanzhangji') {
-      return [
-        {
-          label: '总回风量(m³/min)',
-          value: '${midinfo[0].sysdata.zonghuifeng}',
-        },
-        {
-          label: '总进风量(m³/min)',
-          value: '${midinfo[0].sysdata.zongjinfeng}',
-        },
-        {
-          label: '有效风量率(%)',
-          value: '${midinfo[0].sysinfo.useM3Perent}',
-        },
-      ];
-    }
-    if (props.isDataRealTime) {
-      return [
-        {
-          label: '总回风量(m³/min)',
-          value: '${midinfo[0].sysdata.zonghuifeng}',
-        },
-        {
-          label: '总进风量(m³/min)',
-          value: '${midinfo[0].sysdata.zongjinfeng}',
-        },
-        {
-          label: '计划风量(m³/min)',
-          value: '${midinfo[0].sysdata.xufengliang}',
-        },
-        {
-          label: '通风巷道长度(m)',
-          // value: '223196',
-          value: '${midinfo[0].sysinfo.totallength}',
-        },
-        {
-          label: '有效风量率(%)',
-          value: '${midinfo[0].sysinfo.useM3Perent}',
-        },
-      ];
-    } else {
-      return [
-        {
-          label: '总回风量(m³/min)',
-          value: '${midinfo[0].sysinfo.info.totalRetM3}',
-        },
-        {
-          label: '总进风量(m³/min)',
-          value: '${midinfo[0].sysinfo.info.totalIntM3}',
-        },
-        {
-          label: '计划风量(m³/min)',
-          value: '${midinfo[0].sysinfo.info.totalPlanM3}',
-        },
-        {
-          label: '通风巷道长度(万m)',
-          value: '${midinfo[0].sysinfo.info.flength}',
-        },
-        {
-          label: '有效风量率(%)',
-          value: '${midinfo[0].sysinfo.info.useM3Perent}',
-        },
-      ];
-    }
-  });
+const config = computed(() => {
+  if (sysOrgCode === 'huainanzhangji') {
+    return [
+      {
+        label: '总回风量(m³/min)',
+        value: '${midinfo[0].sysdata.zonghuifeng}',
+      },
+      {
+        label: '总进风量(m³/min)',
+        value: '${midinfo[0].sysdata.zongjinfeng}',
+      },
+      {
+        label: '有效风量率(%)',
+        value: '${midinfo[0].sysinfo.useM3Perent}',
+      },
+    ];
+  }
+  if (props.isDataRealTime) {
+    return [
+      {
+        label: '总回风量(m³/min)',
+        value: '${midinfo[0].sysdata.zonghuifeng}',
+      },
+      {
+        label: '总进风量(m³/min)',
+        value: '${midinfo[0].sysdata.zongjinfeng}',
+      },
+      {
+        label: '计划风量(m³/min)',
+        value: '${midinfo[0].sysdata.xufengliang}',
+      },
+      {
+        label: '通风巷道长度(m)',
+        // value: '223196',
+        value: '${midinfo[0].sysinfo.totallength}',
+      },
+      {
+        label: '有效风量率(%)',
+        value: '${midinfo[0].sysinfo.useM3Perent}',
+      },
+    ];
+  } else {
+    return [
+      {
+        label: '总回风量(m³/min)',
+        value: '${midinfo[0].sysinfo.info.totalRetM3}',
+      },
+      {
+        label: '总进风量(m³/min)',
+        value: '${midinfo[0].sysinfo.info.totalIntM3}',
+      },
+      {
+        label: '计划风量(m³/min)',
+        value: '${midinfo[0].sysinfo.info.totalPlanM3}',
+      },
+      {
+        label: '通风巷道长度(万m)',
+        value: '${midinfo[0].sysinfo.info.flength}',
+      },
+      {
+        label: '有效风量率(%)',
+        value: '${midinfo[0].sysinfo.info.useM3Perent}',
+      },
+    ];
+  }
+});
 
-  const computedConfig = computed(() => {
-    const data = props.data;
-    return config.value.map((e, i) => {
-      return {
-        label: getFormattedText(data, e.label),
-        value: getFormattedText(data, e.value),
-        class: `middata${i}`,
-      };
-    });
+const computedConfig = computed(() => {
+  const data = props.data;
+  return config.value.map((e, i) => {
+    return {
+      label: getFormattedText(data, e.label),
+      value: getFormattedText(data, e.value),
+      class: `middata${i}`,
+    };
   });
+});
 </script>
 <style lang="less" scoped>
-  .midback-info {
-    pointer-events: none;
-  }
-  .middata {
-    // margin-top: 7px;
-    padding: 5px 0px 5px 60px;
-    width: 175px;
-    height: 64px;
-    // margin-top: 4px;
-    // margin-left: 10px;
-    // margin-bottom: 5px;
-    background-size: 100% 100%;
-  }
-  .middata0 {
-    background: url('/@/assets/images/home-container/configurable/middata1.png');
-  }
-  .middata1 {
-    background: url('/@/assets/images/home-container/configurable/middata2.png');
-  }
-  .middata2 {
-    background: url('/@/assets/images/home-container/configurable/middata3.png');
-  }
-  .middata3 {
-    background: url('/@/assets/images/home-container/configurable/middata6.png');
+.midback-info {
+  pointer-events: none;
+}
+
+.middata {
+  // margin-top: 7px;
+  padding: 5px 0px 5px 60px;
+  width: 175px;
+  height: 64px;
+  // margin-top: 4px;
+  // margin-left: 10px;
+  // margin-bottom: 5px;
+  background-size: 100% 100%;
+  text-align: center;
+}
+
+.middata0 {
+  .midnumberval {
+    color: #e6ffee;
   }
-  .middata4 {
-    background: url('/@/assets/images/home-container/configurable/middata5.png');
+  background: url('/@/assets/images/home-container/configurable/middata1.png');
+}
+
+.middata1 {
+   .midnumberval {
+    color: #d3f8ff;
   }
-  .middata5 {
-    background: url('/@/assets/images/home-container/configurable/middata4.png');
+  background: url('/@/assets/images/home-container/configurable/middata2.png');
+}
+
+.middata2 {
+   .midnumberval {
+    color: #fffcd5;
   }
-  .midnumberval {
-    z-index: 4;
-    padding-top: -10px;
-    // position: absolute;
-    // top: -6px;
-    text-align: center;
-    font-weight: 600;
-    color: #f6ca0e;
-    font-size: 18px;
-    font-family: 'Microsoft YaHei', Arial;
-    // font-family: 'UnidreamLED';
+  background: url('/@/assets/images/home-container/configurable/middata3.png');
+}
+
+.middata3 {
+   .midnumberval {
+    color: #deeafe;
   }
-  .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;
+  background: url('/@/assets/images/home-container/configurable/middata6.png');
+}
+
+.middata4 {
+   .midnumberval {
+    color: #f3e3ff;
   }
+  background: url('/@/assets/images/home-container/configurable/middata5.png');
+}
+
+.middata5 {
+  background: url('/@/assets/images/home-container/configurable/middata4.png');
+}
+
+.midnumberval {
+  z-index: 4;
+  padding-top: -10px;
+  // position: absolute;
+  // top: -6px;
+  text-align: center;
+  font-weight: 600;
+  // color: #f6ca0e;
+  font-size: 12px;
+  // font-family: 'Microsoft YaHei', Arial;
+  font-family: 'douyuFont';
+}
+
+.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>

+ 353 - 324
src/views/vent/home/configurable/components/content.vue

@@ -5,7 +5,7 @@
     <!-- 背景 -->
     <img v-if="background.show && background.type === 'image'" class="content__background" :src="background.link" />
     <video
-      v-if="background.show && background.type === 'video'"
+      v-if="background.show && background.type === 'video' && background.isBoard"
       class="content__background"
       width="100%"
       autoplay
@@ -16,6 +16,19 @@
     >
       <source :src="background.link" />
       Not Supportted Link Or Browser
+    </video>
+     <video
+     v-if="background.show && background.type === 'video' && !background.isBoard"
+      class="content__background_1"
+      width="100%"
+      autoplay
+      loop
+      muted
+      disablepictureinpicture
+      playsinline
+    >
+      <source :src="background.link" />
+      Not Supportted Link Or Browser
     </video>
     <div class="flex w-full h-full" :style="{ flexDirection: layout.direction }">
       <div v-for="config in layoutConfig" :key="config.name" :style="{ flexBasis: config.basis, overflow: config.overflow ? 'auto' : 'none' }">
@@ -150,6 +163,9 @@
         <template v-else-if="config.name === 'generalist'">
           <generalList class="content__module" :generalData="config.data" />
         </template>
+        <template v-if="config.name === 'nitrogenBtnList'">
+          <NitrogenBtnList class="content__module" :type="config.config.type" :config="config.config" :buttonList="config.config.buttonList" />
+        </template>
         <!-- <template v-if="config.key === 'fire_control'">
         <FIreControl class="content__module" />
       </template>
@@ -161,405 +177,418 @@
   </div>
 </template>
 <script lang="ts" setup>
-  import { computed } from 'vue';
-  import {
-    CommonItem,
-    Config,
-    // ModuleDataBoard,
-    // ModuleDataChart,
-    // ModuleDataList,
-    // ModuleDataPreset,
-    // ModuleDataTable,
-  } from '../../../deviceManager/configurationTable/types';
-  import MiniBoard from './detail/MiniBoard.vue';
-  import TimelineList from './detail/TimelineList.vue';
-  import TimelineListNew from './detail/TimelineListNew.vue';
-  import CustomList from './detail/CustomList.vue';
-  import CustomGallery from './detail/CustomGallery.vue';
-  import ComplexList from './detail/ComplexList.vue';
-  import GalleryList from './detail/GalleryList.vue';
-  import CustomTable from './detail/CustomTable.vue';
-  import CustomChart from './detail/CustomChart.vue';
-  import { clone } from 'lodash-es';
-  import { getData, getFormattedText } from '../hooks/helper';
-  import BlastDelta from '../../../monitorManager/deviceMonitor/components/device/modal/blastDelta.vue';
-  import QHCurve from './preset/QHCurve.vue';
-  import MeasureDetail from './preset/MeasureDetail.vue';
-  import CustomTabs from './preset/CustomTabs.vue';
-  import AIChat from '/@/components/AIChat/MiniChat.vue';
-  import DeviceAlarm from './preset/DeviceAlarm.vue';
-  import SelectCs from './preset/SelectCs.vue';
-  import MiniBoardNew from './detail/MiniBoard-New.vue';
-  import Partition from './preset/partition.vue';
-  import SelectorDualChart from './preset/selectorDualChart.vue';
-  import RadioLabel from './preset/radioLabel.vue';
-  import ButtonList from './preset/buttonList.vue';
-  import cardList from './preset/cardList.vue';
-  import generalList from './preset/generalList.vue';
+import { computed } from 'vue';
+import {
+  CommonItem,
+  Config,
+  // ModuleDataBoard,
+  // ModuleDataChart,
+  // ModuleDataList,
+  // ModuleDataPreset,
+  // ModuleDataTable,
+} from '../../../deviceManager/configurationTable/types';
+import MiniBoard from './detail/MiniBoard.vue';
+import TimelineList from './detail/TimelineList.vue';
+import TimelineListNew from './detail/TimelineListNew.vue';
+import CustomList from './detail/CustomList.vue';
+import CustomGallery from './detail/CustomGallery.vue';
+import ComplexList from './detail/ComplexList.vue';
+import GalleryList from './detail/GalleryList.vue';
+import CustomTable from './detail/CustomTable.vue';
+import CustomChart from './detail/CustomChart.vue';
+import { clone } from 'lodash-es';
+import { getData, getFormattedText } from '../hooks/helper';
+import BlastDelta from '../../../monitorManager/deviceMonitor/components/device/modal/blastDelta.vue';
+import QHCurve from './preset/QHCurve.vue';
+import MeasureDetail from './preset/MeasureDetail.vue';
+import CustomTabs from './preset/CustomTabs.vue';
+import AIChat from '/@/components/AIChat/MiniChat.vue';
+import DeviceAlarm from './preset/DeviceAlarm.vue';
+import SelectCs from './preset/SelectCs.vue';
+import MiniBoardNew from './detail/MiniBoard-New.vue';
+import Partition from './preset/partition.vue';
+import SelectorDualChart from './preset/selectorDualChart.vue';
+import RadioLabel from './preset/radioLabel.vue';
+import ButtonList from './preset/buttonList.vue';
+import NitrogenBtnList from './preset/nitrogenBtnList.vue';
+import cardList from './preset/cardList.vue';
+import generalList from './preset/generalList.vue';
 
-  // import FIreWarn from './preset/FIreWarn.vue';
-  // import FIreControl from './preset/FIreControl.vue';
+// import FIreWarn from './preset/FIreWarn.vue';
+// import FIreControl from './preset/FIreControl.vue';
 
-  const props = defineProps<{
-    data: any;
-    moduleData: Config['moduleData'];
-    chartData: any;
-  }>();
+const props = defineProps<{
+  data: any;
+  moduleData: Config['moduleData'];
+  chartData: any;
+}>();
 
-  const { background, layout } = props.moduleData;
+const { background, layout } = props.moduleData;
 
-  // 获取当原始配置带 items 项时的最终 items 配置
-  function getItems(raw, items: CommonItem[]) {
-    return items.map((i) => {
+// 获取当原始配置带 items 项时的最终 items 配置
+function getItems(raw, items: CommonItem[]) {
+  return items.map((i) => {
+    return {
+      ...i,
+      label: getFormattedText(raw, i.label, i.trans),
+      value: getFormattedText(raw, i.value, i.trans),
+    };
+  });
+}
+
+// 获取当 List 组件配置带 items 项时的最终 items 配置
+function getListItems(raw: any, items: CommonItem[], mapFromData?: boolean) {
+  if (mapFromData && Array.isArray(raw)) {
+    return raw.map((data) => {
+      const item = items[0];
       return {
-        ...i,
-        label: getFormattedText(raw, i.label, i.trans),
-        value: getFormattedText(raw, i.value, i.trans),
+        ...item,
+        label: getFormattedText(data, item.label, item.trans),
+        value: getFormattedText(data, item.value, item.trans),
       };
     });
   }
+  return getItems(raw, items);
+}
+
+/** 根据配置里的layout将配置格式化为带 key 的具体配置,例如:[{ key: 'list', value: any, ...ModuleDataList }] */
+const layoutConfig = computed(() => {
+  const refData = props.data;
+  const board = clone(props.moduleData.board) || [];
+  const list = clone(props.moduleData.list) || [];
+  const gallery = clone(props.moduleData.gallery) || [];
+  const complex_list = clone(props.moduleData.complex_list) || [];
+  const gallery_list = clone(props.moduleData.gallery_list) || [];
+  const tabs = clone(props.moduleData.tabs) || [];
+  const chart = clone(props.moduleData.chart) || [];
+  const table = clone(props.moduleData.table) || [];
+  const preset = clone(props.moduleData.preset) || [];
+  const partition = clone(props.moduleData.partition) || [];
+  const mockData = clone(props.chartData) || [];
+  return layout.items.reduce((arr: any[], item) => {
+    switch (item.name) {
+      case 'board': {
+        const cfg = board.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
-  // 获取当 List 组件配置带 items 项时的最终 items 配置
-  function getListItems(raw: any, items: CommonItem[], mapFromData?: boolean) {
-    if (mapFromData && Array.isArray(raw)) {
-      return raw.map((data) => {
-        const item = items[0];
-        return {
+        arr.push({
+          overflow: true,
           ...item,
-          label: getFormattedText(data, item.label, item.trans),
-          value: getFormattedText(data, item.value, item.trans),
-        };
-      });
-    }
-    return getItems(raw, items);
-  }
+          ...cfg,
+          items: getItems(data, cfg.items),
+        });
+        break;
+      }
+      case 'list': {
+        const cfg = list.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
-  /** 根据配置里的layout将配置格式化为带 key 的具体配置,例如:[{ key: 'list', value: any, ...ModuleDataList }] */
-  const layoutConfig = computed(() => {
-    const refData = props.data;
-    const board = clone(props.moduleData.board) || [];
-    const list = clone(props.moduleData.list) || [];
-    const gallery = clone(props.moduleData.gallery) || [];
-    const complex_list = clone(props.moduleData.complex_list) || [];
-    const gallery_list = clone(props.moduleData.gallery_list) || [];
-    const tabs = clone(props.moduleData.tabs) || [];
-    const chart = clone(props.moduleData.chart) || [];
-    const table = clone(props.moduleData.table) || [];
-    const preset = clone(props.moduleData.preset) || [];
-    const partition = clone(props.moduleData.partition) || [];
-    const mockData = clone(props.chartData) || [];
-    return layout.items.reduce((arr: any[], item) => {
-      switch (item.name) {
-        case 'board': {
-          const cfg = board.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+        arr.push({
+          overflow: true,
+          ...item,
+          ...cfg,
+          items: getListItems(data, cfg.items, cfg.mapFromData),
+        });
+        break;
+      }
+      case 'gallery': {
+        const cfg = gallery.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
+        arr.push({
+          overflow: true,
+          ...item,
+          ...cfg,
+          items: getItems(data, cfg.items),
+        });
+        break;
+      }
+      case 'complex_list': {
+        const cfg = complex_list.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
+
+        if (cfg.mapFromData) {
+          const firstListItem = cfg.items[0];
           arr.push({
             overflow: true,
             ...item,
             ...cfg,
-            items: getItems(data, cfg.items),
+            items: (data || []).map((d) => {
+              return {
+                title: getFormattedText(d, firstListItem.title, firstListItem.trans),
+                contents: firstListItem.contents.map((e) => {
+                  return {
+                    ...e,
+                    label: getFormattedText(d, e.label, e.trans),
+                    value: getFormattedText(d, e.value, e.trans),
+                  };
+                }),
+              };
+            }),
           });
-          break;
-        }
-        case 'list': {
-          const cfg = list.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
-
+        } else {
           arr.push({
             overflow: true,
             ...item,
             ...cfg,
-            items: getListItems(data, cfg.items, cfg.mapFromData),
+            items: cfg.items.map((i) => {
+              return {
+                title: getFormattedText(data, i.title, i.trans),
+                contents: i.contents.map((e) => {
+                  return {
+                    ...e,
+                    label: getFormattedText(data, e.label, e.trans),
+                    value: getFormattedText(data, e.value, e.trans),
+                  };
+                }),
+              };
+            }),
           });
-          break;
         }
-        case 'gallery': {
-          const cfg = gallery.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+        break;
+      }
+      case 'gallery_list': {
+        const cfg = gallery_list.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
+
+        arr.push({
+          overflow: true,
+          ...item,
+          ...cfg,
+          items: getItems(data, cfg.items),
+          galleryItems: getItems(data, cfg.galleryItems),
+        });
+        break;
+      }
+      case 'tabs': {
+        const cfg = tabs.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
+        if (cfg.mapFromData) {
+          const firstListItem = cfg.items[0];
           arr.push({
             overflow: true,
             ...item,
             ...cfg,
-            items: getItems(data, cfg.items),
+            items: (data || []).map((d) => {
+              return {
+                title: getFormattedText(d, firstListItem.title, firstListItem.trans),
+                contents: firstListItem.contents.map((e) => {
+                  return {
+                    ...e,
+                    label: getFormattedText(d, e.label, e.trans),
+                    value: getFormattedText(d, e.value, e.trans),
+                  };
+                }),
+              };
+            }),
           });
-          break;
-        }
-        case 'complex_list': {
-          const cfg = complex_list.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
-
-          if (cfg.mapFromData) {
-            const firstListItem = cfg.items[0];
-            arr.push({
-              overflow: true,
-              ...item,
-              ...cfg,
-              items: (data || []).map((d) => {
-                return {
-                  title: getFormattedText(d, firstListItem.title, firstListItem.trans),
-                  contents: firstListItem.contents.map((e) => {
-                    return {
-                      ...e,
-                      label: getFormattedText(d, e.label, e.trans),
-                      value: getFormattedText(d, e.value, e.trans),
-                    };
-                  }),
-                };
-              }),
-            });
-          } else {
-            arr.push({
-              overflow: true,
-              ...item,
-              ...cfg,
-              items: cfg.items.map((i) => {
-                return {
-                  title: getFormattedText(data, i.title, i.trans),
-                  contents: i.contents.map((e) => {
-                    return {
-                      ...e,
-                      label: getFormattedText(data, e.label, e.trans),
-                      value: getFormattedText(data, e.value, e.trans),
-                    };
-                  }),
-                };
-              }),
-            });
-          }
-          break;
-        }
-        case 'gallery_list': {
-          const cfg = gallery_list.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
-
+        } else {
           arr.push({
             overflow: true,
             ...item,
             ...cfg,
-            items: getItems(data, cfg.items),
-            galleryItems: getItems(data, cfg.galleryItems),
+            items: cfg.items.map((i) => {
+              return {
+                title: getFormattedText(data, i.title, i.trans),
+                contents: i.contents.map((e) => {
+                  return {
+                    ...e,
+                    label: getFormattedText(data, e.label, e.trans),
+                    value: getFormattedText(data, e.value, e.trans),
+                  };
+                }),
+              };
+            }),
           });
-          break;
         }
-        case 'tabs': {
-          const cfg = tabs.shift();
+        break;
+      }
+      case 'chart': {
+        const cfg = chart.shift();
+        if (cfg?.type == 'scatter') {
           if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
-
-          if (cfg.mapFromData) {
-            const firstListItem = cfg.items[0];
-            arr.push({
-              overflow: true,
-              ...item,
-              ...cfg,
-              items: (data || []).map((d) => {
-                return {
-                  title: getFormattedText(d, firstListItem.title, firstListItem.trans),
-                  contents: firstListItem.contents.map((e) => {
-                    return {
-                      ...e,
-                      label: getFormattedText(d, e.label, e.trans),
-                      value: getFormattedText(d, e.value, e.trans),
-                    };
-                  }),
-                };
-              }),
-            });
-          } else {
-            arr.push({
-              overflow: true,
-              ...item,
-              ...cfg,
-              items: cfg.items.map((i) => {
-                return {
-                  title: getFormattedText(data, i.title, i.trans),
-                  contents: i.contents.map((e) => {
-                    return {
-                      ...e,
-                      label: getFormattedText(data, e.label, e.trans),
-                      value: getFormattedText(data, e.value, e.trans),
-                    };
-                  }),
-                };
-              }),
-            });
-          }
-          break;
-        }
-        case 'chart': {
-          const cfg = chart.shift();
-          if (cfg?.type == 'scatter') {
-            if (!cfg) break;
-            const data = getData(mockData, cfg.readFrom, cfg.parser);
-
-            arr.push({
-              ...item,
-              config: cfg,
-              data,
-            });
-            break;
-          } else {
-            if (!cfg) break;
-            const data = getData(refData, cfg.readFrom, cfg.parser);
-            arr.push({
-              ...item,
-              config: cfg,
-              data,
-            });
-            break;
-          }
-        }
-        case 'table': {
-          const cfg = table.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+          const data = getData(mockData, cfg.readFrom, cfg.parser);
 
           arr.push({
-            ...cfg,
             ...item,
-            columns: cfg.columns,
+            config: cfg,
             data,
           });
           break;
-        }
-        case 'partition': {
-          const cfg = partition.shift();
+        } else {
           if (!cfg) break;
           const data = getData(refData, cfg.readFrom, cfg.parser);
           arr.push({
-            overflow: true,
             ...item,
+            config: cfg,
             data,
-            ...cfg,
           });
           break;
         }
-        default: {
-          const cfg = preset.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+      }
+      case 'table': {
+        const cfg = table.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
-          arr.push({
-            ...item,
-            data,
-            config: cfg,
-          });
-          break;
-        }
+        arr.push({
+          ...cfg,
+          ...item,
+          columns: cfg.columns,
+          data,
+        });
+        break;
       }
-      // console.log(arr,'arr---')
-      return arr;
-    }, []);
-  });
+      case 'partition': {
+        const cfg = partition.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
+        arr.push({
+          overflow: true,
+          ...item,
+          data,
+          ...cfg,
+        });
+        break;
+      }
+      default: {
+        const cfg = preset.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
+
+        arr.push({
+          ...item,
+          data,
+          config: cfg,
+        });
+        break;
+      }
+    }
+    // console.log(arr,'arr---')
+    return arr;
+  }, []);
+});
 </script>
 <style lang="less" scoped>
-  @import '@/design/theme.less';
+@import '@/design/theme.less';
 
-  .content {
-    height: calc(100% - 30px);
-    position: relative;
-    // z-index: -2;
-    display: flex;
-    flex-direction: column;
-    overflow-y: auto; // 这里会导致样式无故添加滚动条
-    overflow-x: hidden;
-  }
+.content {
+  height: calc(100% - 30px);
+  position: relative;
+  // z-index: -2;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto; // 这里会导致样式无故添加滚动条
+  overflow-x: hidden;
+}
 
   .content__background {
     width: 100%;
-    height: 100%;
+    // height: 100%;
+    height: calc(100% - 65px);
     position: absolute;
-    top: 0;
+    top: 65px;
     left: 0;
     z-index: 0;
     object-fit: fill;
+    padding: 5px;
+    box-sizing: border-box;
   }
-
-  .image__background {
-    width: 35%;
-    height: 61%;
-    left: 30%;
-  }
-
-  .content__module {
-    // margin-top: 5px;
-    // margin-bottom: 5px;
+   .content__background_1 {
     width: 100%;
     height: 100%;
+    position: absolute;
+    top: 0px;
+    left: 0;
+    z-index: 0;
+    object-fit: fill;
   }
 
-  .content__module1 {
-    background: url('@/assets/images/vent/homeNew/databg/4.png');
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-    height: 129px;
-    margin-top: 20%;
-  }
+.image__background {
+  width: 35%;
+  height: 61%;
+  left: 30%;
+}
 
-  .content__moduleFire {
-    width: 100%;
-    height: 100%;
-    margin-left: -24% !important;
-  }
+.content__module {
+  // margin-top: 5px;
+  // margin-bottom: 5px;
+  width: 100%;
+  height: 100%;
+}
 
-  .content__module_dust {
-    background: url('@/assets/images/vent/homeNew/bottomBg.png');
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-    width: 100%;
-    height: 100%;
-  }
+.content__module1 {
+  background: url('@/assets/images/vent/homeNew/databg/4.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  height: 129px;
+  margin-top: 20%;
+}
 
-  // .content__module:first-of-type {
-  //   margin-top: 0;
-  // }
-  // .content__module:last-of-type {
-  //   margin-bottom: 0;
-  // }
-  ::-webkit-scrollbar {
-    width: 5px !important;
-  }
+.content__moduleFire {
+  width: 100%;
+  height: 100%;
+  margin-left: -24% !important;
+}
 
-  ::-webkit-scrollbar-thumb {
-    width: 5px !important;
-  }
+.content__module_dust {
+  background: url('@/assets/images/vent/homeNew/bottomBg.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  width: 100%;
+  height: 100%;
+}
 
-  :deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
-    /* background-color: transparent; */
-    color: #fff;
-  }
+// .content__module:first-of-type {
+//   margin-top: 0;
+// }
+// .content__module:last-of-type {
+//   margin-bottom: 0;
+// }
+::-webkit-scrollbar {
+  width: 5px !important;
+}
 
-  :deep(.zxm-select-arrow) {
-    color: #fff;
-  }
+::-webkit-scrollbar-thumb {
+  width: 5px !important;
+}
 
-  :deep(.zxm-select-selection-item) {
-    color: #fff !important;
-  }
+:deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
+  /* background-color: transparent; */
+  color: #fff;
+}
 
-  :deep(.zxm-select-selection-placeholder) {
-    color: #fff !important;
-  }
+:deep(.zxm-select-arrow) {
+  color: #fff;
+}
 
-  :deep(.dialog-overlay) {
-    width: 100%;
-    height: 100%;
-    position: unset;
-    box-shadow: unset;
-  }
+:deep(.zxm-select-selection-item) {
+  color: #fff !important;
+}
 
-  ::-webkit-scrollbar {
-    width: 5px !important;
-  }
+:deep(.zxm-select-selection-placeholder) {
+  color: #fff !important;
+}
 
-  ::-webkit-scrollbar-thumb {
-    width: 5px !important;
-  }
+:deep(.dialog-overlay) {
+  width: 100%;
+  height: 100%;
+  position: unset;
+  box-shadow: unset;
+}
+
+::-webkit-scrollbar {
+  width: 5px !important;
+}
+
+::-webkit-scrollbar-thumb {
+  width: 5px !important;
+}
 </style>

+ 504 - 507
src/views/vent/home/configurable/components/detail/ComplexList.vue

@@ -8,541 +8,538 @@
     <div class="flex-grow h-full" :class="`list__wrapper_${type}`">
       <div v-for="(item, i) in listConfig" :key="`vvhccdcl${i}`" :class="`list-item_${type}`">
         <!-- 列表项前面的图标 -->
-        <div :class="`list-item__title_${type}`">{{ item.title }}</div>
+        <div :class="['list-item__title_I', { 'bg-1': type == 'I' && i == 0 }]" v-if="type == 'I' && i == 0">
+          <img src="/src/assets/images/home-container/configurable/device-group-paramer.svg" alt="" style="width: 45px; height: 45px" />
+          <div>{{ item.title }}</div>
+        </div>
+        <div :class="['list-item__title_I', { 'bg-2': type == 'I' && i == 1 }]" v-else-if="type == 'I' && i == 1">
+          <SvgIcon class="icon" size="40" name="device-paramer" /><div>{{ item.title }}</div>
+        </div>
+        <div :class="`list-item__title_${type}`" v-else>{{ item.title }}</div>
         <!-- 列表项的具体内容填充剩余宽度 -->
-          <div v-for="(ctx, j) in item.contents" :key="`vvhccdclc${j}`" :class="`list-item__content_${type}`">
-            <div class="list-item__label">{{ ctx.label }}</div>
-            <div class="list-item__info" :class="`list-item__info_${ctx.color}`">{{ ctx.info }}</div>
-            <div class="list-item__value" :class="`list-item__value_${ctx.color} list-item__value_${type}`">{{ ctx.value }}</div>
-          </div>
+        <div v-for="(ctx, j) in item.contents" :key="`vvhccdclc${j}`" :class="`list-item__content_${type}`">
+          <div class="list-item__label">{{ ctx.label }}</div>
+          <div class="list-item__info" :class="`list-item__info_${ctx.color}`">{{ ctx.info }}</div>
+          <div class="list-item__value" :class="`list-item__value_${ctx.color} list-item__value_${type}`">{{ ctx.value }}</div>
+        </div>
       </div>
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-  withDefaults(
-    defineProps<{
-      listConfig: {
-        title: string;
-        contents: {
-          value: string;
-          color: string;
-          label: string;
-          info: string;
-        }[];
+import { SvgIcon } from '/@/components/Icon';
+withDefaults(
+  defineProps<{
+    listConfig: {
+      title: string;
+      contents: {
+        value: string;
+        color: string;
+        label: string;
+        info: string;
       }[];
-      /** A B */
-      type: string;
-    }>(),
-    {
-      listConfig: () => [],
-      type: 'A',
-    }
-  );
+    }[];
+    /** A B */
+    type: string;
+  }>(),
+  {
+    listConfig: () => [],
+    type: 'A',
+  }
+);
 
-  //   defineEmits(['click']);
+//   defineEmits(['click']);
 </script>
 <style lang="less" scoped>
-    @import '@/design/theme.less';
-    @import '@/design/theme.less';
-    /* Timeline 相关的样式 */
-    @{theme-green} {
-      .list {
-      // --image-img-3: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-3.png);
-      // --image-img-7: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-7.png);
-      // --image-img-8: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-8.png);
-      --image-img-9: url(/@/assets/images/themify/green/home-container/configurable/firehome/img-9.png);
-      --image-list_bg_1: url(/@/assets/images/themify/green/home-container/configurable/dusthome/list_bg_1.png);
-      }
-    }
-    @{theme-deepblue} {
-      .list {
-      --image-img-3: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-3.png);
-      --image-img-7: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-7.png);
-      --image-img-8: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-8.png);
-      --image-img-9: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-9.png);
-      --image-list_bg_1: url(/@/assets/images/themify/deepblue/home-container/configurable/dusthome/list_bg_1.png);
-      }
-    }
-
-    .list {
-      --image-img-3: url(/@/assets/images/home-container/configurable/firehome/img-3.png);
-      --image-img-7: url(/@/assets/images/home-container/configurable/firehome/img-7.png);
-      --image-img-8: url(/@/assets/images/home-container/configurable/firehome/img-8.png);
-      --image-img-9: url(/@/assets/images/home-container/configurable/firehome/img-9.png);
-      --image-list_bg_1: url(/@/assets/images/home-container/configurable/dusthome/list_bg_1.png);
-      --image-list_bg_h_title: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-title.png);
-      --image-list_bg_h_border: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-border.png);
-      --image-list_bg_h_content: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-content.png);
-      --image-linear-gradient-1: linear-gradient(to right, #39a3ff50, #39a3ff00);
-      --image-linear-gradient-2: linear-gradient(to right, #3df6ff40, #3df6ff00);
-      --image-linear-gradient-3: linear-gradient(to right, #39deff15, #3977e500);
-      padding: 5px 20px;
-      position: relative;
-      width: 100%;
-      height: 100%;
-    }
-
-    .list-item_A {
-      position: relative;
-      height: 140px;
-      background-repeat: no-repeat;
-      background-image: var(--image-img-3);
-      background-size: auto 100%;
-      background-position: center;
-    }
-    .list-item__title_A {
-      position: absolute;
-      left: 41%;
-      // font-size: 14px;
-      top: 15px;
-    }
-    // .list-item__content_A {
-    //   position: absolute;
-    //   left: 35%;
-    //   top: 55px;
-    //   display: flex;
-    //   justify-content: space-evenly;
-    // }
-    .list-item__content_A:nth-of-type(2) {
-      position: absolute;
-      top: 15px;
-      left: 14%;
-      width: 22%;
-      text-align: center;
-      display: block;
-
-      .list-item__label {
-        font-size: 18px;
-        margin-bottom: 25px;
-      }
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 24px;
-      }
-    }
-    .list-item__content_A:nth-of-type(3) {
-      position: absolute;
-      left: 41%;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-      }
-    }
-    .list-item__content_A:nth-of-type(4) {
-      position: absolute;
-      left: 66%;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-      }
-    }
-    .list-item__content_A:nth-of-type(5) {
-      position: absolute;
-      left: 35%;
-      bottom: 10px;
-      display: flex;
-      align-items: center;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 20px;
-        margin-left: 5px;
-      }
-    }
-
-    .list-item_B {
-      // height: 155px;
-      background-repeat: no-repeat;
-      // background-size: 100% 100%;
-      // background-size: auto 100%;
-      background-size: 87% auto;
-      background-position: center;
-      background-image: var(--image-img-7);
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      text-align: center;
-      padding: 0 10%;
-      margin-top: 5px;
-      height: 33px;
-
-      .list-item__label {
-        font-size: 11px;
-      }
-      .list-item__value {
-        font-size: 18px;
-        margin-left: 5px;
-      }
-      .list-item__content_B {
-        height: 100%;
-        display: flex;
-        align-items: center;
-        flex-basis: 100px;
-        flex-grow: 1;
-      }
-      .list-item__title_B {
-        width: 40px;
-        text-align: center;
-        margin-right: 50px
-        // height: 30px;
-        // background-size: auto 80%;
-        // background-position: center;
-        // background-repeat: no-repeat;
-      }
-      .list-item__info {
-        display: none;
-      }
-    }
-
-    .list_C {
-      padding: 5px 10px;
-    }
-
-    .list__wrapper_C {
-      display: flex;
-      justify-content: space-between;
-      flex-wrap: wrap;
-    }
-
-    .list-item_C {
-      position: relative;
-      height: 140px;
-      width: 200px;
-      background-repeat: no-repeat;
-      background-image: var(--image-img-8);
-      background-size: 100% 100%;
-      background-position: left center;
-    }
-    .list-item__title_C {
-      position: absolute;
-      left: 99px;
-      // font-size: 14px;
-      top: 15px;
-    }
-    .list-item__content_C:nth-of-type(2) {
-      position: absolute;
-      top: 15px;
-      left: 19px;
-      width: 30%;
-      text-align: center;
-      display: block;
-
-      .list-item__label {
-        // font-size: 18px;
-        margin-bottom: 25px;
-      }
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
-    .list-item__content_C:nth-of-type(3) {
-      position: absolute;
-      left: 99px;
-      top: 55px;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
-
-
-    .list_D {
-      padding: 5px 10px;
-    }
-
-    .list__wrapper_D {
-      display: flex;
-      justify-content: space-between;
-      flex-wrap: wrap;
-    }
-
-    .list-item_D {
-      position: relative;
-      height: 110px;
-      width: 200px;
-      background-repeat: no-repeat;
-      background-image: var(--image-img-9);
-      background-size: 100% auto;
-      background-position: center top;
-      text-align: center;
-      margin-bottom: 20px;
-    }
-    .list-item__title_D {
-      position: absolute;
-      width: 100%;
-      bottom: 0;
-      font-size: 16px;
-      font-weight: bold;
-    }
-    .list-item__content_D:nth-of-type(2) {
-      position: absolute;
-      top: 10%;
-      left: 10%;
-      width: 30%;
-      text-align: center;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
-    .list-item__content_D:nth-of-type(3) {
-      position: absolute;
-      top: 10%;
-      right: 10%;
-      width: 30%;
-      text-align: center;
-
-      .list-item__info {
-        display: none;
-      }
-      .list-item__value {
-        font-size: 18px;
-      }
-    }
-
-  .list_E {
-    padding: 5px 10px;
+@import '@/design/theme.less';
+@import '@/design/theme.less';
+/* Timeline 相关的样式 */
+@{theme-green} {
+  .list {
+    --image-img-9: url(/@/assets/images/themify/green/home-container/configurable/firehome/img-9.png);
+    --image-list_bg_1: url(/@/assets/images/themify/green/home-container/configurable/dusthome/list_bg_1.png);
   }
-
-  .list__wrapper_E {
+}
+@{theme-deepblue} {
+  .list {
+    --image-img-3: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-3.png);
+    --image-img-7: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-7.png);
+    --image-img-8: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-8.png);
+    --image-img-9: url(/@/assets/images/themify/deepblue/home-container/configurable/firehome/img-9.png);
+    --image-list_bg_1: url(/@/assets/images/themify/deepblue/home-container/configurable/dusthome/list_bg_1.png);
+  }
+}
+
+.list {
+  --image-img-3: url(/@/assets/images/home-container/configurable/firehome/img-3.png);
+  --image-img-7: url(/@/assets/images/home-container/configurable/firehome/img-7.png);
+  --image-img-8: url(/@/assets/images/home-container/configurable/firehome/img-8.png);
+  --image-img-9: url(/@/assets/images/home-container/configurable/firehome/img-9.png);
+  --image-list_bg_1: url(/@/assets/images/home-container/configurable/dusthome/list_bg_1.png);
+  --image-list_bg_h_title: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-title.png);
+  --image-list_bg_h_border: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-border.png);
+  --image-list_bg_h_content: url(/@/assets/images/home-container/configurable/minehome/list-bg-h-content.png);
+  --image-linear-gradient-1: linear-gradient(to right, #39a3ff50, #39a3ff00);
+  --image-linear-gradient-2: linear-gradient(to right, #3df6ff40, #3df6ff00);
+  --image-linear-gradient-3: linear-gradient(to right, #39deff15, #3977e500);
+  --image-linear-gradient-4: linear-gradient(to right, #091d34, #0b223b);
+  padding: 5px 20px;
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.list-item_A {
+  position: relative;
+  height: 140px;
+  background-repeat: no-repeat;
+  background-image: var(--image-img-3);
+  background-size: auto 100%;
+  background-position: center;
+}
+.list-item__title_A {
+  position: absolute;
+  left: 41%;
+  // font-size: 14px;
+  top: 15px;
+}
+.list-item__content_A:nth-of-type(2) {
+  position: absolute;
+  top: 15px;
+  left: 14%;
+  width: 22%;
+  text-align: center;
+  display: block;
+
+  .list-item__label {
+    font-size: 18px;
+    margin-bottom: 25px;
+  }
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 24px;
+  }
+}
+.list-item__content_A:nth-of-type(3) {
+  position: absolute;
+  left: 41%;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 20px;
+  }
+}
+.list-item__content_A:nth-of-type(4) {
+  position: absolute;
+  left: 66%;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 20px;
+  }
+}
+.list-item__content_A:nth-of-type(5) {
+  position: absolute;
+  left: 35%;
+  bottom: 10px;
+  display: flex;
+  align-items: center;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 20px;
+    margin-left: 5px;
+  }
+}
+
+.list-item_B {
+  background-repeat: no-repeat;
+  background-size: 87% auto;
+  background-position: center;
+  background-image: var(--image-img-7);
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  text-align: center;
+  padding: 0 10%;
+  margin-top: 5px;
+  height: 33px;
+
+  .list-item__label {
+    font-size: 11px;
+  }
+  .list-item__value {
+    font-size: 18px;
+    margin-left: 5px;
+  }
+  .list-item__content_B {
+    height: 100%;
     display: flex;
-    justify-content: space-between;
-    flex-wrap: wrap;
-    padding: 0 10px;
+    align-items: center;
+    flex-basis: 100px;
+    flex-grow: 1;
   }
-
-  .list-item_E {
-    position: relative;
-    height: 104px;
-    width: 188px;
-    background-repeat: no-repeat;
-    background-image: var(--image-list_bg_1);
-    background-size: 100% auto;
-    background-position: center top;
+  .list-item__title_B {
+    width: 40px;
     text-align: center;
-    margin-bottom: 20px;
+    margin-right: 50px;
   }
-  .list-item__title_E {
-    // position: absolute;
-    width: 100%;
-    // top: 0;
+  .list-item__info {
+    display: none;
+  }
+}
+
+.list_C {
+  padding: 5px 10px;
+}
+
+.list__wrapper_C {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.list-item_C {
+  position: relative;
+  height: 140px;
+  width: 200px;
+  background-repeat: no-repeat;
+  background-image: var(--image-img-8);
+  background-size: 100% 100%;
+  background-position: left center;
+}
+.list-item__title_C {
+  position: absolute;
+  left: 99px;
+  top: 15px;
+}
+.list-item__content_C:nth-of-type(2) {
+  position: absolute;
+  top: 15px;
+  left: 19px;
+  width: 30%;
+  text-align: center;
+  display: block;
+
+  .list-item__label {
+    margin-bottom: 25px;
+  }
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+.list-item__content_C:nth-of-type(3) {
+  position: absolute;
+  left: 99px;
+  top: 55px;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+
+.list_D {
+  padding: 5px 10px;
+}
+
+.list__wrapper_D {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.list-item_D {
+  position: relative;
+  height: 110px;
+  width: 200px;
+  background-repeat: no-repeat;
+  background-image: var(--image-img-9);
+  background-size: 100% auto;
+  background-position: center top;
+  text-align: center;
+  margin-bottom: 20px;
+}
+.list-item__title_D {
+  position: absolute;
+  width: 100%;
+  bottom: 0;
+  font-size: 16px;
+  font-weight: bold;
+}
+.list-item__content_D:nth-of-type(2) {
+  position: absolute;
+  top: 10%;
+  left: 10%;
+  width: 30%;
+  text-align: center;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+.list-item__content_D:nth-of-type(3) {
+  position: absolute;
+  top: 10%;
+  right: 10%;
+  width: 30%;
+  text-align: center;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
+    font-size: 18px;
+  }
+}
+
+.list_E {
+  padding: 5px 10px;
+}
+
+.list__wrapper_E {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+  padding: 0 10px;
+}
+
+.list-item_E {
+  position: relative;
+  height: 104px;
+  width: 188px;
+  background-repeat: no-repeat;
+  background-image: var(--image-list_bg_1);
+  background-size: 100% auto;
+  background-position: center top;
+  text-align: center;
+  margin-bottom: 20px;
+}
+.list-item__title_E {
+  width: 100%;
+  font-size: 16px;
+  font-weight: bold;
+  margin-top: 10px;
+}
+.list-item__content_E:nth-of-type(2) {
+  position: absolute;
+  top: 40%;
+  left: 5%;
+  text-align: left;
+
+  .list-item__info {
+    display: none;
+  }
+  .list-item__value {
     font-size: 16px;
-    font-weight: bold;
-    margin-top: 10px;
   }
-  .list-item__content_E:nth-of-type(2) {
-    position: absolute;
-    top: 40%;
-    left: 5%;
-    text-align: left;
-
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      font-size: 16px;
-    }
+}
+.list-item__content_E:nth-of-type(3) {
+  position: absolute;
+  top: 40%;
+  right: 5%;
+  text-align: right;
+
+  .list-item__info {
+    display: none;
   }
-  .list-item__content_E:nth-of-type(3) {
-    position: absolute;
-    top: 40%;
-    right: 5%;
-    text-align: right;
-
-    .list-item__info {
-      display: none;
-    }
-    .list-item__value {
-      font-size: 16px;
-    }
+  .list-item__value {
+    font-size: 16px;
   }
-
-  .list-item_F {
-      // height: 155px;
-      background-repeat: no-repeat;
-      background-size: 100% 100%;
-      // background-size: auto 100%;
-      background-position: center;
-      background-image: @vent-gas-list-item-bg-img;
-      display: flex;
-      align-items: center;
-      // justify-content: space-between;
-      // text-align: center;
-      padding: 0 5%;
-      margin-top: 5px;
-      height: 50px;
-
-      .list-item__title_F {
-        flex-basis: 25%;
-      }
-      .list-item__content_F {
-        flex-basis: 25%;
-      }
-      .list-item__label::after {
-        content: ':';
-      }
-      .list-item__value {
-        font-size: 18px;
-        margin-left: 5px;
-        font-weight: bold;
-      }
-      .list-item__content_F {
-        display: flex;
-        align-items: center;
-      }
-      .list-item__info {
-        display: none;
-      }
+}
+
+.list-item_F {
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  // background-size: auto 100%;
+  background-position: center;
+  background-image: @vent-gas-list-item-bg-img;
+  display: flex;
+  align-items: center;
+  padding: 0 5%;
+  margin-top: 5px;
+  height: 50px;
+
+  .list-item__title_F {
+    flex-basis: 25%;
   }
-
-  .list-item_G {
-      // height: 155px;
-      // background-repeat: no-repeat;
-      // background-size: 100% 100%;
-      // background-position: center;
-      // justify-content: space-between;
-      // text-align: center;
-      margin-top: 5px;
-
-      .list-item__title_G {
-        position: relative;
-        width: 100%;
-        height: 16px;
-        margin-top: 10px;
-        margin-bottom: 5px;
-        padding-left: 8px;
-        top: -2px;
-        background-image: var(--image-linear-gradient-1);
-        &::before{
-          position: absolute;
-          content: '';
-          width: 100%;
-          height: 100%;
-          top: 4px;
-          left: 0;
-          background-image: var(--image-linear-gradient-2);
-        }
-      }
-      .list-item__content_G {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        padding: 4px;
-        margin: 8px 0;
-        background-image: var(--image-linear-gradient-3);
-      }
-  }
-  .list_H {
-    padding: 0 10px;
+  .list-item__content_F {
+    flex-basis: 25%;
   }
-
-  .list__wrapper_H {
+  .list-item__label::after {
+    content: ':';
+  }
+  .list-item__value {
+    font-size: 18px;
+    margin-left: 5px;
+    font-weight: bold;
+  }
+  .list-item__content_F {
     display: flex;
-    justify-content: space-between;
-    flex-wrap: wrap;
-    gap: 20px;
+    align-items: center;
   }
+  .list-item__info {
+    display: none;
+  }
+}
+
+.list-item_G {
+  margin-top: 5px;
 
-  .list-item_H {
+  .list-item__title_G {
     position: relative;
-    height: 80px;
-    width: calc((100% - 20px) / 2);
-    background-image: var(--image-list_bg_h_border);
-    background-size: 100% 100%;
-    background-repeat: no-repeat;
-    background-position: center top;
-    margin-top: 25px;
-  }
-  .list-item__title_H {
-    position: absolute;
-    top: -26px;
-    left: 0;
     width: 100%;
-    height: 32px;
-    line-height: 32px;
-    background-repeat: no-repeat;
-    background-image: var(--image-list_bg_h_title);
-    background-size: 100% auto;
-    background-position: center top;
-    font-size: 16px;
-    text-align: center;
+    height: 16px;
+    margin-top: 10px;
+    margin-bottom: 5px;
+    padding-left: 8px;
+    top: -2px;
+    background-image: var(--image-linear-gradient-1);
+    &::before {
+      position: absolute;
+      content: '';
+      width: 100%;
+      height: 100%;
+      top: 4px;
+      left: 0;
+      background-image: var(--image-linear-gradient-2);
+    }
   }
-
-  .list-item__content_H {
+  .list-item__content_G {
     display: flex;
-    flex-direction: row;
-    align-items: center;
     justify-content: space-between;
-    background-repeat: no-repeat;
-    background-image: var(--image-list_bg_h_content);
-    background-size: 100% auto;
-    background-position: center top;
-    margin: 13px 10px;
+    align-items: center;
+    padding: 4px;
+    margin: 8px 0;
+    background-image: var(--image-linear-gradient-3);
   }
-
-    // .list-item__title_B_O2 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/O₂.png);
-    // }
-    // .list-item__title_B_CH4 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CH₄.png);
-    // }
-    // .list-item__title_B_CO {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CO.png);
-    // }
-    // .list-item__title_B_CO2 {
-    //   background-image: url(/@/assets/images/home-container/configurable/firehome/CO₂.png);
-    // }
-
-    // .list-item__label {
-    //   flex-basis: 55%;
-    // }
-    // .list-item__info {
-    //   flex-grow: 1;
-    // }
-    // .list-item__value {
-    //   flex-basis: 30%;
-    // }
-    .list-item__value_red {
-      color: red;
-    }
-    .list-item__value_orange {
-      color: orange;
-    }
-    .list-item__value_yellow {
-      color: yellow;
-    }
-    .list-item__value_green {
-      color: yellowgreen;
-    }
-    .list-item__value_blue {
-      color: @vent-table-action-link;
-    }
-    .list-item__value_white {
-      color: white;
-    }
-    .gallery-item__value_lightblue {
-      color: @vent-configurable-home-light-border;
-    }
+}
+.list_H {
+  padding: 0 10px;
+}
+
+.list__wrapper_H {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+  gap: 20px;
+}
+
+.list-item_H {
+  position: relative;
+  height: 80px;
+  width: calc((100% - 20px) / 2);
+  background-image: var(--image-list_bg_h_border);
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center top;
+  margin-top: 25px;
+}
+.list-item__title_H {
+  position: absolute;
+  top: -26px;
+  left: 0;
+  width: 100%;
+  height: 32px;
+  line-height: 32px;
+  background-repeat: no-repeat;
+  background-image: var(--image-list_bg_h_title);
+  background-size: 100% auto;
+  background-position: center top;
+  font-size: 16px;
+  text-align: center;
+}
+
+.list-item__content_H {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  background-repeat: no-repeat;
+  background-image: var(--image-list_bg_h_content);
+  background-size: 100% auto;
+  background-position: center top;
+  margin: 13px 10px;
+}
+.list-item_I {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 5px;
+}
+.list-item__title_I {
+  position: relative;
+  width: 100%;
+  height: 25px;
+  margin-top: 10px;
+  margin-bottom: 5px;
+  padding-left: 8px;
+  top: -2px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.list-item__title_I.bg-1 {
+  background-image: var(--image-linear-gradient-1);
+}
+.list-item__title_I.bg-2 {
+  background-image: var(--image-linear-gradient-2);
+}
+.list-item__title_I .icon {
+  display: inline-block;
+  flex-shrink: 0;
+  vertical-align: middle;
+}
+.list-item__title_I > div {
+  flex: 1;
+  line-height: 25px;
+  font-size: 14px;
+}
+.list-item__content_I {
+  width: 50%;
+  display: flex;
+  justify-content: space-around;
+  align-items: center;
+  padding: 4px;
+  margin: 8px 0;
+  background-image: var(--image-linear-gradient-4);
+}
+.list-item__value_red {
+  color: red;
+}
+.list-item__value_orange {
+  color: orange;
+}
+.list-item__value_yellow {
+  color: yellow;
+}
+.list-item__value_green {
+  color: yellowgreen;
+}
+.list-item__value_blue {
+  color: @vent-table-action-link;
+}
+.list-item__value_white {
+  color: white;
+}
+.gallery-item__value_lightblue {
+  color: @vent-configurable-home-light-border;
+}
 </style>

+ 8 - 5
src/views/vent/home/configurable/components/detail/TimelineList.vue

@@ -4,7 +4,7 @@
     <div v-for="item in listConfig" :key="item.prop" class="flex items-center timeline-item">
       <div class="timeline-item__icon" :class="`timeline-item__icon_${item.color}`"></div><div class="timeline-item__dot"></div>
       <div class="timeline-item__label">{{ item.label }}</div>
-      <div :class="`timeline-item__value_${item.color}`">
+      <div class="timeline-item__value" :class="`timeline-item__value_${item.color}`">
         {{ item.value }}
       </div>
     </div>
@@ -67,14 +67,14 @@
   .timeline-item__icon {
     width: 33px;
     height: 35px;
-    margin-left: 50px;
+    margin-left: 35px;
     background-repeat: no-repeat;
     background-position: center center;
   }
   .timeline-item__dot {
     width: 10px;
     height: 10px;
-    margin-left: 70px;
+    margin-left: 55px;
     background-color: @vent-gas-primary-bg;
     border-radius: 5px;
     position: relative;
@@ -91,7 +91,10 @@
   }
   .timeline-item__label {
     width: 100px;
-    margin-left: 70px;
+    margin-left: 55px;
+  }
+  .timeline-item__value {
+    margin-left: 55px;
   }
   .timeline-item__value_red {
     color: red;
@@ -119,7 +122,7 @@
     width: 2px;
     height: 180px;
     top: 20px;
-    left: 162px;
+    left: 132px;
     background-image: @vent-configurable-home-timeline;
   }
 </style>

+ 167 - 0
src/views/vent/home/configurable/components/preset/nitrogenBtnList.vue

@@ -0,0 +1,167 @@
+<template>
+  <div class="buttons-wrapper">
+    <div class="button-group-container" v-for="(btn, index) in buttonList" :key="index" :class="index % 2 === 0 ? 'green-item' : 'blue-item'">
+      <div class="button-name">{{ btn.label }}</div>
+      <div class="radio-card" @click="toggleStatus(btn, index)">
+        <a-radio class="radio-dot" :checked="btn.isRunning"></a-radio>
+        <span class="radio-text">
+          {{ btn.isRunning ? '开启' : '关闭' }}
+        </span>
+      </div>
+    </div>
+  </div>
+  <ConfirmModal v-model:visible="modalVisible" @cancel="handleCancel" class="btn-confirm-modal">
+    <SvgIcon class="icon" size="34" name="warning-icon-gas" />
+    <span> {{ currentButton?.content }} </span>
+    <inputPassword v-model:value="password" placeholder="请输入密码校验(必填)" class="pswInput" />
+    <template #footer>
+      <a-button type="primary" @click="handleConfirm">确认</a-button>
+      <a-button type="primary" @click="handleCancel">取消</a-button>
+    </template>
+  </ConfirmModal>
+</template>
+
+<script setup lang="ts">
+import ConfirmModal from '@/views/vent/gas/components/modal/confirmModal.vue';
+import { ref, inject } from 'vue';
+import { InputPassword } from 'ant-design-vue';
+let props = defineProps({
+  buttonList: {
+    type: Array,
+    default: () => {
+      return [];
+    },
+  },
+});
+// 定义按钮项的类型
+interface ButtonItem {
+  isShowInput: boolean;
+  inputContent: string;
+  inputValue: string | number;
+  value: string | number;
+  label: string;
+  content: string;
+  // 可以根据实际需求添加其他属性
+}
+const password = ref('');
+const inputWarn = ref('密码错误请重新输入');
+const modalVisible = ref(false);
+const currentButton = ref<ButtonItem | null>(null); // 记录当前点击的按钮
+const toggleStatus = (btn: ButtonItem, index) => {
+  modalVisible.value = !modalVisible.value;
+  props.buttonList[index].isRunning = !props.buttonList[index].isRunning;
+  const currentBtn = props.buttonList[index];
+  console.log(`【${currentBtn.name}】当前状态:${currentBtn.isRunning ? '开启' : '关闭'}`);
+  // 根据当前状态执行对应的控制功能
+  // if (currentBtn.isRunning) {
+  // } else {
+  // }
+};
+// 注入祖父组件提供的处理函数
+const handleButtonConfirm = inject<(button: ButtonItem) => void>(
+  'handleButtonConfirm',
+  () => console.warn('未提供handleButtonConfirm函数') // 默认值,避免报错
+);
+const handleConfirm = () => {
+  if (!currentButton.value) return;
+  password.value = '';
+  inputWarn.value = '';
+  handleButtonConfirm(currentButton.value);
+  modalVisible.value = false;
+};
+const handleCancel = () => {
+  console.log('取消!!!!!!!!');
+  password.value = '';
+  inputWarn.value = '';
+  modalVisible.value = false;
+};
+</script>
+
+<style lang="less" scoped>
+@font-face {
+  font-family: 'douyuFont';
+  src: url('../../../../assets/font/douyuFont.otf');
+}
+/* 外层容器:适配多按钮组的布局(网格/横向排列可选) */
+.buttons-wrapper {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  margin-top: 10px;
+}
+
+/* 单个按钮组容器 */
+.button-group-container {
+  height: 80px;
+  width: 28%;
+  margin: 6px;
+  text-align: center;
+  display: flex;
+  flex-direction: column;
+}
+.blue-item {
+  background: url('/@/assets/images/home-container/configurable/blueBtn.png') no-repeat;
+  background-size: 100% 100%;
+  .button-name {
+    margin-top: 18px;
+    color: #05beee;
+    font-family: 'douyuFont';
+    font-size: 12px;
+  }
+}
+.green-item {
+  background: url('/@/assets/images/home-container/configurable/greenBtn.png') no-repeat;
+  background-size: 100% 100%;
+  .button-name {
+    margin-top: 18px;
+    color: #2bfedc;
+    font-family: 'douyuFont';
+    font-size: 12px;
+  }
+}
+
+.radio-card {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  cursor: pointer;
+  margin-left: 27px;
+  margin-top: 5px;
+}
+
+.radio-dot {
+  margin-left: 10px;
+  margin-right: 5px;
+}
+
+.radio-text {
+  color: #fff;
+  font-size: 12px;
+}
+
+.radio-dot input {
+  display: none;
+}
+.btn-confirm-modal {
+  .zxm-modal-content {
+    height: 240px !important;
+    background-size: 100% 100%;
+    .zxm-modal-body {
+      height: 187px !important;
+      padding-top: 33px !important;
+    }
+    .zxm-modal-footer {
+      text-align: center;
+    }
+  }
+}
+.pswInput {
+  margin-top: 55px;
+  width: 50%;
+  margin-left: 25% !important;
+}
+.warn-text {
+  color: red;
+  font-size: 15px;
+}
+</style>

+ 1 - 1
src/views/vent/home/configurable/configurable.data.bd.ts

@@ -216,7 +216,7 @@ export const testConfigBDDust: Config[] = [
           readFrom: '',
           legend: { show: false },
           xAxis: [{ show: true }],
-          yAxis: [{ show: true, name: '浓度', position: 'left' }],
+          yAxis: [{ show: true, name: '浓度(%)', position: 'left' }],
           series: [
             {
               label: '${strinstallpos}',

+ 3 - 1
src/views/vent/home/configurable/dustBD.vue

@@ -86,7 +86,9 @@
   // }
   onMounted(() => {
     fetchConfigs('BD_dust').then(() => {
-      configs.value = testConfigBDDust;
+      if (!configs.value.length) {
+        configs.value = testConfigBDDust;
+      }
 
       getDisHome({
         dataList: devicesTypes.value.concat('dustAllMineWarn').join(','),

+ 3 - 1
src/views/vent/home/configurable/fireBD.vue

@@ -112,7 +112,9 @@
   // }
   onMounted(() => {
     fetchConfigs('BD_fire').then(() => {
-      configs.value = testConfigBDFire;
+      if (!configs.value.length) {
+        configs.value = testConfigBDFire;
+      }
 
       getDisHome({
         dataList: devicesTypes.value.concat('fireAllMineWarn').join(','),

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

@@ -64,15 +64,17 @@
           </div>
         </div>
       </template>
-      <div
-        v-if="sysDataType === 'all'"
-        :class="{ 'realtime-mode': isDataRealTime }"
-        alt="切换数据模式"
-        class="switch-button report-mode right-525px"
-        @click="switchDataMode"
-      ></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>
+      <div class="switch-button-group">
+        <div
+          v-if="sysDataType === 'all'"
+          :class="{ 'realtime-mode': isDataRealTime }"
+          alt="切换数据模式"
+          class="switch-button report-mode"
+          @click="switchDataMode"
+        ></div>
+        <div v-if="hasPermission('show:modalChange')" class="switch-button icon-goto2D" @click="goMicroApp('2.5')"></div>
+        <div class="switch-button icon-goto3D" @click="goMicroApp('3')"></div>
+      </div>
     </template>
   </div>
 </template>
@@ -92,10 +94,12 @@
   import { useRoute, useRouter } from 'vue-router';
   import { useGlobSetting } from '/@/hooks/setting';
   import { testConfigVent182 } from './configurable.data';
+  import { usePermission } from '/@/hooks/web/usePermission';
 
   const { sysDataType = 'monitor', title = '智能通风管控系统' } = useGlobSetting();
   const { configs, isOriginal, isCommon, fetchConfigs } = useInitConfigs();
   const { mainTitle, enhancedConfigs, hiddenList, data, updateData, updateEnhancedConfigs } = useInitPage(title);
+  const { hasPermission } = usePermission();
   const route = useRoute();
   const router = useRouter();
   const isDataRealTime = ref(sysDataType === 'monitor');
@@ -200,15 +204,17 @@
 
     .top-bg {
       width: 100%;
-      height: 56px;
+      height: 48px;
       background: var(--image-modal-top) no-repeat center;
       position: absolute;
       z-index: 1;
+      background-size: 100% 100%;
       .main-title {
-        height: 56px;
+        height: 48px;
         font-family: 'douyuFont';
         font-size: 20px;
         letter-spacing: 2px;
+        padding-top: 8px;
         display: flex;
         justify-content: center;
         align-items: center;
@@ -257,6 +263,13 @@
       border-bottom: 2px solid @vent-configurable-home-light-border;
     }
 
+    .switch-button-group {
+      display: flex;
+      position: absolute;
+      right: 430px;
+      top: 580px;
+    }
+
     .switch-button {
       width: 50px;
       height: 50px;
@@ -264,10 +277,11 @@
       padding: 8px;
       backdrop-filter: blur(10px);
       background-color: rgba(30, 58, 117, 0.418);
-      position: absolute;
+      //position: absolute;
       // right: 5px;
       bottom: 300px;
       z-index: 5;
+      margin: 0 5px;
     }
     .report-mode {
       &::after {

+ 1 - 0
src/views/vent/monitorManager/deviceMonitor/index.vue

@@ -84,6 +84,7 @@
     } else {
       isShow.value = false;
     }
+
     actions.onGlobalStateChange((newState) => {
       const pageObj = newState['pageObj'];
       if (pageObj && pageObj.pageType) {

+ 32 - 5
src/views/vent/monitorManager/gateMonitor/components/CarDamageTable.vue

@@ -22,16 +22,23 @@
       <a-table size="small" :dataSource="dataSource" :columns="carColumns" :pagination="pagination"
         :scroll="{ y: 200, }" @change="pageChange">
         <template #bodyCell="{ column, text }">
-            <template v-if="column.dataIndex=='carNo' || column.dataIndex=='distance' || column.dataIndex=='photo' || column.dataIndex=='captureTime'">
-            <div>{{ text  ? text : '-' }}</div>
+          <template
+            v-if="column.dataIndex == 'carNo' || column.dataIndex == 'distance' || column.dataIndex == 'photo' || column.dataIndex == 'captureTime'">
+            <div>{{ text ? text : '-' }}</div>
           </template>
           <template v-if="column.dataIndex == 'type'">
             <div>{{ text == '1' ? '风门损坏' : '正常开闭' }}</div>
           </template>
+          <template v-if="column.dataIndex == 'photoC'">
+            <img :src="text" alt="" class="photo_zp" @click="preViewImg(text)">
+          </template>
         </template>
       </a-table>
     </div>
-
+    <!-- 预览抓拍图像 -->
+    <a-modal v-model:visible="visiblePhoto" width="850px" :title="titlePhoto" :footer="null" centered destroyOnClose>
+      <img :src="imgSrc" alt="">
+    </a-modal>
   </div>
 </template>
 
@@ -51,7 +58,6 @@ let props = defineProps({
 })
 
 let type = ref('0')
-
 let dataSource = ref<any[]>([])
 //分页参数配置
 let pagination = reactive({
@@ -62,11 +68,22 @@ let pagination = reactive({
   showSizeChanger: true, // 是否可改变每页显示条数
   pageSizeOptions: ['10', '20', '50'], // 可选的每页显示条数
 });
+let visiblePhoto = ref(false)
+let titlePhoto = ref('抓拍图像预览')
+let imgSrc = ref('')//抓拍图像路径
+
+
 
 //查询列表
 async function getCarList() {
   let res = await props.deviceListApi({ gateIds: props.gateId, type: type.value, pageNo: pagination.current, pageSize: pagination.pageSize })
-  dataSource.value = res.records || []
+  const remoteUrl = import.meta.env.DEV ? 'http://182.92.126.35' : 'http://' + window.location.hostname;
+  dataSource.value = res.records.map(el => {
+    return {
+      photoC: `${remoteUrl}:9999/sys/common/static/${el.photo.substring(el.photo.indexOf('/gate_monitor'))}`,
+      ...el
+    }
+  })
   pagination.total = res.total
 }
 function pageChange(val) {
@@ -85,6 +102,11 @@ function getReset() {
   pagination.current = 1
   getCarList()
 }
+//预览抓拍图像
+function preViewImg(img) {
+  visiblePhoto.value = true
+  imgSrc.value = img
+}
 onMounted(() => {
   getCarList()
 })
@@ -111,5 +133,10 @@ onMounted(() => {
     background-color: #ffffff00;
     color: #fff;
   }
+
+  .photo_zp {
+    width: 120px;
+    cursor: pointer;
+  }
 }
 </style>

+ 2 - 7
src/views/vent/monitorManager/gateMonitor/gate.data.ts

@@ -128,12 +128,7 @@ export let typeList:any[] =[
 export const carColumns: BasicColumn[] = [
   {
     title: '安装位置',
-    dataIndex: 'strInstallPos',
-    align: 'center'
-  },
-  {
-    title: '设备名称',
-    dataIndex: 'strName',
+    dataIndex: 'gateInstallPos',
     align: 'center'
   },
   {
@@ -153,7 +148,7 @@ export const carColumns: BasicColumn[] = [
   },
   {
     title: '抓拍图像路径',
-    dataIndex: 'photo',
+    dataIndex: 'photoC',
     align: 'center'
   },
   {

+ 90 - 0
src/views/vent/monitorManager/nitrogenMonitor/index.vue

@@ -0,0 +1,90 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="spray-wrapper">
+    <BasicMonitoring
+      main-title="智能注氮管控系统"
+      :main-config="{
+        operations: [],
+        configs: configs,
+      }"
+      monitor-type="nitrogenMonitor"
+      :monitor-history-config="{}"
+      :handler-history-config="{}"
+      :alarm-history-config="{}"
+      strtype="sys_nitrogen"
+    >
+      <template #default="{ monitorData }">
+        <div id="nitrogen2D" 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; pointer-events: none"> </div> -->
+          <!-- <div class="nitrogen2D"></div> -->
+          <modelTip :statusData="monitorData"></modelTip>
+        </div>
+      </template>
+    </BasicMonitoring>
+  </div>
+</template>
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import BasicMonitoring from '/@/components/vent/BasicMonitoring.vue';
+import { useInitConfigs, useInitPage } from '../../home/configurable/hooks/useInit';
+import { nitrogenConfigs } from './nitrogen.data';
+import modelTip from './model-tip.vue';
+import { list } from './nitorgen.api';
+
+const loading = ref(false);
+const { configs, fetchConfigs } = useInitConfigs();
+const { updateEnhancedConfigs, updateData } = useInitPage('智能注氮管控系统');
+const monitorData = ref<any[]>([]);
+function refresh() {
+  // fetchConfigs(isDataRealTime.value ? 'vent_realtime' : 'vent').then(() => {
+  fetchConfigs('nitrogen').then(() => {
+    // configs.value = nitrogenConfigs;
+    updateEnhancedConfigs(configs.value);
+    list({ devicetype: 'nitrogen', pagetype: 'normal' }).then(updateData, (res) => {
+      if (res && res.msgTxt[0]) {
+        const dataList: any[] = [];
+        if (res.msgTxt[0].datalist && res.msgTxt[0].datalist.length > 0) {
+          res.msgTxt[0].datalist.forEach((data: any) => {
+            const readData = data.readData;
+            data = Object.assign(data, readData);
+            dataList.push(data);
+          });
+          monitorData.value = res.msgTxt[0].dataList;
+        }
+      }
+    });
+  });
+}
+
+function initInterval() {
+  setInterval(() => {
+    refresh();
+  }, 60000);
+}
+onMounted(() => {
+  refresh();
+  initInterval();
+});
+</script>
+
+<style lang="less" scoped>
+.spray-wrapper {
+  width: 100%;
+  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');
+}
+.spray-wrapper :deep(.list-item_N:nth-child(1)) {
+  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n5.png');
+}
+.spray-wrapper :deep(.list-item_N:nth-child(2)) {
+  background-image: url('/@/assets/images/home-container/configurable/minehome/list-bg-n6.png');
+}
+</style>

+ 279 - 0
src/views/vent/monitorManager/nitrogenMonitor/model-tip.vue

@@ -0,0 +1,279 @@
+<template>
+  <div class="model-tip">
+    <div>
+      <div class="model-name-zdj1">1#</div>
+      <div class="model-name-zdj2">2#</div>
+      <div class="model-name-zdj3">3#</div>
+      <!-- 累计流量 -->
+      <div class="zdj-ll">
+        <div class="cq-zdj-item">
+          <span class="text-label">累计流量(Nm³):</span>
+          <span class="text-val">{{ statusData.readData['Fan1ljll'] }}</span>
+        </div>
+      </div>
+      <div class="zdj-ll1">
+        <div class="cq-zdj-item">
+          <span class="text-label">累计流量(Nm³):</span>
+          <span class="text-val">{{ statusData.readData['Fan2ljll'] }}</span>
+        </div>
+      </div>
+      <!-- 加热器温度 -->
+      <div class="jrq1">
+        <div class="cq-zdj-item">
+          <span class="text-label">加热器</span>
+          <span class="text-val">{{ statusData.readData['Fan1jrqTemp'] }}℃</span>
+        </div>
+      </div>
+      <div class="jrq2">
+        <div class="cq-zdj-item">
+          <span class="text-val">{{ statusData.readData['Fan2jrqTemp'] }}℃</span>
+        </div>
+      </div>
+      <!-- 滤芯 -->
+      <div class="filter">
+        <div class="cq-zdj-item">
+          <span class="text-label">滤芯1</span>
+          <span class="text-val">{{ statusData.readData['Fan1lxyc'] }}MPa</span>
+        </div>
+      </div>
+      <div class="filter2">
+        <div class="cq-zdj-item">
+          <span class="text-label">滤芯2</span>
+          <span class="text-val">{{ statusData.readData['Fan2lxyc'] }}MPa</span>
+        </div>
+      </div>
+      <!-- 活性炭 -->
+      <div class="hxt1">
+        <span class="text-label">活性炭</span>
+      </div>
+      <div class="hxt2">
+        <span class="text-label">活性炭</span>
+      </div>
+      <!-- 上方浓度  流量  氮气压力  氮气温度 -->
+      <div class="nd1">
+        <span class="text-label">浓度</span>
+        <span class="text-val">{{ statusData.readData['Fan1dqnd'] }}%</span>
+      </div>
+      <div class="ll1">
+        <span class="text-label">流量</span>
+        <span class="text-val">{{ statusData.readData['Fan1ll'] }}Nm³/h</span>
+      </div>
+      <div class="dqyl1">
+        <span class="text-label">氮气压力</span>
+        <span class="text-val">{{ statusData.readData['Fan1dqyl'] }}MPa</span>
+      </div>
+      <div class="dqwd1">
+        <span class="text-label">氮气温度</span>
+        <span class="text-val">{{ statusData.readData['Fan1dqwd'] }}℃</span>
+      </div>
+      <!-- 底部浓度  流量  氮气压力  氮气温度 -->
+      <div class="nd">
+        <span class="text-label">浓度</span>
+        <span class="text-val">{{ statusData.readData['Fan2dqnd'] }}%</span>
+      </div>
+      <div class="ll">
+        <span class="text-label">流量</span>
+        <span class="text-val">{{ statusData.readData['Fan2ll'] }}Nm³/h</span>
+      </div>
+      <div class="dqyl">
+        <span class="text-label">氮气压力</span>
+        <span class="text-val">{{ statusData.readData['Fan2dqyl'] }}MPa</span>
+      </div>
+      <div class="dqwd">
+        <span class="text-label">氮气温度</span>
+        <span class="text-val">{{ statusData.readData['Fan2dqwd'] }}℃</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue';
+
+let props = defineProps({
+  statusData: {
+    type: Object,
+    default: () => {
+      return {};
+    },
+  },
+});
+</script>
+
+<style lang="less" scoped>
+.model-tip {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background-image: url('/@/assets/images/home-container/configurable/ZhiDan_Ts.png');
+  background-repeat: no-repeat;
+  background-size: 58%;
+  background-position: 50% 30%;
+
+  .model-name-zdj1 {
+    position: absolute;
+    left: 26%;
+    top: 17%;
+    color: yellow;
+    font-size: 16px;
+  }
+  .model-name-zdj2 {
+    position: absolute;
+    left: 26%;
+    top: 30%;
+    color: yellow;
+    font-size: 16px;
+  }
+  .model-name-zdj3 {
+    position: absolute;
+    left: 26%;
+    top: 47%;
+    color: yellow;
+    font-size: 16px;
+  }
+  .zdj-ll {
+    position: absolute;
+    left: 28%;
+    top: 43%;
+    color: #fff;
+    font-size: 12px;
+  }
+  .zdj-ll1 {
+    position: absolute;
+    left: 28%;
+    bottom: 38%;
+    color: #fff;
+    font-size: 12px;
+  }
+  .text-val {
+    color: #00edfe;
+    margin: 5px;
+  }
+  .jrq1 {
+    position: absolute;
+    right: 42%;
+    top: 15%;
+    color: #fff;
+    font-size: 14px;
+    .cq-zdj-item {
+      display: flex;
+      flex-direction: column;
+    }
+  }
+  .jrq2 {
+    position: absolute;
+    right: 44%;
+    bottom: 53%;
+    color: #fff;
+    font-size: 14px;
+  }
+  .filter {
+    position: absolute;
+    right: 47%;
+    top: 21%;
+    color: #fff;
+    font-size: 12px;
+    .cq-zdj-item {
+      display: flex;
+      flex-direction: column;
+    }
+  }
+  .filter2 {
+    position: absolute;
+    right: 47%;
+    top: 46%;
+    color: #fff;
+    font-size: 12px;
+    .cq-zdj-item {
+      display: flex;
+      flex-direction: column;
+    }
+  }
+  .nd {
+    position: absolute;
+    right: 47%;
+    bottom: 34%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .ll {
+    position: absolute;
+    right: 37%;
+    bottom: 34%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .dqyl {
+    position: absolute;
+    right: 28%;
+    bottom: 34%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .dqwd {
+    position: absolute;
+    right: 24%;
+    bottom: 34%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .nd1 {
+    position: absolute;
+    right: 47%;
+    bottom: 56%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .ll1 {
+    position: absolute;
+    right: 37%;
+    bottom: 56%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .dqyl1 {
+    position: absolute;
+    right: 28%;
+    bottom: 56%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .dqwd1 {
+    position: absolute;
+    right: 24%;
+    bottom: 56%;
+    color: #fff;
+    font-size: 12px;
+    display: flex;
+    flex-direction: column;
+  }
+  .hxt1 {
+    position: absolute;
+    right: 35%;
+    top: 21%;
+    color: #fff;
+    font-size: 14px;
+  }
+  .hxt2 {
+    position: absolute;
+    right: 35%;
+    bottom: 51%;
+    color: #fff;
+    font-size: 14px;
+  }
+}
+</style>

+ 15 - 0
src/views/vent/monitorManager/nitrogenMonitor/nitorgen.api.ts

@@ -0,0 +1,15 @@
+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 { reactive } from 'vue';
+import _ from 'lodash';
+
+enum Api {
+  device = '/monitor/device',
+}
+
+// 搞这个缓存是由于:目前代码上的设计是多个模块发出多次请求,每个模块自己负责消费前者的响应。
+// 这会导致相同的请求被同时发送多次。
+const cache = new Map<string, Promise<any>>();
+export const list = (params) => defHttp.post({ url: Api.device, params });

+ 325 - 0
src/views/vent/monitorManager/nitrogenMonitor/nitrogen.data.ts

@@ -0,0 +1,325 @@
+import { Config } from '../../deviceManager/configurationTable/types';
+export const nitrogenConfigs: Config[] = [
+  {
+    deviceType: '',
+    moduleName: '制氮机',
+    pageType: '',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'row',
+        items: [
+          {
+            name: 'complex_list',
+            basis: '100%',
+            overflow: false,
+          },
+        ],
+      },
+      complex_list: [
+        {
+          type: 'I',
+          readFrom: '',
+          items: [
+            {
+              title: '机组参数',
+              contents: [
+                {
+                  label: '累计流量(m³)',
+                  value: '${nitrogen_auto[0].readData.cumulativeFlow}',
+                  color: 'blue',
+                },
+                {
+                  label: '加热器中心温度(℃)',
+                  value: '${nitrogen_auto[0].readData.centerTemperature}',
+                  color: 'blue',
+                },
+                {
+                  label: '加热器出口温度(℃)',
+                  value: '${nitrogen_auto[0].readData.outletTemperature}',
+                  color: 'blue',
+                },
+              ],
+            },
+            {
+              title: '电机数据',
+              contents: [
+                {
+                  label: 'A相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ia}',
+                  color: 'blue',
+                },
+                {
+                  label: 'B相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ib}',
+                  color: 'blue',
+                },
+                {
+                  label: 'C相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ic}',
+                  color: 'blue',
+                },
+                {
+                  label: 'AB项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vab}',
+                  color: 'blue',
+                },
+                {
+                  label: 'BC项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vbc}',
+                  color: 'blue',
+                },
+                {
+                  label: 'AC项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vac}',
+                  color: 'blue',
+                },
+              ],
+            },
+          ],
+        },
+      ],
+    },
+    showStyle: {
+      size: 'width:440px;height:390px;',
+      version: '保德',
+      position: 'top:40px;left:10px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '制氮机',
+    pageType: '',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: {
+        show: false,
+        type: 'video',
+        link: '',
+      },
+      layout: {
+        direction: 'row',
+        items: [
+          {
+            name: 'complex_list',
+            basis: '100%',
+            overflow: false,
+          },
+        ],
+      },
+      complex_list: [
+        {
+          type: 'I',
+          readFrom: '',
+          items: [
+            {
+              title: '机组参数',
+              contents: [
+                {
+                  label: '累计流量(m³)',
+                  value: '${nitrogen_auto[0].readData.cumulativeFlow}',
+                  color: 'blue',
+                },
+                {
+                  label: '加热器中心温度(℃)',
+                  value: '${nitrogen_auto[0].readData.centerTemperature}',
+                  color: 'blue',
+                },
+                {
+                  label: '加热器出口温度(℃)',
+                  value: '${nitrogen_auto[0].readData.outletTemperature}',
+                  color: 'blue',
+                },
+              ],
+            },
+            {
+              title: '电机数据',
+              contents: [
+                {
+                  label: 'A相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ia}',
+                  color: 'blue',
+                },
+                {
+                  label: 'B相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ib}',
+                  color: 'blue',
+                },
+                {
+                  label: 'C相电流(A)',
+                  value: '${nitrogen_auto[0].readData.Ic}',
+                  color: 'blue',
+                },
+                {
+                  label: 'AB项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vab}',
+                  color: 'blue',
+                },
+                {
+                  label: 'BC项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vbc}',
+                  color: 'blue',
+                },
+                {
+                  label: 'AC项间电压(V)',
+                  value: '${nitrogen_auto[0].readData.Vac}',
+                  color: 'blue',
+                },
+              ],
+            },
+          ],
+        },
+      ],
+      preset: [],
+    },
+    showStyle: {
+      size: 'width:440px;height:390px;',
+      version: '保德',
+      position: 'top:440px;left:10px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '远程控制',
+    pageType: '',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: { show: false, type: 'video', link: '' },
+      layout: {
+        direction: 'column',
+        items: [
+          {
+            name: 'nitrogenBtnList',
+            basis: 'auto',
+            overflow: false,
+          },
+        ],
+      },
+      table: [],
+      board: [],
+      chart: [],
+      gallery: [],
+      gallery_list: [],
+      list: [],
+      complex_list: [],
+      preset: [
+        {
+          readFrom: '',
+          type: 'A',
+          buttonList: [
+            { label: '制氮机1', value: 'isRunning', content: '' },
+            { label: '制氮机2', value: 'isRunning', content: '' },
+            { label: '制氮机3', value: 'isRunning', content: '' },
+            { label: '制氮机4', value: 'isRunning', content: '' },
+            { label: '1号空压机', value: 'isRunning', content: '' },
+            { label: '2号空压机', value: 'isRunning', content: '' },
+            { label: '3号空压机', value: 'isRunning', content: '' },
+            { label: '是否开启联动', value: 'isRunning', content: '' },
+          ],
+        },
+      ],
+      partition: [],
+    },
+    showStyle: {
+      size: 'width:440px;height:390px;',
+      version: '保德',
+      position: 'top:40px;right:10px;',
+    },
+  },
+  {
+    deviceType: '',
+    moduleName: '实时数据监测曲线',
+    pageType: '',
+    moduleData: {
+      header: {
+        show: false,
+        readFrom: '',
+        selector: {
+          show: false,
+          value: '',
+        },
+        slot: {
+          show: false,
+          value: '',
+        },
+      },
+      background: { show: false, type: 'video', link: '' },
+      layout: {
+        direction: 'row',
+        items: [
+          {
+            name: 'chart',
+            basis: '100%',
+          },
+        ],
+      },
+      board: [],
+      chart: [
+        {
+          type: 'line',
+          readFrom: '',
+          legend: { show: true },
+          xAxis: [{ show: true }],
+          yAxis: [{ show: true, name: '瞬时流量(m³/h)', position: 'left' }],
+          series: [
+            {
+              readFrom: 'nitrogen_auto[0].readData',
+              xprop: 'tTime',
+              yprop: 'cumulativeFlow',
+              label: '制氮机1',
+            },
+            {
+              readFrom: 'nitrogen_auto[1].readData',
+              xprop: 'tTime',
+              yprop: 'cumulativeFlow',
+              label: '制氮机2',
+            },
+          ],
+        },
+      ],
+    },
+    showStyle: {
+      size: 'width:440px;height:390px;',
+      version: '原版',
+      position: 'top:440px;right:10px;',
+    },
+  },
+];

+ 108 - 0
src/views/vent/monitorManager/nitrogenMonitor/nitrogen.threejs.base.ts

@@ -0,0 +1,108 @@
+import * as THREE from 'three';
+import { CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer';
+import { setModalCenter } from '/@/utils/threejs/util';
+// import { setModalCenter } from '/@/utils/threejs/util';
+// import * as dat from 'dat.gui';
+// const gui = new dat.GUI();
+// gui.domElement.style = 'position:absolute;top:100px;left:10px;z-index:99999999999999';
+
+class ModelContext {
+  model;
+  modelName = 'spray-upper';
+  // modelName = 'spray-ts';
+  // modelName = 'dedust';
+  group: THREE.Object3D | null = null;
+  /** 本模型内支持的锚点位置数组 */
+  anchors: [number, number, number][] = [
+    [1.5, 0.5, 1.6],
+    // [0, 0, 0],
+    // 右侧传感器
+    [1.3, 0.5, 1.6],
+    // [-25.847, 8.783, 3.267],
+    // [31.142, 8.783, 3.267],
+  ];
+  /** 本模型显示的css详情元素数组 */
+  cssSprites: CSS3DSprite[] = [];
+  /** 初始化时用到的摄像头位置参数,可以供外部访问或初始化时使用 */
+  cameraPostion = {
+    x: 2.0063999826191625,
+    y: 0.3963997081529602,
+    z: 3.275298992446514,
+  };
+  /** 初始化时用到的滑轨目标参数,可以供外部访问或初始化时使用,配合摄像头可以实现更精确的控制 */
+  orbitTarget = {
+    x: 2.023679325280445,
+    y: -0.1461634482645532,
+    z: 0.04677126793423104,
+  };
+
+  constructor(model) {
+    this.model = model;
+  }
+
+  addLight() {
+    const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
+    directionalLight.position.set(6.3, 28, 20);
+    this.group?.add(directionalLight);
+    directionalLight.target = this.group as THREE.Object3D;
+
+    const pointLight = new THREE.PointLight(0xffffff, 1, 1000);
+    pointLight.position.set(45, 51, -4.1);
+    pointLight.shadow.bias = 0.05;
+    this.model.scene.add(pointLight);
+  }
+
+  /** 初始化css元素,将css元素选择器传入,该方法会将这些元素按顺序放入本模型支持的锚点中 */
+  initCssElement(selectors: string[]) {
+    selectors.forEach((selector, index) => {
+      const element = document.querySelector(selector) as HTMLElement;
+      if (element) {
+        const css3D = new CSS3DSprite(element);
+        this.cssSprites.push(css3D);
+        css3D.name = selector;
+        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);
+        // ff.add(css3D.position, 'z', -100, 100);
+        if (index < this.anchors.length) {
+          const [x, y, z] = this.anchors[index];
+          css3D.position.set(x, y, z);
+          this.group?.add(css3D);
+        } else {
+          console.warn(`指定的元素${selector}没有合适的位置放置`);
+        }
+      }
+    });
+  }
+
+  /** 清除css元素 */
+  clearCssElement() {
+    this.cssSprites.forEach((sprite) => {
+      this.group?.remove(sprite);
+    });
+    this.cssSprites = [];
+  }
+
+  mountedThree() {
+    return new Promise((resolve) => {
+      this.model.setGLTFModel([this.modelName]).then(async (gltf) => {
+        this.group = gltf[0];
+        if (this.group) {
+          setModalCenter(this.group);
+          resolve(null);
+          // this.addLight();
+        }
+      });
+    });
+  }
+
+  destroy() {
+    if (this.model) {
+      this.model.clearGroup(this.group);
+      this.model = null;
+      this.group = null;
+    }
+  }
+}
+export default ModelContext;