瀏覽代碼

[Feat 0000]蓝色通风首页相关组件合并

bobo04052021@163.com 1 周之前
父節點
當前提交
2681986df2
共有 23 個文件被更改,包括 3613 次插入312 次删除
  1. 1 1
      src/views/vent/home/configurable/bule/components/ModuleNew.vue
  2. 537 0
      src/views/vent/home/configurable/bule/components/content.vue
  3. 109 0
      src/views/vent/home/configurable/bule/components/header.vue
  4. 283 0
      src/views/vent/home/configurable/bule/components/originalNew/NewNav.vue
  5. 319 0
      src/views/vent/home/configurable/bule/components/originalNew/NewNavFire.vue
  6. 295 0
      src/views/vent/home/configurable/bule/components/originalNew/NewNavWarn.vue
  7. 95 0
      src/views/vent/home/configurable/bule/components/originalNew/leftHeader1.vue
  8. 89 0
      src/views/vent/home/configurable/bule/components/originalNew/leftHeader2.vue
  9. 133 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleBottom.vue
  10. 95 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleLeft.vue
  11. 104 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleLeftBottom.vue
  12. 103 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleLeftCenter.vue
  13. 104 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleLeftTop.vue
  14. 102 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleRightBottom.vue
  15. 103 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleRightCenter.vue
  16. 104 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleRightTop.vue
  17. 122 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleTop.vue
  18. 161 0
      src/views/vent/home/configurable/bule/components/originalNew/moduleTopFire.vue
  19. 97 0
      src/views/vent/home/configurable/bule/components/originalNew/rightHeader1.vue
  20. 90 0
      src/views/vent/home/configurable/bule/components/originalNew/rightHeader2.vue
  21. 90 0
      src/views/vent/home/configurable/bule/components/originalNew/rightHeader3.vue
  22. 166 0
      src/views/vent/home/configurable/bule/ventNew.vue
  23. 311 311
      src/views/vent/home/configurable/components/content-New.vue

+ 1 - 1
src/views/vent/home/configurable/components/ModuleNew.vue → src/views/vent/home/configurable/bule/components/ModuleNew.vue

@@ -36,7 +36,7 @@ import ModuleBottom from './originalNew/moduleBottom.vue';
 import ModuleTop from './originalNew/moduleTop.vue';
 import { computed, ref } from 'vue';
 import { openWindow } from '/@/utils';
-import { getFormattedText } from '../hooks/helper';
+import { getFormattedText } from '../../hooks/helper';
 import LeftHeader1 from './originalNew/leftHeader1.vue';
 import LeftHeader2 from './originalNew/leftHeader2.vue';
 import RightHeader1 from './originalNew/rightHeader1.vue';

+ 537 - 0
src/views/vent/home/configurable/bule/components/content.vue

