guonan
2025-06-07 0719dc09c2d9b297f836c4e0832c9db622adfcd0
增加隐患点及监测设备以及解决ID报错问题
已修改5个文件
235 ■■■■■ 文件已修改
src/api/hpApi.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menu/Device.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menu/Location.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/map.js 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/GisView.vue 91 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/hpApi.js
@@ -90,7 +90,8 @@
    "filterObject": {
      "belongObjList": [
        "1797461961110261762"
      ]
      ],
      "townCode": data
      // "id": "",
      // "deviceName": "",
      // "deviceCode": "",
@@ -103,7 +104,6 @@
      // "dictCommunicationType": "",
      // "dictDeviceStatus": "",
      // "divisionDistrict": "",
      // "townCode": ""
    },
    "sortedList": [
      {
src/components/menu/Device.vue
@@ -72,7 +72,7 @@
<script setup>
import { ref, computed, onMounted, watch, onBeforeUnmount } from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { createPoint, removeEntities } from "@/utils/map";
import { createPoint, removeEntities, clearAllPoints } from "@/utils/map";
import { deviceDictList, getDictName } from "@/constant/dict.js";
import { getDeviceInfoShg, getDeviceInfo } from "@/api/hpApi";
import { initeWaterPrimitiveView } from "@/utils/water"; //相机flyTo函数,后续options列表中有对应经纬度后弃用
@@ -93,17 +93,17 @@
  }
});
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") {
    handleCleanup();
    clearAllPoints();
  }
});
watch(
@@ -112,18 +112,14 @@
    if (newValue) {
      initializeDevicePoints();
    } else {
      handleCleanup();
      clearAllPoints();
    }
  }
);
const deviceListAll = ref([]);
const deviceEntities = ref([]);
const handleCleanup = () => {
  deviceListAll.value.forEach((item) => {
    removeEntities(item.deviceId);
  });
};
// const initializeDevicePoints = (val) => {
const initializeDevicePoints = () => {
  const list = [];
@@ -149,14 +145,15 @@
// 根据区域名称加载设备列表
const loadDeviceList = async (areaName) => {
  try {
    clearAllPoints();
    isLoading.value = true;
    handleCleanup();
    // const res = await getDeviceInfoShg();
    // const allDevices = res.data.pageData;
    const devicesInArea = allDevices.value.filter((item) =>
    deviceListAll.value = allDevices.value.filter((item) =>
      item.deviceName?.includes(areaName)
    );
    deviceListAll.value = devicesInArea;
    initializeDevicePoints();
    // getDeviceInfo().then((res) => {
    //   const list = res.data.pageData;
    //   deviceListAll.value = [];
@@ -292,8 +289,8 @@
.tree-container {
  position: relative;
  height: calc(100% - 80px);
  margin-top: 10px;
  height: calc(100% - 22px);
  // margin-top: 10px;
  overflow-y: auto;
}
src/components/menu/Location.vue
@@ -66,7 +66,7 @@
<script setup>
import { ref, onMounted, watch, onBeforeUnmount } from "vue";
import { createPoint, removeEntities } from "@/utils/map";
import { createPoint,clearAllPoints } from "@/utils/map";
import { useSimStore } from "@/store/simulation";
import { initeWaterPrimitiveView } from "@/utils/water"; //相机flyTo函数,后续options列表中有对应经纬度后弃用
import { useRoute, onBeforeRouteUpdate } from "vue-router";
@@ -91,7 +91,7 @@
const simStore = useSimStore();
// onBeforeRouteUpdate((to, from, next) => {
//   if (to.path !== "/yhgl") {
//     handleCleanup();
//     clearAllPoints();
//   }
//   next();
// });
@@ -99,7 +99,7 @@
onBeforeUnmount(() => {
  if (route.path !== "/yhgl") {
    handleCleanup();
    clearAllPoints();
  }
});
const selectValue = ref("孙胡沟");
@@ -134,6 +134,8 @@
const loading = ref(true); // 控制加载状态
function handleClick(district) {
  // 此处调用是因为GisView页面会在点击下一乡镇之前把上一个选择的区域的隐患点清除掉(如果刚好选择了孙胡沟,那么下一个点击将会清空孙胡沟的隐患点)
  initializeDevicePoints();
  const entity = viewer.entities.getById(district.hdId);
  if (entity) {
    viewer.flyTo(entity, {
@@ -145,11 +147,7 @@
    });
  }
}
const handleCleanup = async () => {
  await Promise.all(
    districtList.value.map((item) => removeEntities(item.hdId))
  );
};
const initializeDevicePoints = async () => {
  await Promise.all(
    districtList.value.map(async (item, index) => {
@@ -169,7 +167,7 @@
};
const filterDataByArea = async (areaName) => {
  handleCleanup();
  clearAllPoints();
  if (!areaName || !simStore.DangerPoint || simStore.DangerPoint.length === 0) {
    districtList.value = [];
    return;
@@ -207,7 +205,7 @@
        isInitialized = true;
      }
    } else {
      handleCleanup();
      clearAllPoints();
      isInitialized = false;
    }
  }
@@ -220,7 +218,7 @@
      filterDataByArea(selectValue.value);
      loading.value = false; // 数据加载完成
    } else {
      handleCleanup();
      clearAllPoints();
      districtList.value = [];
      loading.value = true; // 数据未准备就绪
    }
@@ -228,7 +226,7 @@
);
onMounted(() => {
  handleCleanup();
  clearAllPoints();
  // initeWaterPrimitiveView();
  // 默认先检查一遍数据
  if (simStore.DangerPoint && simStore.DangerPoint.length > 0) {
src/utils/map.js
@@ -1,4 +1,5 @@
import { showDeviceDetail, deviceDetail, className, dialogPositon } from "@/store";
import { componentToSlot } from "element-plus/es/components/table-v2/src/utils.mjs";
export function addTerrain(url) {
  // console.log("加载地形");
  var terrainProvider = new Cesium.CesiumTerrainProvider({
@@ -57,23 +58,19 @@
}
let entities = [];
// 假设你有一个全局存储 entities 的结构(建议用 Map 提高性能查找)
const pointEntityMap = new Map(); // key: id, value: entity
export function createPoint(option) {
  const {
    id,
    type = "",
    name = "默认名称",
    view,
    latitude,
    longitude,
    height,
    callback,
    imgWidth = 56,
    imgHeight = 67,
    showBillboard = true,
    showLabel = true,
    className = "device",
  } = option;
  let position = Cesium.Cartesian3.fromDegrees(longitude, latitude, 50);
  const { id, type = "", name = "默认名称", view, latitude, longitude, height, callback, imgWidth = 56, imgHeight = 67, showBillboard = true, showLabel = true, className = "device" } = option;
  // 如果已经存在该 id 的 entity,则跳过创建
  if (pointEntityMap.has(id)) {
    console.log(`点 ${id} 已存在,跳过创建`);
    return;
  }
  let position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height || 50);
  let model = {
    id,
    name: name,
@@ -83,88 +80,66 @@
    attrs: option,
    className: className,
    label: {
      // 文本。支持显式换行符“ \ n”
      text: name || "默认标签",
      // 字体样式,以CSS语法指定字体
      font: "14pt Source Han Sans CN",
      // 字体颜色
      fillColor: type.includes("active") ? Cesium.Color.AQUA : Cesium.Color.WHITE,
      // 背景颜色
      backgroundColor: showBillboard ? Cesium.Color.BLACK.withAlpha(0.5) : Cesium.Color.SKYBLUE,
      // 是否显示背景颜色
      showBackground: true,
      // 字体边框
      outline: false,
      // 字体边框颜色
      outlineColor: Cesium.Color.WHITE,
      // 字体边框尺寸
      outlineWidth: 0,
      // 应用于图像的统一比例。比例大于会1.0放大标签,而比例小于会1.0缩小标签。
      scale: 1.0,
      scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.5),
      // 设置样式:FILL:填写标签的文本,但不要勾勒轮廓;OUTLINE:概述标签的文本,但不要填写;FILL_AND_OUTLINE:填写并概述标签文本。
      style: Cesium.LabelStyle.FILL_AND_OUTLINE,
      // 相对于坐标的水平位置
      verticalOrigin: Cesium.VerticalOrigin.CENTER,
      // 相对于坐标的水平位置
      horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
      // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
      pixelOffset: new Cesium.Cartesian2(0, -40),
      // pixelOffset: new Cesium.Cartesian2(0, 0),
      // 显示在距相机的距离处的属性,多少区间内是可以显示的
      distanceDisplayCondition: type.includes("河流")
        ? new Cesium.DistanceDisplayCondition(0, 5000000)
        : new Cesium.DistanceDisplayCondition(0, 50000),
      heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
      // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      // 是否显示
      show: showLabel,
    },
  };
  model.billboard = {
    // 图像地址,URI或Canvas的属性
    image: type ? `/images/poi/${type}.png` : `/images/poi/村庄.png`,
    // 高度(以像素为单位)
    height: imgHeight || 20,
    // 宽度(以像素为单位)
    width: imgWidth || 20,
    // 逆时针旋转
    // 大小是否以米为单位
    sizeInMeters: false,
    // 相对于坐标的垂直位置
    verticalOrigin: Cesium.VerticalOrigin.CENTER,
    // 相对于坐标的水平位置
    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
    // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
    pixelOffset: new Cesium.Cartesian2(0, 3),
    // 应用于图像的统一比例。比例大于会1.0放大标签,而比例小于会1.0缩小标签。
    scale: 0.8,
    scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e7, 0.5),
    // 显示在距相机的距离处的属性,多少区间内是可以显示的
    distanceDisplayCondition: type.includes("河流")
      ? new Cesium.DistanceDisplayCondition(0, 5000000)
      : new Cesium.DistanceDisplayCondition(0, 50000),
    // 是否显示
    show: showBillboard,
    heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
    backgroundColor: showBillboard ? Cesium.Color.BLACK.withAlpha(0.8) : Cesium.Color.SKYBLUE,
  };
  // 创建新实体并添加到地图中
  const entity = viewer.entities.add(model);
  entities.push(entity);
  pointEntityMap.set(id, entity); // 存入 map
  return entity;
}
// export function removeEntities() {
//   entities.forEach(entity => {
//     viewer.entities.remove(entity)
//     entity.show = false;
//   });
//   // entities = []
// }
/**
 * 删除所有已创建的点
 */
export function clearAllPoints() {
  for (let [id, entity] of pointEntityMap.entries()) {
    viewer.entities.remove(entity);
  }
  pointEntityMap.clear();
}
export function removeEntities(id) {
  entities.forEach((entity, index) => {
  pointEntityMap.forEach((entity, index) => {
    if (entity.id === id) {
      viewer.entities.remove(entity);
    }
src/views/GisView.vue
@@ -36,6 +36,7 @@
  addTileset,
  addTerrain,
  removeEntities,
  clearAllPoints,
} from "@/utils/map.js";
import { loadAreaPolygon } from "@/utils/area.js";
import { loadAreaPolygonAll } from "@/utils/area_all.js";
@@ -46,7 +47,7 @@
import { useSimStore } from "@/store/simulation";
const simStore = useSimStore();
import { getDangerPoint } from "@/api/hpApi";
import { getDangerPoint, getDeviceInfo } from "@/api/hpApi";
/////////////////////////地图影像选择/////////////////////////
const views = [
  { label: "地图", value: "map", icon: "地图.png" },
@@ -450,78 +451,82 @@
}
import { ElMessage } from "element-plus";
const districtList = ref([]); // 当前区域的隐患点列表
const loadedPointIds = new Set(); // 已加载的隐患点ID集合
const districtList = ref([]);
// 设置二级区域点击处理(请求隐患点,监测设备等)
function secondaryHandler(html, item, config) {
  html.element.addEventListener("click", async () => {
    try {
      // handleCleanup();
      // 清理已有点
      handleCleanup();
      // 显示 loading 提示
      // 显示 loading 提示(无遮罩,仅文字+转圈)
      const loadingInstance = ElMessage({
        type: "success",
        message: "隐患点正在加载中...",
        duration: 0,
        icon: "el-icon-loading", // 确保使用的是Element Plus提供的loading图标类名
        grouping: true,
        customClass: "custom-loading-message", // 添加自定义类名
        type: "info",
        message: "数据正在加载中...",
        duration: 0, // 持续显示,直到手动关闭
        icon: "loading", // 显示为 loading 图标(Element Plus 支持)
        grouping: true, // 相同内容的消息合并,避免重复提示
      });
      // 请求隐患点数据
      const res = await getDangerPoint(item.districtCode);
      const newPoints = res.data.pageData;
      let res;
      if (route.path === "/yhgl") {
        // 请求隐患点数据
        res = await getDangerPoint(item.districtCode);
      } else if (route.path === "/zhjc") {
        // 请求监测设备数据
        res = await getDeviceInfo(item.districtCode);
      } else {
        loadingInstance.close();
        return;
      }
      districtList.value = [];
      // 更新数据
      districtList.value = res.data.pageData;
      if (newPoints.length === 0) {
        ElMessage.warning("该区域暂无隐患点");
        districtList.value = [];
      if (districtList.value.length === 0) {
        ElMessage.warning("该区域暂无相关数据");
        loadingInstance.close();
        return;
      }
      // 清空当前区域显示的隐患点列表(但不清除已加载的标记)
      districtList.value = [];
      // 遍历新数据,过滤掉已加载过的点
      for (const [index, point] of newPoints.entries()) {
        if (loadedPointIds.has(point.hdId)) {
          continue; // 如果已加载,跳过
        }
        // 添加到已加载集合
        loadedPointIds.add(point.hdId);
        // 设置点属性
        point.id = point.hdId;
        point.name = point.hdName;
        point.latitude = point.lat;
        point.longitude = point.lon;
      // 创建地图点
      for (const [index, point] of districtList.value.entries()) {
        point.id = point.hdId || point.deviceId; // 根据实际情况调整
        point.latitude = point.lat || point.latitude; // 根据实际情况调整
        point.longitude = point.lon || point.longitude; // 根据实际情况调整
        point.showBillboard = true;
        point.type = point.disasterType;
        point.type = point.disasterType || point.deviceTypeName; // 根据实际情况调整
        point.className = "district";
        districtList.value.push(point); // 更新当前区域隐患点列表
        // ✅ 根据路由决定名称字段
        if (route.path === "/yhgl") {
          point.name = point.hdName; // 隐患点名称
          point.className = "district";
        } else if (route.path === "/zhjc") {
          point.name = point.deviceForShort; // 设备简称
          point.className = "device";
        }
        await createPoint(point); // 创建地图点
        await createPoint(point);
      }
      // 飞向指定位置
      await flyToDistrict(item.longitude, item.latitude, config.flyToHeight);
      // 加载完成后关闭 loading
      // 加载完成后关闭 loading 提示
      loadingInstance.close();
    } catch (error) {
      console.error("区域点击处理失败:", error);
      ElMessage.error("数据加载失败,请稍后再试");
      loadingInstance.close(); // 确保在发生错误时也关闭 loading 提示
    }
  });
}
const handleCleanup = async () => {
  await Promise.all(
    districtList.value.map((item) => removeEntities(item.hdId))
  );
  districtList.value = [];
  clearAllPoints();
};
// 设置一级区域点击处理
@@ -727,9 +732,5 @@
}
.earthBox.shift-right {
  right: 13%;
}
.custom-loading-message .el-icon-loading {
  color: #409eff !important; /* 设置为你想要的颜色 */
  font-size: 18px; /* 可选:调整图标大小 */
}
</style>