Browse Source

[Wip 0000] 可配置首页各模块样式相关开发

houzekong 9 months ago
parent
commit
50f5dda764

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


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


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


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


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


+ 111 - 0
src/components/chart/PictorialBar.vue

@@ -0,0 +1,111 @@
+<template>
+  <div ref="chartRef" :style="{ height, width }"></div>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, Ref, reactive, watchEffect } from 'vue';
+  import { useECharts } from '/@/hooks/web/useECharts';
+  import { EChartsOption } from 'echarts';
+  import { assign } from 'lodash-es';
+  export default defineComponent({
+    name: 'PictorialBar',
+    props: {
+      chartData: {
+        type: Array,
+        default: () => [],
+      },
+      /** 图表配置 */
+      option: {
+        type: Object,
+        default: () => ({}),
+      },
+      xAxisPropType: {
+        type: String,
+        required: true,
+      },
+      seriesPropType: {
+        type: String,
+        required: true,
+      },
+      width: {
+        type: String as PropType<string>,
+        default: '100%',
+      },
+      height: {
+        type: String as PropType<string>,
+        default: 'calc(100vh - 78px)',
+      },
+    },
+    setup(props) {
+      const chartRef = ref<HTMLDivElement | null>(null);
+      const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
+      const option: EChartsOption = reactive({
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow',
+            label: {
+              show: true,
+              backgroundColor: '#333',
+            },
+          },
+        },
+        grid: {
+          left: 60,
+          right: 50,
+          bottom: 50,
+        },
+        xAxis: {
+          type: 'category',
+          data: [],
+        },
+        yAxis: {
+          type: 'value',
+          nameTextStyle: {
+            fontSize: 14,
+          },
+        },
+        series: [
+          {
+            name: 'bar',
+            type: 'bar',
+            showBackground: true,
+            backgroundStyle: {
+              color: 'rgba(220, 220, 220, 0.8)',
+            },
+            data: [],
+          },
+        ],
+      });
+
+      watchEffect(() => {
+        props.chartData && initCharts();
+      });
+
+      function initCharts() {
+        if (props.option) {
+          assign(option, props.option);
+        }
+        let seriesData = props.chartData.map((item: any) => {
+          // return item.value;
+          return item[props.seriesPropType];
+        });
+        let xAxisData = props.chartData.map((item: any) => {
+          // return item.name;
+          return item[props.xAxisPropType];
+        });
+        if (Array.isArray(option.series)) {
+          option.series.forEach((ele) => {
+            ele.data = seriesData;
+          });
+        } else {
+          option.series!.data = seriesData;
+        }
+
+        (option.xAxis as any).data = xAxisData;
+
+        setOptions(option, false);
+      }
+      return { chartRef };
+    },
+  });
+</script>

+ 1 - 0
src/design/vent/color.less