@@ -0,0 +1,537 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- 主体内容部分 -->
+  <div class="content">
+    <!-- 背景 -->
+    <img v-if="background.show && background.type === 'image'" class="content__background image__background" :src="background.link" />
+    <video
+      v-if="background.show && background.type === 'video'"
+      class="content__background content__background_video"
+      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' }">
+        <!-- 告示板部分 -->
+        <template v-if="config.name === 'board'">
+          <div
+            v-if="config.pageType == 'vent_New'"
+            style="padding-top: 11%"
+            class="content__module content__module1 flex flex-justify-around flex-items-center flex-wrap"
+          >
+            <MiniBoard
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :type="config.type"
+              :layout="config.layout"
+            />
+          </div>
+          <div v-else-if="config.pageType == 'New_fire'" class="content__module flex flex-justify-around flex-items-center flex-wrap">
+            <MiniBoardNew
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :type="config.type"
+              :layout="config.layout"
+            />
+          </div>
+          <div v-else class="content__module flex flex-justify-around flex-items-center flex-wrap">
+            <MiniBoard
+              v-for="item in config.items"
+              :key="item.prop"
+              :label="item.label"
+              :value="item.value"
+              :type="config.type"
+              :layout="config.layout"
+            />
+          </div>
+        </template>
+        <!-- 图表部分,这部分通常需要填充,有告示板、Header等内容需要填充父级 -->
+        <template v-if="config.name === 'chart'">
+          <CustomChart v-if="config.pageType == 'New_dust'" class="content__module_dust" :chart-config="config.config" :chart-data="config.data" />
+          <CustomChart v-else class="content__module" :chart-config="config.config" :chart-data="config.data" />
+        </template>
+        <!-- 通常列表部分 -->
+        <template v-if="config.name === 'list'">
+          <template v-if="config.type === 'timeline'">
+            <TimelineList class="content__module" :list-config="config.items" />
+          </template>
+          <template v-else-if="config.type === 'timelineNew'">
+            <TimelineListNew class="content__module" :list-config="config.items" />
+          </template>
+          <template v-else>
+            <CustomList class="content__module" :type="config.type" :list-config="config.items" />
+          </template>
+        </template>
+        <template v-if="config.name === 'fireList'">
+          <CustomList class="content__module" :type="config.type" :list-config="config.items" />
+        </template>
+        <!-- 画廊部分 -->
+        <template v-if="config.name === 'gallery'">
+          <CustomGallery class="content__module" :type="config.type" :gallery-config="config.items" />
+        </template>
+        <!-- 复杂列表部分 -->
+        <template v-if="config.name === 'gallery_list'">
+          <GalleryList class="content__module" :type="config.type" :list-config="config.items" :gallery-config="config.galleryItems" />
+        </template>
+        <!-- 复杂列表部分 -->
+        <template v-if="config.name === 'complex_list'">
+          <ComplexList class="content__module" :type="config.type" :list-config="config.items" />
+        </template>
+        <!-- 表格部分,这部分通常是占一整个模块的 -->
+        <template v-if="config.name === 'table'">
+          <CustomTable class="content__module text-center overflow-auto" :type="config.type" :columns="config.columns" :data="config.data" />
+        </template>
+        <template v-if="config.name === 'tabs'">
+          <CustomTabs class="content__module" :type="config.type" :tab-config="config.items" :overflow="config.overflow" />
+        </template>
+        <template v-if="config.name === 'blast_delta'">
+          <BlastDelta
+            v-if="config.pageType === 'New_fire'"
+            class="content__moduleFire"
+            :pos-monitor="config.data"
+            :canvasSize="{ width: 250, height: 200 }"
+          />
+          <BlastDelta v-else class="content__module" :pos-monitor="config.data" :canvasSize="{ width: 250, height: 200 }" />
+        </template>
+        <template v-if="config.name === 'qh_curve'">
+          <QHCurve class="content__module" :mainfan="config.data" :fan1-prop="config.config.fan1Prop" :fan2-prop="config.config.fan2Prop" />
+        </template>
+        <template v-if="config.name === 'ai_chat'">
+          <AIChat class="content__module" />
+        </template>
+        <template v-if="config.name === 'device_alarm'">
+          <DeviceAlarm class="content__module" :devicedata="config.data" />
+        </template>
+        <!-- lxh -->
+        <template v-if="config.name === 'select_cs'">
+          <SelectCs :devicedata="config.data" :setLabelData="config.config.setLabelConfig" />
+        </template>
+        <template v-if="config.name === 'measure_detail'">
+          <MeasureDetail
+            class="content__module"
+            :show-title="false"
+            :composite-data="config.data"
+            :topconfig="config.config.topconfig"
+            :btnconfig="config.config.btnconfig"
+          />
+        </template>
+        <template v-if="config.name === 'partition'">
+          <Partition class="content__module" :type="config.type" :label="config.label" :icon="config.icon" />
+        </template>
+        <template v-if="config.name === 'selector_dual_chart'">
+          <SelectorDualChart :data="config.data" :moduleData="props.moduleData" :config="config" />
+        </template>
+        <template v-if="config.name === 'radio_label'">
+          <RadioLabel class="content__module" :type="config.config.type" :config="config.config" />
+        </template>
+        <template v-if="config.name === 'button_list'">
+          <ButtonList 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>
+      <template v-if="config.key === 'fire_warn'">
+        <FIreWarn class="content__module" />
+      </template> -->
+      </div>
+    </div>
+  </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 '../../components/detail/MiniBoard.vue';
+import TimelineList from '../../components/detail/TimelineList.vue';
+import TimelineListNew from '../../components/detail/TimelineListNew.vue';
+import CustomList from '../../components/detail/CustomList.vue';
+import CustomGallery from '../../components/detail/CustomGallery.vue';
+import ComplexList from '../../components/detail/ComplexList.vue';
+import GalleryList from '../../components/detail/GalleryList.vue';
+import CustomTable from '../../components/detail/CustomTable.vue';
+import CustomChart from '../../components/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 '../../components/preset/QHCurve.vue';
+import MeasureDetail from '../../components/preset/MeasureDetail.vue';
+import CustomTabs from '../../components/preset/CustomTabs.vue';
+import AIChat from '/@/components/AIChat/MiniChat.vue';
+import DeviceAlarm from '../../components/preset/DeviceAlarm.vue';
+import SelectCs from '../../components/preset/SelectCs.vue';
+import MiniBoardNew from '../../components/detail/MiniBoard-New.vue';
+import Partition from '../../components/preset/partition.vue';
+import SelectorDualChart from '../../components/preset/selectorDualChart.vue';
+import RadioLabel from '../../components/preset/radioLabel.vue';
+import ButtonList from '../../components/preset/buttonList.vue';
+// import FIreWarn from './preset/FIreWarn.vue';
+// import FIreControl from './preset/FIreControl.vue';
+
+const props = defineProps<{
+  data: any;
+  moduleData: Config['moduleData'];
+}>();
+
+const { background, layout } = props.moduleData;
+
+// 获取当原始配置带 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 {
+        ...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) || [];
+
+  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: getItems(data, cfg.items),
+        });
+        break;
+      }
+      case 'list': {
+        const cfg = list.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: (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);
+
+        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: (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) 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);
+
+        arr.push({
+          ...cfg,
+          ...item,
+          columns: cfg.columns,
+          data,
+        });
+        break;
+      }
+      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';
+
+.content {
+  height: calc(100% - 30px);
+  position: relative;
+  // z-index: -2;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto; // 这里会导致样式无故添加滚动条
+}
+
+.content__background {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 0;
+  object-fit: fill;
+}
+
+.image__background {
+  width: 35%;
+  height: 61%;
+  left: 30%;
+}
+
+.content__module {
+  // margin-top: 5px;
+  // margin-bottom: 5px;
+  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__moduleFire {
+  width: 100%;
+  height: 100%;
+  margin-left: -24% !important;
+}
+
+.content__module_dust {
+  background: url('@/assets/images/vent/homeNew/bottomBg.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  width: 100%;
+  height: 100%;
+}
+
+// .content__module:first-of-type {
+//   margin-top: 0;
+// }
+// .content__module:last-of-type {
+//   margin-bottom: 0;
+// }
+::-webkit-scrollbar {
+  width: 5px !important;
+}
+
+::-webkit-scrollbar-thumb {
+  width: 5px !important;
+}
+
+:deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
+  /* background-color: transparent; */
+  color: #fff;
+}
+
+:deep(.zxm-select-arrow) {
+  color: #fff;
+}
+
+:deep(.zxm-select-selection-item) {
+  color: #fff !important;
+}
+
+:deep(.zxm-select-selection-placeholder) {
+  color: #fff !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>

+ 109 - 0
src/views/vent/home/configurable/bule/components/header.vue

@@ -0,0 +1,109 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <SwapOutlined class="w-30px" />
+        <div class="w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="flex-basis-50% flex flex-items-center flex-grow-1 costume-header_right">
+        <SwapOutlined class="w-30px" />
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  height: 30px;
+  background-image: linear-gradient(90deg, var(--vent-base-light-bg-opcity), transparent 20%, transparent 80%, var(--vent-base-light-bg-opcity));
+}
+.costume-header_left {
+  border-left: 3px solid;
+  border-right: 3px solid;
+  border-image-source: linear-gradient(to top, #00000033, var(--vent-base-light-bg), #00000033);
+  border-image-slice: 1;
+}
+.costume-header_right {
+  border-right: 3px solid;
+  border-image-source: linear-gradient(to top, #00000033, var(--vent-base-light-bg), #00000033);
+  border-image-slice: 1;
+}
+
+::v-deep .zxm-select:not(.zxm-select-customize-input) .zxm-select-selector {
+  /* background-color: transparent; */
+  color: #fff;
+}
+::v-deep .zxm-select-arrow {
+  color: #fff;
+}
+::v-deep .zxm-select-selection-item {
+  color: #fff !important;
+}
+::v-deep .zxm-select-selection-placeholder {
+  color: #fff !important;
+}
+</style>

+ 283 - 0
src/views/vent/home/configurable/bule/components/originalNew/NewNav.vue

@@ -0,0 +1,283 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="New-nav">
+    <div class="main-title">{{ Title }}</div>
+    <!-- menu区域 -->
+    <div class="nav-menu">
+      <div class="nav-menu-left">
+        <div v-for="(item, index) in leftMenus" :key="index">
+          <div :class="activeIndex == index ? 'nav-menu-active' : 'nav-menu-unactive'" :key="index" @click="menuClick({ item, index })">
+            <div style="color: #ddd">{{ item.name }}</div>
+
+            <div v-if="activeIndex == index && isShowMenuItem" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="nav-menu-right">
+        <div v-for="(item, index) in rightMenus" :key="index + 4">
+          <div
+            :class="activeIndex == index + 4 ? 'nav-menu-active' : 'nav-menu-unactive'"
+            :key="index + 4"
+            @click="menuClick({ item, index: index + 4 })"
+          >
+            <div style="color: #ddd">{{ item.name }}</div>
+            <div v-if="activeIndex == index + 4 && isShowMenuItem" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+let props = defineProps({
+  Title: {
+    type: String,
+    default: '',
+  },
+});
+
+let menuList = ref<any[]>([
+  {
+    name: '智能通风',
+    targatUrl: '/micro-vent-3dModal/configurable/ventNew/home',
+  },
+  {
+    name: '火灾监控',
+    targatUrl: '/micro-vent-3dModal/configurable/fireNew/home',
+  },
+  {
+    name: '粉尘监控',
+    targatUrl: '/micro-vent-3dModal/configurable/dustNew/home',
+  },
+  {
+    name: '瓦斯监控',
+    MenuItemList: [{ name: '多灾融合预警' }, { name: '通风监测预警' }, { name: '火灾监测预警' }, { name: '粉尘监测预警' }, { name: '瓦斯监测预警' }],
+  },
+  {
+    name: '灾害预警',
+    MenuItemList: [{ name: '多灾融合预警' }, { name: '通风监测预警' }, { name: '火灾监测预警' }, { name: '粉尘监测预警' }, { name: '瓦斯监测预警' }],
+  },
+]); //一级菜单列表
+let activeIndex = ref(0); //当前激活menu索引
+const router = useRouter();
+const route = useRoute();
+let isShowMenuItem = ref(false); //是否显示menuItem下拉选项菜单
+let menuItemActive = ref(0); //menuItem当前激活选项
+const leftMenus = computed(() => menuList.value.slice(0, 4));
+const rightMenus = computed(() => menuList.value.slice(4));
+function menuClick(data) {
+  activeIndex.value = data.index;
+  isShowMenuItem.value = !isShowMenuItem.value;
+  router.push({ path: data.item.targatUrl });
+}
+function menuItemClick(index) {
+  menuItemActive.value = index;
+  isShowMenuItem.value = false;
+}
+function updateActiveState(path: string) {
+  menuList.value.forEach((menu, index) => {
+    // 处理有直接链接的菜单项
+    if (menu.targatUrl === path) {
+      activeIndex.value = index;
+      isShowMenuItem.value = false;
+      return;
+    }
+    // 处理有子菜单的菜单项
+    if (menu.MenuItemList) {
+      const itemIndex = menu.MenuItemList.findIndex((item) => item.targatUrl === path);
+      if (itemIndex !== -1) {
+        activeIndex.value = index;
+        menuItemActive.value = itemIndex;
+        isShowMenuItem.value = true;
+      }
+    }
+  });
+}
+watch(
+  () => route.path,
+  (newPath) => {
+    updateActiveState(newPath);
+  }
+);
+onMounted(() => {
+  updateActiveState(route.path);
+});
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@font-face {
+  font-family: 'douyuFont';
+  src: url('/@/assets/font/douyuFont.otf');
+}
+
+.New-nav {
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .main-title {
+    width: 518px;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    font-family: 'douyuFont';
+    font-size: 25px;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    width: auto;
+    padding: 0;
+  }
+
+  .nav-menu {
+    position: absolute;
+    top: 0;
+    left: 675px;
+    height: 100%;
+    display: flex;
+    position: static; // 移除绝对定位
+    display: flex;
+    width: auto;
+    .nav-menu-left {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      float: left;
+    }
+    .nav-menu-right {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      float: left;
+      margin-left: 40%;
+    }
+    .nav-menu-active {
+      position: relative;
+      cursor: pointer;
+      width: 120px;
+      height: 60px;
+      margin-top: 20px;
+      line-height: 45px;
+      text-align: center;
+      font-size: 16px;
+      letter-spacing: 2px;
+      background: url('@/assets/images/vent/homeNew/Select-btn.png') no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .nav-menu-unactive {
+      position: relative;
+      width: 120px;
+      height: 60px;
+      line-height: 65px;
+      text-align: center;
+      margin: 0px 10px;
+      font-size: 16px;
+      letter-spacing: 2px;
+      background-size: 100% 100%;
+      cursor: pointer;
+    }
+
+    .nav-menu-item {
+      position: absolute;
+      top: 23px;
+      width: 130px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      box-sizing: border-box;
+
+      .nav-menu-content {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        margin-top: 25%;
+        .nav-menu-List {
+          background: url('@/assets/images/vent/homeNew/menuList.png') no-repeat;
+          background-size: 100% 100%;
+        }
+        .menu-item-active {
+          color: #ddd;
+          z-index: 999;
+          width: 100%;
+          height: 36px;
+          line-height: 36px;
+          font-size: 14px;
+          background: url('@/assets/images/vent/homeNew/selectActive.png') no-repeat;
+          background-size: 100% 100%;
+        }
+
+        .menu-item-unactive {
+          color: #ddd;
+          width: 100%;
+          height: 40px;
+          line-height: 40px;
+          font-size: 14px;
+        }
+      }
+    }
+
+    @keyframes fadeIn {
+      from {
+        opacity: 0;
+      }
+
+      to {
+        opacity: 1;
+      }
+    }
+
+    /* 定义淡出动画 */
+    @keyframes fadeOut {
+      from {
+        opacity: 1;
+      }
+
+      to {
+        opacity: 0;
+      }
+    }
+  }
+
+  .userInfo {
+    width: 120px;
+    float: right;
+    background: url(/src/assets/images/vent/homeNew/user.png) no-repeat;
+    background-size: 100% 100%;
+    position: absolute;
+    top: 14px;
+    right: 0;
+    .userName {
+      margin-left: 40px;
+      font-size: 20px;
+    }
+  }
+}
+</style>

+ 319 - 0
src/views/vent/home/configurable/bule/components/originalNew/NewNavFire.vue

@@ -0,0 +1,319 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="New-nav">
+    <div class="main-title">{{ Title }}</div>
+    <!-- menu区域 -->
+    <div class="nav-menu">
+      <div v-if="!disabled" class="nav-menu-left">
+        <div v-for="(item, index) in leftMenus" :key="index">
+          <div :class="activeIndex == index ? 'nav-menu-active' : 'nav-menu-unactive'" :key="index" @click="menuClick({ item, index })">
+            <div style="color: #ddd">{{ item.name }}</div>
+
+            <div v-if="activeIndex == index && isShowMenuItem" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div v-if="!disabled" class="nav-menu-right">
+        <div v-for="(item, index) in rightMenus" :key="index + 4">
+          <div
+            :class="activeIndex == index + 4 ? 'nav-menu-active' : 'nav-menu-unactive'"
+            :key="index + 4"
+            @click="menuClick({ item, index: index + 4 })"
+          >
+            <div style="color: #ddd">{{ item.name }}</div>
+            <div v-if="activeIndex == index + 4 && isShowMenuItem && !disabled" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+let props = defineProps({
+  Title: {
+    type: String,
+    default: '',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+let menuList = ref<any[]>([
+  {
+    name: '智能通风',
+    targatUrl: '/micro-vent-3dModal/configurable/ventNew/home',
+  },
+  {
+    name: '火灾监控',
+    targatUrl: '/micro-vent-3dModal/configurable/fireNew/home',
+  },
+  {
+    name: '粉尘监控',
+    targatUrl: '/micro-vent-3dModal/configurable/dustNew/home',
+  },
+  {
+    name: '瓦斯监控',
+    MenuItemList: [{ name: '多灾融合预警' }, { name: '通风监测预警' }, { name: '火灾监测预警' }, { name: '粉尘监测预警' }, { name: '瓦斯监测预警' }],
+  },
+  {
+    name: '灾害预警',
+    MenuItemList: [{ name: '多灾融合预警' }, { name: '通风监测预警' }, { name: '火灾监测预警' }, { name: '粉尘监测预警' }, { name: '瓦斯监测预警' }],
+  },
+]); //一级菜单列表
+let activeIndex = ref(0); //当前激活menu索引
+const router = useRouter();
+const route = useRoute();
+let isShowMenuItem = ref(false); //是否显示menuItem下拉选项菜单
+let menuItemActive = ref(0); //menuItem当前激活选项
+const leftMenus = computed(() => menuList.value.slice(0, 4));
+const rightMenus = computed(() => menuList.value.slice(4));
+function menuClick(data) {
+  activeIndex.value = data.index;
+  isShowMenuItem.value = !isShowMenuItem.value;
+  router.push({ path: data.item.targatUrl });
+}
+function menuItemClick(index) {
+  menuItemActive.value = index;
+  isShowMenuItem.value = false;
+}
+function updateActiveState(path: string) {
+  menuList.value.forEach((menu, index) => {
+    // 处理有直接链接的菜单项
+    if (menu.targatUrl === path) {
+      activeIndex.value = index;
+      isShowMenuItem.value = false;
+      return;
+    }
+    // 处理有子菜单的菜单项
+    if (menu.MenuItemList) {
+      const itemIndex = menu.MenuItemList.findIndex((item) => item.targatUrl === path);
+      if (itemIndex !== -1) {
+        activeIndex.value = index;
+        menuItemActive.value = itemIndex;
+        isShowMenuItem.value = true;
+      }
+    }
+  });
+}
+watch(
+  () => route.path,
+  (newPath) => {
+    updateActiveState(newPath);
+  },
+  {
+    immediate: true,
+  }
+);
+onMounted(() => {
+  updateActiveState(route.path);
+});
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@font-face {
+  font-family: 'douyuFont';
+  src: url('/@/assets/font/douyuFont.otf');
+}
+
+.New-nav {
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .main-title {
+    width: 518px;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    font-family: 'douyuFont';
+    font-size: 25px;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    width: auto;
+    padding: 0;
+    color: #fff;
+  }
+
+  .nav-menu {
+    position: absolute;
+    top: 0;
+    left: 675px;
+    height: 100%;
+    display: flex;
+    position: static; // 移除绝对定位
+    display: flex;
+    width: auto;
+    .nav-menu-left {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      float: left;
+      .nav-menu-active {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-1.png) no-repeat;
+        background-size: 100% 100%;
+      }
+
+      .nav-menu-unactive {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-2.png) no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+    .nav-menu-right {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      float: left;
+      margin-left: 42%;
+      .nav-menu-active {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-3.png) no-repeat;
+        background-size: 100% 100%;
+      }
+
+      .nav-menu-unactive {
+        position: relative;
+        cursor: pointer;
+        width: 100px;
+        height: 40px;
+        margin-top: 10px;
+        margin-right: 40px;
+        line-height: 35px;
+        text-align: center;
+        font-size: 16px;
+        background: url(/src/assets/images/fireNew/2-4.png) no-repeat;
+        background-size: 100% 100%;
+      }
+    }
+
+    .nav-menu-item {
+      position: absolute;
+      top: 23px;
+      width: 130px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      box-sizing: border-box;
+
+      .nav-menu-content {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        margin-top: 25%;
+        .nav-menu-List {
+          background: url('@/assets/images/vent/homeNew/menuList.png') no-repeat;
+          background-size: 100% 100%;
+        }
+        .menu-item-active {
+          color: #ddd;
+          z-index: 999;
+          width: 100%;
+          height: 36px;
+          line-height: 36px;
+          font-size: 14px;
+          background: url('@/assets/images/fireNew/2-2.png') no-repeat;
+          background-size: 100% 100%;
+        }
+
+        .menu-item-unactive {
+          color: #ddd;
+          width: 100%;
+          height: 40px;
+          line-height: 40px;
+          font-size: 14px;
+        }
+      }
+    }
+
+    @keyframes fadeIn {
+      from {
+        opacity: 0;
+      }
+
+      to {
+        opacity: 1;
+      }
+    }
+
+    /* 定义淡出动画 */
+    @keyframes fadeOut {
+      from {
+        opacity: 1;
+      }
+
+      to {
+        opacity: 0;
+      }
+    }
+  }
+
+  .userInfo {
+    width: 120px;
+    float: right;
+    background: url(/src/assets/images/vent/homeNew/user.png) no-repeat;
+    background-size: 100% 100%;
+    position: absolute;
+    top: 14px;
+    right: 0;
+    .userName {
+      margin-left: 40px;
+      font-size: 20px;
+    }
+  }
+}
+</style>

+ 295 - 0
src/views/vent/home/configurable/bule/components/originalNew/NewNavWarn.vue

@@ -0,0 +1,295 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="New-nav">
+    <div class="main-title">{{ Title }}</div>
+    <!-- menu区域 -->
+    <div class="nav-menu">
+      <div class="nav-menu-left">
+        <div v-for="(item, index) in leftMenus" :key="index">
+          <div :class="activeIndex == index ? 'nav-menu-active' : 'nav-menu-unactive'" :key="index" @click="menuClick({ item, index })">
+            <div style="color: #ddd">{{ item.name }}</div>
+
+            <div v-if="activeIndex == index && isShowMenuItem" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="nav-menu-right">
+        <div v-for="(item, index) in rightMenus" :key="index + 4">
+          <div
+            :class="activeIndex == index + 4 ? 'nav-menu-active' : 'nav-menu-unactive'"
+            :key="index + 4"
+            @click="menuClick({ item, index: index + 4 })"
+          >
+            <div style="color: #ddd">{{ item.name }}</div>
+            <div v-if="activeIndex == index + 4 && isShowMenuItem" class="nav-menu-item">
+              <div class="nav-menu-content">
+                <div class="nav-menu-List">
+                  <div
+                    :class="menuItemActive == ind ? 'menu-item-active' : 'menu-item-unactive'"
+                    v-for="(ite, ind) in item.MenuItemList"
+                    :key="ind"
+                    @click.stop="menuItemClick(ind)"
+                    >{{ ite.name }}</div
+                  >
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
+  import { useRouter, useRoute } from 'vue-router';
+  let props = defineProps({
+    Title: {
+      type: String,
+      default: '',
+    },
+  });
+
+  let menuList = ref<any[]>([
+    {
+      name: '智能通风',
+      targatUrl: '/micro-vent-3dModal/configurable/ventNew/home',
+    },
+    {
+      name: '火灾监控',
+      targatUrl: '/micro-vent-3dModal/configurable/fireNew/home',
+    },
+    {
+      name: '粉尘监控',
+      targatUrl: '/micro-vent-3dModal/configurable/dustNew/home',
+    },
+    {
+      name: '瓦斯监控',
+      MenuItemList: [
+        { name: '多灾融合预警' },
+        { name: '通风监测预警' },
+        { name: '火灾监测预警' },
+        { name: '粉尘监测预警' },
+        { name: '瓦斯监测预警' },
+      ],
+    },
+    {
+      name: '灾害预警',
+      MenuItemList: [
+        { name: '多灾融合预警' },
+        { name: '通风监测预警' },
+        { name: '火灾监测预警' },
+        { name: '粉尘监测预警' },
+        { name: '瓦斯监测预警' },
+      ],
+    },
+  ]); //一级菜单列表
+  let activeIndex = ref(0); //当前激活menu索引
+  const router = useRouter();
+  const route = useRoute();
+  let isShowMenuItem = ref(false); //是否显示menuItem下拉选项菜单
+  let menuItemActive = ref(0); //menuItem当前激活选项
+  const leftMenus = computed(() => menuList.value.slice(0, 4));
+  const rightMenus = computed(() => menuList.value.slice(4));
+  function menuClick(data) {
+    activeIndex.value = data.index;
+    isShowMenuItem.value = !isShowMenuItem.value;
+    router.push({ path: data.item.targatUrl });
+  }
+  function menuItemClick(index) {
+    menuItemActive.value = index;
+    isShowMenuItem.value = false;
+  }
+  function updateActiveState(path: string) {
+    menuList.value.forEach((menu, index) => {
+      // 处理有直接链接的菜单项
+      if (menu.targatUrl === path) {
+        activeIndex.value = index;
+        isShowMenuItem.value = false;
+        return;
+      }
+      // 处理有子菜单的菜单项
+      if (menu.MenuItemList) {
+        const itemIndex = menu.MenuItemList.findIndex((item) => item.targatUrl === path);
+        if (itemIndex !== -1) {
+          activeIndex.value = index;
+          menuItemActive.value = itemIndex;
+          isShowMenuItem.value = true;
+        }
+      }
+    });
+  }
+  watch(
+    () => route.path,
+    (newPath) => {
+      updateActiveState(newPath);
+    }
+  );
+  onMounted(() => {
+    updateActiveState(route.path);
+  });
+</script>
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  @font-face {
+    font-family: 'douyuFont';
+    src: url('/@/assets/font/douyuFont.otf');
+  }
+
+  .New-nav {
+    position: relative;
+    width: 100%;
+    height: 100%;
+
+    .main-title {
+      width: 518px;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      font-family: 'douyuFont';
+      font-size: 25px;
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      width: auto;
+      padding: 0;
+    }
+
+    .nav-menu {
+      position: absolute;
+      top: 0;
+      left: 675px;
+      height: 100%;
+      display: flex;
+      position: static; // 移除绝对定位
+      display: flex;
+      width: auto;
+      .nav-menu-left {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        float: left;
+      }
+      .nav-menu-right {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        float: left;
+        margin-left: 40%;
+      }
+      .nav-menu-active {
+        position: relative;
+        cursor: pointer;
+        width: 120px;
+        height: 60px;
+        margin-top: 20px;
+        line-height: 45px;
+        text-align: center;
+        font-size: 16px;
+        letter-spacing: 2px;
+        background: url('@/assets/images/vent/homeNew/Select-btn.png') no-repeat;
+        background-size: 100% 100%;
+      }
+
+      .nav-menu-unactive {
+        position: relative;
+        width: 120px;
+        height: 60px;
+        line-height: 65px;
+        text-align: center;
+        margin: 0px 10px;
+        font-size: 16px;
+        letter-spacing: 2px;
+        background-size: 100% 100%;
+        cursor: pointer;
+      }
+
+      .nav-menu-item {
+        position: absolute;
+        top: 23px;
+        width: 130px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        box-sizing: border-box;
+
+        .nav-menu-content {
+          width: 100%;
+          height: 100%;
+          overflow-y: auto;
+          margin-top: 25%;
+          .nav-menu-List {
+            background: url('@/assets/images/vent/homeNew/menuList.png') no-repeat;
+            background-size: 100% 100%;
+          }
+          .menu-item-active {
+            color: #ddd;
+            z-index: 999;
+            width: 100%;
+            height: 36px;
+            line-height: 36px;
+            font-size: 14px;
+            background: url('@/assets/images/vent/homeNew/selectActive.png') no-repeat;
+            background-size: 100% 100%;
+          }
+
+          .menu-item-unactive {
+            color: #ddd;
+            width: 100%;
+            height: 40px;
+            line-height: 40px;
+            font-size: 14px;
+          }
+        }
+      }
+
+      @keyframes fadeIn {
+        from {
+          opacity: 0;
+        }
+
+        to {
+          opacity: 1;
+        }
+      }
+
+      /* 定义淡出动画 */
+      @keyframes fadeOut {
+        from {
+          opacity: 1;
+        }
+
+        to {
+          opacity: 0;
+        }
+      }
+    }
+
+    .userInfo {
+      width: 120px;
+      float: right;
+      background: url(/src/assets/images/vent/homeNew/user.png) no-repeat;
+      background-size: 100% 100%;
+      position: absolute;
+      top: 14px;
+      right: 0;
+      .userName {
+        margin-left: 40px;
+        font-size: 20px;
+      }
+    }
+  }
+</style>

+ 95 - 0
src/views/vent/home/configurable/bule/components/originalNew/leftHeader1.vue

@@ -0,0 +1,95 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <div class="headerType w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="divider"> </div>
+      <div class="headerType flex-basis-80% flex flex-items-center flex-grow-1 costume-header_right">
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  height: 30px;
+  margin-bottom: 10px;
+  width: 111%;
+  background: url('@/assets/images/vent/homeNew/Left-top.png') no-repeat;
+  background-size: 100% 100%;
+}
+.costume-header_left {
+}
+.costume-header_right {
+}
+.divider {
+  width: 40px;
+  background: url('@/assets/images/vent/homeNew/left-divider.png') no-repeat;
+  background-size: 100% 100%;
+}
+</style>

+ 89 - 0
src/views/vent/home/configurable/bule/components/originalNew/leftHeader2.vue

@@ -0,0 +1,89 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <div class="headerType w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="headerType flex-basis-50% flex flex-items-center flex-grow-1 costume-header_right">
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  width: 100%;
+  height: 30px;
+  margin-bottom: 10px;
+  background: url('@/assets/images/vent/homeNew/Left-bottom.png') no-repeat;
+  background-size: 100% 100%;
+}
+.costume-header_left {
+}
+.costume-header_right {
+}
+</style>

+ 133 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleBottom.vue

@@ -0,0 +1,133 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div class="left-solt" @click="scrollLeft"></div>
+    <div class="center-solt">
+      <div v-for="(item, index) in visibleTabs" :key="index" class="tab-item" :class="{ active: activeIndex === index }" @click="selectTab(index)">
+        <span class="tab-name">{{ item }}</span>
+      </div>
+    </div>
+    <div class="right-solt" @click="scrollRight"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed } from 'vue';
+
+  defineProps<{ title: string; visible: boolean }>();
+  const emit = defineEmits(['close', 'click']);
+
+  const tabList = ['Tab标题名称 1', 'Tab标题名称 2', 'Tab标题名称 3', 'Tab标题名称 4', 'Tab标题名称 5', 'Tab标题名称 6'];
+  const activeIndex = ref(0);
+  const currentStart = ref(0);
+  const visibleCount = 4;
+
+  const visibleTabs = computed(() => {
+    return tabList.slice(currentStart.value, currentStart.value + visibleCount);
+  });
+
+  function closeModel() {
+    emit('close');
+  }
+
+  function clickHandler() {
+    emit('click');
+  }
+
+  function selectTab(index: number) {
+    activeIndex.value = index;
+  }
+
+  function scrollLeft() {
+    if (currentStart.value > 0) {
+      currentStart.value--;
+    }
+  }
+
+  function scrollRight() {
+    if (currentStart.value + visibleCount < tabList.length) {
+      currentStart.value++;
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  .module-content {
+    color: #fff;
+    box-sizing: border-box;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+  }
+
+  .left-solt,
+  .right-solt {
+    width: 15%;
+    height: 60%;
+    cursor: pointer;
+    position: relative;
+    z-index: 1;
+  }
+
+  .left-solt {
+    background: url('@/assets/images/vent/homeNew/Bottom-left.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .right-solt {
+    background: url('@/assets/images/vent/homeNew/Bottom_right.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .center-solt {
+    display: flex;
+    width: 70%;
+    height: 60%;
+    justify-content: space-between;
+  }
+
+  .tab-item {
+    flex: 1;
+    text-align: center;
+    line-height: 60px;
+    cursor: pointer;
+    background: url('@/assets/images/vent/homeNew/tab-defult.png') no-repeat;
+    background-size: 100% 100%;
+    transition: all 0.3s;
+    margin: 0 5px;
+    color: #999;
+  }
+  .tab-name {
+    font-size: 14px;
+    display: inline-block;
+    float: right;
+    margin-right: 13px;
+  }
+  .tab-item.active {
+    background: url('@/assets/images/vent/homeNew/tab-active.png') no-repeat;
+    background-size: 100% 100%;
+    font-weight: bold;
+    color: #fff;
+    transform: translateY(-5px);
+  }
+
+  .module-slot {
+    height: calc(100% - 33px);
+    width: calc(100% - 15px);
+    margin-left: 10px;
+  }
+
+  .v-enter-active,
+  .v-leave-active {
+    transition: all 0.3s ease;
+  }
+
+  .v-enter-from,
+  .v-leave-to {
+    opacity: 0;
+    transform: translateY(-33px);
+  }
+</style>

+ 95 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleLeft.vue

@@ -0,0 +1,95 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/Lefttop.png');
+  }
+}
+
+.module-content {
+  --image-model_original_title_bg: url('@/assets/images/vent/homeNew/Lefttop.png');
+  --bg-height: 33px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+
+.module-content__title__expand {
+  width: 100%;
+  height: var(--bg-height);
+  background: var(--image-model_original_title_bg) no-repeat;
+  background-size: 100% 100%;
+  position: relative;
+  text-align: center;
+  line-height: var(--bg-height);
+}
+
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
+
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
+
+.module-slot {
+  height: calc(100% - 33px);
+  width: calc(100% - 20px);
+  backdrop-filter: blur(5px);
+  margin-left: 10px;
+}
+
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
+
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
+</style>

+ 104 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleLeftBottom.vue

@@ -0,0 +1,104 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  defineProps<{ title: string; visible: boolean }>();
+  const emit = defineEmits(['close', 'click']);
+
+  function closeModel() {
+    emit('close');
+  }
+  function clickHandler() {
+    emit('click');
+  }
+</script>
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  @{theme-deepblue} {
+    .module-content {
+      --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left3.png');
+    }
+  }
+
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left3.png');
+    --bg-height: 40px;
+    color: #fff;
+    box-sizing: border-box;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
+  .title {
+    font-size: 14px;
+    font-weight: 500;
+    color: #fff;
+    float: right;
+    padding-right: 25px;
+    font-family: 'douyuFont';
+    line-height: 30px;
+  }
+  .module-content__title__expand {
+    width: 100%;
+    height: var(--bg-height);
+    background: var(--image-model_original_title_bg) no-repeat;
+    background-size: 100% 100%;
+    position: relative;
+    text-align: center;
+
+    line-height: var(--bg-height);
+  }
+
+  // .module-content__title {
+  //   width: 50%;
+  //   height: var(--bg-height);
+  //   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+  //   background-size: 100% 100%;
+  //   position: relative;
+  //   text-align: right;
+  //   padding: 4px 10% 0 0;
+  // }
+
+  // 固定在父容器右上角的按钮图标
+  // .action-btn {
+  //   width: 18px;
+  //   height: 18px;
+  //   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+  //   position: absolute;
+  //   right: 0;
+  //   top: 0;
+  // }
+  // .close-btn {
+  //   transform: rotate(-90deg);
+  // }
+
+  .module-slot {
+    height: calc(100% - 33px);
+    width: calc(100% - 20px);
+    backdrop-filter: blur(5px);
+    margin-left: 10px;
+  }
+
+  // Transition动画相关
+  .v-enter-active,
+  .v-leave-active {
+    transition: all 0.3s ease;
+  }
+
+  .v-enter-from,
+  .v-leave-to {
+    // opacity: 1;
+    transform: translateX(-100%);
+    // transform: scaleY(0);
+    // transform-origin: center top;
+  }
+</style>

+ 103 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleLeftCenter.vue

@@ -0,0 +1,103 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  defineProps<{ title: string; visible: boolean }>();
+  const emit = defineEmits(['close', 'click']);
+
+  function closeModel() {
+    emit('close');
+  }
+  function clickHandler() {
+    emit('click');
+  }
+</script>
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  @{theme-deepblue} {
+    .module-content {
+      --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left2.png');
+    }
+  }
+
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left2.png');
+    --bg-height: 40px;
+    color: #fff;
+    box-sizing: border-box;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
+  .title {
+    font-size: 14px;
+    font-weight: 500;
+    color: #fff;
+    float: right;
+    padding-right: 25px;
+    line-height: 30px;
+    font-family: 'douyuFont';
+  }
+  .module-content__title__expand {
+    width: 100%;
+    height: var(--bg-height);
+    background: var(--image-model_original_title_bg) no-repeat;
+    background-size: 100% 100%;
+    position: relative;
+    text-align: center;
+    line-height: var(--bg-height);
+  }
+
+  // .module-content__title {
+  //   width: 50%;
+  //   height: var(--bg-height);
+  //   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+  //   background-size: 100% 100%;
+  //   position: relative;
+  //   text-align: right;
+  //   padding: 4px 10% 0 0;
+  // }
+
+  // 固定在父容器右上角的按钮图标
+  // .action-btn {
+  //   width: 18px;
+  //   height: 18px;
+  //   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+  //   position: absolute;
+  //   right: 0;
+  //   top: 0;
+  // }
+  // .close-btn {
+  //   transform: rotate(-90deg);
+  // }
+
+  .module-slot {
+    height: calc(100% - 33px);
+    width: calc(100% - 20px);
+    backdrop-filter: blur(5px);
+    margin-left: 10px;
+  }
+
+  // Transition动画相关
+  .v-enter-active,
+  .v-leave-active {
+    transition: all 0.3s ease;
+  }
+
+  .v-enter-from,
+  .v-leave-to {
+    // opacity: 1;
+    transform: translateX(-100%);
+    // transform: scaleY(0);
+    // transform-origin: center top;
+  }
+</style>

+ 104 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleLeftTop.vue

@@ -0,0 +1,104 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left1.png');
+  }
+}
+
+.module-content {
+  --image-model_original_title_bg: url('@/assets/images/vent/homeNew/left1.png');
+  --bg-height: 40px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+.title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #fff;
+  float: right;
+  padding-right: 25px;
+  font-family: 'douyuFont';
+  line-height: 30px;
+}
+.module-content__title__expand {
+  width: 100%;
+  height: var(--bg-height);
+  background: var(--image-model_original_title_bg) no-repeat;
+  background-size: 100% 100%;
+  position: relative;
+  text-align: center;
+  margin-left: 70px;
+  line-height: var(--bg-height);
+}
+
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
+
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
+
+.module-slot {
+  height: calc(100% - 33px);
+  width: calc(100% - 20px);
+  backdrop-filter: blur(5px);
+  margin-left: 10px;
+}
+
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
+
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
+</style>

+ 102 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleRightBottom.vue

@@ -0,0 +1,102 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right3.png');
+  }
+}
+
+.module-content {
+  --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right3.png');
+  --bg-height: 40px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+
+.module-content__title__expand {
+  width: 100%;
+  height: var(--bg-height);
+  background: var(--image-model_original_title_bg) no-repeat;
+  background-size: 100% 100%;
+  position: relative;
+  text-align: center;
+  line-height: var(--bg-height);
+}
+.title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #fff;
+  float: left;
+  padding-left: 23px;    font-family: 'douyuFont';
+  line-height: 30px;
+}
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
+
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
+
+.module-slot {
+  height: calc(100% - 33px);
+  width: calc(100% - 20px);
+  backdrop-filter: blur(5px);
+  margin-left: 10px;
+}
+
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
+
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
+</style>

+ 103 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleRightCenter.vue

@@ -0,0 +1,103 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right2.png');
+  }
+}
+
+.module-content {
+  --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right2.png');
+  --bg-height: 40px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+
+.module-content__title__expand {
+  width: 100%;
+  height: var(--bg-height);
+  background: var(--image-model_original_title_bg) no-repeat;
+  background-size: 100% 100%;
+  position: relative;
+  text-align: center;
+  line-height: var(--bg-height);
+}
+.title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #fff;
+  float: left;
+  padding-left: 23px;
+  font-family: 'douyuFont';
+  line-height: 30px;
+}
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
+
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
+
+.module-slot {
+  height: calc(100% - 33px);
+  width: calc(100% - 20px);
+  backdrop-filter: blur(5px);
+  margin-left: 10px;
+}
+
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
+
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
+</style>

