浏览代码

[Feat 0000]新首页样式组件开发

bobo04052021@163.com 3 天之前
父节点
当前提交
c950f08075
共有 23 个文件被更改,包括 1168 次插入19 次删除
  1. 二进制
      src/assets/images/vent/homeNew/Left-bottom.png
  2. 二进制
      src/assets/images/vent/homeNew/Left-top.png
  3. 二进制
      src/assets/images/vent/homeNew/Right-bottom.png
  4. 二进制
      src/assets/images/vent/homeNew/Right-mid.png
  5. 二进制
      src/assets/images/vent/homeNew/Right-top.png
  6. 二进制
      src/assets/images/vent/homeNew/left-divider.png
  7. 二进制
      src/assets/images/vent/homeNew/right-divider.png
  8. 2 0
      src/views/vent/deviceManager/configurationTable/types.ts
  9. 71 12
      src/views/vent/home/configurable/components/ModuleNew.vue
  10. 3 3
      src/views/vent/home/configurable/components/content.vue
  11. 2 2
      src/views/vent/home/configurable/components/originalNew/NewNav.vue
  12. 95 0
      src/views/vent/home/configurable/components/originalNew/leftHeader1.vue
  13. 89 0
      src/views/vent/home/configurable/components/originalNew/leftHeader2.vue
  14. 104 0
      src/views/vent/home/configurable/components/originalNew/moduleLeftBottom.vue
  15. 102 0
      src/views/vent/home/configurable/components/originalNew/moduleLeftCenter.vue
  16. 104 0
      src/views/vent/home/configurable/components/originalNew/moduleLeftTop.vue
  17. 102 0
      src/views/vent/home/configurable/components/originalNew/moduleRightBottom.vue
  18. 103 0
      src/views/vent/home/configurable/components/originalNew/moduleRightCenter.vue
  19. 104 0
      src/views/vent/home/configurable/components/originalNew/moduleRightTop.vue
  20. 97 0
      src/views/vent/home/configurable/components/originalNew/rightHeader1.vue
  21. 90 0
      src/views/vent/home/configurable/components/originalNew/rightHeader2.vue
  22. 90 0
      src/views/vent/home/configurable/components/originalNew/rightHeader3.vue
  23. 10 2
      src/views/vent/home/configurable/configurable.data.ts

二进制
src/assets/images/vent/homeNew/Left-bottom.png


二进制
src/assets/images/vent/homeNew/Left-top.png


二进制
src/assets/images/vent/homeNew/Right-bottom.png


二进制
src/assets/images/vent/homeNew/Right-mid.png


二进制
src/assets/images/vent/homeNew/Right-top.png


二进制
src/assets/images/vent/homeNew/left-divider.png


二进制
src/assets/images/vent/homeNew/right-divider.png


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

