Browse Source

feat(views): 初步添加“环境监控”模块

wangshun 2 tháng trước cách đây
mục cha
commit
8cd2bf10ea
2 tập tin đã thay đổi với 1022 bổ sung1 xóa
  1. BIN
      src/assets/img/env-monitor-bgc.png
  2. 1022 1
      src/views/env-monitor/EnvMonitor.vue

BIN
src/assets/img/env-monitor-bgc.png


+ 1022 - 1
src/views/env-monitor/EnvMonitor.vue

@@ -1,3 +1,1024 @@
+<script setup lang="ts">
+import { onMounted, ref, watch } from 'vue';
+import { message } from 'ant-design-vue';
+
+import ButtonTabs from '@/components/ButtonTabs.vue';
+import LineChart from '@/components/LineChart.vue';
+import SvgIcon from '@/components/SvgIcon.vue';
+import { useRequest } from '@/hooks/request';
+import { t } from '@/i18n';
+import {
+  addMonitorPoint,
+  addRegion,
+  deleteMonitorPoint,
+  getDeviceGroupList,
+  getDeviceListSimple,
+  getGroupRegions,
+  getMonitorPointInfo,
+  getPageList,
+  getRegionsPointsData,
+  queryDevicesList,
+  regionDelete,
+  regionUpdate,
+  updateMonitorPoint,
+  updateRegionMonitorPoint,
+} from '@/api';
+import { HumitureType } from '@/constants';
+
+import envMonitorBgc from '@/assets/img/env-monitor-bgc.png';
+
+import type { CSSProperties } from 'vue';
+import type { FormInstance, Rule } from 'ant-design-vue/es/form';
+import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
+import type {
+  DeviceGroup,
+  DeviceGroupItem,
+  DeviceParams,
+  DevicesListItem,
+  GroupRegions,
+  MonitoringForm,
+  MonitoringPointData,
+  OutdooForm,
+  RegionNameList,
+  RegionsPointsItem,
+} from '@/types';
+
+interface EnvMonitorStyle {
+  background: string;
+  backgroundSize: string;
+}
+
+const oneDeviceGroup = ref<DeviceGroup[]>([]);
+const twoDeviceGroup = ref<DeviceGroup[]>([]);
+
+const gradeOne = ref<number | undefined>(undefined);
+const gradeTwo = ref<number | undefined>(undefined);
+const monitoringPointOpen = ref<boolean>(false);
+const outdoorOpen = ref<boolean>(false);
+const regionNameOpen = ref<boolean>(false);
+const regionName = ref<string>('');
+const groupRegions = ref<GroupRegions[]>([]);
+const titleRegions = ref<boolean>(true);
+const titleMonitoring = ref<boolean>(true);
+const deviceGroup = ref<DeviceGroupItem[]>([]);
+const returnDevicesList = ref<DevicesListItem[]>([]);
+const supplyDevicesList = ref<DevicesListItem[]>([]);
+const outdoorDevicesList = ref<DevicesListItem[]>([]);
+const returnDeviceParamsList = ref<DeviceParams[]>([]);
+const supplyDeviceParamsList = ref<DeviceParams[]>([]);
+const outdoorDeviceParamsList = ref<DeviceParams[]>([]);
+
+const monitoringPointData = ref<MonitoringPointData[]>([]);
+const regionNameList = ref<RegionNameList[]>([]);
+const regionList = ref<RegionsPointsItem[]>([]);
+const selectId = ref<number>(0);
+
+const monitoringId = ref<number>();
+
+const formRef = ref<FormInstance>();
+
+const envMonitorStyle = ref<EnvMonitorStyle>({
+  background: `url(${envMonitorBgc})`,
+  backgroundSize: 'cover',
+});
+
+const monitoringForm = ref<MonitoringForm>({
+  name: '',
+  tempHumidityControlMode: '2',
+  regionId: undefined,
+  returnDevGroupId: undefined,
+  returnDevId: undefined,
+  returnTempParamCode: '',
+  returnHumidityParamCode: '',
+  supplyDevGroupId: undefined,
+  supplyTempParamCode: '',
+  supplyHumidityParamCode: '',
+  tempUpper: 0,
+  tempLower: 0,
+  tempPreset: 0,
+  humidityUpper: 0,
+  humidityLower: 0,
+  humidityPreset: 0,
+});
+
+const outdooForm = ref<OutdooForm>({
+  regionId: undefined,
+  outsideDevGroupId: undefined,
+  outsideDevId: undefined,
+  outsideTempParamCode: '',
+  outsideHumidityParamCode: '',
+  outsideEnthalpyParamCode: '',
+});
+
+const headerStyle: CSSProperties = {
+  borderBottom: 0,
+  padding: '16px 16px 0 24px',
+};
+
+const footerStyle: CSSProperties = {
+  borderTop: 0,
+  padding: '16px 24px',
+};
+
+const rules: Record<string, Rule[]> = {
+  name: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  regionId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  returnDevGroupId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  returnDevId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  returnTempParamCode: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  returnHumidityParamCode: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  supplyDevGroupId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  supplyDevId: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  supplyTempParamCode: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+  supplyHumidityParamCode: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
+};
+
+const { handleRequest } = useRequest();
+
+const getGroupRegionsList = () => {
+  console.log(gradeTwo.value);
+  handleRequest(async () => {
+    if (gradeTwo.value) {
+      groupRegions.value = await getGroupRegions(gradeTwo.value);
+    }
+  });
+};
+
+const regionNameOk = () => {
+  if (!regionName.value) {
+    return message.warning('请输入区域名称');
+  }
+  handleRequest(async () => {
+    if (gradeTwo.value && regionName.value) {
+      if (titleRegions.value) {
+        await addRegion({
+          devGroupId: gradeTwo.value,
+          regionName: regionName.value,
+        });
+      } else {
+        await regionUpdate({
+          id: monitoringForm.value.regionId,
+          devGroupId: gradeTwo.value,
+          regionName: regionName.value,
+        });
+      }
+      getGroupRegionsList();
+      regionNameOpen.value = false;
+    }
+  });
+};
+
+const regionNameDelete = () => {
+  if (!regionName.value) {
+    return message.warning('请输入区域名称');
+  }
+  handleRequest(async () => {
+    if (monitoringForm.value.regionId) {
+      await regionDelete(monitoringForm.value.regionId);
+      monitoringForm.value.regionId = undefined;
+      getGroupRegionsList();
+      regionNameOpen.value = false;
+    }
+  });
+};
+
+const addRegionName = () => {
+  titleRegions.value = true;
+  regionNameOpen.value = true;
+  regionName.value = '';
+};
+
+const addRegionList = (value: SelectValue, option: DefaultOptionType) => {
+  regionName.value = option.regionName;
+};
+
+const editingRegionName = () => {
+  if (monitoringForm.value.regionId) {
+    titleRegions.value = false;
+    regionNameOpen.value = true;
+  } else {
+    return message.warning('请选择区域名称');
+  }
+};
+
+const addMonitoringPoint = () => {
+  titleMonitoring.value = true;
+  monitoringPointOpen.value = true;
+};
+const addAllGatewayList = (value: number) => {
+  handleRequest(async () => {
+    const data = await getDeviceGroupList({
+      parentId: value,
+    });
+    if (data.length) {
+      if (value === -1) {
+        oneDeviceGroup.value = data;
+        gradeOne.value = data[0].id;
+      } else {
+        twoDeviceGroup.value = data;
+        gradeTwo.value = data[0].id;
+      }
+    }
+  });
+};
+
+const getRegionsPointsList = (value: number) => {
+  monitoringPointData.value = [];
+  regionNameList.value = [];
+
+  handleRequest(async () => {
+    const data = await getRegionsPointsData(value);
+
+    if (data.length) {
+      data.forEach((item) => {
+        const { id, regionName } = item;
+        if (item.points.length) {
+          monitoringPointData.value.push(...item.points);
+        }
+
+        regionNameList.value.push({
+          name: regionName,
+          id,
+        });
+      });
+      if (data[0].id) {
+        selectId.value = data[0].id;
+      }
+    }
+  });
+};
+
+const obtainRegionsPointsData = (value: number) => {
+  handleRequest(async () => {
+    regionList.value = await getRegionsPointsData(value);
+  });
+};
+
+const adddDeviceGroup = (value: SelectValue, option: DefaultOptionType, judgment: number) => {
+  getDevicesGroupList(option.id, judgment);
+};
+
+const adddDevice = (value: SelectValue, option: DefaultOptionType, judgment: number) => {
+  getDeviceParamsList(option.id, judgment);
+};
+
+const getDeviceParamsList = (id: number, judgment: number) => {
+  handleRequest(async () => {
+    const data = await getDeviceListSimple(id);
+
+    if (judgment === HumitureType.ReturnAir) {
+      returnDeviceParamsList.value = data;
+      monitoringForm.value.returnHumidityParamCode = '';
+      monitoringForm.value.returnTempParamCode = '';
+    } else if (judgment === HumitureType.SupplyAir) {
+      supplyDeviceParamsList.value = data;
+      monitoringForm.value.supplyHumidityParamCode = '';
+      monitoringForm.value.supplyTempParamCode = '';
+    } else if (judgment === HumitureType.Outdoor) {
+      outdoorDeviceParamsList.value = data;
+      outdooForm.value.outsideEnthalpyParamCode = '';
+      outdooForm.value.outsideHumidityParamCode = '';
+      outdooForm.value.outsideTempParamCode = '';
+    }
+  });
+};
+
+const getDevicesGroupList = (id: number, judgment: number) => {
+  handleRequest(async () => {
+    const { records } = await queryDevicesList({
+      pageIndex: 1,
+      pageSize: 99999,
+      groupId: id,
+    });
+    if (judgment === HumitureType.ReturnAir) {
+      returnDevicesList.value = records;
+      monitoringForm.value.returnDevId = undefined;
+      monitoringForm.value.returnHumidityParamCode = '';
+      monitoringForm.value.returnTempParamCode = '';
+    } else if (judgment === HumitureType.SupplyAir) {
+      supplyDevicesList.value = records;
+      monitoringForm.value.supplyDevId = undefined;
+      monitoringForm.value.supplyHumidityParamCode = '';
+      monitoringForm.value.supplyTempParamCode = '';
+    } else if (judgment === HumitureType.Outdoor) {
+      outdoorDevicesList.value = records;
+      outdooForm.value.outsideDevId = undefined;
+      outdooForm.value.outsideEnthalpyParamCode = '';
+      outdooForm.value.outsideHumidityParamCode = '';
+      outdooForm.value.outsideTempParamCode = '';
+    }
+  });
+};
+const deleteMonitoringPoint = () => {
+  if (!titleMonitoring.value) {
+    handleRequest(async () => {
+      if (monitoringId.value) {
+        await deleteMonitorPoint(monitoringId.value);
+      }
+    });
+  }
+
+  monitoringPointOpen.value = false;
+};
+
+const saveMonitoringPoint = () => {
+  formRef.value
+    ?.validate()
+    .then(() => {
+      handleRequest(async () => {
+        if (titleMonitoring.value) {
+          await addMonitorPoint(monitoringForm.value);
+        } else {
+          await updateMonitorPoint(monitoringForm.value);
+        }
+        if (gradeTwo.value) {
+          getRegionsPointsList(gradeTwo.value);
+        }
+        monitoringPointOpen.value = false;
+      });
+    })
+    .catch(() => {});
+};
+
+const getDeviceGroup = () => {
+  handleRequest(async () => {
+    deviceGroup.value = await getPageList();
+  });
+};
+
+const offDrawer = () => {
+  monitoringPointOpen.value = false;
+};
+
+const selectClick = (id: number) => {
+  selectId.value = id;
+};
+
+const editorClick = (monitoringPointId: number) => {
+  handleRequest(async () => {
+    const {
+      humidityLower,
+      humidityPreset,
+      humidityUpper,
+      name,
+      regionId,
+      returnDevGroupId,
+      returnDevId,
+      returnHumidityParamCode,
+      returnTempParamCode,
+      supplyDevGroupId,
+      supplyDevId,
+      supplyHumidityParamCode,
+      supplyTempParamCode,
+      tempHumidityControlMode,
+      tempLower,
+      tempPreset,
+      tempUpper,
+      id,
+    } = await getMonitorPointInfo(monitoringPointId);
+    Object.assign(monitoringForm.value, {
+      humidityLower,
+      humidityPreset,
+      humidityUpper,
+      name,
+      regionId,
+      returnDevGroupId,
+      returnDevId,
+      returnHumidityParamCode,
+      returnTempParamCode,
+      supplyDevGroupId,
+      supplyDevId,
+      supplyHumidityParamCode,
+      supplyTempParamCode,
+      tempHumidityControlMode: String(tempHumidityControlMode),
+      tempLower,
+      tempPreset,
+      tempUpper,
+      id,
+    });
+    monitoringId.value = id;
+    getDevicesGroupList(returnDevGroupId, HumitureType.ReturnAir);
+    getDevicesGroupList(supplyDevGroupId, HumitureType.SupplyAir);
+
+    getDeviceParamsList(returnDevId, HumitureType.ReturnAir);
+    getDeviceParamsList(supplyDevId, HumitureType.SupplyAir);
+
+    titleMonitoring.value = false;
+    monitoringPointOpen.value = true;
+  });
+};
+
+const addOutdoorDrawer = () => {
+  outdoorOpen.value = true;
+  regionList.value.forEach((item) => {
+    const {
+      outsideEnthalpyParamCode,
+      outsideHumidityParamCode,
+      outsideTempParamCode,
+      outsideDevGroupId,
+      outsideDevId,
+      id,
+    } = item;
+    if (item.id === selectId.value) {
+      Object.assign(outdooForm.value, {
+        outsideEnthalpyParamCode,
+        outsideHumidityParamCode,
+        outsideTempParamCode,
+        outsideDevGroupId,
+        outsideDevId,
+        regionId: id,
+      });
+      if (outsideDevGroupId) {
+        getDevicesGroupList(outsideDevGroupId, HumitureType.Outdoor);
+      }
+
+      if (outsideDevId) {
+        getDeviceParamsList(outsideDevId, HumitureType.Outdoor);
+      }
+    }
+  });
+};
+
+const saveOutdoo = () => {
+  handleRequest(async () => {
+    await updateRegionMonitorPoint(outdooForm.value);
+    if (gradeTwo.value) {
+      obtainRegionsPointsData(gradeTwo.value);
+    }
+
+    outdoorOpen.value = false;
+  });
+};
+
+const offOutdoorDrawer = () => {
+  outdoorOpen.value = false;
+};
+
+watch(
+  () => gradeOne.value,
+  (count) => {
+    if (count) {
+      addAllGatewayList(count);
+    }
+  },
+);
+
+watch(
+  () => gradeTwo.value,
+  (count) => {
+    if (count) {
+      getRegionsPointsList(count);
+      obtainRegionsPointsData(count);
+      getGroupRegionsList();
+    }
+  },
+);
+
+onMounted(() => {
+  addAllGatewayList(-1);
+  getDeviceGroup();
+});
+</script>
+
 <template>