+ 104 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleRightTop.vue

@@ -0,0 +1,104 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div v-if="title" class="module-content__title__expand">
+      <span class="action-btn close-btn" @click="closeModel"></span>
+      <span @click="clickHandler" class="title">{{ title }}</span>
+    </div>
+    <div class="module-slot">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+
+function closeModel() {
+  emit('close');
+}
+function clickHandler() {
+  emit('click');
+}
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@{theme-deepblue} {
+  .module-content {
+    --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right1.png');
+  }
+}
+
+.module-content {
+  --image-model_original_title_bg: url('@/assets/images/vent/homeNew/right1.png');
+  --bg-height: 40px;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+}
+
+.module-content__title__expand {
+  width: 100%;
+  height: var(--bg-height);
+  background: var(--image-model_original_title_bg) no-repeat;
+  background-size: 100% 100%;
+  position: relative;
+  text-align: center;
+  margin-left: -70px;
+  line-height: var(--bg-height);
+}
+.title {
+  font-size: 14px;
+  font-weight: 500;
+  color: #fff;
+  float: left;
+  font-family: 'douyuFont';
+  padding-left: 25px;
+  line-height: 30px;
+}
+// .module-content__title {
+//   width: 50%;
+//   height: var(--bg-height);
+//   background: url('@/assets/images/home-container/configurable/model_left_title_bg.png') no-repeat;
+//   background-size: 100% 100%;
+//   position: relative;
+//   text-align: right;
+//   padding: 4px 10% 0 0;
+// }
+
+// 固定在父容器右上角的按钮图标
+// .action-btn {
+//   width: 18px;
+//   height: 18px;
+//   background: url('@/assets/images/home-container/configurable/expand.svg') no-repeat center;
+//   position: absolute;
+//   right: 0;
+//   top: 0;
+// }
+// .close-btn {
+//   transform: rotate(-90deg);
+// }
+
+.module-slot {
+  height: calc(100% - 33px);
+  width: calc(100% - 20px);
+  backdrop-filter: blur(5px);
+  margin-left: 10px;
+}
+
+// Transition动画相关
+.v-enter-active,
+.v-leave-active {
+  transition: all 0.3s ease;
+}
+
+.v-enter-from,
+.v-leave-to {
+  // opacity: 1;
+  transform: translateX(-100%);
+  // transform: scaleY(0);
+  // transform-origin: center top;
+}
+</style>