@@ -26,4 +26,5 @@
 // vent/gas 模块下的客制ListItem组件背景色
 @vent-gas-list-item-bg-img: linear-gradient(to right, #39a3ff55, #3977e500);
 @vent-configurable-home-bg-img: linear-gradient(to top, #39a3ff00, #0091ff99);
+@vent-configurable-home-timeline: linear-gradient(to top, #39a3ff00, #0091ff99, #39a3ff00);
 @vent-configurable-home-light-border: #3df6ff;

+ 36 - 15
src/views/vent/home/configurable/components/AirVolumeMonitor.vue

@@ -1,27 +1,27 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
-  <CostumeHeader :api="fetchOptions" @change="selectDeviceByID">
+  <!-- <CostumeHeader :api="fetchOptions" @change="selectDeviceByID">
     <div class="w-200px flex flex-items-center">
       <RightCircleOutlined class="w-30px" />
       <div class="flex-grow-1">
         {{ selectedDevice.strinstallpos }}
       </div>
     </div>
-  </CostumeHeader>
-  <Bar
+  </CostumeHeader> -->
+  <PictorialBar
     :option="chartOption"
     series-prop-type="valueA"
     x-axis-prop-type="x"
     :chart-data="[
       { valueA: 1, valueB: 1, x: 2 },
+      { valueA: 1, valueB: 1, x: 3 },
       { valueA: 1, valueB: 1, x: 4 },
-      { valueA: 1, valueB: 1, x: 4 },
-      { valueA: 1, valueB: 1, x: 4 },
-      { valueA: 2, valueB: 2, x: 4 },
-      { valueA: 3, valueB: 1, x: 4 },
-      { valueA: 1, valueB: 1, x: 4 },
+      { valueA: 1, valueB: 1, x: 5 },
+      { valueA: 2, valueB: 2, x: 6 },
+      { valueA: 3, valueB: 1, x: 7 },
+      { valueA: 1, valueB: 1, x: 8 },
     ]"
-    height="220px"
+    height="100%"
   />
   <!-- <div class="flex justify-around mt-10px">
     <MiniBoard v-for="item in configs" :key="item.prop" :label="item.label" :value="selectedDevice[item.prop]" />
@@ -33,7 +33,7 @@
   import { list } from '@/views/vent/deviceManager/deviceTable/device.api';
   import CostumeHeader from './CostumeHeader.vue';
   import { RightCircleOutlined } from '@ant-design/icons-vue';
-  import Bar from '/@/components/chart/Bar.vue';
+  import PictorialBar from '/@/components/chart/PictorialBar.vue';
   import { EChartsOption, graphic } from 'echarts';
   // import MiniBoard from './MiniBoard.vue';
   // import mapComponent from './components/3Dmap/index.vue';
@@ -81,6 +81,10 @@
 
   // 图表相关
   const chartOption: EChartsOption = {
+    grid: {
+      top: 50,
+      height: 150,
+    },
     yAxis: {
       splitLine: {
         lineStyle: {
@@ -89,18 +93,35 @@
         },
       },
     },
+    legend: { show: false },
     series: [
       {
-        name: 'bar',
+        type: 'pictorialBar',
+        name: 'pictorial element',
+        symbol: 'circle',
+        symbolPosition: 'end',
+        symbolSize: [16, 16],
+        symbolOffset: [0, -8],
+        itemStyle: {
+          color: '#66ffff',
+        },
+        data: [],
+      },
+      {
+        name: 'reference bar',
         type: 'bar',
+        silent: true,
         itemStyle: {
           color: new graphic.LinearGradient(0, 0, 0, 1, [
-            { offset: 0, color: '#0091ffff' },
-            { offset: 1, color: '#0091ff44' },
+            { offset: 0, color: '#66ffff' },
+            { offset: 0.2, color: '#66ffff' },
+            { offset: 1, color: '#66ffff22' },
           ]),
-          borderRadius: [50, 50, 0, 0],
         },
-        barWidth: 20,
+        tooltip: {
+          show: false,
+        },
+        barWidth: 8,
         data: [],
       },
     ],

+ 38 - 10
src/views/vent/home/configurable/components/DeviceWarning.vue

@@ -13,13 +13,14 @@
       </div>
     </div>
   </CostumeHeader>
-  <div>
+  <div class="timeline">
     <div v-for="(item, i) in warns" :key="`svvhccdw-${i}`" 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.count }}</div>
     </div>
+    <div class="position-absolute timeline-component"></div>
   </div>
 </template>
 <script lang="ts" setup>
@@ -58,6 +59,11 @@
     {
       label: 'test',
       count: 0,
+      color: 'orange',
+    },
+    {
+      label: 'test',
+      count: 0,
       color: 'yellow',
     },
     {
@@ -95,29 +101,34 @@
   @import '@/design/vent/color.less';
 
   .timeline-item {
-    height: 50px;
+    height: 20%;
   }
   .timeline-item__icon_red {
-    background-image: url('@/assets/images/home-container/warn-icon.png');
+    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/warn-icon1.png');
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_3.png');
   }
   .timeline-item__icon_green {
-    background-image: url('@/assets/images/home-container/warn-icon2.png');
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_2.png');
   }
   .timeline-item__icon_blue {
-    background-image: url('@/assets/images/home-container/warn-icon3.png');
+    background-image: url('@/assets/images/home-container/configurable/warn_icon_1.png');
   }
   .timeline-item__icon {
     width: 54px;
     height: 45px;
     margin: 0 50px 0 50px;
+    background-repeat: no-repeat;
   }
   .timeline-item__dot {
+    position: absolute;
     width: 10px;
     height: 10px;
-    margin: 0 50px 0 0;
+    left: 7px;
     background-color: @vent-gas-primary-bg;
     border-radius: 5px;
     position: relative;
@@ -133,18 +144,35 @@
     border: 1px solid @vent-gas-tab-border;
   }
   .timeline-item__label {
-    width: 150px;
+    width: 100px;
+    margin-left: 100px;
   }
   .timeline-item__value_red {
     color: red;
   }
+  .timeline-item__value_orange {
+    color: orange;
+  }
   .timeline-item__value_yellow {
     color: yellow;
   }
   .timeline-item__value_green {
-    color: green;
+    color: yellowgreen;
   }
   .timeline-item__value_blue {
-    color: blue;
+    color: lightblue;
+  }
+
+  .timeline {
+    height: 220px;
+    padding: 5px;
+    position: relative;
+  }
+  .timeline-component {
+    width: 2px;
+    height: 180px;
+    top: 20px;
+    left: 170px;
+    background-image: @vent-configurable-home-timeline;
   }
 </style>

+ 5 - 5
src/views/vent/home/configurable/components/MiniBoard.vue

@@ -98,14 +98,15 @@
   // }
 
   .mini-board__value_B {
+    color: @vent-gas-primary-text;
     font-size: 20px;
     font-weight: bold;
     height: 40px;
     line-height: 40px;
   }
   .mini-board__label_B {
-    line-height: 20px;
-    height: 20px;
+    line-height: 24px;
+    height: 24px;
   }
 
   .mini-board__value_C {
@@ -119,14 +120,13 @@
   // }
 
   .mini-board__value_D {
-    color: @vent-gas-primary-text;
     font-size: 20px;
     font-weight: bold;
     height: 40px;
     line-height: 40px;
   }
   .mini-board__label_D {
-    line-height: 20px;
-    height: 20px;
+    line-height: 17px;
+    height: 17px;
   }
 </style>

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

@@ -1,12 +1,12 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
   <CostumeHeader :api="fetchOptions" @change="selectDeviceByID">
-    <div class="w-200px flex flex-items-center">
+    <!-- <div class="w-200px flex flex-items-center">
       <RightCircleOutlined class="w-30px" />
       <div class="flex-grow-1">
         {{ selectedDevice.strinstallpos }}
       </div>
-    </div>
+    </div> -->
   </CostumeHeader>
   <Pie
     :option="chartOption"

+ 14 - 5
src/views/vent/home/configurable/components/WorkSurface.vue

@@ -1,12 +1,12 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <template>
   <CostumeHeader :api="fetchOptions" @change="selectDeviceByID">
-    <div class="w-200px flex flex-items-center">
+    <!-- <div class="w-200px flex flex-items-center">
       <RightCircleOutlined class="w-30px" />
       <div class="flex-grow-1">
         {{ selectedDevice.strinstallpos }}
       </div>
-    </div>
+    </div> -->
   </CostumeHeader>
   <LineMulti
     :option="chartOption"
@@ -26,6 +26,7 @@
       { valueA: 1, valueB: 2, x: 7 },
     ]"
     height="140px"
+    class="worksurface-chart"
   />
   <div class="flex justify-around">
     <MiniBoard v-for="item in configs" :key="item.prop" :label="item.label" :value="selectedDevice[item.prop]" layout="label-top" type="B" />
@@ -93,8 +94,8 @@
       },
     },
     grid: {
-      top: 40,
-      height: 70,
+      top: 35,
+      height: 80,
     },
     yAxis: {
       type: 'value',
@@ -108,10 +109,18 @@
     textStyle: {
       color: '#fff',
     },
+    backgroundColor: '#081f33',
+    // backgroundColor: '#0091ff12',
   };
 
   onMounted(() => {
     fetchConfig();
   });
 </script>
