无人机管理后台前端(已迁走)
张含笑
2025-06-12 81701042c720dbf040d5b3820b363f0a656df46b
feat:数据中心地图,编辑文件名
7 files modified
500 ■■■■ changed files
src/api/dataCenter/dataCenter.js 23 ●●●●● patch | view | raw | blame | history
src/assets/images/dataCenter/datamap/activeevent.png patch | view | raw | blame | history
src/assets/images/dataCenter/datamap/eventCompleted.png patch | view | raw | blame | history
src/hooks/components/EventPopUpBox.vue 32 ●●●● patch | view | raw | blame | history
src/views/dataCenter/components/dataCenterMap.vue 264 ●●●●● patch | view | raw | blame | history
src/views/dataCenter/components/searchData.vue 6 ●●●● patch | view | raw | blame | history
src/views/dataCenter/dataCenter.vue 175 ●●●● patch | view | raw | blame | history
src/api/dataCenter/dataCenter.js
@@ -1,10 +1,10 @@
import request from '@/axios';
// 列表接口
export const getaiImagesPageAPI = (data) => {
export const getaiImagesPageAPI = (data,params) => {
    return request({
        url: `/blade-resource//attach/attachmentsPage`,
        method: 'post',
        data
        data,params
    })
  };
  // 详情接口
@@ -34,4 +34,23 @@
        method: 'post',
        data
    })
}
// 地图
export const getMapInfoAPI = (jobId) => {
    return request({
        url: `/blade-resource/attach/getAttachInfoByJobId`,
        method: 'get',
        params: {
            jobId,
          },
    })
  };
//编辑文件名
export const updataTitleApi = (params) => {
    return request({
        url: `/blade-resource/attach/updateFileName`,
        method: 'post',
        params
    })
}
src/assets/images/dataCenter/datamap/activeevent.png

src/assets/images/dataCenter/datamap/eventCompleted.png

src/hooks/components/EventPopUpBox.vue
@@ -1,8 +1,8 @@
<template>
    <div class="mapPopUpBox">
        <div class="title">
            <span>222</span>
            <span>{{ info.event_name }}</span>
<el-icon class="header-close" @click.stop="props.detailClick"><Warning /></el-icon>
            <el-icon class="header-close" @click.stop="props.removeLabel">
                <Close />
            </el-icon>
@@ -11,8 +11,8 @@
            <div class="medium">
                <el-image
                    class="eventImage"
                    :src="infoList?.smallUrl"
                    :preview-src-list="[infoList?.smallUrl]"
                    :src="getSmallImg(infoList?.link)"
                    :preview-src-list="[getSmallImg(infoList?.link)]"
                    fit="cover"
                    preview-teleported
                ></el-image>
@@ -21,11 +21,16 @@
            <div class="details">
                <div class="label">时间:</div>
                <div class="value point">
                    11
                    {{
                        infoList?.create_time?.slice(5, 16).replace('-', '/') ||
                        infoList?.createTime?.slice(5, 16).replace('-', '/')
                    }}
                </div>
                <div class="label">地点:</div>
                <div class="value">
                    22
                    {{ _.round(infoList?.metadata?.shootPosition?.lng, 3) }},{{
                        _.round(infoList?.metadata?.shootPosition?.lat, 3)
                    }}
                </div>
            </div>
        </div>
