forked from drone/command-center-dashboard

罗广辉
2025-04-17 25ff7cbac4d8db9692ab63c7d24a2ededa0c8921
Merge remote-tracking branch 'origin/master'
9 files modified
206 ■■■■ changed files
src/assets/images/home/useUavHome/eventAggregationImg.png patch | view | raw | blame | history
src/hooks/useMapAggregation/useMapAggregation.js 83 ●●●●● patch | view | raw | blame | history
src/store/modules/task.js 2 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/SearchBox.vue 62 ●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/AddTask.vue 1 ●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue patch | view | raw | blame | history
src/views/TaskManage/TaskTop/TaskEvent.vue 1 ●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskTop/TaskIndustry.vue 48 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/components/TaskAlgorithmBusiness.vue 9 ●●●●● patch | view | raw | blame | history
src/assets/images/home/useUavHome/eventAggregationImg.png

src/hooks/useMapAggregation/useMapAggregation.js
@@ -6,7 +6,9 @@
import DevicePopUpBox from '@/hooks/components/DevicePopUpBox.vue'
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
// 图标
import offlineImg from '@/assets/images/home/useEventOperate/offline.png'
import onlineImg from '@/assets/images/home/useEventOperate/eventSingle.png'
import { render } from 'vue'
import { useStore } from 'vuex'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
@@ -18,17 +20,27 @@
 * 机巢聚合功能
 */