-<style scoped></style>
+<style scoped lang="less">
+  @import '@/design/vent/color.less';
+
+  .worksurface-chart {
+    border: 1px solid @vent-configurable-home-light-border;
+  }
+</style>

+ 0 - 156
src/views/vent/home/configurable/configurable.data.ts

@@ -1,156 +0,0 @@
-import dayjs from 'dayjs';
-import { cloneDeep } from 'lodash-es';
-
-export const airVolumeMonitorKey = [
-  {
-    code: 'name',
-    value: '矿井名称',
-  },
-  {
-    code: 'jin',
-    value: '总进风量',
-  },
-  {
-    code: 'hui',
-    value: '总回风量',
-  },
-  {
-    code: 'xufeng',
-    value: '总需风量',
-  },
-];
-export const airVolumeMonitor = [
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-  {
-    name: 'XXX',
-    jin: 10215,
-    hui: 10215,
-    xufeng: 10215,
-  },
-];
-
-let latestData = [];
-let latestTime = 0;
-export function getDate(workData) {
-  // debugger;
-  const workTypeList = cloneDeep(workData);
-  workData = latestData;
-  const getTime = (val) => {
-    const fiveSecondsAgo = dayjs().subtract(val, 'second');
-    // 获取时分秒
-    const hours = fiveSecondsAgo.hour();
-    const minutes = fiveSecondsAgo.minute();
-    const seconds = fiveSecondsAgo.second();
-    return `${hours}:${minutes}:${seconds}`;
-  };
-  if (latestData.length == 0) {
-    for (let j = 0; j < workTypeList.length; j++) {
-      const itemData = {
-        jin: '0',
-        hui: '0',
-        history: [],
-        deviceName: workTypeList[j]['deviceName'],
-        deviceID: workTypeList[j]['deviceID'],
-        deviceType: 'sys_surface_caimei',
-      };
-      itemData['jin'] = (1042 + (Math.random() * 2 - 1 * 50)).toFixed(2);
-      itemData['hui'] = (1082 + (Math.random() * 2 - 1 * 50)).toFixed(2);
-
-      // 第一次模拟
-      for (let i = 0; i < 5; i++) {
-        const item = {
-          jin: (1042 + (Math.random() * 2 - 1 * 50)).toFixed(2),
-          hui: (1082 + (Math.random() * 2 - 1 * 50)).toFixed(2),
-          time: getTime((i + 1) * 5),
-          deviceName: workTypeList[j]['deviceName'],
-          deviceID: workTypeList[j]['deviceID'],
-          deviceType: 'sys_surface_caimei',
-        };
-        itemData['history'].unshift(item);
-      }
-      workData.push(itemData);
-    }
-    latestTime = new Date().getTime();
-  } else {
-    if (new Date().getTime() - latestTime >= 5000) {
-      for (let j = 0; j < workTypeList.length; j++) {
-        const now = dayjs(latestTime + 5000);
-        // 获取时分秒
-        const hours = now.hour() > 9 ? now.hour() : `0${now.hour()}`;
-        const minutes = now.minute() > 9 ? now.minute() : `0${now.minute()}`;
-        const seconds = now.second() > 9 ? now.second() : `0${now.second()}`;
-        workData[j]['history'].shift();
-        workData[j]['jin'] = (1042 + (Math.random() * 2 - 1 * 50)).toFixed(2);
-        workData[j]['hui'] = (1082 + (Math.random() * 2 - 1 * 50)).toFixed(2);
-        workData[j]['history'].push({
-          jin: (1042 + (Math.random() * 2 - 1 * 50)).toFixed(2),
-          hui: (1082 + (Math.random() * 2 - 1 * 50)).toFixed(2),
-          time: `${hours}:${minutes}:${seconds}`,
-        });
-      }
-      latestTime = new Date().getTime();
-    }
-  }
-  latestData = workData;
-
-  return workData;
-}

