无人机管理后台前端(已迁走)
张含笑
2025-10-29 d95ca015de7641aba381e1c61de9dbdada085d4e
src/views/layerManagement/index.vue
@@ -10,15 +10,23 @@
      <!-- 地图 -->
      <div class="mapContainer">
        <div id="layMap" class="ztzf-cesium"></div>
        <leftList @update:coverData="handleCoverDataUpdate" :activeName="activeName"></leftList>
        <rightEdit></rightEdit>
        <leftList
          v-if="!layerParams.addNest && !layerParams.editNest"
          @update:coverData="handleCoverDataUpdate"
          @update:deitData="handleEdit"
          :activeName="activeName"
        ></leftList>
        <rightEdit
          v-if="layerParams.addNest || layerParams.editNest"
          @callParentMethod="parentMethod"
        ></rightEdit>
      </div>
    </div>
  </basic-container>
</template>
<script setup>
import { flyVisual } from '@/utils/cesium/mapUtil'
import { flyVisual } from '@/utils/cesium/mapUtil';
import rightEdit from '@/views/layerManagement/components/rightEdit.vue';
import leftList from '@/views/layerManagement/components/leftList.vue';
import { DrawPolygon } from '@/views/layerManagement/components/utils';
@@ -26,8 +34,11 @@
import { PublicCesium } from '@/utils/cesium/publicCesium';
import _, { cloneDeep, throttle } from 'lodash';
import { ElButton } from 'element-plus';
import { provide } from 'vue';
const activeName = ref('电子围栏');
const activeType = ref('0');
// 当前面位置信息
let curPolygonPosition = [];
const tabData = ref([
  {
    name: '电子围栏',
@@ -42,116 +53,94 @@
    type: '2',
  },
]);
const layerParams = ref({
  addNest: false,
  editNest: false,
  editDetailData: null,
  total_count: 0,
  total_area: 0,
});
provide('layerParams', layerParams);
const handleClick = tab => {
  const clickedTab = tabData.value.find(item => item.type === tab.paneName);
  activeType.value = clickedTab.type;
  activeName.value = clickedTab.name;
};
// 解析 geo_data 并转换为 DrawPolygon 所需的坐标格式
// 解析 geo_data 并转换为坐标格式(兼容 Polygon 和 LineString)
const parseGeoDataToPositions = (geoDataStr, altitude) => {
  try {
    // 解析 JSON 字符串为 GeoJSON 对象
    const geoData = JSON.parse(geoDataStr);
    // 仅处理 Polygon 类型的地理数据
    if (geoData.type !== 'Polygon') return [];
    let coordinates = [];
    // 提取 coordinates(取第一个面,忽略孔洞),并补充高度
    const coordinates = geoData.coordinates[0];
    if (geoData.type === 'Polygon') {
      // Polygon 坐标格式:[[[lng, lat], ...]] → 取第一个多边形的顶点
      coordinates = geoData.coordinates[0] || [];
    } else if (geoData.type === 'LineString') {
      // LineString 坐标格式:[[lng, lat], ...] → 直接取顶点
      coordinates = geoData.coordinates || [];
    } else {
      console.warn(`不支持的地理数据类型:${geoData.type}`);
      return [];
    }
    // 转换为 {lng, lat, height} 格式
    return coordinates.map(([lng, lat]) => ({
      lng: Number(lng),
      lat: Number(lat),
      height: Number(altitude) || 0, // 高度默认取数据中的 altitude,无则为 0
      height: Number(altitude) || 0,
    }));
  } catch (error) {
    console.error('解析 geo_data 失败:', error);
    return [];
  }
};
let tbJwdList = [];
const selectDataList = ref([]);
// 提取图斑坐标
function polygonWktToTargetArray(wktPolygon, defaultHeight = 140.6) {
  try {
    if (!/^POLYGON\(\([\d\s,.]+\)\)$/.test(wktPolygon)) {
      throw new Error('输入不是有效的 WKT POLYGON 格式');
    }
    const coordStr = wktPolygon
      .replace(/^POLYGON\(\(/, '')
      .replace(/\)\)$/, '')
      .trim();
    const coordPairs = coordStr.split(/,\s*/).filter(pair => pair.trim() !== '');
    const result = coordPairs.map(pair => {
      const [lng, lat] = pair
        .split(/\s+/)
        .map(Number)
        .filter(num => !isNaN(num));
      if (
        lng === undefined ||
        lat === undefined ||
        lng < -180 ||
        lng > 180 ||
        lat < -90 ||
        lat > 90
      ) {
        throw new Error(`无效的经纬度对:${pair}`);
      }
      return {
        lng: lng,
        lat: lat,
        longitude: lng, // 与 lng 一致
        latitude: lat, // 与 lat 一致
      };
    });
    const uniqueResult = Array.from(
      new Map(result.map(item => [`${item.lng}-${item.lat}`, item])).values()
    );
    return uniqueResult;
  } catch (error) {
    return []; // 异常时返回空数组
  }
}
// 编辑区域
const handleEdit = val => {
  console.log('编辑数据', val);
  layerParams.value.editDetailData = val;
  drawPolygonExample.editThePatch(true);
  const positions = parseGeoDataToPositions(val.geo_data, val.altitude);
  if (positions.length < 3) return; // 少于3个点无法构成多边形
  drawPolygonExample.initPolygon(viewer, positions, true);
};
// 清除地图数据
const parentMethod = () => {
drawPolygonExample.delPolygon()
};
// 点击显示区域
const handleCoverDataUpdate = data => {
  selectDataList.value = data;
console.log('获取的',selectDataList.value);
  if (selectDataList.value.length > 0) {
    loadDataToMap(selectDataList.value);
  } else {
    drawPolygonExample.removeEntities();
    viewer.entities.removeAll();
  }
};
const loadDataToMap = (dataList) => {
  if (!viewer) return;
const loadDataToMap = dataList => {
  if (!viewer) return;
  viewer.entities.removeAll();
    // 存储所有有效坐标,用于后续定位
  const allCoordinates = [];
  // 存储所有有效坐标,用于后续定位
  tbJwdList = [];
  dataList.forEach(item => {
    // 解析 geo_data 为经纬度坐标数组(使用已有的 parseGeoDataToPositions 方法)
    const positions = parseGeoDataToPositions(item.geo_data, item.altitude);
    if (positions.length < 3) {
      console.warn(`数据 ${item.name} 坐标点不足,无法绘制`);
      return;
    }
console.log('positions',positions);
    tbJwdList.push(...positions);
    //  转换为 Cesium 所需的 [lng, lat, lng, lat, ...] 扁平数组格式
    let degreesArray = [];
    positions.forEach(pos => {
      degreesArray.push(pos.lng, pos.lat, pos.height);
    });
      degreesArray.push(pos.lng, pos.lat, pos.height);
    });
    viewer.entities.add({
      id: `polygon_${item.id}`,
      customType: 'fence_polygon',
      id: `polygon_${item.id}`,
      customType: 'fence_polygon',
      customInfo: item,
      polygon: {
        hierarchy: new Cesium.PolygonHierarchy(
@@ -162,9 +151,8 @@
        outlineColor: Cesium.Color.YELLOW, // 边框色
        outlineWidth: 2, // 边框宽度
        clampToGround: true, // 贴地显示
      },
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(degreesArray),
        width: 2,
@@ -174,15 +162,33 @@
      zIndex: 99, // 层级,确保在其他图层上方显示
    });
  });
  focusOnAllFeatures();
  //  添加点击事件(参考 entitiesAddSpot 的 spotHighlighting 逻辑)
  // 移除已有点击事件,避免重复绑定
  // viewInstance.value?.removeLeftClickEvent('fenceHighlighting');
  // 绑定新的点击事件,用于高亮选中的围栏
  // viewInstance.value?.addLeftClickEvent(null, handleFenceClick, 'fenceHighlighting');
};
//计算所有图斑的包围球并定位
const focusOnAllFeatures = () => {
  if (tbJwdList.length === 0 || !viewer) return;
  // 转换所有经纬度坐标为Cartesian3
  const allPositions = tbJwdList.flatMap(pos =>
    Cesium.Cartesian3.fromDegrees(pos.lng, pos.lat, pos.height || 0)
  );
  if (allPositions.length === 0) return;
  const boundingSphere = Cesium.BoundingSphere.fromPoints(allPositions);
  viewer.camera.flyToBoundingSphere(boundingSphere, {
    duration: 0, // 飞行时间(秒)
    offset: new Cesium.HeadingPitchRange(
      Cesium.Math.toRadians(0), // 方向角
      Cesium.Math.toRadians(-90) // 俯仰角
    ),
  });
};
// 围栏点击处理函数(高亮选中项 + 可联动右侧编辑组件)
const handleFenceClick = (movement) => {
const handleFenceClick = movement => {
  if (!viewer) return;
  // 拾取点击的实体
@@ -195,9 +201,10 @@
    viewer.entities.values.forEach(entity => {
      if (entity.customType === 'fence_polygon') {
        // 恢复默认样式
        entity.polygon.material = entity === selectedEntity
          ? selectedEntity.polygon.material.color.withAlpha(0.8) // 选中时加深透明度
          : selectedEntity.polygon.material.color.withAlpha(0.5); // 未选中恢复默认
        entity.polygon.material =
          entity === selectedEntity
            ? selectedEntity.polygon.material.color.withAlpha(0.8) // 选中时加深透明度
            : selectedEntity.polygon.material.color.withAlpha(0.5); // 未选中恢复默认
      }
    });
@@ -226,27 +233,32 @@
  viewer = publicCesiumInstance.getViewer();
  viewInstance.value = publicCesiumInstance;
  drawPolygonExample.initHandler(viewer);
  // drawPolygonExample.initHandler(viewer);
};
// 处理绘制完成后的多边形坐标(示例)
// 编辑/绘制
const loadPlanarRoute = async (positions = null, save = false) => {
  if (!positions || positions.length < 3) return;
  // console.log('绘制的多边形顶点坐标(Cartesian3):', positions);
  //坐标转换
  const cartographics = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions);
  const latLngPositions = cartographics.map(cartographic => ({
    lng: Cesium.Math.toDegrees(cartographic.longitude),
    lat: Cesium.Math.toDegrees(cartographic.latitude),
    height: cartographic.height || 0,
  }));
  // console.log('转换后的经纬度坐标:', latLngPositions);
};
  if (positions) {
    curPolygonPosition = positions.map(item => {
      let cartographic = Cesium.Cartographic.fromCartesian(item);
      let lng = Cesium.Math.toDegrees(cartographic.longitude); // 经度
      let lat = Cesium.Math.toDegrees(cartographic.latitude); // 纬度
      let height = Cesium.Math.toDegrees(cartographic.height); //高度
      return {
        lng: _.round(lng, 6),
        lat: _.round(lat, 6),
        height: _.round(height, 6),
      };
    });
  }
  let polygon = curPolygonPosition.map(item => [item?.lng, item?.lat]);
  polygon.push([curPolygonPosition[0]?.lng, curPolygonPosition[0]?.lat]);
  console.log('绘制的多边形顶点坐标(Cartesian3):', polygon);
  console.log('转换后的经纬度坐标:', curPolygonPosition);
};
const throttleLoadPlanarRoute = throttle(loadPlanarRoute, 200);
drawPolygonExample.subscribe('getPolygonPositions', data => {
console.log('绘制的数据',data);
  throttleLoadPlanarRoute(data);
});