| | |
| | | * @Author : yuan |
| | | * @Date : 2026-01-06 16:35:50 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2026-01-09 16:34:39 |
| | | * @LastEditTime : 2026-01-31 15:40:15 |
| | | * @FilePath : \applications\drone-command\src\views\dataCockpit\index.vue |
| | | * @Description : |
| | | * Copyright 2026 OBKoro1, All Rights Reserved. |
| | |
| | | --> |
| | | <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" /> |
| | | <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" |
| | | @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" /> |
| | | <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" /> |
| | | <LegendBar class="legend-container" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import MapContainer from './components/MapContainer.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 rightCollapsed = ref(false); |
| | | const onlineDevices = ref([]); |
| | | const allDevices = ref([]); |
| | | const realWarningData = ref([]); |
| | | const realWarningLoading = ref(true); |
| | | const minLoadingMs = 400; |
| | | |
| | | const onSelectWarning = (type) => { |
| | | leftActiveId.value = type; |
| | |
| | | const onSelectEquipment = () => { |
| | | rightCollapsed.value = false; |
| | | }; |
| | | |
| | | 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 () => { |
| | | const startAt = Date.now(); |
| | | realWarningLoading.value = true; |
| | | 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 { |
| | | const elapsed = Date.now() - startAt; |
| | | if (elapsed < minLoadingMs) { |
| | | await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed)); |
| | | } |
| | | realWarningLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | fetchRealWarning(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | .left-container, |
| | | .right-container { |
| | | position: absolute; |
| | | top: 96px; |
| | | bottom: 0; |
| | | width: 401px; |
| | | 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: 17px; |
| | | bottom: 20px; |
| | | top: 0; |
| | | bottom: 0; |
| | | 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; |
| | | 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: 54px; |
| | | right: 0; |
| | | 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%); |
| | | } |
| | | |
| | | ::v-deep(.wrapper) { |
| | | top: 21px; |
| | | right: 17px; |
| | | } |
| | | .legend-container { |
| | | position: absolute; |
| | | left: 50%; |
| | | bottom: 22px; |
| | | transform: translateX(-50%); |
| | | z-index: 9; |
| | | } |
| | | </style> |