Browse Source

[Wip 0000] 可配置首页图表模块开发

houzekong 8 months ago
parent
commit
c6a7018f8c

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

@@ -51,7 +51,7 @@ export interface ModuleData {
   /** 模块的布局,使用规定的枚举组合为一个数组,代表着从上到下所应展示的元素 */
   layout: ('board' | 'list' | 'chart')[];
   /** 展示牌元素 */
-  board?: {
+  board: {
     /** 展示牌说明内容 */
     label: string;
     /** 展示牌预设的背景类型 */
@@ -62,15 +62,15 @@ export interface ModuleData {
     prop: string;
   }[];
   /** 列表元素 */
-  list?: {
+  list: {
     color: string;
     /** 列表项说明内容 */
     label: string;
     formatter?: string;
     prop: string;
   }[];
-  /** 图表元素 */
-  chart?: {
+  /** 图表元素,仅第一个配置项将生效 */
+  chart: {
     /** 图表通用类型,一个类型对应一种图表预设 */
     type: 'pie' | 'bar' | 'line';
     /** 读取数据时的基础路径,例如如果图表依赖一个数组,那么该项应设置能读取到该数组的路径。例如:readData.history */
@@ -80,12 +80,12 @@ export interface ModuleData {
     /** 排序规则,desc降序,asc升序 */
     order?: 'desc' | 'asc';
     /** 图表x轴配置(若有),例如:{ prop: 'strInstallPos' } */
-    xAxis?: {
+    xAxis: {
       formatter?: string;
       prop: string;
     }[];
     /** 图表y轴配置(若有),例如:['风量', '风速'] */
-    yAxis?: {
+    yAxis: {
       label: string;
       align: 'left' | 'right';
     }[];

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

@@ -1,3 +1,4 @@
+<!-- eslint-disable vue/multi-word-component-names -->
 <template>
   <!-- Header部分 -->
   <div v-if="headerConfig.show" class="w-100% flex content__header">
@@ -34,8 +35,9 @@
       </div>
     </template>
   </div>
-  <!-- 主内容部分 -->
+  <!-- 主内容部分 -->
   <div class="content">
+    <!-- 背景 -->
     <img v-if="background.show && background.type === 'image'" class="content__background" :src="background.link" />
     <video
       v-if="background.show && background.type === 'video'"
@@ -49,20 +51,18 @@
       Not Supportted Link Or Browser
     </video>
     <template v-for="val in layout" :key="val">
+      <!-- 告示板部分 -->
       <div v-if="val === 'board'" class="flex flex-justify-around pt-10px pb-10px">
         <MiniBoard v-for="item in boardConfig" :key="item.prop" :type="item.type" :label="item.label" :layout="item.layout" :value="item.value" />
       </div>
-      <template v-if="val === 'chart'"></template>
-      <div v-if="val === 'list'" class="timeline">
-        <div v-for="item in listConfig" :key="item.prop" class="flex items-center timeline-item">
-          <div class="timeline-item__icon" :class="`timeline-item__icon_${item.color}`"></div><div class="timeline-item__dot"></div>
-          <div class="timeline-item__label">{{ item.label }}</div>
-          <div :class="`timeline-item__value_${item.color}`">
-            {{ item.value }}
-          </div>
-        </div>
-        <div class="timeline-component"></div>
-      </div>
+      <!-- 图表部分,这部分通常需要填充,有告示板、Header等内容需要填充父级 -->
+      <template v-if="val === 'chart'">
+        <CustomChart :chart-config="chartConfig" :chart-data="chartData" style="flex-grow: 1" />
+      </template>
+      <!-- 时间线列表部分,这部分通常是占一整个模块的 -->
+      <template v-if="val === 'list'">
+        <TimelineList :list-config="listConfig" />
+      </template>
     </template>
   </div>
 </template>
@@ -73,7 +73,10 @@
   import { MenuItem, Menu, Dropdown } from 'ant-design-vue';
   import { SwapOutlined, CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons-vue';
   import MiniBoard from './miniBoard.vue';
+  import TimelineList from './timelineList.vue';
   import { getFormattedText } from '../../../deviceManager/configurationTable/adapters';
+  import CustomChart from './customChart.vue';
+  import { get } from 'lodash-es';
 
   const props = defineProps<{
     deviceType: Config['deviceType'];
@@ -81,7 +84,7 @@
     showStyle: Config['showStyle'];
   }>();
 
-  const { header: headerConfig, background, board, layout, list } = props.moduleData;
+  const { header: headerConfig, background, board, layout, list, chart } = props.moduleData;
 
   // 额外的 header 相关的变量
   const headerVisible = ref(false);
@@ -111,6 +114,15 @@
     });
   });
 
+  const chartConfig = computed(() => {
+    return chart[0];
+  });
+  const chartData = computed(() => {
+    const data = selectedDevice.value;
+    console.log('debug', data, get(data, ''));
+    return get(data, chart[0]?.readFrom, []);
+  });
+
   const { selectedDeviceID, selectedDevice, selectedDeviceSlot, selectedDeviceLabel, options, fetchDevices } = useInitDevices(
     props.deviceType,
     headerConfig
@@ -145,6 +157,8 @@
     height: calc(100% - 30px);
     position: relative;
     z-index: -2;
+    display: flex;
+    flex-direction: column;
   }
   .content__background {
     width: 100%;
@@ -168,83 +182,4 @@
   ::v-deep .zxm-select-selection-placeholder {
     color: #fff !important;
   }
-
-  /* Timeline 相关的样式 */
-
-  .timeline-item {
-    height: 20%;
-  }
-  .timeline-item__icon_red {
-    background-image: url('@/assets/images/home-container/configurable/warn_icon_5.png');
-  }
-  .timeline-item__icon_orange {
-    background-image: url('@/assets/images/home-container/configurable/warn_icon_4.png');
-  }
-  .timeline-item__icon_yellow {
-    background-image: url('@/assets/images/home-container/configurable/warn_icon_3.png');
-  }
-  .timeline-item__icon_green {
-    background-image: url('@/assets/images/home-container/configurable/warn_icon_2.png');
-  }
-  .timeline-item__icon_blue {
-    background-image: url('@/assets/images/home-container/configurable/warn_icon_1.png');
-  }
-  .timeline-item__icon {
-    width: 33px;
-    height: 35px;
-    margin-left: 50px;
-    background-repeat: no-repeat;
-    background-position: center center;
-  }
-  .timeline-item__dot {
-    width: 10px;
-    height: 10px;
-    margin-left: 70px;
-    background-color: @vent-gas-primary-bg;
-    border-radius: 5px;
-    position: relative;
-  }
-  .timeline-item__dot::before {
-    content: '';
-    position: absolute;
-    top: -3px;
-    left: -3px;
-    width: 16px;
-    height: 16px;
-    border-radius: 8px;
-    border: 1px solid @vent-gas-tab-border;
-  }
-  .timeline-item__label {
-    width: 100px;
-    margin-left: 70px;
-  }
-  .timeline-item__value_red {
-    color: red;
-  }
-  .timeline-item__value_orange {
-    color: orange;
-  }
-  .timeline-item__value_yellow {
-    color: yellow;
-  }
-  .timeline-item__value_green {
-    color: yellowgreen;
-  }
-  .timeline-item__value_blue {
-    color: lightblue;
-  }
-
-  .timeline {
-    height: 220px;
-    padding: 5px;
-    position: relative;
-  }
-  .timeline-component {
-    position: absolute;
-    width: 2px;
-    height: 180px;
-    top: 20px;
-    left: 162px;
-    background-image: @vent-configurable-home-timeline;
-  }
 </style>

+ 221 - 0
src/views/vent/home/configurable/components/customChart.vue

@@ -0,0 +1,221 @@
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts" setup>
+  import { ref, Ref, watch } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  import { get } from 'lodash-es';
+  import { Config } from '/@/views/vent/deviceManager/configurationTable/types';
+  import { EChartsOption, graphic } from 'echarts';
+  import { getFormattedText } from '../../../deviceManager/configurationTable/adapters';
+
+  const props = withDefaults(
+    defineProps<{
+      chartData: Record<string, any> | Record<string, any>[];
+      chartConfig: Config['moduleData']['chart']['0'];
+      height: string;
+      width: string;
+    }>(),
+    {
+      chartData: () => [],
+      height: '100%',
+      width: '100%',
+    }
+  );
+
+  const chartRef = ref<HTMLDivElement | null>(null);
+  const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+
+  // 核心方法,生成适用与 echart 的选项
+  const genChartOption = () => {
+    // 依据每一个图表配置生成图表选项
+    const { yAxis = [], xAxis = [], order, type, sortBy, series } = props.chartConfig;
+    const isArray = Array.isArray(props.chartData);
+
+    // 饼状图通常依赖单个对象的不同字段绘制
+    if (type === 'pie' && !isArray) {
+      return {
+        legend: { show: false },
+        color: ['#d9a1ff', '#00d1ff', '#82fe78'],
+        series: [
+          {
+            type: 'pie',
+            radius: ['50%', '75%'],
+            center: ['50%', '55%'],
+            data: series.map((serie) => {
+              return {
+                name: serie.label,
+                value: get(props.chartData, serie.prop, 0),
+                labelLine: { show: false },
+                label: { show: false },
+                itemStyle: {
+                  shadowBlur: 20,
+                  shadowColor: '#259bcf',
+                },
+              };
+            }),
+          },
+        ],
+      };
+    }
+    if (!isArray) return {};
+
+    let sorttedData = [...(props.chartData as any[])];
+
+    // 如果这个配置指明了需要排序则执行排序
+    if (sortBy && order) {
+      sorttedData.sort((pre, cur) => {
+        if (order === 'asc') {
+          return get(pre, sortBy, 0) - get(cur, sortBy, 0);
+        } else {
+          return get(cur, sortBy, 0) - get(pre, sortBy, 0);
+        }
+      });
+    }
+
+    // 柱状图则要求使用数组形式的数据作依赖
+    if (type === 'bar') {
+      return {
+        grid: {
+          top: 50,
+          height: 150,
+        },
+        textStyle: {
+          color: '#fff',
+        },
+        legend: { show: false },
+        xAxis: xAxis.map((e) => {
+          return {
+            type: 'category',
+            data: sorttedData.map((d) => {
+              return getFormattedText(d, e.prop, e.formatter);
+            }),
+          };
+        }),
+        yAxis: yAxis.map((e) => {
+          return {
+            name: e.label,
+            position: e.align,
+            splitLine: {
+              lineStyle: {
+                color: '#ffffff',
+                opacity: 0.3,
+              },
+            },
+          };
+        }),
+        series: series.reduce((curr: EChartsOption[], serie, index) => {
+          const colors = ['#66ffff', '#ffff66'];
+          // 系列选项,如果指定了x轴配置则以x轴配置优先
+          const data = sorttedData.map((d) => {
+            return {
+              name: serie.label,
+              value: get(d, serie.prop, 0),
+            };
+          });
+          curr.push({
+            name: 'pictorial element',
+            type: 'pictorialBar',
+            symbol: 'circle',
+            symbolPosition: 'end',
+            symbolSize: [16, 16],
+            symbolOffset: [0, -8],
+            yAxisIndex: index,
+            itemStyle: {
+              color: colors[index % colors.length],
+            },
+            data,
+          });
+          curr.push({
+            name: 'reference bar',
+            type: 'bar',
+            silent: true,
+            yAxisIndex: index,
+            itemStyle: {
+              color: new graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: colors[index % colors.length] },
+                { offset: 0.2, color: colors[index % colors.length] },
+                { offset: 1, color: `${colors[index % colors.length]}22` },
+              ]),
+            },
+            tooltip: { show: false },
+            barWidth: 8,
+            data,
+          });
+
+          return curr;
+        }, []),
+      };
+    }
+    // 折线图和上面的柱状图类似
+    if (type === 'line') {
+      return {
+        legend: {
+          top: 10,
+          right: 10,
+          textStyle: {
+            color: '#fff',
+          },
+        },
+        // backgroundColor: '#081f33',
+        textStyle: {
+          color: '#fff',
+        },
+        grid: {
+          left: 80,
+          top: 50,
+          right: 80,
+          bottom: 50,
+        },
+        xAxis: xAxis.map((e) => {
+          return {
+            type: 'category',
+            data: sorttedData.map((d) => {
+              return getFormattedText(d, e.prop, e.formatter);
+            }),
+          };
+        }),
+        yAxis: yAxis.map((e) => {
+          return {
+            name: e.label,
+            position: e.align,
+            splitLine: {
+              lineStyle: {
+                color: '#fff',
+                opacity: 0.3,
+              },
+            },
+          };
+        }),
+        series: series.map((serie) => {
+          const data = sorttedData.map((d) => {
+            return {
+              name: serie.label,
+              value: get(d, serie.prop, 0),
+            };
+          });
+
+          return {
+            type: 'line',
+            data,
+          };
+        }),
+      };
+
+      return {};
+    }
+  };
+
+  watch(
+    () => props.chartData,
+    () => {
+      initCharts();
+    }
+  );
+
+  function initCharts() {
+    const o = genChartOption();
+    console.log('debug', o);
+    setOptions(o as EChartsOption, false);
+  }
+</script>

+ 1 - 0
src/views/vent/home/configurable/components/enhanced/moduleBottom.vue

@@ -32,6 +32,7 @@
     --bg-height: 33px;
     color: #fff;
     box-sizing: border-box;
+    position: absolute;
 
     .module-content {
       width: 100%;

+ 1 - 0
src/views/vent/home/configurable/components/enhanced/moduleLeft.vue

@@ -32,6 +32,7 @@
     --bg-height: 33px;
     color: #fff;
     box-sizing: border-box;
+    position: absolute;
 
     .module-content {
       width: 100%;

+ 1 - 0
src/views/vent/home/configurable/components/enhanced/moduleRight.vue

@@ -32,6 +32,7 @@
     --bg-height: 33px;
     color: #fff;
     box-sizing: border-box;
+    position: absolute;
 
     .module-content {
       width: 100%;

+ 1 - 1
src/views/vent/home/configurable/components/moduleEnhanced.vue

@@ -35,7 +35,7 @@
     if (position.includes('right:0')) {
       return ModuleRight;
     }
-    if (position.includes('bottom:0')) {
+    if (position.includes('top:640px;left:460px')) {
       return ModuleBottom;
     }
     return ModuleLeft; //

+ 1 - 1
src/views/vent/home/configurable/components/moduleOriginal.vue

@@ -28,7 +28,7 @@
 
   // 根据配置里的定位判断应该使用哪个module组件
   function getModuleComponent(position) {
-    if (position.includes('bottom:0')) {
+    if (position.includes('top:640px;left:460px')) {
       return ModuleBottom;
     }
     return ModuleLeft;

+ 0 - 0
src/views/vent/home/configurable/components/MonitorCenter.vue → src/views/vent/home/configurable/components/monitorCenter.vue


+ 3 - 2
src/views/vent/home/configurable/components/original/moduleBottom.vue

@@ -32,6 +32,7 @@
     --bg-height: 33px;
     color: #fff;
     box-sizing: border-box;
+    position: absolute;
 
     .module-content {
       width: 100%;
@@ -73,10 +74,10 @@
 
     .module-slot {
       height: calc(100% - 33px);
-      width: calc(100% - 40px);
+      width: calc(100% - 15px);
       backdrop-filter: blur(5px);
       background-color: #6ad6ff1c;
-      margin-left: 20px;
+      margin-left: 5px;
     }
   }
 

+ 1 - 0
src/views/vent/home/configurable/components/original/moduleLeft.vue

@@ -32,6 +32,7 @@
     --bg-height: 33px;
     color: #fff;
     box-sizing: border-box;
+    position: absolute;
 
     .module-content {
       width: 100%;

+ 111 - 0
src/views/vent/home/configurable/components/timelineList.vue

@@ -0,0 +1,111 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="timeline">
+    <div v-for="item in listConfig" :key="item.prop" class="flex items-center timeline-item">
+      <div class="timeline-item__icon" :class="`timeline-item__icon_${item.color}`"></div><div class="timeline-item__dot"></div>
+      <div class="timeline-item__label">{{ item.label }}</div>
+      <div :class="`timeline-item__value_${item.color}`">
+        {{ item.value }}
+      </div>
+    </div>
+    <div class="timeline-component"></div>
+  </div>
+</template>
+<script lang="ts" setup>
+  withDefaults(
+    defineProps<{
+      listConfig: {
+        value: string;
+        color: string;
+        label: string;
+        prop: string;
+      }[];
+    }>(),
+    {
+      listConfig: () => [],
+    }
+  );
+
+  //   defineEmits(['click']);
+</script>
+<style lang="less" scoped>
+  @import '@/design/vent/color.less';
+  /* Timeline 相关的样式 */
+
+  .timeline-item {
+    height: 20%;
+  }
+  .timeline-item__icon_red {
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_5.png');
+  }
+  .timeline-item__icon_orange {
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_4.png');
+  }
+  .timeline-item__icon_yellow {
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_3.png');
+  }
+  .timeline-item__icon_green {
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_2.png');
+  }
+  .timeline-item__icon_blue {
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_1.png');
+  }
+  .timeline-item__icon {
+    width: 33px;
+    height: 35px;
+    margin-left: 50px;
+    background-repeat: no-repeat;
+    background-position: center center;
+  }
+  .timeline-item__dot {
+    width: 10px;
+    height: 10px;
+    margin-left: 70px;
+    background-color: @vent-gas-primary-bg;
+    border-radius: 5px;
+    position: relative;
+  }
+  .timeline-item__dot::before {
+    content: '';
+    position: absolute;
+    top: -3px;
+    left: -3px;
+    width: 16px;
+    height: 16px;
+    border-radius: 8px;
+    border: 1px solid @vent-gas-tab-border;
+  }
+  .timeline-item__label {
+    width: 100px;
+    margin-left: 70px;
+  }
+  .timeline-item__value_red {
+    color: red;
+  }
+  .timeline-item__value_orange {
+    color: orange;
+  }
+  .timeline-item__value_yellow {
+    color: yellow;
+  }
+  .timeline-item__value_green {
+    color: yellowgreen;
+  }
+  .timeline-item__value_blue {
+    color: lightblue;
+  }
+
+  .timeline {
+    height: 220px;
+    padding: 5px;
+    position: relative;
+  }
+  .timeline-component {
+    position: absolute;
+    width: 2px;
+    height: 180px;
+    top: 20px;
+    left: 162px;
+    background-image: @vent-configurable-home-timeline;
+  }
+</style>

+ 165 - 71
src/views/vent/home/configurable/index.vue

@@ -22,16 +22,16 @@
     <VentilateAnalysis />
     <WorkSurface />
     <DeviceWarning /> -->
-    <div v-if="isOriginal">
+    <template v-if="isOriginal">
       <ModuleOriginal v-for="cfg in configs" :key="cfg.deviceType" :show-style="cfg.showStyle" :module-name="cfg.moduleName" :visible="visible">
         <Content v-bind="cfg" />
       </ModuleOriginal>
-    </div>
-    <div v-else>
+    </template>
+    <template v-else>
       <ModuleEnhanced v-for="cfg in configs" :key="cfg.deviceType" :show-style="cfg.showStyle" :module-name="cfg.moduleName" :visible="visible">
         <Content v-bind="cfg" />
       </ModuleEnhanced>
-    </div>
+    </template>
   </div>
 </template>
 <script lang="ts" setup>
@@ -44,7 +44,7 @@
   // import VentilateAnalysis from './components/VentilateAnalysis.vue';
   // import WorkSurface from './components/WorkSurface.vue';
   // import DeviceWarning from './components/DeviceWarning.vue';
-  import MonitorCenter from './components/MonitorCenter.vue';
+  import MonitorCenter from './components/monitorCenter.vue';
   // import { useInitConfigs } from './hooks/useInit';
   import { Config } from '../../deviceManager/configurationTable/types';
   import ModuleEnhanced from './components/moduleEnhanced.vue';
@@ -67,11 +67,9 @@
           showSelector: true,
           showSlot: true,
           selector: {
-            icon: 'SwapOutlined',
             prop: 'strinstallpos',
           },
           slot: {
-            icon: 'SwapOutlined',
             prop: 'strinstallpos',
           },
         },
@@ -80,7 +78,7 @@
           type: 'video',
           link: '/src/assets/vedio/fanLocal.mp4',
         },
-        layout: ['board', 'chart'],
+        layout: ['board'],
         board: [
           {
             label: '风速1',
@@ -90,48 +88,140 @@
           },
           {
             label: '风速2',
-            type: 'D',
+            type: 'B',
             layout: 'label-top',
             prop: 'readData.windSpeed2',
           },
         ],
+        list: [],
+        chart: [],
+      },
+      showStyle: {
+        size: 'width:450px;height:280px;',
+        version: 'enhanced',
+        position: 'top:60px;left:0;',
+      },
+    },
+    {
+      deviceType: 'warn',
+      moduleName: '测试报警',
+      pageType: '',
+      moduleData: {
+        header: {
+          show: true,
+          showSelector: false,
+          showSlot: true,
+          selector: {
+            prop: 'strinstallpos',
+          },
+          slot: {
+            prop: 'netstatus.val',
+            formatter: '网络异常:${} 台',
+          },
+        },
+        background: {
+          show: false,
+          type: 'video',
+          link: '',
+        },
+        layout: ['list'],
+        board: [],
+        chart: [],
+        list: [
+          {
+            label: '正常',
+            prop: 'blue.val',
+            color: 'blue',
+          },
+          {
+            label: '告警',
+            prop: 'orange.val',
+            color: 'orange',
+          },
+          {
+            label: '报警',
+            prop: 'yellow.val',
+            color: 'yellow',
+          },
+          {
+            label: '危险',
+            prop: 'red.val',
+            color: 'red',
+          },
+          {
+            label: '错误',
+            prop: 'alarm.val',
+            color: 'green',
+          },
+        ],
+      },
+      showStyle: {
+        size: 'width:450px;height:280px;',
+        version: 'enhanced',
+        position: 'top:640px;left:0;',
+      },
+    },
+    {
+      deviceType: 'midinfo',
+      moduleName: '测试中间模块',
+      pageType: '',
+      moduleData: {
+        header: {
+          show: false,
+          showSelector: false,
+          showSlot: false,
+          selector: {
+            icon: 'SwapOutlined',
+            prop: 'strinstallpos',
+          },
+          slot: {
+            icon: 'SwapOutlined',
+            prop: 'strinstallpos',
+          },
+        },
+        background: {
+          show: false,
+          type: 'video',
+          link: '',
+        },
+        layout: ['chart'],
+        board: [],
+        list: [],
         chart: [
           {
-            type: 'line',
-            readFrom: 'readData.history',
-            xAxis: [{ prop: 'strinstallpos' }],
+            type: 'bar',
+            readFrom: 'sysdata.history',
+            xAxis: [{ prop: 'time' }],
             yAxis: [
-              { label: '风量', align: 'left' },
-              { label: '风速', align: 'right' },
+              { label: '回1', align: 'left' },
+              // { label: '回2', align: 'right' },
             ],
             series: [
-              { label: '风量', prop: 'f1Val' },
-              { label: '风速', prop: 'f2Val' },
+              { label: '回1', prop: 'hui1' },
+              // { label: '回2', prop: 'hui2' },
             ],
           },
         ],
       },
       showStyle: {
-        size: 'width:450px;height:280px;',
+        size: 'width:1000px;height:280px;',
         version: 'enhanced',
-        position: 'top:60px;left:0;',
+        position: 'top:640px;left:460px;',
       },
     },
     {
-      deviceType: 'fanlocal',
-      moduleName: '测试局扇',
+      deviceType: 'sys_wind',
+      moduleName: '测试测风装置',
       pageType: '',
       moduleData: {
         header: {
           show: true,
-          showSelector: false,
-          showSlot: true,
+          showSelector: true,
+          showSlot: false,
           selector: {
-            icon: 'SwapOutlined',
             prop: 'strinstallpos',
           },
           slot: {
-            icon: 'SwapOutlined',
             prop: 'strinstallpos',
           },
         },
@@ -140,33 +230,35 @@
           type: 'video',
           link: '',
         },
-        layout: ['board', 'chart'],
+        layout: ['chart', 'board'],
         board: [
           {
             label: '风量',
-            type: 'A',
+            type: 'C',
             layout: 'val-top',
             prop: 'f1Val',
           },
           {
             label: '风速',
-            type: 'A',
+            type: 'C',
             layout: 'val-top',
             prop: 'f2Val',
           },
         ],
+        list: [],
         chart: [
           {
-            type: 'line',
-            readFrom: 'readData.history',
-            xAxis: [{ prop: 'strinstallpos' }],
+            type: 'pie',
+            readFrom: 'readData',
+            xAxis: [{ prop: 'stationname' }],
             yAxis: [
-              { label: '风量', align: 'left' },
-              { label: '风速', align: 'right' },
+              { label: '风量1', align: 'left' },
+              // { label: '回2', align: 'right' },
             ],
             series: [
-              { label: '风量', prop: 'f1Val' },
-              { label: '风速', prop: 'f2Val' },
+              { label: '风量1', prop: 'va' },
+              { label: '风量2', prop: 'va2' },
+              // { label: '回2', prop: 'hui2' },
             ],
           },
         ],
@@ -174,25 +266,23 @@
       showStyle: {
         size: 'width:450px;height:280px;',
         version: 'enhanced',
-        position: 'top:350px;left:0;',
+        position: 'top:60px;right:0;',
       },
     },
     {
-      deviceType: 'fanlocal',
-      moduleName: '测试局扇',
+      deviceType: 'sys_majorpath',
+      moduleName: '测试关键路线',
       pageType: '',
       moduleData: {
         header: {
           show: true,
           showSelector: true,
-          showSlot: false,
+          showSlot: true,
           selector: {
-            icon: 'SwapOutlined',
-            prop: 'strinstallpos',
+            prop: 'devicePos',
           },
           slot: {
-            icon: 'SwapOutlined',
-            prop: 'strinstallpos',
+            prop: 'devicePos',
           },
         },
         background: {
@@ -200,39 +290,43 @@
           type: 'video',
           link: '',
         },
-        layout: ['list'],
-        list: [
+        layout: ['chart', 'board'],
+        board: [
           {
             label: '风量',
+            type: 'D',
+            layout: 'label-top',
             prop: 'f1Val',
-            color: 'blue',
           },
           {
             label: '风速',
+            type: 'D',
+            layout: 'label-top',
             prop: 'f2Val',
-            color: 'green',
           },
         ],
+        list: [],
         chart: [
           {
             type: 'line',
-            readFrom: 'readData.history',
-            xAxis: [{ prop: 'strinstallpos' }],
+            readFrom: 'majorpath.paths',
+            xAxis: [{ prop: 'name' }],
             yAxis: [
-              { label: '风量', align: 'left' },
-              { label: '风速', align: 'right' },
+              { label: 'Drag', align: 'left' },
+              { label: 'M3', align: 'right' },
             ],
             series: [
-              { label: '风量', prop: 'f1Val' },
-              { label: '风速', prop: 'f2Val' },
+              { label: 'Drag', prop: 'drag' },
+              { label: 'M3', prop: 'm3' },
+              // { label: '回2', prop: 'hui2' },
             ],
           },
         ],
       },
       showStyle: {
-        size: 'width:450px;height:280px;',
+        size: 'width:450px;height:570px;',
         version: 'enhanced',
-        position: 'top:640px;left:0;',
+        position: 'top:350px;right:0;',
       },
     },
   ]);
@@ -273,23 +367,23 @@
       }
     }
 
-    .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-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: linear-gradient(to bottom, #036886, #072a40);