无人机管理后台前端(已迁走)
张含笑
2025-08-25 80283182ffdce61f3f5ed3f36ede94c00cad2d53
feat:图斑详情
2 files modified
2 files added
389 ■■■■■ changed files
src/api/patchManagement/index.js 9 ●●●●● patch | view | raw | blame | history
src/views/resource/components/spotDetails.vue 351 ●●●●● patch | view | raw | blame | history
src/views/resource/patchManagement.vue 18 ●●●●● patch | view | raw | blame | history
src/views/resource/patchTypeManagement.vue 11 ●●●●● patch | view | raw | blame | history
src/api/patchManagement/index.js
New file
@@ -0,0 +1,9 @@
import request from '@/axios';
// 获取图斑列表
export const getPatchesSpotList = params => {
    return request({
        url: `/drone-device-core/patches/api/v1/Patches/listPatches`,
        method: 'get',
        params,
    })
}
src/views/resource/components/spotDetails.vue
New file
@@ -0,0 +1,351 @@
<template>
  <el-dialog title="图斑详情" append-to-body v-model="uploadPatchDialog" width="70%" align-center>
    <div class="container">
      <!-- 信息展示区 -->
      <div class="infoBox">
        <div class="itemBoxLeft">
          <div v-for="(item, index) in infoList" :key="index" class="itemCon">
            <div class="itemBox">
              <div class="itemTitle">{{ item.name }}:</div>
              <div
                truncated
                class="itemValue"
                v-if="item.name !== '飞行事件' && item.name !== '任务执行次数'"
              >
                {{ item.value ? item.value : '--' }}
              </div>
              <div class="itemValue" v-if="item.name === '任务执行次数'">
                {{ item.value ? item.value : '0' }}次
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- 地图和表格容器 -->
      <div class="map-container">
        <!-- 图斑列表 -->
        <div class="table-overlay">
          <div class="table-content">
            <div>图斑列表</div>
            <el-table :data="tableData" border style="width: 100%">
              <el-table-column type="index" label="序号" width="80">
                <template #default="{ $index }">
                  {{ ($index + 1).toString().padStart(2, '0') }}
                </template>
              </el-table-column>
              <el-table-column prop="dkbh" label="图斑名称" />
              <el-table-column prop="investigate" label="图斑状态" />
              <el-table-column label="操作" width="100" align="center">
                <el-button type="text">删除</el-button>
                <el-button type="text">编辑</el-button>
              </el-table-column>
            </el-table>
            <div class="pagination-container">
              <el-pagination
                background
                layout="prev, pager, next"
                :total="10"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
              />
            </div>
          </div>
        </div>
        <!-- 地图 -->
        <div id="spotMap" class="ztzf-cesium" v-show="uploadPatchDialog"></div>
      </div>
    </div>
  </el-dialog>
</template>
<script setup>
import {getPatchesSpotList} from '@/api/patchManagement/index'
import * as Cesium from 'cesium';
import { PublicCesium } from '@/utils/cesium/publicCesium';
import { ref, watch, onBeforeUnmount } from 'vue';
const uploadPatchDialog = defineModel('show');
let publicCesiumInstance = null;
let viewer = null;
const viewInstance = shallowRef(null)
const homeViewer = shallowRef(null)
let tbJwdList = []
const infoList = ref([
  { name: '文件名称', value: '', field: 'job_info_num' },
  { name: '图斑类型', value: '', field: 'name' },
  { name: '图斑数量', value: '', field: 'industry_type_str' },
  { name: '异常图斑数量', value: '', field: 'event_number' },
  { name: '行政区划', value: '', field: 'device_names' },
  { name: '数据来源', value: '', field: 'dept_name' },
  { name: '创建时间', value: '', field: 'cycle_time_value' },
  { name: '创建人', value: '', field: 'ai_type_str' },
]);
const params = ref({
  current: 1,
  size: 20,
});
const handleSizeChange = () => {};
const handleCurrentChange = () => {};
const tableData = ref([])
const polygonSearch = ref({
        investigate: '',
        isPush: '',
        isException: '',
        inRange: '1',
        dkbh: '',
        dkmj: '',
        page: 1,
        pageSize: 20,
        total: 0,
        workspaceId: 'ede19ef4-2e92-42f3-83cc-89afb54e95a7',
        area_code: '360000000000',
        device_sn: '',
    })
