forked from drone/command-center-dashboard

罗广辉
2025-04-02 bb1a2a686cce84a9784fcd1e0076e41c41594116
feat: 机巢聚合60%
7 files modified
1 files added
215 ■■■■■ changed files
.prettierrc.json 2 ●●● patch | view | raw | blame | history
src/api/home/aggregation.js 17 ●●●●● patch | view | raw | blame | history
src/api/home/index.js 3 ●●●● patch | view | raw | blame | history
src/api/home/machineNest.js 4 ●●●● patch | view | raw | blame | history
src/views/Home/components/HomeLeft/InspectionRaskDetails.vue 1 ●●●● patch | view | raw | blame | history
src/views/Home/components/HomeLeft/OverviewNext.vue 14 ●●●● patch | view | raw | blame | history
src/views/Home/useUavHome/MapPopUpBox.vue 17 ●●●● patch | view | raw | blame | history
src/views/Home/useUavHome/useUavHome.js 157 ●●●●● patch | view | raw | blame | history
.prettierrc.json
@@ -1,5 +1,5 @@
{
  "printWidth": 100,
  "printWidth": 120,
  "tabWidth": 2,
  "semi": true,
  "singleAttributePerLine": false,
src/api/home/aggregation.js
New file
@@ -0,0 +1,17 @@
import request from '@/axios';
export const getDeviceRegionCount = () => {
  return request({
    url: '/drone-device-core/manage/api/v1/devices/getDeviceRegionCount',
    method: 'get',
  });
};
export const getDeviceRegion = () => {
  return request({
    url: '/drone-device-core/manage/api/v1/devices/getDeviceRegion',
    method: 'get',
  });
};
src/api/home/index.js
@@ -1,10 +1,11 @@
import request from '@/axios';
// 巡检总任务数量
export const getTotalJobNum = () => {
export const getTotalJobNum = (params) => {
  return request({
    url: '/drone-device-core/wayline/waylineJobInfo/totalJobNum',
    method: 'get',
    params
  });
};
src/api/home/machineNest.js
@@ -1,11 +1,11 @@
import request from '@/axios';
// 机巢统计
export const getDeviceInfoNum = () => {
export const getDeviceInfoNum = (params) => {
  return request({
    url: '/drone-device-core/manage/api/v1/devices/getDeviceInfoNum',
    method: 'get',
    params: {},
    params,
  });
};
// 机巢列表
src/views/Home/components/HomeLeft/InspectionRaskDetails.vue
@@ -182,7 +182,6 @@
      .value {
        font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
        margin-left: 8px;
        font-size: 26px;
      }
    }
src/views/Home/components/HomeLeft/OverviewNext.vue
@@ -94,12 +94,12 @@
const tableList = ref([]);
// 机巢列表数据
const getTableList = () => {
  const params = {
    nickname: searchText.value,
    is_execute: false,
    type: 1,
    current: pageParams.value.current,
    size: pageParams.value.size
  const params = {
    nickname: searchText.value,
    is_execute: false,
    type: 1,
    current: pageParams.value.current,
    size: pageParams.value.size
  };
  selectDevicePage(params).then((res) => {
    // if (res.data.code !== 0) return;
@@ -173,8 +173,8 @@
      line-height: 19px;
      .value {
        font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
        margin-left: 8px;
        font-size: 26px;
        text-align: center;
      }
    }
    .status {
src/views/Home/useUavHome/MapPopUpBox.vue
@@ -3,7 +3,7 @@
    <div class="header">
      <div class="headerLeft">
        <img class="header-image" src="../../../assets/images/home/mapPopUpBox/titleImg.png" alt="" />
        <div class="header-title">安义县机场</div>
        <div class="header-title">{{props.data.region_name}}</div>
      </div>
      <el-icon class="header-close" @click="props.removeLabel">
        <Close />
@@ -12,11 +12,11 @@
    <div class="info">
      <div class="info-item">
        机巢数:
        <div class="num">10</div>
        <div class="num">{{props.data.device_num}}</div>
      </div>
      <div class="info-item">
        任务数:
        <div class="num">32</div>
        <div class="num">{{props.data.jobNum}}</div>
      </div>
    </div>
    <div class="status-title">机巢状态</div>
@@ -31,14 +31,13 @@
<script setup>
import { Close } from '@element-plus/icons-vue';
const props = defineProps(['data', 'removeLabel']);
const list = [
  { name: '执行中', value: '1', color: '#FFA768' },
  { name: '在线', value: '20', color: '#8EFFAC' },
  { name: '离线', value: '14', color: '#FFFFFF' },
  { name: '异常', value: '25', color: '#FF6262' },
  { name: '空闲中', value: props.data.ex_num || 0, color: '#FFA768' },
  { name: '作业', value:props.data.leisure_num || 0, color: '#8EFFAC' },
  { name: '离线中', value: props.data.offline_num || 0, color: '#FFFFFF' },
];
const props = defineProps(['data', 'removeLabel']);
</script>
<style scoped lang="scss">
.mapPopUpBox {
@@ -133,7 +132,7 @@
  .status-list {
    display: flex;
    justify-content: space-between;
    justify-content: space-evenly;
    align-items: center;
    .status-item {
src/views/Home/useUavHome/useUavHome.js
@@ -7,6 +7,10 @@
import { getCenterPoint } from '@/utils/cesium/mapUtil';
import cesiumOperation from '@/utils/cesium-tsa';
import { HeadingPitchRange } from 'cesium';
import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation';
import _ from 'lodash';
import { getDeviceInfoNum } from '@/api/home/machineNest';
import { getTotalJobNum } from '@/api/home';
/**
 * 机巢聚合功能
@@ -15,14 +19,15 @@
  const { flyTo } = cesiumOperation();
  let scalingJudgment = [
    { name: '县', value: [0, 48651], gJson: null, height: 31753,show:false },
    { name: '市', value: [48651, 314863], gJson: null, height: 257731,show:false },
    { name: '省', value: [314863, 3796280000], gJson: null, height: 1987280,show:false, },
    { name: '县', splashedList: [], gJson: null, show: false, value: [0, 48651], height: 31753 },
    { name: '市', splashedList: [], gJson: null, show: false, value: [48651, 314863], height: 257731 },
    { name: '省', splashedList: [], gJson: null, show: false, value: [314863, 3796280000], height: 1987280 },
  ];
  let viewer = null;
  let active = null;
  let handler = null;
  let positionC3 = null;
  let currentEntity = null;
  const listenerHeight = () => {
    determineScaling();
    viewer.camera.moveEnd.addEventListener(determineScaling);
@@ -30,20 +35,21 @@
  // 确定缩放比例
  const determineScaling = index => {
    console.log(8989);
    console.log('确定缩放比例');
    if (!viewer) return;
    let height = viewer.camera.positionCartographic.height;
    // 根据高度展示对应的 gJson
    for (let [index, item] of scalingJudgment.entries()) {
      if (!item.show) return;
      if (height > item.value[0] && height <= item.value[1]) {
        if (active === item.name) return;
        active = item.name;
        removeEntities();
        removeLabel();
        if (item.gJson && item.name !== '县'){
          aggregation(item)
        }else{
          splashed()
        if (item.gJson && item.name !== '县') {
          aggregation(item);
        } else {
          splashed(item);
        }
        break;
      }
@@ -65,56 +71,76 @@
  const store = useStore();
  const selectedAreaCode = computed(() => store.state.user.selectedAreaCode);
  function getDeviceCount() {
    return getDeviceRegionCount().then(res => {
      return res?.data?.data || [];
    });
  }
  function getDeviceList() {
    return getDeviceRegion().then(res => {
      return res?.data?.data || [];
    });
  }
  watch(
    selectedAreaCode,
    async (newValue, oldValue) => {
      if (newValue) {
        const list = await getDeviceCount();
        const splashedList = await getDeviceList();
        console.log(list);
        console.log(splashedList);
        const code = newValue.slice(0, 6);
        const hierarchy = convertToHierarchy(code);
        const jsonPath = hierarchy.join('/');
        console.log(jsonPath);
        const { VITE_APP_BASE } = import.meta.env;
        const defaultDir = `${VITE_APP_BASE}public/geoJson/100000/`;
        scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true }));
        if (hierarchy.length === 1) {
          scalingJudgment = scalingJudgment.map(item => ({...item,show:true}))
          scalingJudgment[0].gJson = null;
          scalingJudgment[1].gJson = await import(
            `${VITE_APP_BASE}public/geoJson/100000/${jsonPath}/indexDistrict.json`
          );
          scalingJudgment[2].gJson = await import(
            `${VITE_APP_BASE}public/geoJson/100000/${jsonPath}/index.json`
          );
          const center = getCenterPoint(scalingJudgment[2].gJson.features.map(item => item.properties.center))
          flyTo({longitude: center.lng, latitude: center.lat},0,scalingJudgment[2].height)
          // determineScaling(2);
          scalingJudgment[0].splashedList = splashedList;
          const gJson1 = await import(/* @vite-ignore */ `${defaultDir}${jsonPath}/indexDistrict.json`);
          const gJson1Copy = JSON.parse(JSON.stringify(gJson1));
          const gJson2 = await import(/* @vite-ignore */ `${defaultDir}${jsonPath}/index.json`);
          const gJson2Copy = JSON.parse(JSON.stringify(gJson2));
          const findFun = (item1, item) => Number(item1.region_code.slice(0, 6)) === item.properties.adcode;
          scalingJudgment[1].gJson = {
            ...gJson1Copy,
            features: gJson1Copy.features.map(item => ({
              ...item,
              data: list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item)),
            })),
          };
          scalingJudgment[2].gJson = {
            ...gJson2Copy,
            features: gJson2Copy.features.map(item => ({ ...item, data: list.find(item1 => findFun(item1, item)) })),
          };
          const { lng, lat } = getCenterPoint(gJson2Copy.features.map(item => item.properties.center));
          flyTo({ longitude: lng, latitude: lat }, 0, scalingJudgment[2].height);
        }
        if (hierarchy.length === 2) {
          scalingJudgment = scalingJudgment.map(item => ({...item,show:true}))
          const gJson1 = await import(/* @vite-ignore */ `${defaultDir}${jsonPath}/index.json`);
          scalingJudgment[0].gJson = null;
          scalingJudgment[1].gJson = await import(
            `${VITE_APP_BASE}public/geoJson/100000/${jsonPath}/index.json`
          );
          scalingJudgment[1].gJson = JSON.parse(JSON.stringify(gJson1));
          scalingJudgment[2].gJson = null;
          scalingJudgment[2].show = false;
          const center = getCenterPoint(scalingJudgment[1].gJson.features.map(item => item.properties.center))
          flyTo({longitude: center.lng, latitude: center.lat},0,scalingJudgment[1].height)
          // determineScaling(1);
          const center = getCenterPoint(scalingJudgment[1].gJson.features.map(item => item.properties.center));
          flyTo({ longitude: center.lng, latitude: center.lat }, 0, scalingJudgment[1].height);
        }
        if (hierarchy.length === 3) {
          const gJson = await import(
            `${VITE_APP_BASE}public/geoJson/100000/${jsonPath.slice(0, -7)}/index.json`
          );
          const gJson = await import(/* @vite-ignore */ `${defaultDir}${jsonPath.slice(0, -7)}/index.json`);
          const gJson1 = gJson.features.find(
            item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1])
          );
          // scalingJudgment[0].gJson = { type: "FeatureCollection", features: [gJson1] }
          scalingJudgment[0].gJson = null
          scalingJudgment[0].gJson = null;
          scalingJudgment[1].gJson = null;
          scalingJudgment[1].show = null;
          scalingJudgment[1].show = false;
          scalingJudgment[2].gJson = null;
          scalingJudgment[2].show = false;
          const center = gJson1.properties.center
          flyTo({longitude: center[0], latitude: center[1]},0,scalingJudgment[0].height)
          // determineScaling(0);
          const center = gJson1.properties.center;
          flyTo({ longitude: center[0], latitude: center[1] }, 0, scalingJudgment[0].height);
        }
      }
    },
@@ -122,16 +148,13 @@
  );
  //散点机巢
  function splashed() {
    for (let i = 0; i < 50; i++) {
  function splashed(row) {
    row.splashedList.forEach(item => {
      viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(
          115.89 + Math.random() * 0.2,
          28.68 + Math.random() * 0.2
        ),
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude),
        label: {
          // 随机整数
          text: Math.floor(Math.random() * 100) + '号机巢',
          text: item.nickname,
          font: '14pt monospace',
          fillColor: Cesium.Color.WHITE,
          outlineColor: Cesium.Color.BLACK,
@@ -145,8 +168,13 @@
          width: 24,
          height: 24,
        },
        properties: {
          customData: {
            data: item,
          },
        },
      });
    }
    });
  }
  // 聚合机巢