@@ -120,6 +120,8 @@ export interface ShowStyle {
   version: '原版' | '新版' | '普通版' | '保德';
   /** 模块的位置,即定位,特殊情况下可以自定义定位 */
   position: string;
+  // 模块位置  根据这个配置决定使用hearder的方式
+  headerPosition?: 'leftTop' | 'leftCenter' | 'leftBottom' | 'rightTop' | 'rightCenter' | 'rightBottom' | 'centerTop' | 'centerBottom';
 }
 
 /**

+ 71 - 12
src/views/vent/home/configurable/components/ModuleNew.vue

@@ -11,20 +11,37 @@
     class="component-module"
   >
     <slot>
-      <Header :deviceType="deviceType" :moduleData="moduleData" :data="data" @select="selectedData = $event" />
-      <Content :style="{ height: header.show ? 'calc(100% - 30px)' : '100%' }" :moduleData="moduleData" :data="selectedData" />
+      <!-- <Header :deviceType="deviceType" :moduleData="moduleData" :data="data" @select="selectedData = $event" /> -->
+      <component
+        :is="getHeaderComponent(showStyle.headerPosition)"
+        :deviceType="deviceType"
+        :moduleData="moduleData"
+        :data="data"
+        @select="selectedData = $event"
+      />
+      <Content :style="{ height: header.show ? 'calc(100% - 45px)' : '100%' }" :moduleData="moduleData" :data="selectedData" />
     </slot>
   </component>
 </template>
 <script lang="ts" setup>
 import Header from './header.vue';
 import Content from './content.vue';
-import ModuleLeft from './originalNew/moduleLeft.vue';
+import ModuleLeftTop from './originalNew/moduleLeftTop.vue';
+import ModuleLeftCenter from './originalNew/moduleLeftCenter.vue';
+import ModuleLeftBottom from './originalNew/moduleLeftBottom.vue';
+import ModuleRightTop from './originalNew/moduleRightTop.vue';
+import ModuleRightCenter from './originalNew/moduleRightCenter.vue';
+import ModuleRightBottom from './originalNew/moduleRightBottom.vue';
 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 LeftHeader1 from './originalNew/leftHeader1.vue';
+import LeftHeader2 from './originalNew/leftHeader2.vue';
+import RightHeader1 from './originalNew/rightHeader1.vue';
+import RightHeader2 from './originalNew/rightHeader2.vue';
+import RightHeader3 from './originalNew/rightHeader3.vue';
 // import { ModuleProps } from '../types';
 
 const props = defineProps<{
@@ -47,27 +64,69 @@ const selectedData = ref();
 const style = computed(() => {
   const size = props.showStyle.size;
   const position = props.showStyle.position;
+  // const headerPosition = props.showStyle.headerPosition;
   return size + position;
 });
 
 // 根据配置里的定位判断应该使用哪个module组件
-function getModuleComponent({ size, position }) {
-  const [_, width] = size.match(/width:([0-9]+)px/) || [];
-  if (position.includes('top') && parseInt(width) > 800) {
+function getModuleComponent({ size, position, headerPosition }) {
+  // const [_, width] = size.match(/width:([0-9]+)px/) || [];
+  // if (position.includes('top') && parseInt(width) > 800) {
+  //   return ModuleTop;
+  // }
+  // if (position.includes('bottom')) {
+  //   return ModuleBottom;
+  // }
+  if (headerPosition === 'centerTop') {
     return ModuleTop;
   }
-  if (position.includes('bottom')) {
+  if (headerPosition === 'centerBottom') {
     return ModuleBottom;
   }
-  if (position.includes('left')) {
-    return ModuleLeft;
+  if (headerPosition === 'leftTop') {
+    return ModuleLeftTop;
   }
-  if (position.includes('right')) {
-    return ModuleLeft;
+  if (headerPosition === 'leftCenter') {
+    return ModuleLeftCenter;
   }
+  if (headerPosition === 'leftBottom') {
+    return ModuleLeftBottom;
+  }
+  if (headerPosition === 'rightTop') {
+    return ModuleRightTop;
+  }
+  if (headerPosition === 'rightCenter') {
+    return ModuleRightCenter;
+  }
+  if (headerPosition === 'rightBottom') {
+    return ModuleRightBottom;
+  }
+  // if (position.includes('left')) {
+  //   return ModuleLeft;
+  // }
+  // if (position.includes('right')) {
+  //   return ModuleLeft;
+  // }
   return ModuleBottom;
 }
-
+function getHeaderComponent(headerType) {
+  if (headerType === 'leftTop') {
+    return LeftHeader1;
+  }
+  if (headerType === 'leftBottom') {
+    return LeftHeader2;
+  }
+  if (headerType === 'rightTop') {
+    return RightHeader1;
+  }
+  if (headerType === 'rightCenter') {
+    return RightHeader2;
+  }
+  if (headerType === 'rightBottom') {
+    return RightHeader3;
+  }
+  return Header; // 默认返回顶部模块
+}
 function redirectTo() {
   const { to } = props.moduleData;
   if (!to) return;

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

@@ -396,8 +396,8 @@ const layoutConfig = computed(() => {
   object-fit: fill;
 }
 .image__background {
-  width: 40%;
-  height: 70%;
+  width: 35%;
+  height: 61%;
   left: 30%;
 }
 .content__module {
@@ -411,7 +411,7 @@ const layoutConfig = computed(() => {
   background-repeat: no-repeat;
   background-size: 100% 100%;
   height: 129px;
-  margin-top: 24%;
+  margin-top: 20%;
 }
 // .content__module:first-of-type {
 //   margin-top: 0;

+ 2 - 2
src/views/vent/home/configurable/components/originalNew/NewNav.vue

@@ -27,9 +27,9 @@
       </div>
       <div class="nav-menu-right">
         <div v-for="(item, index) in rightMenus" :key="index">
-          <div :class="activeIndex == index ? 'nav-menu-active' : 'nav-menu-unactive'" :key="index" @click="menuRightClick(index)">
+          <div :class="activeIndexR == index ? 'nav-menu-active' : 'nav-menu-unactive'" :key="index" @click="menuRightClick(index)">
             <div style="color: #ddd">{{ item.name }}</div>
-            <div v-if="activeIndex == index && isShowMenuItemR" class="nav-menu-item">
+            <div v-if="activeIndexR == index && isShowMenuItemR" class="nav-menu-item">
               <div class="nav-menu-content">
                 <div class="nav-menu-List">
                   <div

+ 95 - 0
src/views/vent/home/configurable/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/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>

+ 104 - 0
src/views/vent/home/configurable/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>

+ 102 - 0
src/views/vent/home/configurable/components/originalNew/moduleLeftCenter.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/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/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/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/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/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>

+ 97 - 0
src/views/vent/home/configurable/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/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/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>

+ 10 - 2
src/views/vent/home/configurable/configurable.data.ts

@@ -196,7 +196,7 @@ export const testConfigVent: Config[] = [
       position: 'top:575px;left:15px;',
     },
   },
-   
+
   {
     deviceType: 'sys_wind',
     moduleName: '风量监测',
@@ -428,7 +428,7 @@ export const testConfigVent: Config[] = [
       position: 'bottom:15px;left:975px;',
     },
   },
- {
+  {
     // deviceType: 'warn',
     // moduleName: '预警监测',
     deviceType: '',
@@ -2141,6 +2141,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:380px;height:280px;',
       version: '原版',
       position: 'top:60px;left:0;',
+      headerPosition: 'leftTop',
     },
   },
   {
@@ -2193,6 +2194,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:377px;height:280px;',
       version: '原版',
       position: 'top:350px;left:0;',
+      headerPosition: 'leftCenter',
     },
   },
   {
@@ -2271,6 +2273,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:387px;height:280px;',
       version: '新版',
       position: 'top:640px;left:0;',
+      headerPosition: 'leftBottom',
     },
   },
   {
@@ -2318,6 +2321,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:1000px;height:120px;',
       version: '新版',
       position: 'top:85px;left:460px;',
+      headerPosition: 'centerTop',
     },
   },
   {
@@ -2365,6 +2369,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:1000px;height:100px;',
       version: '新版',
       position: 'bottom:0;left:460px;',
+      headerPosition: 'centerBottom',
     },
   },
   {
@@ -2436,6 +2441,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:377px;height:280px;',
       version: '原版',
       position: 'top:60px;right:0;',
+      headerPosition: 'rightTop',
     },
   },
 
@@ -2504,6 +2510,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:377px;height:280px;',
       version: '新版',
       position: 'top:350px;right:0;',
+      headerPosition: 'rightCenter',
     },
   },
   {
@@ -2583,6 +2590,7 @@ export const testConfigVentNew: Config[] = [
       size: 'width:377px;height:280px;',
       version: '新版',
       position: 'top:640px;right:0;',
+      headerPosition: 'rightBottom',
     },
   },
 ];