// 获取列表数据
// const getTableList = async () => {
//     tableData.value = []
//     const res = await getPatchesSpotList(polygonSearch.value)
//     if (res.data.code !== 0) return
//     tableData.value = res.data.data.list.map(item => ({
//         ...item,
//         dkfw: item.sdfw && item.is_exception == 1 ? item.sdfw : item.dkfw,
//     }))
//     console.log('shuju ',tableData.value);
//     // 加载图斑
//     entitiesAddSpot()
// }
   const getTableList = async () => {
     tableData.value = []
     const res = await getPatchesSpotList(polygonSearch.value)
     if (res.data.code !== 0) return
     tableData.value = res.data.data.list.map(item => ({
       ...item,
       dkfw: item.sdfw && item.is_exception == 1 ? item.sdfw : item.dkfw,
     }))
     // 先清空地图现有图斑
     tbJwdList = []
     viewer.entities.removeAll() // 清除所有实体
     // 再加载新图斑
     entitiesAddSpot()
   }
// 初始化所有图斑
   const entitiesAddSpot = () => {
     tableData.value.forEach(item => {
       if (!item.dkfw) return
       const numbersWithCommas = item.dkfw.match(/\d+(\.\d+)?/g)
       if (!numbersWithCommas || numbersWithCommas.length % 2 !== 0) {
         console.error('Invalid coordinate format for item:', item.id)
         return
       }
       const grouped = numbersWithCommas.map(Number)
       tbJwdList.push({
         ...item,
         grouped: grouped,
       })
       try {
         homeViewer.value.entities.add({
           id: 'polygon_dk' + item.id,
           name: item.dkbh || '未命名图斑',
           polygon: {
             hierarchy: new Cesium.PolygonHierarchy(
               Cesium.Cartesian3.fromDegreesArray(grouped)
             ),
             material: item.is_exception == 1
               ? Cesium.Color.RED.withAlpha(0.5)
               : Cesium.Color.YELLOW.withAlpha(0.5),
             outline: true,
             outlineColor: Cesium.Color.WHITE,
             outlineWidth: 2,
             clampToGround: true
           },
           label: {
             text: item.dkbh || '',
             font: '14px sans-serif',
             style: Cesium.LabelStyle.FILL_AND_OUTLINE,
             outlineWidth: 2,
             verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
             pixelOffset: new Cesium.Cartesian2(0, -10)
           }
         })
       } catch (e) {
         console.error('Failed to add polygon:', e)
       }
     })
     // 自动缩放到所有图斑范围
     if (tbJwdList.length > 0) {
       const positions = tbJwdList.flatMap(item =>
         Cesium.Cartesian3.fromDegreesArray(item.grouped)
       )
       homeViewer.value.camera.flyToBoundingSphere(
         new Cesium.BoundingSphere.fromPoints(positions)
       )
     }
   }
// 地图
const initMap = () => {
  if (!document.getElementById('spotMap')) {
    return;
  }
  publicCesiumInstance = new PublicCesium({
    dom: 'spotMap',
    terrain: true,
    flatMode: false,
    layerMode: 4,
  });
homeViewer.value = publicCesiumInstance.getViewer()
  viewer = publicCesiumInstance.getViewer();
      viewInstance.value = publicCesiumInstance
  viewer.scene.globe.depthTestAgainstTerrain = true;
};
// 销毁
const destroyMap = () => {
  if (viewer) {
    viewer.destroy();
    viewer = null;
  }
  publicCesiumInstance = null;
};
watch(uploadPatchDialog, newVal => {
  if (newVal) {
    setTimeout(() => {
      initMap();
      getTableList()
    }, 0);
  } else {
    destroyMap();
  }
});
onBeforeUnmount(() => {
  destroyMap();
});
</script>
<style scoped lang="scss">
.container {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.infoBox {
  display: flex;
  justify-content: space-between;
  margin-bottom: 16px;
  .itemBoxLeft {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    row-gap: 0;
    padding: 10px;
    font-size: 14px;
    .itemBox {
      display: flex;
      align-items: center;
      background: #fff;
      height: 33px;
      border-top: 2px solid #efefef;
    }
  }
  .itemCon:nth-last-child(-n + 2) {
    border-bottom: 2px solid #efefef;
  }
  .itemBox:nth-last-child(-n + 2) .itemTitle {
    border-bottom: 1px solid #efefef;
  }
  .itemTitle {
    color: #0b1d38;
    width: 26%;
    text-align: right;
    background: #f3f6ff;
    height: 33px;
    line-height: 33px;
    padding-right: 10px;
    border-top: 1px solid #efefef;
  }
  .itemValue {
    font-size: 14px;
    color: #747e91;
    text-align: center;
    width: 74%;
  }
  .flightEvents {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 10px;
    width: 74%;
    justify-content: center;
    img {
      width: 30px;
      height: 30px;
    }
  }
}
.map-container {
  position: relative;
  height: 600px;
  width: 100%;
}
.table-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 30%;
  height: 100%;
  z-index: 999;
  //   background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  padding: 10px;
  display: flex;
  flex-direction: column;
}
.table-content {
  display: flex;
  flex-direction: column;
  height: 90%;
}
.pagination-container {
  margin-top: auto; /* 将分页推到容器底部 */
  padding: 10px 0;
  background: white;
  display: flex;
  justify-content: center;
  height: 20px;
}
.el-table {
  flex: 1; /* 让表格占据剩余空间 */
  overflow: auto;
}
#spotMap {
  height: 100%;
  width: 100%;
}
</style>
src/views/resource/patchManagement.vue
@@ -5,7 +5,7 @@
      @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button type="primary" icon="el-icon-upload" @click="uploadPatch">
        <el-button type="primary" icon="el-icon-upload" @click=" handleDebug">
          上传图斑
        </el-button>
        <el-button type="primary" icon="el-icon-setting" @click="goTypeManagement">
