吉安感知网项目-前端
chenyao
18 hours ago 4c390d209b45d516fbe2f7552d26048687c83972
applications/drone-command/src/views/dataCockpit/index.vue
@@ -2,91 +2,211 @@
 * @Author       : yuan
 * @Date         : 2026-01-06 16:35:50
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-09 16:34:39
 * @LastEditTime : 2026-02-09 15:40:07
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-06 16:35:50
-->
<template>
  <div class="page-container">
    <MapContainer :online-devices="onlineDevices" :left-collapsed="leftCollapsed" />
    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />
    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />
    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />
    <DeviceMapContainer ref="deviceMapRef" :all-devices="allDevices" :alarm-drones="realWarningData"
      :right-collapsed="rightCollapsed" 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"
      @scene-click="handleSceneClick" />
    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />
    <LegendBar class="legend-container" />
  </div>
</template>
<script setup>
  import MapContainer from './components/MapContainer.vue';
  import LeftContainer from './components/LeftContainer.vue';
  import RightContainer from './components/RightContainer.vue';
  import CenterContainer from './components/CenterContainer.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'
  const leftActiveId = ref(1);
  const leftCollapsed = ref(false);
const leftActiveId = ref(1)
const leftCollapsed = ref(false)
  const rightCollapsed = ref(false);
  const onlineDevices = ref([]);
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;
  };
const onSelectWarning = (type) => {
  leftActiveId.value = type
  leftCollapsed.value = false
}
  const onSelectEquipment = () => {
    rightCollapsed.value = false;
  };
const onSelectEquipment = () => {
  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 getFavoriteId = (item) =>
  item?.favoriteId ??
  item?.alarmFavoriteId ??
  item?.fwAlarmFavoriteId ??
  item?.favoriteRecordId ??
  getAlarmRecordId(item)
const isFavorited = (item) => {
  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] || '操作'
  try {
    const res = await interferenceAndExpulsionApi(buildPayload(type, item))
    if (res?.data?.success) {
      ElMessage({ type: 'success', message: actionText + '成功' })
      await fetchRealWarning()
    } else {
      ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' })
    }
  } catch (error) {
    ElMessage({ type: 'error', message: actionText + '失败' })
  }
}
const handleAlarmFavorite = async (item) => {
  const alarmRecordId = getAlarmRecordId(item)
  if (!alarmRecordId) {
    ElMessage({ type: 'warning', message: '缺少告警记录ID' })
    return
  }
  const favorite = isFavorited(item)
  const actionText = favorite ? '取消关注' : '关注'
  try {
    const res = favorite
      ? await alarmFavoriteRemoveApi(getFavoriteId(item))
      : await alarmFavoriteSaveApi({ alarmRecordId })
    if (res?.data?.success) {
      ElMessage({ type: 'success', message: actionText + '成功' })
      await fetchRealWarning()
    } else {
      ElMessage({ type: 'error', message: res?.data?.msg || actionText + '失败' })
    }
  } catch (error) {
    ElMessage({ type: 'error', message: actionText + '失败' })
  }
}
const fetchRealWarning = async () => {
  // 只有超过200ms才显示loading,避免闪烁
  loadingTimer.value = setTimeout(() => {
    realWarningLoading.value = true
  }, 200)
  try {
    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
  }
}
onMounted(() => {
  fetchRealWarning()
})
</script>
<style scoped lang="scss">
  .page-container {
    position: relative;
    width: 100%;
    height: 100%;
  }
.page-container {
  position: relative;
  width: 100%;
  height: 100%;
}
  .left-container,
  .right-container {
.left-container,
.right-container {
  position: absolute;
  bottom: 20px;
  width: 300px;
  z-index: 9;
  backdrop-filter: blur(2px);
  border-radius: 10px 10px 10px 10px;
  ::v-deep(.wrapper) {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 96px;
    top: 0;
    bottom: 0;
    width: 401px;
    z-index: 9;
    backdrop-filter: blur(2px);
    ::v-deep(.wrapper) {
      display: flex;
      flex-direction: column;
      position: absolute;
      top: 17px;
      bottom: 20px;
      padding: 20px;
      width: 300px;
      background: rgba(0,0,0,0.3);
      backdrop-filter: blur(16px);
      border-radius: 10px 10px 10px 10px;
    }
    padding: 20px;
    width: 300px;
    background: rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(16px);
    border-radius: 10px 10px 10px 10px;
    box-sizing: border-box;
  }
}
  .left-container {
    top: 96px;
    left: 0;
    background: linear-gradient( 270deg, rgba(17,23,34,0) 0%, rgba(17,23,34,0.56) 50%, rgba(17,23,34,0.96) 100%);
.left-container {
  top: 113px;
  left: 17px;
  background: linear-gradient(270deg, rgba(17, 23, 34, 0) 0%, rgba(17, 23, 34, 0.56) 50%, rgba(17, 23, 34, 0.96) 100%);
}
    ::v-deep(.wrapper) {
      left: 17px;
    }
  }
.right-container {
  top: 75px;
  right: 17px;
  background: linear-gradient(270deg, rgba(17, 23, 34, 0.96) 0.16%, rgba(17, 23, 34, 0.56) 57.57%, rgba(4, 30, 37, 0) 100%);
}
  .right-container {
    top: 54px;
    right: 0;
    background: linear-gradient( 270deg, rgba(17,23,34,0.96) 0.16%, rgba(17,23,34,0.56) 57.57%, rgba(4,30,37,0) 100%);
    ::v-deep(.wrapper) {
      top: 21px;
      right: 17px;
    }
  }
.legend-container {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 9;
}
</style>