+ 122 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleTop.vue

@@ -0,0 +1,122 @@
+<template>
+  <div v-if="visible" class="module-content">
+    <div class="left-solt"></div>
+    <div class="center-solt">
+      <div class="data-item">
+        <img class="data-icon" src="@/assets/images/vent/homeNew/zonghui.png" />
+        <div class="data-content">
+          <div class="title">总回风量(m³/min)</div>
+          <div class="air-num air-num1">25870</div>
+        </div>
+      </div>
+      <div class="divider"></div>
+      <div class="data-item">
+        <img class="data-icon" src="@/assets/images/vent/homeNew/zongjin.png" />
+        <div class="data-content">
+          <div class="title">总进风量(m³/min)</div>
+          <div class="air-num air-num2">24989</div>
+        </div>
+      </div>
+      <div class="divider"></div>
+      <div class="data-item">
+        <img class="data-icon" src="@/assets/images/vent/homeNew/jihua.png" />
+        <div class="data-content">
+          <div class="title">计划风量(m³/min)</div>
+          <div class="air-num air-num3">348</div>
+        </div>
+      </div>
+    </div>
+    <div class="right-solt"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  defineProps<{ title: string; visible: boolean }>();
+  const emit = defineEmits(['close', 'click']);
+</script>
+
+<style lang="less" scoped>
+  @import '/@/design/theme.less';
+
+  .module-content {
+    background: url('@/assets/images/vent/homeNew/content.png') no-repeat;
+    background-size: 100% 100%;
+    color: #fff;
+    box-sizing: border-box;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+  }
+
+  .left-solt {
+    left: 8%;
+    top: 13%;
+    width: 30px;
+    height: 30px;
+    cursor: pointer;
+    position: absolute;
+    background: url('@/assets/images/vent/homeNew/Top-left.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .right-solt {
+    right: 8%;
+    top: 13%;
+    width: 30px;
+    height: 30px;
+    cursor: pointer;
+    position: absolute;
+    background: url('@/assets/images/vent/homeNew/Top-right.png') no-repeat;
+    background-size: 100% 100%;
+  }
+
+  .center-solt {
+    display: flex;
+    position: absolute;
+    left: 15%;
+    bottom: 50%;
+    width: 70%;
+    height: 60%;
+    align-items: center;
+    justify-content: space-around;
+  }
+
+  .data-item {
+    left: 10%;
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    .data-icon {
+      width: 60px;
+      height: 100%;
+    }
+    .data-content {
+      display: flex;
+      flex-direction: column;
+      .title {
+        font-size: 16px;
+        font-weight: bold;
+        color: #999999;
+      }
+      .air-num {
+        font-size: 25px;
+      }
+    }
+  }
+  .air-num1 {
+    color: #4ca8d5;
+  }
+  .air-num2 {
+    color: #b9f3fc;
+  }
+  .air-num3 {
+    color: #4ad1cc;
+  }
+  .divider {
+    width: 1px;
+    height: 40px;
+    background: url('@/assets/images/vent/homeNew/Top-divider.png') no-repeat;
+  }
+</style>

+ 161 - 0
src/views/vent/home/configurable/bule/components/originalNew/moduleTopFire.vue

@@ -0,0 +1,161 @@
+
+<template>
+  <div v-if="visible" class="module-content">
+    <div class="left-solt">
+      <div class="left-img"></div>
+      <div class="left-title">智能注浆系统</div>
+    </div>
+    <div class="center-solt">
+      <div class="left-content">
+        <div class="img-left"></div>
+        <div class="title-left">监测区域</div>
+      </div>
+      <div class="center-content">
+        <div class="center-top">自燃倾向性等级:容易自燃</div>
+        <div class="center-bottom">低风险</div>
+      </div>
+      <div class="right-content">
+        <div class="title-right">火灾风险</div>
+        <div class="img-right"></div>
+      </div>
+    </div>
+    <div class="right-solt">
+      <div class="right-img"></div>
+      <div class="right-title">智能注氮系统</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+defineProps<{ title: string; visible: boolean }>();
+const emit = defineEmits(['close', 'click']);
+</script>
+
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+.module-content {
+  background: url('@/assets/images/fireNew/3-1.png') no-repeat;
+  background-size: 100% 100%;
+  color: #fff;
+  box-sizing: border-box;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+}
+
+.left-solt {
+  width: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  .left-img {
+    left: 4%;
+    top: 2%;
+    width: 75px;
+    height: 75px;
+    cursor: pointer;
+    position: absolute;
+    background: url('@/assets/images/fireNew/3-4.png') no-repeat;
+    background-size: 100% 100%;
+  }
+  .left-title {
+    left: 3%;
+    top: 85px;
+    font-size: 13px;
+    font-family: 'douyuFont';
+    font-weight: bold;
+    color: #fff;
+    position: absolute;
+  }
+}
+
+.right-solt {
+  width: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  .right-img {
+    right: 4%;
+    top: 2%;
+    width: 75px;
+    height: 75px;
+    cursor: pointer;
+    position: absolute;
+    background: url('@/assets/images/fireNew/3-5.png') no-repeat;
+    background-size: 100% 100%;
+  }
+  .right-title {
+    right: 3%;
+    top: 85px;
+    font-size: 13px;
+    font-family: 'douyuFont';
+    font-weight: bold;
+    color: #fff;
+    position: absolute;
+  }
+}
+.center-solt {
+  display: flex;
+  align-items: center;
+  width: 100%;
+}
+
+/* 左侧区域 */
+.left-content {
+  display: flex;
+  align-items: center; /* 垂直居中对齐 */
+}
+.img-left {
+  width: 50px; /* 图片尺寸 */
+  height: 50px;
+  margin-right: 8px; /* 图片文字间距 */
+  background: url('@/assets/images/fireNew/3-3.png') no-repeat center; /* 替换为实际图片路径 */
+  background-size: contain;
+}
+
+/* 右侧区域 */
+.right-content {
+  display: flex;
+  align-items: center;
+}
+.img-right {
+  width: 50px;
+  height: 50px;
+  margin-left: -10px;
+  background: url('@/assets/images/fireNew/3-2.png') no-repeat center; /* 替换为实际图片路径 */
+  background-size: contain;
+}
+
+/* 中间区域 */
+.center-content {
+  text-align: center;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 330px;
+}
+/* 文字样式 */
+.title-left {
+  width: 100px;
+  font-size: 20px;
+}
+.title-right {
+  width: 100px;
+  font-size: 20px;
+}
+.center-top {
+  margin-bottom: 14px;
+  font-size: 20px;
+  margin-left: -18px;
+}
+.center-bottom {
+  font-size: 30px;
+  color: #52b4e5; /* 红色字体 */
+  font-weight: bold;
+  letter-spacing: 15px;
+  margin-bottom: 18px;
+}
+</style>

+ 97 - 0
src/views/vent/home/configurable/bule/components/originalNew/rightHeader1.vue

@@ -0,0 +1,97 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <div class="headerType w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="divider"> </div>
+      <div class="headerType flex-basis-80% flex flex-items-center flex-grow-1 costume-header_right">
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  height: 30px;
+  margin-bottom: 10px;
+  width: 111%;
+  background: url('@/assets/images/vent/homeNew/Right-top.png') no-repeat;
+  background-size: 100% 100%;
+  margin-left: -40px;
+}
+.costume-header_left {
+  margin-left: 20px;
+}
+.costume-header_right {
+}
+.divider {
+  width: 40px;
+  background: url('@/assets/images/vent/homeNew/right-divider.png') no-repeat;
+  background-size: 100% 100%;
+}
+</style>

