1
wangjuncheng
8 天以前 5a93ff9c70a25e09d77aff8e9175022b63b3060f
src/views/GisView.vue
@@ -1,5 +1,30 @@
<template>
  <div id="gis-view" ref="mapRef"></div>
  <!-- 切换底图影像 -->
  <div
    @click="handleShow"
    class="diqiu"
    :class="{ 'shift-right': simStore.isShowEarth }"
  >
    <img src="@/assets/img/screen/dq.png" alt="" />
  </div>
  <div
    v-show="picShow"
    class="earthBox"
    :class="{ 'shift-right': simStore.isShowEarth }"
  >
    <div
      v-for="(item, index) in views"
      :key="index"
      :class="['item-container', { active: currentIndex === index }]"
      @click="switchView(index)"
    >
      <div class="icon-wrapper">
        <img :src="getImageUrl(item.icon)" :alt="item.label" />
        <span class="label">{{ item.label }}</span>
      </div>
    </div>
  </div>
</template>
<script setup>
@@ -10,16 +35,90 @@
  initView,
  addTileset,
  addTerrain,
  removeEntities,
  clearAllPoints,
} from "@/utils/map.js";
import { loadAreaPolygon } from "@/utils/area.js";
import { loadAreaPolygonAll } from "@/utils/area_all.js";
import { isVisibleDistance } from "@/utils/customEntity";
import { getDistrictCount, getDistrictCountByCity } from "@/api/index";
import { useRoute } from "vue-router";
import { EventBus } from "@/eventBus"; // 引入事件总线
import { useSimStore } from "@/store/simulation";
const route = useRoute();
const simStore = useSimStore();
import {
  getDangerPoint,
  getDeviceInfo,
  getAeraCode,
  getAeraTownCode,
  getDeviceCount,
} from "@/api/hpApi";
/////////////////////////地图影像选择/////////////////////////
const views = [
  { label: "地图", value: "map", icon: "地图.png" },
  { label: "影像", value: "image", icon: "影像.png" },
  { label: "地形", value: "terrain", icon: "地形.png" },
];
const getImageUrl = (iconName) => {
  return `/images/earth/${iconName}`;
};
const picShow = ref(false);
const handleShow = () => {
  picShow.value = !picShow.value;
};
const currentIndex = ref(-1);
let currentLayer = null;
const switchView = async (index) => {
  currentIndex.value = index;
  // 如果已经有图层存在,先从地图中移除
  if (currentLayer) {
    currentLayer.removeFromMap();
    currentLayer = null; // 清空引用
  }
  if (views[index].value === "map") {
    // 百度地图
    currentLayer = await earthCtrl.factory.createImageryLayer({
      sourceType: "baidu",
      url: "https://ss1.bdstatic.com/8bo_dTSlR1gBo1vgoIiO_jowehsv/tile/?qt=vtile&x={x}&y={y}&z={z}&styles=pl&udt=20180810&scaler=1&showtext=1",
    });
  } else if (views[index].value === "image") {
    currentLayer = await earthCtrl.factory.createImageryLayer({
      sourceType: "mapworld",
      url: "https://t0.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=3ec79cf7a9dcc6bb18411a5414b148cb",
      layers: "tdtBasicLayer",
      style: "default",
      format: "image/jpeg",
      maximumLevel: 18,
      layer: "",
      tileMatrixSetID: "",
    });
    //标注层
    currentLayer = await earthCtrl.factory.createImageryLayer({
      sourceType: "mapworld",
      subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"],
      url: "https://{s}.tianditu.gov.cn/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&tk=c53eb074c3fcba5ac86103d4d711bbe8",
    });
  } else if (views[index].value === "terrain") {
    // 地形图层
    currentLayer = await earthCtrl.factory.createTerrainLayer({
      sourceType: "arcgis",
      url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer",
    });
  }
};
/////////////////////////地图影像选择/////////////////////////
let handler = null;
/////////////////////////初始化地图/////////////////////////
function initMap() {
  window.Cesium = SmartEarth.Cesium;
  window.earthCtrl = new SmartEarth.EarthCtrl("gis-view");
@@ -28,19 +127,18 @@
  // 1. 设置初始时间
  const date = new Date(2025, 3, 11, 12, 0, 0, 0);
  const julianDate = SmartEarth.Cesium.JulianDate.fromDate(date);
  // earthCtrl.viewer.clock.currentTime = julianDate;
  // 2. 配置时钟选项,禁止自动推进时间
  earthCtrl.viewer.clockViewModel.shouldAnimate = false; // 禁用动画
  // 2. 配置时钟选项
  earthCtrl.viewer.clockViewModel.shouldAnimate = false;
  earthCtrl.viewer.clockViewModel.clockRange =
    SmartEarth.Cesium.ClockRange.CLAMPED; // 限制时间范围
  earthCtrl.viewer.clockViewModel.multiplier = 0; // 设置时间推进速度为0
  // 3. 设置当前时间并锁定
    SmartEarth.Cesium.ClockRange.CLAMPED;
  earthCtrl.viewer.clockViewModel.multiplier = 0;
  earthCtrl.viewer.clock.currentTime = julianDate;
  //显示fps
  // 显示fps
  earthCtrl.showFPS = true;
  // 先添加底图
  earthCtrl.factory.createImageryLayer({
    sourceType: "mapworld",
    url: "http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=7eb11c0c503429878691ac917238f87f",
@@ -48,13 +146,10 @@
    style: "default",
    format: "image/jpeg",
    maximumLevel: 18,
    layer: "",
    tileMatrixSetID: "",
  });
  // 关闭地形深度检测
  // viewer.scene.globe.depthTestAgainstTerrain = false;
}
/////////////////////////新建方案区域选择/////////////////////////
const MULTIPOLYGON_COORDS = ref([]);
let previousEntities = []; // 用于存储之前创建的实体
const flyToHeight = ref(null);
@@ -171,6 +266,7 @@
  clearPreviousEntities();
  flyToHomeView();
});
/////////////////////////新建方案区域选择/////////////////////////
function addCityPolygon() {
  const url = `/json/110000.geo.json`;
@@ -192,8 +288,6 @@
      50000000
    );
    polygonEntity.forEach((entity) => {
      // console.log("entity", entity)
      entity.polygon.material = new Cesium.ColorMaterialProperty(
        Cesium.Color.LIGHTSTEELBLUE.withAlpha(0)
        // new Cesium.Color.fromCssColorString("#0f2636b3")
@@ -297,136 +391,279 @@
  viewer.scene.camera.flyTo(view);
}
let htmlEntityList = [];
function initDistrictCount() {
  getDistrictCount().then((res) => {
    res.data.forEach((item) => {
      const { districtName, count, lat, lon } = item;
      item.name = `${item.districtName}\n${item.count}`;
      item.longitude = item.lon;
      item.latitude = item.lat;
      item.showBillboard = false;
      item.showLabel = true;
      item.label = {
        text: item.name,
        backgroundColor: SmartEarth.Cesium.Color.SKYBLUE.withAlpha(0.8),
        font: "14pt Source Han Sans CN",
        fillColor: SmartEarth.Cesium.Color.WHITE,
        showBackground: true,
      };
      // createPoint(item)
      const html = earthCtrl.view.createScreenDialog({
        html: `
                  <div class="district-count">
                     <div class="name">${districtName}</div>
                     <div class="value">${count}</div>
               </div>
               `,
        lon: item.lon,
        lat: item.lat,
        height: 0,
      });
      html.maxVisibleDistance = 69000;
      html.minVisibleDistance = 20000;
      html.element.addEventListener("click", () => {
        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(
            item.longitude,
            item.latitude,
            12000
          ),
          orientation: {
            pitch: Cesium.Math.toRadians(-90),
            heading: Cesium.Math.toRadians(0),
            roll: 0,
          },
          duration: 2,
        });
      });
///////////////////////// 区域标记点管理系统 /////////////////////////
// 存储所有创建的HTML实体(地图上的标记点)
const htmlEntityList = [];
      htmlEntityList.push(html);
    });
  });
// 统一管理所有区域标记的样式配置
const ENTITY_CONFIG = {
  label: {
    backgroundColor: SmartEarth.Cesium.Color.SKYBLUE.withAlpha(0.8),
    font: "14pt Source Han Sans CN",
    fillColor: SmartEarth.Cesium.Color.WHITE,
    showBackground: true,
  },
  showBillboard: false,
  showLabel: true,
};
// 区域级别配置
const LEVEL_CONFIG = {
  // 二级区域
  secondary: {
    maxVisibleDistance: 69000,
    minVisibleDistance: 20000,
    flyToHeight: 12000,
  },
  // 一级区域
  primary: {
    maxVisibleDistance: 60000000,
    minVisibleDistance: 70000,
    flyToHeight: 45000,
  },
};
// 初始化区域统计
async function initDistrictCount(level = "secondary") {
  try {
    const deviceAreaTotal = ref([]);
    const deviceTownTotal = ref([]);
    if (level === "primary") {
      // 一级区域处理
      if (route.path === "/zhjc") {
        deviceAreaTotal.value = await getDeviceCount(); // 获取区级设备统计
      }
      const res = await getAeraCode();
      res.data.forEach((item) => {
        if (route.path === "/zhjc") {
          const matchedDistrict = deviceAreaTotal.value.data?.find(
            (d) => d.districtName === item.districtName
          );
          if (matchedDistrict) {
            item.count = matchedDistrict.count; // 更新 count
          }
        }
        processDistrictItem(item, LEVEL_CONFIG[level], level);
      });
    } else if (level === "secondary") {
      // 二级区域处理
      const primaryRes = await getAeraCode();
      if (route.path === "/zhjc") {
        // 并发获取每个区的设备统计
        deviceTownTotal.value = await Promise.all(
          primaryRes.data.map((item) =>
            getDeviceCount({ districtCode: Number(item.districtCode) })
          )
        );
      }
      // 并发获取每个区下的镇数据
      const townResults = await Promise.all(
        primaryRes.data.map((item) => getAeraTownCode(item.districtCode))
      );
      // 处理每个区的镇数据
      for (let i = 0; i < townResults.length; i++) {
        const townRes = townResults[i];
        const deviceCountData = deviceTownTotal.value[i]?.data || []; // 对应区的设备统计数据
        townRes.data.forEach((townItem) => {
          if (route.path === "/zhjc") {
            const matchedTown = deviceCountData.find(
              (d) => d.districtName === townItem.districtName
            );
            if (matchedTown) {
              townItem.count = matchedTown.count;
            }
          }
          processDistrictItem(townItem, LEVEL_CONFIG[level], level);
        });
      }
    } else {
      console.error("未知的 level 类型:", level);
    }
  } catch (error) {
    console.error(`初始化 ${level} 级区域统计失败:`, error);
  }
}
function initDistrictCountByCity() {
  getDistrictCountByCity().then((res) => {
    res.data.forEach((item) => {
      const { districtName, count, lat, lon } = item;
      item.name = `${item.districtName}\n${item.count}`;
      item.longitude = item.lon;
      item.latitude = item.lat;
      item.showBillboard = false;
      item.showLabel = true;
      item.label = {
        text: item.name,
        backgroundColor: SmartEarth.Cesium.Color.SKYBLUE.withAlpha(0.8),
        font: "14pt Source Han Sans CN",
        fillColor: SmartEarth.Cesium.Color.WHITE,
        showBackground: true,
      };
      // createPoint(item)
      const html = earthCtrl.view.createScreenDialog({
        html: `
                  <div class="district-count">
                     <div class="name">${districtName}</div>
                     <div class="value">${count}</div>
               </div>
               `,
        lon: item.lon,
        lat: item.lat,
        height: 0,
      });
      html.maxVisibleDistance = 50000000;
      html.minVisibleDistance = 70000;
      html.element.addEventListener("click", () => {
        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(
            item.longitude,
            item.latitude,
            45000
          ),
          orientation: {
            pitch: Cesium.Math.toRadians(-90),
            heading: Cesium.Math.toRadians(0),
            roll: 0,
          },
          duration: 2,
        });
// 处理单个区域项
function processDistrictItem(item, config, level = "secondary") {
  // 添加默认值
  normalizeItemData(item);
  const html = createDistrictHtmlMarker(item, config);
  if (level === "secondary") {
    secondaryHandler(html, item, config);
  } else {
    primaryHandler(html, item, config);
  }
  // 此处必须加,不然二次调用初始化函数的时候会将一二级区域所有的点都显示在页面上
  html.show = isVisibleDistance(
    config.minVisibleDistance,
    config.maxVisibleDistance
  );
  htmlEntityList.push(html);
}
import { ElMessage } from "element-plus";
const districtList = ref([]);
// 设置二级区域点击处理(请求隐患点,监测设备等)
function secondaryHandler(html, item, config) {
  html.element.addEventListener("click", async () => {
    try {
      // 清理已有点
      handleCleanup();
      // 显示 loading 提示(无遮罩,仅文字+转圈)
      const loadingInstance = ElMessage({
        type: "info",
        message: "数据正在加载中...",
        duration: 0, // 持续显示,直到手动关闭
        icon: "loading", // 显示为 loading 图标(Element Plus 支持)
        grouping: true, // 相同内容的消息合并,避免重复提示
      });
      htmlEntityList.push(html);
    });
      let res;
      if (route.path === "/yhgl") {
        // 请求隐患点数据
        res = await getDangerPoint(item.districtCode);
      } else if (route.path === "/zhjc") {
        // 请求监测设备数据
        res = await getDeviceInfo(null, item.districtCode);
      } else {
        loadingInstance.close();
        return;
      }
      districtList.value = [];
      // 更新数据
      districtList.value = res.data.pageData;
      if (districtList.value.length === 0) {
        ElMessage.warning("该区域暂无相关数据");
        loadingInstance.close();
        return;
      }
      // 创建地图点
      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.deviceTypeName; // 根据实际情况调整
        point.className = "district";
        // ✅ 根据路由决定名称字段
        if (route.path === "/yhgl") {
          point.name = point.hdName; // 隐患点名称
          point.className = "district";
        } else if (route.path === "/zhjc") {
          point.name = point.deviceName; // 设备简称
          point.className = "device";
        }
        await createPoint(point);
      }
      // 飞向指定位置
      await flyToDistrict(item.longitude, item.latitude, config.flyToHeight);
      // 加载完成后关闭 loading 提示
      loadingInstance.close();
    } catch (error) {
      console.error("区域点击处理失败:", error);
      ElMessage.error("数据加载失败,请稍后再试");
      // loadingInstance.close(); // 确保在发生错误时也关闭 loading 提示
    }
  });
}
const validPaths = ["/", "/yhgl"];
const handleCleanup = async () => {
  districtList.value = [];
  clearAllPoints();
};
// 设置一级区域点击处理
function primaryHandler(html, item, config) {
  html.element.addEventListener("click", () => {
    flyToDistrict(item.longitude, item.latitude, config.flyToHeight);
  });
}
// 统一数据格式和添加默认配置
function normalizeItemData(item) {
  item.name = `${item.districtName}\n${item.count}`;
  item.longitude = item.lon;
  item.latitude = item.lat;
  Object.assign(item, ENTITY_CONFIG);
}
// 创建HTML标记
function createDistrictHtmlMarker(item, config) {
  const html = earthCtrl.view.createScreenDialog({
    html: `
      <div class="district-count">
        <div class="name">${item.districtName}</div>
        <div class="value">${item.count}</div>
      </div>
    `,
    lon: item.lon,
    lat: item.lat,
    height: 0,
  });
  html.maxVisibleDistance = config.maxVisibleDistance;
  html.minVisibleDistance = config.minVisibleDistance;
  return html;
}
// 飞向指定区域
function flyToDistrict(longitude, latitude, height) {
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
    orientation: {
      pitch: Cesium.Math.toRadians(-90),
      heading: Cesium.Math.toRadians(0),
      roll: 0,
    },
    duration: 2,
  });
}
// 在路由监听器中
const validPaths = ["/", "/yhgl", "/zhjc"];
watch(
  () => route.fullPath,
  (val) => {
    if (!validPaths.includes(val)) {
      // clusterLayer.dataSource.show = false
    const isValidPath = validPaths.includes(val);
    // 控制相机变化监听
    if (isValidPath) {
      initAllDistrictCounts();
      handleCameraChange();
      // 控制HTML实体显示
      htmlEntityList.forEach((item) => {
        item.show = isValidPath
          ? isVisibleDistance(item.minVisibleDistance, item.maxVisibleDistance)
          : false;
      });
    } else {
      htmlEntityList.forEach((item) => {
        item.show = false;
      });
      removeCameraChange();
    } else {
      handleCameraChange();
      // clusterLayer.dataSource.show = true
      htmlEntityList.forEach((item) => {
        item.show = isVisibleDistance(
          item.minVisibleDistance,
          item.maxVisibleDistance
        );
      });
    }
  }
);
function handleCameraChange() {
  viewer.camera.changed.addEventListener(toggleHtmlLayerByVisibleDistance);
}
function removeCameraChange() {
  viewer.camera.changed.removeEventListener(toggleHtmlLayerByVisibleDistance);
}
let cameraChangeTimer = null;
function toggleHtmlLayerByVisibleDistance() {
@@ -435,7 +672,9 @@
    cameraChangeTimer = null;
    return;
  }
  cameraChangeTimer = setTimeout(() => {
    // 更新HTML实体显示状态
    htmlEntityList.forEach((item) => {
      item.show = isVisibleDistance(
        item.minVisibleDistance,
@@ -445,17 +684,30 @@
  }, 100);
}
function handleCameraChange() {
  viewer.camera.changed.addEventListener(toggleHtmlLayerByVisibleDistance);
}
function removeCameraChange() {
  viewer.camera.changed.removeEventListener(toggleHtmlLayerByVisibleDistance);
}
// 初始化函数
function initAllDistrictCounts() {
  initDistrictCount("primary"); // 一级区域
  initDistrictCount("secondary"); // 二级区域
}
onMounted(() => {
  initMap();
  // 在你的初始化代码中调用这个函数
  addCityPolygon();
  initHandler();
  // initView()
  loadAreaPolygon("/json/nsl_area.geojson");
  loadAreaPolygonAll("/json/geometry.json", true);
  // loadAreaPolygon("/json/nsl_area.geojson");
  // loadAreaPolygonAll("/json/geometry.json", true);
  flyToHomeView();
  initDistrictCount();
  initDistrictCountByCity();
  // 页面加载时初始化
  initAllDistrictCounts();
  handleCameraChange();
  // 设置 billboard 点击事件
});
@@ -472,9 +724,77 @@
  height: 100vh;
  position: absolute;
}
// // 修改指南针位置
/deep/ .compass {
  right: 128px !important;
  top: 112px;
}
.diqiu {
  position: absolute;
  bottom: 8%;
  right: 4%;
  z-index: 9999;
}
.earthBox {
  z-index: 9999;
  width: 12%;
  height: 7%;
  background-color: rgba(0, 9, 9, 0.3);
  position: absolute;
  right: 8%;
  bottom: 6%;
  display: flex;
  justify-content: space-evenly;
  padding: 10px 6px;
}
.icon-wrapper {
  position: relative;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  border: 1px solid transparent; /* 默认透明边框占位 */
  img {
    height: 100%;
    width: auto; /* 确保图片按比例缩放 */
    margin: 0;
    padding: 0;
    display: block; /* 移除图片下方可能存在的空白间隙 */
  }
  .label {
    position: absolute;
    bottom: 0;
    right: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.5);
    text-align: center;
    color: white;
  }
}
.item-container {
  position: relative;
  display: inline-block;
  cursor: pointer;
}
.item-container.active .icon-wrapper {
  border: 1px solid #218967; /* 蓝色边框 */
}
.item-container.active .label {
  background-color: rgba(33, 137, 103, 0.8);
}
.diqiu.shift-right {
  right: 10%;
}
.earthBox.shift-right {
  right: 13%;
}
</style>