吉安感知网项目-前端
shuishen
2026-02-09 d6260866520970082417a795f7dbe9806e1eb431
feat:场景部署列表点击相关事件处理
5 files modified
198 ■■■■■ changed files
applications/drone-command/src/components/map-container/common-cesium-map.vue 15 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue 17 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/SceneDeployment.vue 4 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/index.vue 156 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/common-cesium-map.vue
@@ -203,7 +203,20 @@
const getViewer = () => viewer
const getPublicCesium = () => viewInstance
defineExpose({ getMap, getViewer, getPublicCesium, setAdminBoundaryVisible, zoomToAdminBoundary })
const flyToPoint = (options) => {
    const { longitude, latitude, height } = options.position
    viewer.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
        orientation: {
            heading: Cesium.Math.toRadians(0), // 方向
            pitch: Cesium.Math.toRadians(-90), // 仰角
            roll: Cesium.Math.toRadians(0) // 翻滚角
        }
    })
}
defineExpose({ getMap, getViewer, getPublicCesium, flyToPoint, setAdminBoundaryVisible, zoomToAdminBoundary })
</script>
<style scoped lang="scss">
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -1,4 +1,4 @@
<template>
<template>
    <div class="map-shell">
        <CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
            :flat-mode="false" :terrain="true" :layer-mode="4" :contour="false" :boundary="false"
@@ -848,6 +848,7 @@
                width: 40,
                height: 56,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
            })
            billboard.id = entry.entityId
            devicePickMap.set(entry.entityId, { data: entry.data, billboard })
@@ -1380,6 +1381,20 @@
    viewer = null
    publicCesium = null
})
const getMap = () => {
    return mapRef.value?.getMap()
}
// 暴露给父组件调用
const flyTo = (options) => {
    mapRef.value?.flyToPoint(options)
}
defineExpose({
    getMap,
    flyTo
})
</script>
<style lang="scss" scoped>
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-07 15:17:54
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-31 15:47:40
 * @LastEditTime : 2026-02-09 15:20:23
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -30,7 +30,7 @@
                    v-if="activeTab === 1"
                    @update:allDevices="handleUpdateAllDevices"
                />
                <SceneDeployment v-else />
                <SceneDeployment v-else @click="(item) => emit('scene-click', item)" />
            </div>
        </div>
@@ -53,7 +53,7 @@
    }
})
const emit = defineEmits(['update:collapsed', 'update:allDevices'])
const emit = defineEmits(['update:collapsed', 'update:allDevices', 'scene-click'])
const collapsedModel = computed({
    get: () => props.collapsed,
applications/drone-command/src/views/dataCockpit/components/SceneDeployment.vue
@@ -16,7 +16,7 @@
        <div class="content" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)"
            element-loading-text="加载中...">
            <EmptyState v-if="!sceneList.length && !loading" />
            <div class="scene-card" v-else v-for="(item, index) in sceneList" :key="index">
            <div class="scene-card" v-else v-for="(item, index) in sceneList" :key="index" @click="emit('click', item)">
                <div class="card-header">
                    <el-tooltip class="title-tooltip" :content="item.sceneName || '-'" placement="top" effect="dark"
                        :show-after="200">
@@ -136,6 +136,8 @@
    }
}
const emit = defineEmits(['click'])
const fetchList = async () => {
    // 只有超过200ms才显示loading,避免闪烁
    loadingTimer.value = setTimeout(() => {
applications/drone-command/src/views/dataCockpit/index.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-06 16:35:50
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-31 15:40:15
 * @LastEditTime : 2026-02-09 15:40:07
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -10,151 +10,153 @@
-->
<template>
  <div class="page-container">
    <DeviceMapContainer
      :all-devices="allDevices"
      :alarm-drones="realWarningData"
      :left-collapsed="leftCollapsed"
      container-id="data-cockpit-map"
      @drone-signal="handleAlarmAction('signal', $event)"
      @drone-counter="handleAlarmAction('counter', $event)"
      @drone-favorite="handleAlarmFavorite"
    />
    <LeftContainer
      class="left-container"
      v-model:activeId="leftActiveId"
      v-model:collapsed="leftCollapsed"
      :real-warning-data="realWarningData"
      :real-warning-loading="realWarningLoading"
    <DeviceMapContainer ref="deviceMapRef" :all-devices="allDevices" :alarm-drones="realWarningData"
      :left-collapsed="leftCollapsed" container-id="data-cockpit-map"
      @drone-signal="handleAlarmAction('signal', $event)" @drone-counter="handleAlarmAction('counter', $event)"
      @drone-favorite="handleAlarmFavorite" />
    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed"
      :real-warning-data="realWarningData" :real-warning-loading="realWarningLoading"
      @real-warning-signal="handleAlarmAction('signal', $event)"
      @real-warning-counter="handleAlarmAction('counter', $event)"
      @real-warning-favorite="handleAlarmFavorite"
    />
    <RightContainer class="right-container" v-model:collapsed="rightCollapsed"
      v-model:allDevices="allDevices" />
      @real-warning-counter="handleAlarmAction('counter', $event)" @real-warning-favorite="handleAlarmFavorite" />
    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:allDevices="allDevices"
      @scene-click="handleSceneClick" />
    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
    <LegendBar class="legend-container" />
  </div>
</template>
<script setup>
  import DeviceMapContainer from '@/components/map-container/device-map-container.vue';
  import LeftContainer from './components/LeftContainer.vue';
  import RightContainer from './components/RightContainer.vue';
  import CenterContainer from './components/CenterContainer.vue';
  import LegendBar from './components/LegendBar.vue';
import DeviceMapContainer from '@/components/map-container/device-map-container.vue'
import LeftContainer from './components/LeftContainer.vue'
import RightContainer from './components/RightContainer.vue'
import CenterContainer from './components/CenterContainer.vue'
import LegendBar from './components/LegendBar.vue'
  import {
    alarmLogApi,
    alarmFavoriteRemoveApi,
    alarmFavoriteSaveApi,
    interferenceAndExpulsionApi
  } from '@/api/dataCockpit';
  import { ElMessage } from 'element-plus';
  import { onMounted, ref } from 'vue';
} from '@/api/dataCockpit'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
  const leftActiveId = ref(1);
  const leftCollapsed = ref(false);
const leftActiveId = ref(1)
const leftCollapsed = ref(false)
  const rightCollapsed = ref(false);
  const allDevices = ref([]);
  const realWarningData = ref([]);
  const realWarningLoading = ref(false);
  const loadingTimer = ref(null);
const rightCollapsed = ref(false)
const allDevices = ref([])
const realWarningData = ref([])
const realWarningLoading = ref(false)
const loadingTimer = ref(null)
const deviceMapRef = ref(null)
  const onSelectWarning = (type) => {
    leftActiveId.value = type;
    leftCollapsed.value = false;
  };
  leftActiveId.value = type
  leftCollapsed.value = false
}
  const onSelectEquipment = () => {
    rightCollapsed.value = false;
  };
  rightCollapsed.value = false
}
const handleSceneClick = (item) => {
  if (item?.longitude && item?.latitude) {
    deviceMapRef.value?.flyTo({
      position: {
        longitude: Number(item.longitude),
        latitude: Number(item.latitude),
        height: 18000
      },
    })
  }
}
  const actionTextMap = {
    signal: '信号干扰',
    counter: '诱导驱离'
  };
}
  const getAlarmRecordId = (item) => item?.alarmRecordId ?? item?.id;