+ 90 - 0
src/views/vent/home/configurable/bule/components/originalNew/rightHeader2.vue

@@ -0,0 +1,90 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <div class="headerType w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="headerType flex-basis-50% flex flex-items-center flex-grow-1 costume-header_right">
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  width: 100%;
+  height: 30px;
+  margin-bottom: 10px;
+  background: url('@/assets/images/vent/homeNew/Right-mid.png') no-repeat;
+  background-size: 100% 100%;
+}
+.costume-header_left {
+  margin-left: 20px;
+}
+.costume-header_right {
+}
+</style>

+ 90 - 0
src/views/vent/home/configurable/bule/components/originalNew/rightHeader3.vue

@@ -0,0 +1,90 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <!-- Header部分 -->
+  <div v-if="headerConfig.show" class="w-100% flex costume-header">
+    <!-- 选择下拉框,自动填充剩余空间,这种实现是因为 Select 不支持 suffix -->
+    <Dropdown
+      v-if="headerConfig.selector.show"
+      class="flex-grow-1 costume-header_left"
+      :trigger="['click']"
+      :bordered="false"
+      @open-change="visible = $event"
+    >
+      <div class="flex-basis-100% flex flex-items-center" @click.prevent>
+        <div class="headerType w-100px flex-grow-1 overflow-hidden whitespace-nowrap text-ellipsis">
+          {{ selectedDeviceLabel }}
+        </div>
+        <CaretUpOutlined class="w-30px" v-if="visible" />
+        <CaretDownOutlined class="w-30px" v-else />
+      </div>
+      <template #overlay>
+        <Menu :selected-keys="[selectedDeviceID]" @click="selectHandler">
+          <MenuItem v-for="item in options" :key="item.value" :title="item.label">
+            {{ item.label }}
+          </MenuItem>
+        </Menu>
+      </template>
+    </Dropdown>
+    <template v-if="headerConfig.slot.show">
+      <div class="headerType flex-basis-50% flex flex-items-center flex-grow-1 costume-header_right">
+        <div class="flex-grow-1">
+          {{ selectedDeviceSlot }}
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Config } from '../../../../../deviceManager/configurationTable/types';
+import { useInitModule } from '../../../hooks/useInit';
+import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
+import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
+
+const props = defineProps<{
+  moduleData: Config['moduleData'];
+  deviceType: Config['deviceType'];
+  data: any;
+}>();
+
+const emit = defineEmits(['select']);
+
+const visible = ref(false);
+const headerConfig = props.moduleData.header;
+const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, init } = useInitModule(
+  props.deviceType,
+  props.moduleData
+);
+
+function selectHandler({ key }) {
+  selectedDeviceID.value = key;
+  emit('select', selectedDevice.value);
+}
+
+watch(
+  () => props.data,
+  (d) => {
+    init(d);
+    emit('select', selectedDevice.value);
+  },
+  {
+    immediate: true,
+  }
+);
+</script>
+<style scoped>
+@import '/@/design/theme.less';
+
+.costume-header {
+  width: 100%;
+  height: 30px;
+  margin-bottom: 10px;
+  background: url('@/assets/images/vent/homeNew/Right-bottom.png') no-repeat;
+  background-size: 100% 100%;
+}
+.costume-header_left {
+  margin-left: 20px;
+}
+.costume-header_right {
+}
+</style>