@@ -155,6 +183,8 @@
    const featuresList = item.gJson.features.map(item => ({
      name: item.properties.name,
      position: item.properties.center,
      data: item.data,
      id: item.region_code,
    }));
    // 遍历特征并添加实体
    featuresList.forEach(feature => {
@@ -165,7 +195,7 @@
        position: position,
        label: {
          // 随机整数
          text: feature.name + Math.floor(Math.random() * 100),
          text: feature.name + feature.data.total_device_count,
          font: '14pt monospace',
          fillColor: Cesium.Color.WHITE,
          outlineColor: Cesium.Color.BLACK,
@@ -182,7 +212,7 @@
        properties: {
          id: feature.id,
          customData: {
            name: feature.name,
            data: feature.data,
          },
        },
      });
@@ -213,8 +243,8 @@
  };
  // 获取弹框box
  const getLabelDom = () => {
    const vNode = h(MapPopUpBox, { data: '参数', removeLabel });
  const getLabelDom = data => {
    const vNode = h(MapPopUpBox, { data, removeLabel });
    const tooltipContainer = document.createElement('div');
    tooltipContainer.id = 'mapPopUpBox';
    tooltipContainer.style.position = 'absolute';
@@ -229,7 +259,9 @@
  const labelBox = () => {
    let dom = document.querySelector('#mapPopUpBox');
    if (!dom) {
      dom = getLabelDom();
      const data = { ...currentEntity.requestData, ...currentEntity.customData };
      console.log(data);
      dom = getLabelDom(data);
    }
    const screenPosition = viewer.scene.cartesianToCanvasCoordinates(positionC3);
    if (screenPosition) {
@@ -240,14 +272,36 @@
  };
  // 左键单机事件
  const singleMachineEvent = click => {
  const singleMachineEvent = async click => {
    const pickedObject = viewer.scene.pick(click.position);
    if (Cesium.defined(pickedObject) && pickedObject.id) {
      const entity = pickedObject.id;
      currentEntity = entity;
      positionC3 = entity?.position?._value;
      if (!positionC3) return;
      viewer.scene.postRender.removeEventListener(labelBox);
      removeLabel();
      const customData = entity.properties.customData._value.data
      console.log(customData);
      if (customData.device_sn){
        // todo
        return
      }
      const areaCode = customData.region_code;
      const res = await getDeviceInfoNum({ areaCode });
      const resJob = await getTotalJobNum({ areaCode })
      currentEntity = {
        ...entity,
        requestData: {...res.data.data,jobNum:resJob.data.data},
        customData: entity.properties.customData._value.data
      };
      viewer.scene.postRender.addEventListener(labelBox);
    }
  };
  const removeDom = () => {
    const dom = document.querySelector('#mapPopUpBox');
    if (dom && dom.parentNode) {
      dom.parentNode.removeChild(dom);
    }
  };
@@ -259,10 +313,7 @@
  // 移除弹框标签
  const removeLabel = () => {
    viewer.scene.postRender.removeEventListener(labelBox);
    const dom = document.querySelector('#mapPopUpBox');
    if (dom && dom.parentNode) {
      dom.parentNode.removeChild(dom);
    }
    removeDom();
  };
  // 移除所有监听事件,变量置空