@@ -17,20 +17,21 @@
      </template>
      <template #menu="scope">
        <el-button type="primary" text icon="el-icon-video-play" @click="handleDebug(scope.row)">详情
        <el-button type="primary" text icon="el-icon-view" @click="uploadPatch(scope.row)">详情
        </el-button>
        <el-button type="primary" text icon="el-icon-video-play" @click="handleDebug(scope.row)">编辑
        <el-button type="primary" text icon="el-icon-edit" @click="uploadPatch(scope.row)">编辑
        </el-button>
        <el-button type="primary" text icon="el-icon-video-play" @click="handleDebug(scope.row)">删除
        <el-button type="primary" text icon="el-icon-delete" @click="handleDebug(scope.row)">删除
        </el-button>
      </template>
    </avue-crud>
    <el-dialog title="对象存储上传调试" append-to-body v-model="box" width="550px">
    <el-dialog title="上传图斑" append-to-body v-model="box" width="550px">
      <avue-form ref="form" :option="debugOption" v-model="debugForm" @submit="handleSubmit" />
    </el-dialog>
    <patchDetails ref="patchDetails" />
    <!-- 图斑详情 -->
  <SpotDetails v-model:show="uploadPatchDialog"></SpotDetails>
    <!-- <patchDetails ref="patchDetails" /> -->
  </basic-container>
</template>
@@ -42,6 +43,7 @@
import { getListPage, getDetail, add, update, remove, enable, disable } from '@/api/resource/oss'
import func from '@/utils/func'
import patchDetails from '@/views/resource/components/patchDetails.vue'
import SpotDetails from '@/views/resource/components/spotDetails.vue';
const store = useStore()
const router = useRouter()
@@ -351,7 +353,7 @@
// 上传图斑
const uploadPatch = () => {
  uploadPatchDialog.value.open()
  uploadPatchDialog.value = true
}
// 跳转至图斑类型管理页面
src/views/resource/patchTypeManagement.vue
@@ -207,6 +207,7 @@
}
const searchChange = (params, done) => {
console.log('list');
  query.value = params
  page.value.currentPage = 1
  onLoad(page.value, params)
@@ -214,12 +215,14 @@
}
const selectionChange = list => {
  selectionList.value = list
  // selectionList.value = list
}
const selectionClear = () => {
  selectionList.value = []
  crudRef.value.toggleSelection()
  // crudRef.value.toggleSelection()
}
const handleEnable = row => {
@@ -232,7 +235,7 @@
    .then(() => {
      onLoad(page.value)
      ElMessage.success('操作成功!')
      crudRef.value.toggleSelection()
      // crudRef.value.toggleSelection()
    })
}
@@ -246,7 +249,7 @@
    .then(() => {
      onLoad(page.value)
      ElMessage.success('操作成功!')
      crudRef.value.toggleSelection()
      // crudRef.value.toggleSelection()
    })
}