+ 31 - 52
src/views/vent/home/configurable/index.vue

@@ -12,33 +12,28 @@
         <MonitorCenter />
       </template>
     </a-dropdown>
-    <div class="module-left-wrapper">
-      <ModuleLeft class="module-left" title="局部通风机监测">
-        <SubVentilate />
-      </ModuleLeft>
-      <ModuleLeft class="module-left" title="主通风机监测">
-        <Ventilate />
-      </ModuleLeft>
-      <ModuleLeft class="module-left" title="通风设施远程控制">
-        <VentilateControl />
-      </ModuleLeft>
-    </div>
-    <div class="module-bottom-wrapper">
-      <ModuleBottom class="module-bottom" title="矿井风量实时监测">
-        <AirVolumeMonitor />
-      </ModuleBottom>
-    </div>
-    <div class="module-right-wrapper">
-      <ModuleRight class="module-right" title="通风系统监测与分析">
-        <VentilateAnalysis />
-      </ModuleRight>
-      <ModuleRight class="module-right" title="采煤工作面智能管控">
-        <WorkSurface />
-      </ModuleRight>
-      <ModuleRight class="module-right" title="设备告警">
-        <DeviceWarning />
-      </ModuleRight>
-    </div>
+    <!-- 采用定位方式以避免出现各个模块隐藏时其他模块下移的问题 -->
+    <ModuleLeft class="module-left top-60px" title="局部通风机监测">
+      <SubVentilate />
+    </ModuleLeft>
+    <ModuleLeft class="module-left top-350px" title="主通风机监测">
+      <Ventilate />
+    </ModuleLeft>
+    <ModuleLeft class="module-left top-640px" title="通风设施远程控制">
+      <VentilateControl />
+    </ModuleLeft>
+    <ModuleBottom class="module-bottom top-640px left-460px" title="矿井风量实时监测">
+      <AirVolumeMonitor />
+    </ModuleBottom>
+    <ModuleRight class="module-right top-60px" title="通风系统监测与分析">
+      <VentilateAnalysis />
+    </ModuleRight>
+    <ModuleRight class="module-right top-350px" title="采煤工作面智能管控">
+      <WorkSurface />
+    </ModuleRight>
+    <ModuleRight class="module-right top-640px" title="设备告警">
+      <DeviceWarning />
+    </ModuleRight>
   </div>
 </template>
 <script lang="ts" setup>
@@ -88,38 +83,22 @@
       }
     }
 
-    .module-left-wrapper {
+    .module-left {
       position: absolute;
-      top: 50px;
+      width: 450px;
+      height: 280px;
       left: 0;
-
-      .module-left {
-        width: 450px;
-        height: 280px;
-        margin-top: 10px;
-      }
     }
-    .module-right-wrapper {
+    .module-right {
       position: absolute;
-      top: 50px;
+      width: 450px;
+      height: 280px;
       right: 0;
-
-      .module-right {
-        width: 450px;
-        height: 280px;
-        margin-top: 10px;
-      }
     }
-
-    .module-bottom-wrapper {
+    .module-bottom {
       position: absolute;
-      bottom: 10px;
-      left: 460px;
-
-      .module-bottom {
-        width: 1000px;
-        height: 280px;
-      }
+      width: 1000px;
+      height: 280px;
     }
     .module-dropdown {
       padding: 10px;