guonan
2025-06-06 cf4ed06dea0076e518319de24c5120bb3fe0dae9
src/components/menu/Device.vue
@@ -4,72 +4,129 @@
      <span>监测设备</span>
    </div>
    <div class="left-content device-content">
      <div style="margin-left: 5px">
        <span style="color: white">重点沟:</span>
        <el-select @change="handleChange" v-model="selectValue" placeholder="Select" size="large" style="width: 240px">
          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
      <div style="margin-left: 5px; margin-bottom: 5px">
        <span style="color: white">北京市:</span>
        <el-select
          @change="handleChange1"
          v-model="selectValue1"
          placeholder="Select"
          size="mini"
          style="width: 240px"
        >
          <el-option
            v-for="item in BJoptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </div>
      <el-tree :data="deviceTree" node-key="deviceId" :props="treeProps" @node-click="handleTreeNodeClick"
        class="device-tree">
        <template #default="{ node, data }">
          <span v-if="!data.children" class="device-tree-item">
            <!-- <div class="device-item-icon"></div> -->
            <span class="device-item-text">{{ node.label }}</span>
          </span>
          <span v-else class="device-tree-category">
            {{ node.label }} ({{ data.children.length }})
          </span>
        </template>
      </el-tree>
      <div style="margin-left: 5px">
        <span style="color: white">重点沟:</span>
        <el-select
          @change="handleChange"
          v-model="selectValue"
          placeholder="Select"
          size="mini"
          style="width: 240px"
        >
          <el-option
            v-for="item in options"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          />
        </el-select>
      </div>
      <!-- 加载遮罩层和内容容器 -->
      <div class="tree-container">
        <div v-if="isLoading" class="loading-overlay">
          <div class="loading-content">
            <el-icon class="loading-icon"><Loading /></el-icon>
            <span class="loading-text">数据加载中...</span>
          </div>
        </div>
        <el-tree
          v-show="!isLoading"
          :data="deviceTree"
          node-key="deviceId"
          :props="treeProps"
          @node-click="handleTreeNodeClick"
          class="device-tree"
        >
          <template #default="{ node, data }">
            <span v-if="!data.children" class="device-tree-item">
              <span class="device-item-text">{{ node.label }}</span>
            </span>
            <span v-else class="device-tree-category">
              {{ node.label }} ({{ data.children.length }})
            </span>
          </template>
        </el-tree>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, watch, onBeforeUnmount, } from "vue";
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { ref, computed, onMounted, watch, onBeforeUnmount } from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { createPoint, removeEntities } from "@/utils/map";
import { deviceDictList, getDictName } from "@/constant/dict.js";
import { getDeviceInfo } from "@/api/hpApi";
import { getDeviceInfoShg, getDeviceInfo } from "@/api/hpApi";
import { initeWaterPrimitiveView } from "@/utils/water"; //相机flyTo函数,后续options列表中有对应经纬度后弃用
import { useSimStore } from "@/store/simulation";
import { Loading } from "@element-plus/icons-vue";
const simStore = useSimStore();
onMounted(() => {
  loadDeviceList("孙胡沟");
  initeWaterPrimitiveView()
const isLoading = ref(true); // 初始设置为加载状态
onMounted(async () => {
  try {
    await getData();
    await loadDeviceList(selectValue.value);
    // initeWaterPrimitiveView();
  } finally {
    isLoading.value = false;
  }
});
// onBeforeRouteUpdate((to, from, next) => {
//   if (to.path !== '/zhjc') {
//     handleCleanup();
//   }
//   next();
// });
onBeforeRouteUpdate((to, from, next) => {
  if (to.path !== "/zhjc") {
    handleCleanup();
  }
  next();
});
const route = useRoute();
onBeforeUnmount(() => {
  if (route.path !== '/zhjc') {
  if (route.path !== "/zhjc") {
    handleCleanup();
  }
});
watch(() => simStore.DeviceShowSwitch, (newValue, oldValue) => {
  if (newValue) {
    initializeDevicePoints();
  } else {
    handleCleanup()
watch(
  () => simStore.DeviceShowSwitch,
  (newValue, oldValue) => {
    if (newValue) {
      initializeDevicePoints();
    } else {
      handleCleanup();
    }
  }
});
);
const deviceListAll = ref([]);
const deviceEntities = ref([]);
const handleCleanup = () => {
  deviceListAll.value.forEach(item => {
  deviceListAll.value.forEach((item) => {
    removeEntities(item.deviceId);
  });
}
const initializeDevicePoints = () => {
};
const initializeDevicePoints = (val) => {
  const list = [];
  deviceListAll.value.forEach((item, index) => {
  val.forEach((item, index) => {
    // 根据需求可增删
    item.type = getDictName(deviceDictList, item.dictDeviceType);
    item.name = item.deviceName.split(selectValue.value)[1] || item.deviceName;
@@ -82,22 +139,55 @@
  });
  deviceEntities.value = list;
};
const allDevices = ref([]);
const getData = async () => {
  const res = await getDeviceInfoShg();
  allDevices.value = res.data.pageData;
};
// 根据区域名称加载设备列表
const loadDeviceList = async (areaName) => {
  try {
    handleCleanup()
    const res = await getDeviceInfo();
    const allDevices = res.data.pageData;
    const devicesInArea = allDevices.filter((item) =>
    isLoading.value = true;
    handleCleanup();
    // const res = await getDeviceInfoShg();
    // const allDevices = res.data.pageData;
    const devicesInArea = allDevices.value.filter((item) =>
      item.deviceName?.includes(areaName)
    );
    deviceListAll.value = devicesInArea;
    deviceListAll.length = 0;
    initializeDevicePoints();
    // deviceListAll.value = devicesInArea;
    getDeviceInfo().then((res) => {
      const list = res.data.pageData;
      deviceListAll.value = [];
      let index = 0;
      const batchSize = 50; // 每次处理的数量
      const delay = 100; // 每隔多少毫秒处理一次
      const intervalId = setInterval(() => {
        // 取出当前批次的数据
        const batch = list.slice(index, index + batchSize);
        if (batch.length === 0) {
          clearInterval(intervalId); // 数据处理完了,停止定时器
          return;
        }
        // 把当前批次的数据 push 到 deviceListAll
        deviceListAll.value = [...deviceListAll.value, ...batch];
        // 对当前批次执行初始化方法
        initializeDevicePoints(batch);
        index += batchSize;
      }, delay);
    });
  } catch (error) {
    console.error("加载设备信息失败", error);
  } finally {
    isLoading.value = false;
  }
};
// 处理区域变化事件
const handleChange = (item) => {
  if (!item) {
@@ -109,29 +199,18 @@
  // console.log(deviceListAll.value);
};
const selectValue1 = ref("北京市");
const BJoptions = ref([]);
const selectValue = ref("孙胡沟");
const options = ref([
  {
    value: "孙胡沟",
    label: "孙胡沟",
  },
  {
    value: "鱼水洞后沟",
    label: "鱼水洞后沟",
  },
  {
    value: "于家西沟",
    label: "于家西沟",
  },
  {
    value: "北河沟",
    label: "北河沟",
  },
  {
    value: "龙泉峪村",
    label: "龙泉峪村",
  },
  { value: "孙胡沟", label: "孙胡沟" },
  { value: "鱼水洞后沟", label: "鱼水洞后沟" },
  { value: "于家西沟", label: "于家西沟" },
  { value: "北河沟", label: "北河沟" },
  { value: "龙泉峪村", label: "龙泉峪村" },
]);
const treeProps = {
@@ -166,6 +245,7 @@
    children: typeMap[typeName],
  }));
});
function handleTreeNodeClick(data) {
  // 只有设备节点才处理点击事件
  if (!data.children) {
@@ -182,7 +262,6 @@
  }
}
</script>
<style lang="less" scoped>
.device {
  position: absolute;
@@ -193,11 +272,67 @@
  z-index: 99;
}
.left-top {
  height: 40px;
  line-height: 40px;
  color: white;
  font-size: 16px;
  font-weight: bold;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.device-content {
  padding: 10px;
  box-sizing: border-box;
  overflow-y: auto;
  height: calc(100% - 70px);
  position: relative;
}
.tree-container {
  position: relative;
  height: calc(100% - 80px);
  margin-top: 10px;
  overflow-y: auto;
}
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  // background-color: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 100;
  // border-radius: 4px;
}
.loading-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #fff;
}
.loading-icon {
  font-size: 24px;
  margin-bottom: 8px;
  animation: rotating 2s linear infinite;
}
.loading-text {
  font-size: 14px;
}
@keyframes rotating {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
.device-tree {
@@ -233,21 +368,12 @@
  color: #fff;
}
.device-item-icon {
  background: url("@/assets/img/menu/locationicon.png") no-repeat;
  background-position: 5px 5px;
  width: 30px;
  height: 30px;
  margin-right: 5px;
}
.device-item-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
// 保持原有的选择器样式
:deep(.el-select__placeholder) {
  color: white;
}
@@ -255,7 +381,7 @@
:deep(.el-select-dropdown__item.hover),
:deep(.el-select-dropdown__item:hover) {
  color: white !important;
  background-color: rgb(38, 124, 124, 0.5);
  background-color: rgba(38, 124, 124, 0.5);
}
:deep(.el-tree-node__content) {