@@ -33,12 +38,10 @@
</template>
<script setup>
import { ElImage, ElIcon } from 'element-plus'
import { Close } from '@element-plus/icons-vue'
// import { getEventDetails } from '@/api/home/aggregation'
import { Close,Warning } from '@element-plus/icons-vue'
import _ from 'lodash'
import { getSmallImg } from '@/utils/util'
const props = defineProps(['data', 'removeLabel'])
import { getShowImg, getSmallImg } from '@/utils/util';
const props = defineProps(['data', 'removeLabel','detailClick'])
const loading = ref(true)
const info = ref({
@@ -50,12 +53,7 @@
    create_time: '04/01 12:41',
})
const infoList = props.data
onMounted(async () => {})
const disposeUrl = ({ url }) => {
    return getSmallImg(url)
}
</script>
<style scoped lang="scss">
@@ -81,7 +79,7 @@
        margin-bottom: 10px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        justify-content: end;
        .header-close {
            width: 20px;
src/views/dataCenter/components/dataCenterMap.vue
@@ -1,42 +1,55 @@
<template>
  <el-dialog modal-class="mapDialog" v-model="isShow"  width="80%">
  <el-dialog modal-class="mapDialog" v-model="isShow" width="80%">
    <div class="mapBox">
      <div id="dataCenterMap" class="ztzf-cesium"></div>
      <div v-if="isShow" id="dataCenterMap" class="ztzf-cesium"></div>
    </div>
  </el-dialog>
</template>
<script setup>
import { getMapInfoAPI } from '@/api/dataCenter/dataCenter';
import { useStore } from 'vuex';
import { PublicCesium } from '@/utils/cesium/publicCesium';
import { Cartesian3 } from 'cesium';
import * as Cesium from 'cesium';
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue';
import { render } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png';
import { render, nextTick, watch, onMounted, onBeforeUnmount, shallowRef, ref, h } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png'; //默认图标
import activeIcon from '@/assets/images/dataCenter/datamap/activeevent.png'; // 激活图标
const emit = defineEmits(['lookDetail']);
const isShow = defineModel('show');
const viewerRef = shallowRef(null);
let viewer = null;
const viewInstance = shallowRef(null);
const store = useStore();
const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 });
let handler = null;
const props = defineProps(['jobId','mapList']);
// 存储地图实体引用
const dataPointEntities = ref([]);
const isMapInitialized = ref(false); //地图加载
const dataPointList = ref([]);
const activeEntity = ref(null); // 当前激活的点
// 获取弹框box
const detailId = ref('')
const createLabelDom = data => {
console.log('data',data);
    const vNode = h(EventPopUpBox, { data, removeLabel })
        const tooltipContainer = document.createElement('div')
        tooltipContainer.id = 'mapPopUpBox'
        tooltipContainer.style.position = 'absolute'
        tooltipContainer.style.transform = 'translate(-50%,-135%)'
        tooltipContainer.style.pointerEvents = 'none'
        document.querySelector('#dataCenterMap').append(tooltipContainer)
        render(vNode, tooltipContainer)
        return tooltipContainer
  detailId.value = data
  const vNode = h(EventPopUpBox, { data, removeLabel, detailClick });
  const tooltipContainer = document.createElement('div');
  tooltipContainer.id = 'mapPopUpBox';
  tooltipContainer.style.position = 'absolute';
  tooltipContainer.style.transform = 'translate(-50%,-125%)';
  tooltipContainer.style.pointerEvents = 'none';
  document.querySelector('#dataCenterMap').append(tooltipContainer);
  render(vNode, tooltipContainer);
  return tooltipContainer;
};
let currentClickEntity = null;
// 弹框位置刷新
const labelBoxUpdate = () => {
  if (!currentClickEntity) return;
@@ -53,6 +66,10 @@
    dom.style.display = 'block';
  }
};
// 暴露方法给父组件
defineExpose({
  labelBoxUpdate
});
const removeDom = () => {
  const dom = document.querySelector('#mapPopUpBox');
  if (dom && dom.parentNode) {
@@ -64,13 +81,48 @@
const removeLabel = () => {
  viewer?.scene.postRender.removeEventListener(labelBoxUpdate);
  removeDom();
};
// 点击去到详情页面
const detailClick = () => {
  removeLabel()
  // 给父组件传值
  emit('update:show', false);
  emit('lookDetail', detailId.value);
}
// 恢复所有点的默认图标
const restoreAllIcons = () => {
  dataPointEntities.value.forEach(entity => {
    if (entity.billboard) {
      entity.billboard.image = defaultIcon;
    }
  });
  activeEntity.value = null;
};
// 左键单机事件
const singleMachineEvent = async click => {
  let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id);
  if (!clickedEntities.length) return;
  if (!clickedEntities.length) {
    // 点击空白处恢复所有图标并移除弹窗
    restoreAllIcons();
    removeLabel();
    return;
  }
  currentClickEntity = clickedEntities[0];
  // 恢复所有点的默认图标
  restoreAllIcons();
  // 设置当前点击点为激活状态
  if (currentClickEntity.billboard) {
    currentClickEntity.billboard.image = activeIcon;
    activeEntity.value = currentClickEntity;
  }
  removeLabel();
  viewer.scene.postRender.addEventListener(labelBoxUpdate);
};
@@ -82,38 +134,36 @@
  handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 清除所有数据点实体
const clearDataPoints = () => {
  if (!viewer) return;
  dataPointEntities.value.forEach(entity => {
    viewer.entities.remove(entity);
  });
  dataPointEntities.value = [];
  activeEntity.value = null;
};
const removeHandler = () => {
  handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  handler?.destroy();
  handler = null;
};
const dataPointList = [
  {
    longitude: '115.85708286111111',
    latitude: '28.62837602777778',
  },
  {
    longitude: '115.8566981111111',
    latitude: '28.624613027777777',
  },
  {
    longitude: '115.85474438888889',
    latitude: '28.624930444444445',
  },
  {
    longitude: '115.85518375',
    latitude: '28.62479547222222',
  },
];
function renderDataPoint() {
  dataPointList.forEach((item, index) => {
    const sizeObj = { width: 30, height: 30 };
    viewer.entities.add({
      id: `renderDataPoint-${index}`,
      position: Cesium.Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)),
const renderDataPoint = mapList => {
  if (!viewer || !mapList?.length) return;
  // 清除旧实体
  clearDataPoints();
  // 添加新实体
  mapList.forEach((item, index) => {
    const entity = viewer.entities.add({
      id: `dataCenter-point-${index}-${Date.now()}`,
      position: Cesium.Cartesian3.fromDegrees(
        Number(item.metadata.shootPosition.lng),
        Number(item.metadata.shootPosition.lat)
      ),
      label: {
        font: '12pt Source Han Sans CN',
        fillColor: Cesium.Color.WHITE,
@@ -121,11 +171,14 @@
        outlineWidth: 2,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(0, -9),
        eyeOffset: new Cesium.Cartesian3(0, 0, -9),
        pixelOffset: new Cesium.Cartesian2(0, 55),
      },
      billboard: {
        image: defaultIcon,
        ...sizeObj,
        image: defaultIcon, // 初始为默认图标
        width: 40,
        height: 40,
        pixelOffset: new Cesium.Cartesian2(0, -15),
      },
      properties: {
        customData: {
@@ -133,67 +186,116 @@
        },
      },
    });
    dataPointEntities.value.push(entity);
  });
}
};
const initMap = () => {
  const publicCesiumInstance = new PublicCesium({
    dom: 'dataCenterMap',
    flatMode: false,
    terrain: false,
    mapFilter: true,
  });
  viewerRef.value = publicCesiumInstance.getViewer();
  viewer = publicCesiumInstance.getViewer();
  const { longitude, latitude, height } = currentAreaPosition.value;
  const position = Cartesian3.fromDegrees(longitude, latitude, height);
  viewerRef.value.camera.flyTo({
    duration: 0,
    destination: position,
    orientation: {
      heading: Cesium.Math.toRadians(0.0),
      pitch: Cesium.Math.toRadians(-90.0),
      roll: 0.0,
    },
  });
  viewInstance.value = publicCesiumInstance;
  if (viewer || isMapInitialized.value) return;
  const container = document.getElementById('dataCenterMap');
  if (!container) {
    console.error('地图容器未找到');
    return;
  }
  try {
    const publicCesiumInstance = new PublicCesium({
      dom: 'dataCenterMap',
      flatMode: false,
      terrain: false,
      mapFilter: true,
    });
    viewer = publicCesiumInstance.getViewer();
    viewerRef.value = viewer;
    // 相机飞行
   const { longitude, latitude, height } = currentAreaPosition.value
    const position = Cartesian3.fromDegrees(longitude, latitude, height)
    viewerRef.value.camera.flyTo({
        duration: 0,
        destination: position,
        orientation: {
            heading: Cesium.Math.toRadians(0.0),
            pitch: Cesium.Math.toRadians(-90.0),
            roll: 0.0,
        },
    })
    // 初始化事件处理器
    handlerInit();
    isMapInitialized.value = true;
    console.log('地图初始化完成');
    // 初始化后立即渲染已有数据
    if (dataPointList.value.length > 0) {
      renderDataPoint(dataPointList.value);
    }
  } catch (error) {
    console.error('地图初始化失败:', error);
  }
};
// 地图接口
const loading = ref(false);
const getMapInfoAPIFun = async ids => {
  try {
    const res = await getMapInfoAPI(ids);
    dataPointList.value = res.data.data || [];
    // 确保地图已初始化后再渲染
    if (isMapInitialized.value && viewer) {
      renderDataPoint(dataPointList.value);
    }
  } catch (error) {
    console.error('获取地图数据失败:', error);
  }
};
watch(() => props.jobId, (newVal) => {
  if (newVal) {
    getMapInfoAPIFun(newVal);
  }
}, { immediate: true });
// 监听对话框状态
watch(isShow, (newVal) => {
  if (newVal) {
    nextTick(() => {
      initMap();
      handlerInit();
      renderDataPoint();
    });
  } else {
    // 对话框关闭时清理资源
    viewInstance.value?.viewerDestroy();
    // 清理资源
    if (viewer) {
      viewer.destroy();
      viewer = null;
    }
    isMapInitialized.value = false;
    removeHandler();
    clearDataPoints();
  }
});
onMounted(() => {
  nextTick(() => {
    initMap();
    handlerInit();
    renderDataPoint();
  });
});
onMounted(() => {});
onBeforeUnmount(() => {
  viewInstance.value?.viewerDestroy();
  if (viewer) {
    viewer.destroy();
  }
  removeHandler();
  clearDataPoints();
});
</script>
<style >
<style>
.mapDialog .el-dialog__body {
  height: 700px !important;
  padding: 0 !important;
}
</style>
<style scoped lang="scss">
<style scoped lang="scss">
.mapBox {
  z-index: 2;
  height: 650px;
@@ -207,4 +309,4 @@
    height: 100%;
  }
}
</style>
</style>
src/views/dataCenter/components/searchData.vue
@@ -121,6 +121,10 @@
  children: 'childrens',
};
const fileFormatOption = [
{
    value: '',
    label: '全部',
  },
  {
    value: '0',
    label: '照片',
@@ -134,7 +138,7 @@
    label: 'AI识别',
  },
  {
    value: '3',
    value: '5',
    label: '全景',
  },
  {
src/views/dataCenter/dataCenter.vue
@@ -34,21 +34,21 @@
            <img
              class="quanjing"
              @click="clickpanorama(scope.row)"
              v-if="scope.row?.resultType === 3"
              :src="scope.row.link"
              v-if="scope.row?.resultType === 5"
              :src="scope.row?.link"
              alt=""
            />
            <img
              v-else-if="scope.row?.resultType === 1"
              :src="convertVideoUrlToThumbnail(scope.row.link)"
              :src="convertVideoUrlToThumbnail(scope.row?.link)"
              alt=""
              class="imageBox"
              @click="enterFullScreen(scope.row)"
            />
            <el-image
              v-else
              :src="scope.row.smallUrl"
              :preview-src-list="[scope.row.showUrl]"
              :src="scope.row?.smallUrl"
              :preview-src-list="[scope.row?.showUrl]"
              fit="cover"
              preview-teleported
            />
@@ -68,13 +68,13 @@
        <el-table-column label="操作" width="150" align="center">
          <template #default="scope">
            <span class="look" @click="lookDetail(scope.row)">查看</span>
            <span
              class="delete"
              @click="deleteDetail(scope.row)"
              v-if="scope.row.fileformat !== 'AI识别'"
            <span class="delete" @click="deleteDetail(scope.row)" v-if="scope.row.resultType !== 2"
              >删除</span
            >
            <span class="location" @click="positionDetail()" v-if="scope.row.fileformat !== '视频'"
            <span
              class="location"
              @click="positionDetail(scope.row)"
              v-if="scope.row.resultType !== 1"
              >定位</span
            >
          </template>
@@ -98,29 +98,35 @@
    <el-dialog v-model="dialogVisible" width="60%" append-to-body>
      <template #header="{ titleId, titleClass }">
        <div class="my-header">
          <h4 :id="titleId" :class="titleClass">{{ dialogDetailList?.filename }}</h4>
          <el-button type="success" plain icon="el-icon-download">下载</el-button>
          <h4 :id="titleId" :class="titleClass">{{ dialogDetailList?.nickName }}</h4>
          <el-button
            type="success"
            plain
            icon="el-icon-download"
            @click="detailDownLoad(dialogDetailList)"
            >下载</el-button
          >
        </div>
      </template>
      <div class="detailContainer">
        <div class="leftImg">
          <img
            v-if="dialogDetailList?.resultType === 1"
            :src="convertVideoUrlToThumbnail(dialogDetailList.link)"
            :src="convertVideoUrlToThumbnail(dialogDetailList?.link)"
            alt=""
            class="imageBox"
          />
          <img v-else :src="dialogDetailList.link" alt="" />
          <img v-else :src="getSmallImg(dialogDetailList?.link)" alt="" />
        </div>
        <div class="rightDetail">
          <div class="title">
            <div class="inputEdit">
              文件名称:<span v-if="!dialogDetailList.checkedinput">{{
                dialogDetailList?.filename
                dialogDetailList.nickName
              }}</span>
              <el-input
                v-else
                v-model="dialogDetailList.filename"
                v-model="dialogDetailList.nickName"
                @keyup.enter="saveTitle()"
                class="title-input"
                clearable
@@ -131,15 +137,19 @@
                ><el-icon><Edit /></el-icon
              ></span>
              <div v-else class="suffixBoxEdit">
                <div class="editText" @click="submitEditSuffix(item, index)">✔</div>
                <div class="editText" @click="cancelEditSuffix(item, index)">✖</div>
                <div class="editText" @click="submitEditSuffix(dialogDetailList)">✔</div>
                <div class="editText" @click="cancelEditSuffix(dialogDetailList)">✖</div>
              </div>
            </div>
          </div>
          <div>任务名称:{{ dialogDetailList?.jobName }}</div>
          <div>所属区域:{{ dialogDetailList?.regionName }}</div>
          <div>拍摄机巢:{{ dialogDetailList?.nickName }}</div>
          <div>照片位置:{{ dialogDetailList?.longitude }},{{ dialogDetailList?.latitude }}</div>
          <div>拍摄机巢:{{ dialogDetailList?.nestName }}</div>
          <div>
            照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{
              _.round(dialogDetailList?.latitude, 3)
            }}
          </div>
          <div>任务时间:{{ dialogDetailList?.jobTime }}</div>
          <div>拍摄时间:{{ dialogDetailList?.createTime }}</div>
          <div>文件类型:{{ photoTypeMap[dialogDetailList?.photoType] }}</div>
@@ -176,7 +186,13 @@
      </div>
    </el-dialog>
    <!-- 地图弹框 -->
    <dataCenterMap v-model:show="dataCenterMapVisible"></dataCenterMap>
    <dataCenterMap
    ref="mapComponent"
      v-model:show="dataCenterMapVisible"
      :jobId="jobId"
      @lookDetail="lookDetail"
      :mapList="mapList"
    ></dataCenterMap>
  </div>
</template>
@@ -186,11 +202,13 @@
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import searchData from '@/views/dataCenter/components/searchData.vue';
import fy1 from '@/assets/images/dataCenter/1.jpeg';
import _ from 'lodash';
import {
  getaiImagesPageAPI,
  getAttachInfoAPI,
  deleteFileMultipleApi,
  downloadApi,
  updataTitleApi,
} from '@/api/dataCenter/dataCenter';
import { getShowImg, getSmallImg } from '@/utils/util';
import { onMounted } from 'vue';
@@ -209,7 +227,7 @@
  0: '照片',
  1: '视频',
  2: 'AI识别',
  3: '全景',
  5: '全景',
  4: '正射',
};
const photoTypeMap = {
@@ -232,25 +250,26 @@
// 获取列表数据
const getaiImagesPage = () => {
  getaiImagesPageAPI({
    current: jobListParams.current,
    size: jobListParams.size,
  const params = {
    orderByCreateTime: jobListParams.orderByCreateTime,
    ...jobListParams.searchParams,
  }).then(res => {
    loading.value = true;
    total.value = res.data.data.total;
    tableData.value = res.data.data.records.map(i => ({
      ...i,
      checked: false,
      url: i.link,
      smallUrl: getSmallImg(i.link),
      showUrl: getShowImg(i.link),
      file_name: i.name.split('/').pop(),
    }));
    console.log('res', tableData.value);
    loading.value = false;
  });
  };
  getaiImagesPageAPI(params, { current: jobListParams.current, size: jobListParams.size }).then(
    res => {
      loading.value = true;
      total.value = res.data.data.total;
      tableData.value = res.data.data.records.map(i => ({
        ...i,
        checked: false,
        url: i?.link,
        smallUrl: getSmallImg(i?.link),
        showUrl: getShowImg(i?.link),
        file_name: i.name.split('/').pop(),
      }));
      // console.log('res', tableData.value);
      loading.value = false;
    }
  );
};
// 查询
const searchClick = params => {
@@ -262,13 +281,10 @@
const handleSizeChange = val => {
  jobListParams.size = val;
  getaiImagesPage();
  console.log('jobListParams.size', jobListParams.size);
};
const handleCurrentChange = val => {
  jobListParams.current = val;
  getaiImagesPage();
  console.log('jobListParams.current', jobListParams.current);
};
// 多选
const selectedRows = ref([]);
@@ -328,6 +344,9 @@
const downloadFile = () => {
  fileDownload();
};
const detailDownLoad = val => {
  aLinkDownload(val.link, val.nickName);
};
// 全部下载
const aLLDownloadFile = () => {
  console.log('全部下载');
@@ -335,11 +354,12 @@
// 查看弹框
const dialogVisible = ref(false);
const dialogDetailList = ref(null);
const lookDetail = val => {
console.log('val',val);
const lookDetail = val => {
  getAttachInfoAPI(val.id).then(res => {
    dialogDetailList.value = res.data.data;
    dialogDetailList.value= {...res.data.data,  checkedinput: false,}
    console.log('val111', dialogDetailList.value);
  });
  dialogVisible.value = true;
};
@@ -363,10 +383,60 @@
    })
    .catch(() => {});
};
const fileNameedit = ref('')
// 编辑文件名
const editTitle = val => {
  // console.log('val', val);
  val.checkedinput = !val.checkedinput;
const editTitle = (val) => {
    val.checkedinput = true
    fileNameedit.value = val.nickName
}
const cancelEditSuffix = (item) => {
    item.nickName = fileNameedit.value
    item.checkedinput = false
   console.log('item',item);
}
const submitEditSuffix = (item) => {
    saveTitle(item)
}
// 通用空值检查函数
const validateNickname = (name, fieldName) => {
  if (!name || name.trim() === '') {
    ElMessage.warning(`${fieldName}不能为空`);
    return false;
  }
  if (name.length > 50) {
    ElMessage.warning(`${fieldName}不能超过50个字符`);
    return false;
  }
  return true;
};
// 保存文件名
const saveTitle = (item) => {
  const updateparams = {
    id:Number( item.id),
    nickName: item.nickName,
  };
  // 验证并提示
  if (!validateNickname(updateparams.nickName, '名称')) return;
  item.checkedinput = false;
  updataTitleApi(updateparams)
    .then(res => {
    console.log('updateparams',updateparams);
      if (res.status === 200) {
        ElMessage.success('修改成功');
      } else {
        ElMessage.error(res.data.message || '修改失败');
      }
    })
    .catch(error => {
      ElMessage.error('请求失败,请稍后重试');
      console.error('API error:', error);
    });
};
// 全景预览
const panoramaParamsShow = ref(false);
@@ -386,9 +456,18 @@
  VideoShow.value = true;
};
// 地图弹框
// 创建子组件引用
const mapComponent = ref(null);
const mapList = ref(null);
const dataCenterMapVisible = ref(false);
const positionDetail = () => {
const jobId = ref('');
const positionDetail = val => {
  jobId.value = val.wayLineJobId;
  mapList.value = val;
  dataCenterMapVisible.value = true;
if(mapComponent.value){
    mapComponent.value.labelBoxUpdate();
}
};
onMounted(() => {
  getaiImagesPage();