+ 166 - 0
src/views/vent/home/configurable/bule/ventNew.vue

@@ -0,0 +1,166 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="company-home">
+    <div style="width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 0">
+      <!-- <VentModal /> -->
+    </div>
+    <div class="top-bg">
+      <!-- <img style="width: 300px; height: 40px; position: fixed; left: 5px; top: 5px" src="./meeee.png" /> -->
+      <!-- <div class="main-title">{{ mainTitle }}</div> -->
+      <NewNav :Title="mainTitle" />
+    </div>
+    <div class="left-t"> </div>
+    <div class="right-t"> </div>
+    <template v-if="isNew">
+      <ModuleNew
+        v-for="cfg in configs"
+        :key="cfg.deviceType"
+        :show-style="cfg.showStyle"
+        :module-data="cfg.moduleData"
+        :module-name="cfg.moduleName"
+        :device-type="cfg.deviceType"
+        :data="data"
+        :visible="true"
+      />
+    </template>
+  </div>
+</template>
+<script lang="ts" setup>
+import { onMounted, onUnmounted } from 'vue';
+// import { CaretDownOutlined } from '@ant-design/icons-vue';
+// import MonitorCenter from './components/MonitorCenter.vue';
+import NewNav from './components/originalNew/NewNav.vue';
+import { useInitConfigs, useInitPage } from '../hooks/useInit';
+import ModuleNew from './components/ModuleNew.vue';
+// import { useRoute } from 'vue-router';
+import VentModal from '/@/components/vent/micro/ventModal.vue';
+import { getHomeData } from '../configurable.api';
+// import { useRoute } from 'vue-router';
+import { testConfigVentNew } from '../configurable.data';
+const { configs, isNew, fetchConfigs } = useInitConfigs();
+const { mainTitle, data, updateData, updateEnhancedConfigs } = useInitPage('一通三防智能管控平台');
+// const route = useRoute();
+let interval: number | undefined;
+
+onMounted(() => {
+  fetchConfigs('vent').then(() => {
+    configs.value = testConfigVentNew;
+    updateEnhancedConfigs(configs.value);
+
+    getHomeData({}).then(updateData);
+  });
+  setInterval(() => {
+    getHomeData({}).then(updateData);
+  }, 60000);
+});
+
+onUnmounted(() => {
+  clearInterval(interval);
+});
+</script>
+<style lang="less" scoped>
+@import '/@/design/theme.less';
+
+@font-face {
+  font-family: 'douyuFont';
+  src: url('../../../../assets/font/douyuFont.otf');
+}
+
+@{theme-deepblue} {
+  .company-home {
+    --image-modal-top: url('@/assets/images/vent/homeNew/modaltop.png');
+  }
+}
+
+.company-home {
+  --image-modal-top: url('@/assets/images/vent/homeNew/modaltop.png');
+  width: 100%;
+  height: 100%;
+  color: @white;
+  position: relative;
+  background: url('@/assets/images/vent/homeNew/bg.png') no-repeat center;
+
+  .top-bg {
+    width: 100%;
+    height: 56px;
+    background: var(--image-modal-top) no-repeat center;
+    position: absolute;
+    z-index: 1;
+    .main-title {
+      height: 56px;
+      font-family: 'douyuFont';
+      font-size: 20px;
+      letter-spacing: 2px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
+    .top-nav {
+      position: absolute;
+      top: 0;
+      width: 880px;
+      height: 100%;
+      display: flex;
+      justify-content: flex-start;
+    }
+  }
+  .left-t {
+    position: absolute;
+    width: 28%;
+    height: 100%;
+    background: url('@/assets/images/vent/homeNew/leftContent.png') no-repeat center;
+    z-index: 0;
+  }
+  .right-t {
+    position: absolute;
+    width: 172%;
+    height: 100%;
+    background: url('@/assets/images/vent/homeNew/rightContent.png') no-repeat center;
+    z-index: 0;
+  }
+  // .module-left {
+  //   position: absolute;
+  //   width: 450px;
+  //   height: 280px;
+  //   left: 0;
+  // }
+  // .module-right {
+  //   position: absolute;
+  //   width: 450px;
+  //   height: 280px;
+  //   right: 0;
+  // }
+  // .module-bottom {
+  //   position: absolute;
+  //   width: 1000px;
+  //   height: 280px;
+  // }
+  .module-dropdown {
+    padding: 10px;
+    background-image: @vent-configurable-dropdown;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
+    color: @vent-font-color;
+    position: absolute;
+    top: 70px;
+    right: 460px;
+  }
+  .module-dropdown-original {
+    padding: 10px;
+    background-image: @vent-configurable-dropdown;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
+    color: @vent-font-color;
+    position: absolute;
+    top: 70px;
+    right: 460px;
+  }
+  .module-trigger-button {
+    color: @vent-font-color;
+    background-image: @vent-configurable-dropdown;
+    border: none;
+    border-bottom: 2px solid @vent-configurable-home-light-border;
+  }
+}
+:deep(.loading-box) {
+  position: unset;
+}
+</style>

+ 311 - 311
src/views/vent/home/configurable/components/content-New.vue

@@ -141,359 +141,359 @@
   </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 ComplexListNew from './detail/ComplexList-New.vue';
-  import GalleryList from './detail/GalleryList.vue';
-  import CustomTable from './detail/CustomTable-New.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 MiniBoardNew from './detail/MiniBoard-New.vue';
-  import CustomTableNew from './detail/CustomTable-New.vue';
-  // import FIreWarn from './preset/FIreWarn.vue';
-  // import FIreControl from './preset/FIreControl.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 ComplexListNew from './detail/ComplexList-New.vue';
+import GalleryList from './detail/GalleryList.vue';
+import CustomTable from './detail/CustomTable-New.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 MiniBoardNew from './detail/MiniBoard-New.vue';
+import CustomTableNew from './detail/CustomTable-New.vue';
+// import FIreWarn from './preset/FIreWarn.vue';
+// import FIreControl from './preset/FIreControl.vue';
 
-  const props = defineProps<{
-    data: any;
-    moduleData: Config['moduleData'];
-  }>();
+const props = defineProps<{
+  data: any;
+  moduleData: Config['moduleData'];
+}>();
 
-  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) || [];
+
+  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) || [];
+        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);
 
