Bladeren bron

feat(views): 编写“环境监控列表”组件

wangshun 2 maanden geleden
bovenliggende
commit
c4dd983471
1 gewijzigde bestanden met toevoegingen van 364 en 0 verwijderingen
  1. 364 0
      src/views/env-monitor/EnvMonitorList.vue

+ 364 - 0
src/views/env-monitor/EnvMonitorList.vue

@@ -0,0 +1,364 @@
+<script setup lang="ts">
+import { onMounted, ref, useTemplateRef, watch } from 'vue';
+
+import LineChart from '@/components/LineChart.vue';
+import SvgIcon from '@/components/SvgIcon.vue';
+
+import monitoringScoring from '@/assets/img/monitoring-scoring.png';
+
+import HumitureCurve from './HumitureCurve.vue';
+import SetTempHumidityModal from './SetTempHumidityModal.vue';
+
+import type { MonitoringPointData, RegionsPointsItem } from '@/types';
+
+interface Props {
+  monitorData: RegionsPointsItem[];
+  monitoringPointData: MonitoringPointData[];
+}
+const humitureCurveRef = useTemplateRef('humitureCurve');
+const props = defineProps<Props>();
+const monitoringId = ref<number>();
+const checkedList = ref<number[]>([3, 2, 1, -1]);
+const monitoringDataOpen = ref<boolean>(false);
+const monitoringIds = ref<number[]>([]);
+const monitoringPointList = ref<MonitoringPointData[]>([]);
+const standards = ref<number>(0);
+const avgTemperature = ref<string>();
+const avgHumidity = ref<string>();
+const outSideTemperature = ref<string>();
+const outSideHumidity = ref<string>();
+const monitorOptions = ref([
+  {
+    label: '超标',
+    value: 3,
+  },
+  {
+    label: '预警',
+    value: 2,
+  },
+  {
+    label: '正常',
+    value: 1,
+  },
+  {
+    label: '离线',
+    value: -1,
+  },
+]);
+const activeMonitoringType = ref<number>(-1);
+const handleTabClick = () => {};
+const historicalDataClick = (data: MonitoringPointData) => {
+  monitoringId.value = data.id;
+  monitoringDataOpen.value = true;
+};
+
+// 仅显示最后一级内容
+const displayLastLabel = ({ labels }: { labels: string[] }) => {
+  return labels[labels.length - 1];
+};
+
+const setClick = () => {
+  humitureCurveRef.value?.getPointTimeSeriesList();
+};
+
+const switchMonitoringList = () => {
+  if (activeMonitoringType.value === -1) {
+    monitoringPointList.value = props.monitoringPointData.filter((item) => checkedList.value.includes(item.status));
+  } else {
+    props.monitorData.forEach((item) => {
+      if (item.id === activeMonitoringType.value) {
+        monitoringPointList.value = item.points.filter((item) => checkedList.value.includes(item.status));
+        avgTemperature.value = item.avgTemperature;
+        avgHumidity.value = item.avgHumidity;
+        outSideTemperature.value = item.outSideTemperature;
+        outSideHumidity.value = item.outSideHumidity;
+      }
+    });
+  }
+};
+
+watch(
+  () => activeMonitoringType.value,
+  (count) => {
+    if (count) {
+      switchMonitoringList();
+    }
+  },
+);
+
+watch(
+  () => checkedList.value,
+  (count) => {
+    if (count) {
+      switchMonitoringList();
+    }
+  },
+);
+
+const getMonitoringList = () => {
+  activeMonitoringType.value = -1;
+  standards.value = 0;
+  props.monitoringPointData.forEach((item) => {
+    if (item.status === 3) {
+      standards.value++;
+    }
+  });
+  monitoringPointList.value = props.monitoringPointData;
+};
+
+defineExpose({
+  getMonitoringList,
+});
+onMounted(() => {
+  getMonitoringList();
+});
+</script>
+
+<template>
+  <div>
+    <div class="monitoring-header">
+      <AFlex justify="space-between">
+        <AFlex align="center">
+          <img class="scoring-img" :src="monitoringScoring" />
+          <div>
+            <AFlex>
+              <div class="scoring-text">当前环境适宜度</div>
+              <AFlex justify="center" align="center" class="scoring-result">优</AFlex>
+            </AFlex>
+            <div class="scoring-statistics">
+              共 {{ monitoringPointData.length }} 个监测点,<span class="standards-color">{{ standards }}</span>
+              个温湿度超标
+            </div>
+          </div>
+        </AFlex>
+        <AFlex v-show="activeMonitoringType !== -1" justify="flex-end" align="center" class="monitoring-header-right">
+          <AFlex>
+            <AFlex justify="center" align="center" class="icon-background">
+              <SvgIcon class="icon-size" name="temperature" />
+            </AFlex>
+            <div>
+              <div>
+                <span class="degree-value">{{ avgTemperature }}</span
+                ><span>°C</span>
+              </div>
+              <div class="average-text">室内平均温度</div>
+            </div>
+          </AFlex>
+          <AFlex>
+            <AFlex justify="center" align="center" class="icon-background">
+              <SvgIcon class="icon-size" name="humidity" />
+            </AFlex>
+            <div>
+              <div>
+                <span class="degree-value">{{ avgHumidity }}</span
+                ><span>%</span>
+              </div>
+              <div class="average-text">室内平均湿度</div>
+            </div>
+          </AFlex>
+          <div class="dividing-line"></div>
+          <div>
+            <div>
+              <span class="degree-value">{{ outSideTemperature }}</span
+              ><span>°C</span>
+            </div>
+            <div class="average-text text-center">室外温度</div>
+          </div>
+          <div class="monitoring-margin">
+            <div>
+              <span class="degree-value">{{ outSideHumidity }}</span
+              ><span>°C</span>
+            </div>
+            <div class="average-text text-center">室外湿度</div>
+          </div>
+        </AFlex>
+      </AFlex>
+    </div>
+    <ARow>
+      <ACol :span="16">
+        <ATabs
+          class="button-tabs-card"
+          :tab-bar-gutter="18"
+          v-model:active-key="activeMonitoringType"
+          type="card"
+          @tab-click="handleTabClick"
+          tab-position="top"
+        >
+          <ATabPane :key="-1" tab="总览" />
+          <ATabPane v-for="item in monitorData" :key="item.id" :tab="item.regionName" />
+        </ATabs>
+      </ACol>
+      <ACol :span="8">
+        <AFlex justify="flex-end">
+          <ACheckboxGroup style="margin-top: 5px" v-model:value="checkedList" :options="monitorOptions" />
+        </AFlex>
+      </ACol>
+    </ARow>
+    <div class="monitoring-content">
+      <AFlex wrap="wrap" :gap="14">
+        <div v-for="item in monitoringPointList" :key="item.id">
+          <LineChart :data="item" :icon-show="false" @historicalDataClick="historicalDataClick" />
+        </div>
+      </AFlex>
+    </div>
+
+    <AModal v-model:open="monitoringDataOpen" :footer="null" width="920px" :mask-closable="false" :keyboard="false">
+      <template #title>
+        <AFlex>
+          <ACascader
+            class="equipment-group"
+            v-model:value="monitoringIds"
+            :field-names="{ label: 'name', value: 'id', children: 'points' }"
+            :options="monitorData"
+            :placeholder="$t('common.plzSelect')"
+            change-on-select
+            :display-render="displayLastLabel"
+          />
+
+          <SetTempHumidityModal
+            :monitoring-id="monitoringId"
+            :monitoring-data="monitoringPointData"
+            @setClick="setClick"
+          />
+        </AFlex>
+      </template>
+      <HumitureCurve
+        ref="humitureCurve"
+        :monitoring-id="monitoringId"
+        :monitoring-data="monitoringPointData"
+        :set-show="false"
+        :width="'895px'"
+        :height="'650px'"
+      />
+    </AModal>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.standards-color {
+  color: #f56c6c;
+}
+
+.monitoring-header-right .text-center {
+  text-align: center;
+}
+
+.monitoring-margin {
+  margin: 0 48px 0 56px;
+}
+
+.dividing-line {
+  width: 1px;
+  height: 64px;
+  margin: 0 48px 0 49px;
+  border: 1px dashed #32bac0;
+}
+
+.average-text {
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #000;
+  text-align: left;
+}
+
+.degree-value {
+  font-size: 31px;
+  line-height: 36px;
+  color: #000;
+}
+
+.icon-size {
+  font-size: 28px;
+}
+
+.icon-background {
+  width: 64px;
+  height: 64px;
+  margin-right: 16px;
+  margin-left: 48px;
+  color: #32bac0;
+  background: rgb(50 186 192 / 15%);
+  border-radius: 12px;
+}
+
+.monitoring-header-right {
+  width: 854px;
+  background: linear-gradient(270deg, rgb(58 194 110 / 14%) 0%, rgb(255 255 255 / 0%) 100%);
+  border-radius: 0 16px 16px 0;
+}
+
+.scoring-statistics {
+  margin-top: 8px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #666;
+  text-align: left;
+}
+
+.scoring-result {
+  width: 24px;
+  height: 24px;
+  margin-left: 5px;
+  color: #67c23a;
+  background: rgb(103 194 58 / 12%);
+  border: 1px solid #67c23a;
+  border-radius: 4px;
+}
+
+.scoring-text {
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 500;
+  line-height: 24px;
+  color: #333;
+  text-align: left;
+}
+
+.scoring-img {
+  width: 128px;
+  height: 115px;
+  margin: 16px 32px 33px 56px;
+}
+
+.equipment-group {
+  margin-right: 30px;
+  margin-bottom: 16px;
+}
+
+.custom-option {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.status-tag {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 44px;
+  height: 24px;
+  font-size: 12px;
+  color: #67c23a;
+  background: rgb(103 194 58 / 12%);
+  border-radius: 4px;
+}
+
+.monitoring-content {
+  padding: 16px;
+  background: #fff;
+  border-radius: 16px;
+}
+
+.monitoring-header {
+  height: 164px;
+  margin: 24px 0 16px;
+  background: #fff;
+  border: 1px solid #fff;
+  border-radius: 16px;
+}
+</style>