-  <div>环境监控</div>
+  <div>
+    <AFlex justify="space-between">
+      <AFlex align="center">
+        <div class="text-top">环境监控</div>
+        <ASelect
+          class="select-width select-monitoring"
+          v-model:value="gradeOne"
+          :options="oneDeviceGroup"
+          :field-names="{ label: 'groupName', value: 'id' }"
+          placeholder="请选择"
+        />
+        <ASelect
+          class="select-width"
+          v-model:value="gradeTwo"
+          :options="twoDeviceGroup"
+          :field-names="{ label: 'groupName', value: 'id' }"
+          placeholder="请选择"
+        />
+      </AFlex>
+
+      <div>
+        <AButton class="icon-button default-button">
+          <AFlex align="center">
+            <SvgIcon name="plus" />
+            <span>切换到列表显示 </span>
+          </AFlex>
+        </AButton>
+        <AButton type="primary" class="icon-button button-monitoring" @click="addMonitoringPoint">
+          <AFlex align="center">
+            <SvgIcon name="plus" />
+            <span> 添加检测点 </span>
+          </AFlex>
+        </AButton>
+      </div>
+    </AFlex>
+
+    <div class="content-monitoring">
+      <div class="content-monitoring-top">123</div>
+      <AFlex class="content-monitoring-canvas">
+        <div class="content-monitoring-canvas-left">
+          <div v-for="item in monitoringPointData" :key="item.id">
+            <LineChart :data="item" @editorClick="editorClick" />
+          </div>
+        </div>
+
+        <AFlex :style="envMonitorStyle" class="monitoring-img">
+          <AFlex v-if="regionList.length" align="center" class="list-regions">
+            <ButtonTabs
+              :data="regionNameList"
+              :select-id="selectId"
+              :width="'75px'"
+              :radius="'16px'"
+              :tooltip="true"
+              @selectClick="selectClick"
+            />
+          </AFlex>
+          <div v-if="regionList.length" class="canvas-div">
+            <AFlex justify="space-between" class="canvas-div-top">
+              <AButton class="icon-button" @click="addOutdoorDrawer">
+                <SvgIcon name="outdoor" />
+                22.9℃|60.6%
+              </AButton>
+              <AFlex>
+                <AFlex justify="center" align="center" class="button-icon">
+                  <SvgIcon name="edit-o" />
+                </AFlex>
+                <AFlex justify="center" align="center" class="button-icon">
+                  <SvgIcon name="copy" />
+                </AFlex>
+              </AFlex>
+            </AFlex>
+            <div class="canvas-content">
+              <AButton class="icon-button icon-button-margin">
+                <SvgIcon name="outdoor" />
+                22.9℃|60.6%
+              </AButton>
+            </div>
+          </div>
+        </AFlex>
+      </AFlex>
+    </div>
+
+    <ADrawer
+      width="600"
+      :open="monitoringPointOpen"
+      :closable="false"
+      class="drawer-monitoring"
+      :header-style="headerStyle"
+      :footer-style="footerStyle"
+    >
+      <template #title>{{ titleMonitoring ? '添加检测点' : '编辑监测点' }} </template>
+      <template #extra>
+        <SvgIcon class="off-icon" @click="offDrawer" name="close" />
+      </template>
+      <AForm ref="formRef" class="form-ref" :model="monitoringForm" layout="vertical" :rules="rules">
+        <AFormItem name="name">
+          <AInput v-model:value="monitoringForm.name" class="input-wdith region-bottom" placeholder="请输入" />
+        </AFormItem>
+        <AFormItem label="区域" name="regionId">
+          <AFlex align="center">
+            <ASelect
+              class="input-wdith"
+              v-model:value="monitoringForm.regionId"
+              :options="groupRegions"
+              :field-names="{ label: 'regionName', value: 'id' }"
+              placeholder="请选择"
+              @change="addRegionList"
+            />
+
+            <div @click="editingRegionName">
+              <AFlex justify="center" align="center" class="button-flex">
+                <SvgIcon name="edit-o" />
+              </AFlex>
+            </div>
+            <div @click="addRegionName">
+              <AFlex justify="center" align="center" class="button-flex button-plus">
+                <SvgIcon name="plus" />
+              </AFlex>
+            </div>
+          </AFlex>
+        </AFormItem>
+        <ARadioGroup v-model:value="monitoringForm.tempHumidityControlMode" class="radio-group">
+          <ARadio value="2">回风控制</ARadio>
+          <ARadio value="1">送风控制</ARadio>
+        </ARadioGroup>
+        <AFlex justify="space-between">
+          <div>
+            <AFormItem class="form-item" label="回风温湿度仪表" name="returnDevGroupId">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.returnDevGroupId"
+                :options="twoDeviceGroup"
+                :field-names="{ label: 'groupName', value: 'id' }"
+                placeholder="请选择"
+                @change="(value, option) => adddDeviceGroup(value, option, HumitureType.ReturnAir)"
+              />
+            </AFormItem>
+            <AFormItem name="returnDevId">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.returnDevId"
+                :options="returnDevicesList"
+                :field-names="{ label: 'deviceName', value: 'id' }"
+                placeholder="请选择"
+                @change="(value, option) => adddDevice(value, option, HumitureType.ReturnAir)"
+              />
+            </AFormItem>
+
+            <AFormItem name="returnTempParamCode">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.returnTempParamCode"
+                :options="returnDeviceParamsList"
+                :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+                placeholder="请选择"
+              />
+            </AFormItem>
+            <AFormItem name="returnHumidityParamCode">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.returnHumidityParamCode"
+                :options="returnDeviceParamsList"
+                :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+                placeholder="请选择"
+              />
+            </AFormItem>
+          </div>
+          <div>
+            <AFormItem label="送风温湿度仪表" name="supplyDevGroupId">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.supplyDevGroupId"
+                :options="twoDeviceGroup"
+                :field-names="{ label: 'groupName', value: 'id' }"
+                placeholder="请选择"
+                @change="(value, option) => adddDeviceGroup(value, option, HumitureType.SupplyAir)"
+              />
+            </AFormItem>
+            <AFormItem name="supplyDevId">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.supplyDevId"
+                :options="supplyDevicesList"
+                :field-names="{ label: 'deviceName', value: 'id' }"
+                placeholder="请选择"
+                @change="(value, option) => adddDevice(value, option, HumitureType.SupplyAir)"
+              />
+            </AFormItem>
+
+            <AFormItem name="supplyTempParamCode">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.supplyTempParamCode"
+                :options="supplyDeviceParamsList"
+                :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+                placeholder="请选择"
+              />
+            </AFormItem>
+            <AFormItem name="supplyHumidityParamCode">
+              <ASelect
+                class="input-wdith"
+                v-model:value="monitoringForm.supplyHumidityParamCode"
+                :options="supplyDeviceParamsList"
+                :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+                placeholder="请选择"
+              />
+            </AFormItem>
+          </div>
+        </AFlex>
+
+        <AFlex justify="space-between">
+          <div>
+            <AFormItem label="室内温度配置">
+              <AFlex align="center">
+                <AInputNumber class="input-number-width" v-model:value="monitoringForm.tempUpper" :min="0" :max="999" />
+                <div class="configure-text">上限值</div>
+              </AFlex>
+            </AFormItem>
+            <AFormItem>
+              <AFlex align="center">
+                <AInputNumber class="input-number-width" v-model:value="monitoringForm.tempLower" :min="0" :max="999" />
+                <div class="configure-text">下限值</div>
+              </AFlex>
+            </AFormItem>
+            <AFormItem>
+              <AFlex align="center">
+                <AInputNumber
+                  class="input-number-width"
+                  v-model:value="monitoringForm.tempPreset"
+                  :min="0"
+                  :max="999"
+                />
+                <div class="configure-text">限定值</div>
+              </AFlex>
+            </AFormItem>
+          </div>
+          <div>
+            <AFormItem label="室内湿度配置">
+              <AFlex align="center">
+                <AInputNumber
+                  class="input-number-width"
+                  v-model:value="monitoringForm.humidityUpper"
+                  :min="0"
+                  :max="999"
+                />
+                <div class="configure-text">上限值</div>
+              </AFlex>
+            </AFormItem>
+            <AFormItem>
+              <AFlex align="center">
+                <AInputNumber
+                  class="input-number-width"
+                  v-model:value="monitoringForm.humidityLower"
+                  :min="0"
+                  :max="999"
+                />
+                <div class="configure-text">下限值</div>
+              </AFlex>
+            </AFormItem>
+            <AFormItem>
+              <AFlex align="center">
+                <AInputNumber
+                  class="input-number-width"
+                  v-model:value="monitoringForm.humidityPreset"
+                  :min="0"
+                  :max="999"
+                />
+                <div class="configure-text">限定值</div>
+              </AFlex>
+            </AFormItem>
+          </div>
+        </AFlex>
+      </AForm>
+      <template #footer>
+        <AFlex justify="flex-end" :gap="16">
+          <AButton class="default-button" @click="deleteMonitoringPoint">删除</AButton>
+          <AButton type="primary" @click="saveMonitoringPoint">保存</AButton>
+        </AFlex>
+      </template>
+    </ADrawer>
+
+    <ADrawer
+      class="drawer-monitoring"
+      width="304"
+      :open="outdoorOpen"
+      :closable="false"
+      :header-style="headerStyle"
+      :footer-style="footerStyle"
+    >
+      <template #title>室外温湿度</template>
+      <template #extra>
+        <SvgIcon class="off-icon" @click="offOutdoorDrawer" name="close" />
+      </template>
+      <AForm ref="formOutdoorRef" :model="outdooForm" layout="vertical">
+        <AFormItem label="室外温湿度仪表" name="outsideDevGroupId">
+          <ASelect
+            class="input-wdith"
+            v-model:value="outdooForm.outsideDevGroupId"
+            :options="twoDeviceGroup"
+            :field-names="{ label: 'groupName', value: 'id' }"
+            placeholder="请选择"
+            @change="(value, option) => adddDeviceGroup(value, option, HumitureType.Outdoor)"
+          />
+        </AFormItem>
+        <AFormItem name="outsideDevId">
+          <ASelect
+            class="input-wdith"
+            v-model:value="outdooForm.outsideDevId"
+            :options="outdoorDevicesList"
+            :field-names="{ label: 'deviceName', value: 'id' }"
+            placeholder="请选择"
+            @change="(value, option) => adddDevice(value, option, HumitureType.Outdoor)"
+          />
+        </AFormItem>
+        <AFormItem name="outsideTempParamCode">
+          <ASelect
+            class="input-wdith"
+            v-model:value="outdooForm.outsideTempParamCode"
+            :options="outdoorDeviceParamsList"
+            :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+            placeholder="请选择"
+          />
+        </AFormItem>
+        <AFormItem name="outsideHumidityParamCode">
+          <ASelect
+            class="input-wdith"
+            v-model:value="outdooForm.outsideHumidityParamCode"
+            :options="outdoorDeviceParamsList"
+            :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+            placeholder="请选择"
+          />
+        </AFormItem>
+        <AFormItem name="outsideEnthalpyParamCode">
+          <ASelect
+            class="input-wdith"
+            v-model:value="outdooForm.outsideEnthalpyParamCode"
+            :options="outdoorDeviceParamsList"
+            :field-names="{ label: 'deviceParamName', value: 'deviceParamCode' }"
+            placeholder="请选择"
+          />
+        </AFormItem>
+      </AForm>
+      <template #footer>
+        <AFlex justify="flex-end" :gap="16">
+          <AButton type="primary" @click="saveOutdoo">保存</AButton>
+        </AFlex>
+      </template>
+    </ADrawer>
+
+    <AModal
+      v-model:open="regionNameOpen"
+      :title="titleRegions ? '添加区域' : '编辑区域'"
+      :footer="null"
+      width="460px"
+      :mask-closable="false"
+      :keyboard="false"
+    >
+      <div class="region-name">区域名</div>
+      <AInput v-model:value="regionName" placeholder="请输入" />
+
+      <AFlex justify="flex-end" class="region-name-top">
+        <AButton v-if="!titleRegions" class="default-button" @click="regionNameDelete">{{
+          $t('common.delete')
+        }}</AButton>
+        <AButton class="default-button cancel-button" @click="regionNameOpen = false">{{
+          $t('common.cancel')
+        }}</AButton>
+        <AButton type="primary" @click="regionNameOk">{{ $t('common.confirm') }}</AButton>
+      </AFlex>
+    </AModal>
+  </div>
 </template>