-    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: 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();
-          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) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+        break;
+      }
+      case 'chart': {
+        const cfg = chart.shift();
+        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);
+        arr.push({
+          ...item,
+          config: cfg,
+          data,
+        });
+        break;
+      }
+      case 'table': {
+        const cfg = table.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
-          arr.push({
-            ...cfg,
-            ...item,
-            columns: cfg.columns,
-            data,
-          });
-          break;
-        }
-        default: {
-          const cfg = preset.shift();
-          if (!cfg) break;
-          const data = getData(refData, cfg.readFrom, cfg.parser);
+        arr.push({
+          ...cfg,
+          ...item,
+          columns: cfg.columns,
+          data,
+        });
+        break;
+      }
+      default: {
+        const cfg = preset.shift();
+        if (!cfg) break;
+        const data = getData(refData, cfg.readFrom, cfg.parser);
 
-          arr.push({
-            ...item,
-            data,
-            config: cfg,
-          });
-          break;
-        }
+        arr.push({
+          ...item,
+          data,
+          config: cfg,
+        });
+        break;
       }
-      return 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;
-  }
-  .content__background {
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    top: 0;
-    left: 0;
-    z-index: 0;
-    object-fit: fill;
-  }
-  .image__background {
-    width: 35%;
-    height: 61%;
-    left: 30%;
-  }
-  .content__module {
-    // margin-top: 5px;
-    // margin-bottom: 5px;
-    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__moduleFire {
-    width: 100%;
-    height: 100%;
-    margin-left: -24% !important;
-  }
-  .content__module_dust {
-    background: url('@/assets/images/vent/homeNew/bottomBg.png');
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-    width: 100%;
-    height: 100%;
-    padding: 0 34px;
-  }
-  // .content__module:first-of-type {
-  //   margin-top: 0;
-  // }
-  // .content__module:last-of-type {
-  //   margin-bottom: 0;
-  // }
-  ::-webkit-scrollbar {
-    width: 5px !important;
-  }
-  ::-webkit-scrollbar-thumb {
-    width: 5px !important;
-  }
+.content {
+  height: calc(100% - 30px);
+  position: relative;
+  // z-index: -2;
+  display: flex;
+  flex-direction: column;
+}
+.content__background {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 0;
+  object-fit: fill;
+}
+.image__background {
+  width: 35%;
+  height: 61%;
+  left: 30%;
+}
+.content__module {
+  // margin-top: 5px;
+  // margin-bottom: 5px;
+  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__moduleFire {
+  width: 100%;
+  height: 100%;
+  margin-left: -24% !important;
+}
+.content__module_dust {
+  background: url('@/assets/images/vent/homeNew/bottomBg.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  width: 100%;
+  height: 100%;
+  padding: 0 34px;
+}
+// .content__module:first-of-type {
+//   margin-top: 0;
+// }
+// .content__module:last-of-type {
+//   margin-bottom: 0;
+// }
+::-webkit-scrollbar {
+  width: 5px !important;
+}
+::-webkit-scrollbar-thumb {
+  width: 5px !important;
+}
 
-  :deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
-    /* background-color: transparent; */
-    color: #fff;
-  }
-  :deep(.zxm-select-arrow) {
-    color: #fff;
-  }
-  :deep(.zxm-select-selection-item) {
-    color: #fff !important;
-  }
-  :deep(.zxm-select-selection-placeholder) {
-    color: #fff !important;
-  }
-  :deep(.dialog-overlay) {
-    width: 100%;
-    height: 100%;
-    position: unset;
-    box-shadow: unset;
-  }
+:deep(.zxm-select:not(.zxm-select-customize-input) .zxm-select-selector) {
+  /* background-color: transparent; */
+  color: #fff;
+}
+:deep(.zxm-select-arrow) {
+  color: #fff;
+}
+:deep(.zxm-select-selection-item) {
+  color: #fff !important;
+}
+:deep(.zxm-select-selection-placeholder) {
+  color: #fff !important;
+}
+:deep(.dialog-overlay) {
+  width: 100%;
+  height: 100%;
+  position: unset;
+  box-shadow: unset;
+}
 
-  ::-webkit-scrollbar {
-    width: 5px !important;
-  }
-  ::-webkit-scrollbar-thumb {
-    width: 5px !important;
-  }
+::-webkit-scrollbar {
+  width: 5px !important;
+}
+::-webkit-scrollbar-thumb {
+  width: 5px !important;
+}
 </style>