let arrColor = ["rgb(15,176,255)", "rgb(18,76,154)", "#40C4E4", "#42B2BE", "rgb(51,176,204)", "#8CB7E5", "rgb(0,244,188)", "#139FF0"]
let arrColor = [
    'rgb(15,176,255)',
    'rgb(18,76,154)',
    '#40C4E4',
    '#42B2BE',
    'rgb(51,176,204)',
    '#8CB7E5',
    'rgb(0,244,188)',
    '#139FF0',
]
let index = 0
function getColor () {
  return arrColor[++index % arrColor.length]
}
export const useMapAggregation = type => {
export const useMapAggregation = (type, status) => {
  const { flyTo } = cesiumOperation()
  const singleImg = type === 'device' ? uavImg : eventSingle
    const singleImg = type === 'device' ? onlineImg : eventSingle
  const offlinesingleImg = type === 'device' ? offlineImg : eventSingle
  const mergeImg = type === 'device' ? aggregationImg : eventAggregationImg
  const MapPopUpBox = type === 'device' ? DevicePopUpBox : EventPopUpBox
  const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)'
@@ -55,7 +67,6 @@
  const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
  const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
  const eventTimeType = computed(() => store.state.home.eventTimeType)
  const eventTimeRang = computed(() => store.state.home.eventTimeRang)
@@ -71,7 +82,6 @@
  }))
  let saveParams = { area_code: '', date_enum: 'CURRENT_WEEK' }
  // 确定缩放比例
  const determineScaling = () => {
@@ -110,13 +120,16 @@
  // 获取设备聚合
  function getDeviceCount (areaCode) {
    return getDeviceRegionCount({ areaCode }).then(res => {
      return (res?.data?.data || [])
            return res?.data?.data || []
    })
  }
  // 获取设备散点
  function getDeviceList (areaCode) {
    return getDeviceRegion({ areaCode }).then(res => {
      return (res?.data?.data || []).map(i => ({ ...i, type }))
    })
  }
@@ -163,9 +176,7 @@
  }
  const getOutLine = async (jsonPathPre, hierarchy) => {
    const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`)
    let features = parentGJson.features.find(
      item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1])
    )
        let features = parentGJson.features.find(item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1]))
    return { type: 'FeatureCollection', features: [features] }
  }
@@ -184,9 +195,15 @@
    if (!areaCode) return
    saveParams.area_code = areaCode
    const list = type === 'device' ? await getDeviceCount(areaCode) : await getMapEventCount(saveParams)
    const splashedList = type === 'device'
        const splashedList =
            type === 'device'
      ? await getDeviceList(areaCode)
      : eventList.map(i => ({ eventId: i.id, latitude: Number(i.latitude), longitude: Number(i.longitude), type: 'event' }))
                : eventList.map(i => ({
                        eventId: i.id,
                        latitude: Number(i.latitude),
                        longitude: Number(i.longitude),
                        type: 'event',
                  }))
    const hierarchy = convertToHierarchy(areaCode.slice(0, 6))
    const jsonPath = hierarchy.join('/')
    const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/')
@@ -225,21 +242,23 @@
    const outlineGJson = await getOutLine(jsonPathPre, hierarchy)
    scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
    const [longitude, latitude] = outlineGJson.features[0].properties.centroid
    const height = scalingJudgment[(hierarchy.length - 3) * (-1)].height
        const height = scalingJudgment[(hierarchy.length - 3) * -1].height
    setCenterPosition({ longitude, latitude, height })
    flyTo({ longitude, latitude }, 0, height)
  }
  const userAreaPosition = computed(() => store.state.home.userAreaPosition)
  const setCenterPosition = (position) => {
    const setCenterPosition = position => {
    store.commit('setCurrentAreaPosition', position)
    if (!userAreaPosition.value.longitude) {
      store.commit('setUserAreaPosition', position)
    }
  }
  watch(combinedValues, async (newValue, oldValue) => {
    watch(
        combinedValues,
        async (newValue, oldValue) => {
    if (newValue.singleUavHome?.device_sn) {
      clearMapEntity()
      return
@@ -250,7 +269,11 @@
    }
    if (newValue.eventTimeRang) {
      saveParams = { area_code: newValue.selectedAreaCode, start_date: newValue.eventTimeRang[0], end_date: newValue.eventTimeRang[1] }
                saveParams = {
                    area_code: newValue.selectedAreaCode,
                    start_date: newValue.eventTimeRang[0],
                    end_date: newValue.eventTimeRang[1],
                }
    }
    needFly = true
@@ -269,6 +292,7 @@
  //散点机巢
  function splashed (row) {
    row.splashedList.forEach((item, index) => {
      viewer.entities.add({
        id: `aggregation-splashed-${index}`,
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude),
@@ -283,7 +307,10 @@
          pixelOffset: new Cesium.Cartesian2(0, -9),
        },
        billboard: {
          image: new Cesium.ConstantProperty(singleImg),
          // singleImg
                    image: new Cesium.ConstantProperty(()=>{
            return item.status ==="OFFLINE" ? offlinesingleImg:singleImg
          }),
          width: 24,
          height: 24,
        },
@@ -312,7 +339,7 @@
            let material = new PolyGradientMaterial({
              color: Cesium.Color.fromCssColorString(arrColor[randomInt]),
              opacity: 0.7,
              alphaPower: 1.3
                            alphaPower: 1.3,
            })
            entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500
@@ -334,14 +361,14 @@
              }),
            },
            polygon
                        polygon,
          })
        })
      })
  }
  // 聚合机巢
  const aggregation = (item) => {
    const aggregation = item => {
    if (!item.gJson) return
    const featuresList = item.gJson.features.map(item1 => {
      // const {lng,lat} = getCenterPoint(item1.geometry.coordinates[0][0])
@@ -406,7 +433,7 @@
        let material = new PolyGradientMaterial({
          color: Cesium.Color.fromCssColorString(getColor()),
          opacity: 0.7,
          alphaPower: 1.3
                    alphaPower: 1.3,
        })
        const randomInt = Math.floor(Math.random() * 8) + 1
@@ -417,7 +444,8 @@
        entity.polygon.outline = false // 显示边框
      })
      needFly && viewer.flyTo(dataSource, {
            needFly &&
                viewer.flyTo(dataSource, {
        offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0),
        duration: 0.5,
      })
@@ -468,12 +496,15 @@
    let clickTargets = viewer.scene.drillPick(click.position).map(item => item.id)
    if (!clickTargets.length) return
    console.log(clickTargets, 11111)
        // console.log(clickTargets, 11111)
    let deviceAggregationFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'deviceAggregation')
    let deviceFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'device')
        let deviceAggregationFind = findTypeItem(
            clickTargets,
            item => item?.properties?.customData?._value?.data?.type === 'deviceAggregation'
        )
        let deviceFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'device')
    // "event"
    let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'event')
        let eventFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'event')
    // let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'eventAggregation')
    currentEntity = deviceAggregationFind || deviceFind || eventFind
src/store/modules/task.js
@@ -8,8 +8,6 @@
  actions: {},
  mutations: {
    setTaskSearchParams: (state, data) => {
      console.log(data)
            // setStore({ name: 'taskSearchParams', content: data })
            state.taskSearchParams = data;
        },
  },
src/views/TaskManage/SearchBox.vue
@@ -40,7 +40,7 @@
          </div>
        </el-form-item>
        <el-form-item label="任务算法:" v-if="isExpand">
          <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange"/>
          <TaskAlgorithmBusiness :setWidth="186" :showAlgorithm="true" @algorithmChange="algorithmChange"/>
        </el-form-item>
        <el-form-item label="所属部门:" v-if="isExpand">
          <el-select class="ztzf-select" v-model="searchForm.create_dept" placeholder="请选择部门" clearable>
@@ -53,7 +53,7 @@
          </el-select>
        </el-form-item>
        <el-form-item label="任务类型:" v-if="isExpand">
          <TaskAlgorithmBusiness :setWidth="200" :showBusiness="true" @businessChange="businessChange"/>
          <TaskAlgorithmBusiness :setWidth="186" :showBusiness="true" @businessChange="businessChange"/>
        </el-form-item>
        <div class="more" v-if="isExpand" @click="toggleExpand">收起</div>
        <div class="more" v-else @click="toggleExpand">更多</div>
@@ -84,7 +84,7 @@
  ai_types: [], // 算法类型
  area_code: '', // 区域code
  create_dept: '', // 创建部门
  date_enum: '', // 日期枚举,可用值:TODAY,CURRENT_WEEK,CURRENT_MONTH,CURRENT_YEAR
  date_enum: 'TODAY', // 日期枚举,可用值:TODAY,CURRENT_WEEK,CURRENT_MONTH,CURRENT_YEAR
  device_sn: '', // 设备编号
  end_date: null, // 结束时间
  industry_type: '', // 行业key
@@ -102,30 +102,6 @@
];
const emit = defineEmits(['search','addTask']);
const handleSearch = () => {
  // 提交至store
  let params = {
    ...searchForm,
    start_date: dateRange.value.length ? `${dateRange.value[0]} 00:00:00` : null,
    end_date: dateRange.value.length ? `${dateRange.value[1]} 23:59:59` : null
  }
  store.commit('setTaskSearchParams', params);
  emit('search', params);
};
const handleReset = () => {
  dateRange.value = null;
  Object.keys(searchForm).forEach(key => {
    searchForm[key] = '';
  });
  handleSearch();
};
// 新增任务
const addTask = () => {
  emit('addTask');
};
const algorithmChange = (val) => {
  searchForm.ai_types = val;
@@ -160,6 +136,7 @@
};
const deptChange = (value) => {
  // 处理机巢数据
  searchForm.device_sn = '';
  machineData.value = '';
  machineData.value = deptTreeData.value.find(item => item.area_code === value)?.devices || [];
  // 所属部门重新请求值
@@ -193,13 +170,42 @@
  // model.value = dateRanges[item];
  // emit('change', dateRanges[item],timeListEnum[index]);
  searchForm.date_enum = timeListEnum[index];
  handleSearch();
};
// 搜索
const handleSearch = () => {
  if (!dateRange.value) { dateRange.value = []; }
  // 提交至store
  let params = {
    ...searchForm,
    start_date: dateRange.value.length ? `${dateRange?.value[0]} 00:00:00` : null,
    end_date: dateRange.value.length ? `${dateRange?.value[1]} 23:59:59` : null
  }
  store.commit('setTaskSearchParams', params);
  emit('search', params);
};
// 重置
const handleReset = () => {
  dateRange.value = [];
  Object.keys(searchForm).forEach(key => {
    searchForm[key] = '';
  });
  searchForm.ai_types = [];
  searchForm.date_enum = 'TODAY';
  checked.value = 'today';
  handleSearch();
};
// 新增任务
const addTask = () => {
  emit('addTask');
};
onMounted(() => {
  // requestDictionary();
  getDeptsByAreaCode();
  requestDockInfo();
  // 查询一次
  handleSearch();
});
</script>
src/views/TaskManage/TaskIntermediateContent/AddTask.vue
@@ -8,6 +8,7 @@
        :width="pxToRem(1500)"
        :close-on-click-modal="false"
        :destroy-on-close="true"
        @close="cancel"
    >
        <!-- <el-divider content-position="left">新建任务</el-divider> -->
        <div class="task-contain">
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
src/views/TaskManage/TaskTop/TaskEvent.vue
@@ -117,7 +117,6 @@
// 添加监听
watch(() => store.state.task.taskSearchParams, (newVal) => {
  console.log(newVal, '1111');
  if (newVal) {
    getJobEventBar(newVal);
  }
src/views/TaskManage/TaskTop/TaskIndustry.vue
@@ -1,7 +1,7 @@
<!-- 任务算法统计 -->
 <template>
  <div class="task-industry">
    <div class="title">任务算法统计</div>
    <div class="title">任务算法类型</div>
    <div class="chart" ref="chartRef"></div>
  </div>
 </template>
@@ -41,13 +41,14 @@
      roseType: 'radius',
      radius: ['20%', '70%'],
      center: ['50%', '45%'],
      data: [
        { name: '国土类', value: 0, itemStyle: { color: '#3D7FFF' } },
        { name: '城管类', value: 0, itemStyle: { color: '#8277E9' } },
        { name: '消防类', value: 0, itemStyle: { color: '#FFB77E' } },
        { name: '林业类', value: 0, itemStyle: { color: '#44D7B6' } },
        { name: '公安类', value: 0, itemStyle: { color: '#62F4FF' } }
      ],
      data: [],
      // [
      //   { name: '国土类', value: 0, itemStyle: { color: '#3D7FFF' } },
      //   { name: '城管类', value: 0, itemStyle: { color: '#8277E9' } },
      //   { name: '消防类', value: 0, itemStyle: { color: '#FFB77E' } },
      //   { name: '林业类', value: 0, itemStyle: { color: '#44D7B6' } },
      //   { name: '公安类', value: 0, itemStyle: { color: '#62F4FF' } }
      // ],
      label: {
        show: true,
        position: 'outside',
@@ -71,36 +72,19 @@
const getIndustryJobNumPieChart = (value) => {
  industryJobNumPieChart(value).then(res => {
    if (res.data.code !== 0) return;
    option.series[0].data.forEach(item => {
      const matchData = res.data.data.find(d => d.name === item.name);
      if (matchData) {
        item.value = matchData.value;
      }
    });
    option.series[0].data = res.data.data;
    // forEach(item => {
    //   const matchData = res.data.data.find(d => d.name === item.name);
    //   if (matchData) {
    //     item.value = matchData.value;
    //   }
    // });
    chart.value.setOption(option);
  });
};
// 获取行业统计数据
// const getIndustryJobNumPieChart = () => {
//   industryJobNumPieChart().then(res => {
//     if (res.data.code !== 0) return;
//     const data = res.data.data.map(item => ({
//       name: item.name,
//       value: item.value,
//       itemStyle: {
//         color: getRandomColor()
//       }
//     }));
//     option.series[0].data = data;
//     chart.value.setOption(option);
//   });
// };
// 添加监听
watch(() => store.state.task.taskSearchParams, (newVal) => {
  console.log(newVal, '2222');
  if (newVal) {
    getIndustryJobNumPieChart(newVal);
  }
src/views/TaskManage/components/TaskAlgorithmBusiness.vue
@@ -1,12 +1,12 @@
<!-- 关联算法和综合业务 -->
<template>
  <div class="task-algorithm" v-if="showAlgorithm">
    <el-select class="ztzf-select" :style="{ width:setWidth +'px' }" v-model="ai_types" multiple collapse-tags collapse-tags-tooltip placeholder="请选择算法" clearable @change="handleAlgorithmChange">
    <el-select class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="ai_types" multiple collapse-tags collapse-tags-tooltip placeholder="请选择算法" clearable @change="handleAlgorithmChange">
      <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
    </el-select>
  </div>
  <div class="task-business" v-if="showBusiness">
    <el-select class="ztzf-select" :style="{ width:setWidth +'px' }" v-model="industry_type" placeholder="请选择类型" clearable @change="handleBusinessChange">
    <el-select class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="industry_type" placeholder="请选择类型" clearable @change="handleBusinessChange">
      <el-option v-for="item in taskBusiness" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
    </el-select>
  </div>
@@ -14,7 +14,8 @@
<script setup>
import { getMultipleDictionary } from '@/api/system/dictbiz'
import { getMultipleDictionary } from '@/api/system/dictbiz';
import { pxToRem } from '@/utils/rem'
// 接收父组件传参
const props = defineProps({
@@ -27,7 +28,7 @@
    default: false
  },
  setWidth: {
    type: String,
    type: Number,
    default: '100%'
  }
});