+
+<style lang="scss" scoped>
+.icon-button-margin {
+  margin: 16px 0 0 16px;
+}
+
+.canvas-div-top {
+  margin-bottom: 16px;
+  margin-left: 16px;
+}
+
+.canvas-div {
+  width: 820px;
+}
+
+.button-icon {
+  width: 32px;
+  height: 32px;
+  margin-left: 12px;
+  font-size: 16px;
+  color: var(--antd-color-primary);
+  cursor: pointer;
+  background: #fff;
+  border: 1px solid #32bac0;
+  border-radius: 4px;
+}
+
+.canvas-content {
+  width: 820px;
+  height: 660px;
+  background: #f5f7fa;
+  border: 1px solid var(--antd-color-primary);
+  border-radius: 12px;
+}
+
+.list-regions {
+  height: 100%;
+  margin-right: 16px;
+}
+
+.content-monitoring-canvas-left {
+  width: 260px;
+  padding-right: 30px;
+  padding-bottom: 24px;
+  overflow: hidden;
+  overflow: hidden auto; /* 隐藏水平滚动条 */ /* 只显示垂直滚动条 */
+}
+
+.monitoring-img {
+  width: 100%;
+  height: 100%;
+}
+
+.content-monitoring-canvas {
+  height: calc(100% - 45px);
+  padding-top: 24px;
+}
+
+.content-monitoring-top {
+  height: 40px;
+}
+
+.drawer-monitoring .form-ref .ant-form-item .ant-form-item-label > label {
+  color: #666;
+}
+
+.configure-text {
+  margin-left: 12px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 22px;
+  color: #666;
+  text-align: left;
+}
+
+.radio-group {
+  margin: 8px 0 24px;
+}
+
+.region-bottom {
+  margin-bottom: 8px;
+}
+
+.drawer-monitoring .ant-form-item {
+  margin-bottom: 16px;
+}
+
+.input-number-width {
+  width: 192px;
+  margin-bottom: 16px;
+}
+
+.segmented-monitoring {
+  width: 196px;
+  margin-bottom: 24px;
+}
+
+.off-icon {
+  cursor: pointer;
+}
+
+.cancel-button {
+  margin: 0 16px;
+}
+
+.region-name-top {
+  margin-top: 40px;
+}
+
+.region-name {
+  margin-top: 16px;
+  margin-bottom: 5px;
+  color: #666;
+}
+
+.button-plus {
+  margin-left: 12px !important;
+  font-size: 16px;
+}
+
+.button-flex {
+  width: 32px;
+  height: 32px;
+  margin-left: 16px;
+  cursor: pointer;
+  background: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.input-wdith {
+  width: 256px;
+}
+
+.button-monitoring {
+  margin-left: 16px;
+}
+
+.content-monitoring {
+  height: calc(100vh - 85px);
+
+  // min-height: 100%;
+  padding: 24px 0 0 24px; // 上、右、下、左
+  margin-top: 16px;
+
+  // overflow: hidden;
+  background-color: #fff;
+  border-radius: 16px;
+}
+
+.select-monitoring {
+  margin-right: 16px;
+  margin-left: 32px;
+}
+
+.select-width {
+  width: 192px;
+}
+
+.text-top {
+  font-size: 20px;
+  font-style: normal;
+  font-weight: 500;
+  line-height: 32px;
+  color: rgb(0 0 0 / 85%);
+  text-align: left;
+}
+</style>