const getAlarmRecordId = (item) => item?.alarmRecordId ?? item?.id
  const getFavoriteId = (item) =>
    item?.favoriteId ??
    item?.alarmFavoriteId ??
    item?.fwAlarmFavoriteId ??
    item?.favoriteRecordId ??
    getAlarmRecordId(item);
  getAlarmRecordId(item)
  const isFavorited = (item) => {
    const value = item?.favorited ?? item?.isFavorite;
    return value === 1 || value === '1' || value === true;
  };
  const value = item?.favorited ?? item?.isFavorite
  return value === 1 || value === '1' || value === true
}
  const buildPayload = (type, item) => ({
    counterWay: type === 'signal' ? 1 : type === 'counter' ? 2 : '',
    alarmRecordId: getAlarmRecordId(item)
  });
})
  const handleAlarmAction = async (type, item) => {
    const actionText = actionTextMap[type] || '操作';
  const actionText = actionTextMap[type] || '操作'
    try {
      const res = await interferenceAndExpulsionApi(buildPayload(type, item));
    const res = await interferenceAndExpulsionApi(buildPayload(type, item))
      if (res?.data?.success) {
        ElMessage({ type: 'success', message: actionText + '成功' });
        await fetchRealWarning();
      ElMessage({ type: 'success', message: actionText + '成功' })
      await fetchRealWarning()
      } else {
        ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' });
      ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' })
      }
    } catch (error) {
      ElMessage({ type: 'error', message: actionText + '失败' });
    ElMessage({ type: 'error', message: actionText + '失败' })
    }
  };
}
  const handleAlarmFavorite = async (item) => {
    const alarmRecordId = getAlarmRecordId(item);
  const alarmRecordId = getAlarmRecordId(item)
    if (!alarmRecordId) {
      ElMessage({ type: 'warning', message: '缺少告警记录ID' });
      return;
    ElMessage({ type: 'warning', message: '缺少告警记录ID' })
    return
    }
    const favorite = isFavorited(item);
    const actionText = favorite ? '取消关注' : '关注';
  const favorite = isFavorited(item)
  const actionText = favorite ? '取消关注' : '关注'
    try {
      const res = favorite
        ? await alarmFavoriteRemoveApi(getFavoriteId(item))
        : await alarmFavoriteSaveApi({ alarmRecordId });
      : await alarmFavoriteSaveApi({ alarmRecordId })
      if (res?.data?.success) {
        ElMessage({ type: 'success', message: actionText + '成功' });
        await fetchRealWarning();
      ElMessage({ type: 'success', message: actionText + '成功' })
      await fetchRealWarning()
      } else {
        ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' });
      ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' })
      }
    } catch (error) {
      ElMessage({ type: 'error', message: actionText + '失败' });
    ElMessage({ type: 'error', message: actionText + '失败' })
    }
  };
}
  const fetchRealWarning = async () => {
    // 只有超过200ms才显示loading,避免闪烁
    loadingTimer.value = setTimeout(() => {
      realWarningLoading.value = true;
    }, 200);
    realWarningLoading.value = true
  }, 200)
    try {
      const params = { current: 1, size: 9999, alarmType: '1' };
      const res = await alarmLogApi(params);
      const records = res?.data?.data?.records ?? [];
    const params = { current: 1, size: 9999, alarmType: '1' }
    const res = await alarmLogApi(params)
    const records = res?.data?.data?.records ?? []
      realWarningData.value = records.map((record) => ({
        ...record,
        isFavorite:
          record?.isFavorite ??
          (record?.favorited === 1 || record?.favorited === '1')
      }));
    }))
    } finally {
      if (loadingTimer.value) clearTimeout(loadingTimer.value);
      realWarningLoading.value = false;
    if (loadingTimer.value) clearTimeout(loadingTimer.value)
    realWarningLoading.value = false
    }
  };
}
  onMounted(() => {
    fetchRealWarning();
  });
  fetchRealWarning()
})
</script>
<style scoped lang="scss">