|
|
|
@ -9,9 +9,11 @@ |
|
|
|
@drop="handleMapDrop" |
|
|
|
> |
|
|
|
<!-- cesiummap组件 --> |
|
|
|
<cesiumMap ref="cesiumMap" :drawDomClick="drawDom || airspaceDrawDom" |
|
|
|
:tool-mode="drawDom ? 'ranging' : (airspaceDrawDom ? 'airspace' : 'airspace')" |
|
|
|
<cesiumMap ref="cesiumMap" :drawDomClick="drawDom || airspaceDrawDom || (showWhiteboardPanel && whiteboardAirspaceDraw)" |
|
|
|
:tool-mode="drawDom ? 'ranging' : (airspaceDrawDom || (showWhiteboardPanel && whiteboardAirspaceDraw) ? 'airspace' : 'airspace')" |
|
|
|
:scaleConfig="scaleConfig" |
|
|
|
:whiteboard-mode="showWhiteboardPanel" |
|
|
|
:whiteboard-entities="whiteboardDisplayEntities" |
|
|
|
:coordinateFormat="coordinateFormat" |
|
|
|
:bottomPanelVisible="bottomPanelVisible" |
|
|
|
:map-drag-enabled="mapDragEnabled" |
|
|
|
@ -39,7 +41,11 @@ |
|
|
|
@platform-icon-removed="onPlatformIconRemoved" |
|
|
|
@viewer-ready="onViewerReady" |
|
|
|
@drawing-entities-changed="onDrawingEntitiesChanged" |
|
|
|
@platform-style-saved="onPlatformStyleSaved" /> |
|
|
|
@platform-style-saved="onPlatformStyleSaved" |
|
|
|
@whiteboard-draw-complete="handleWhiteboardDrawComplete" |
|
|
|
@whiteboard-platform-updated="handleWhiteboardPlatformUpdated" |
|
|
|
@whiteboard-entity-deleted="handleWhiteboardEntityDeleted" |
|
|
|
@whiteboard-drawing-updated="handleWhiteboardDrawingUpdated" /> |
|
|
|
<div v-show="!screenshotMode" class="map-overlay-text"> |
|
|
|
<!-- <i class="el-icon-location-outline text-3xl mb-2 block"></i> --> |
|
|
|
<!-- <p>二维GIS地图区域</p> |
|
|
|
@ -94,6 +100,9 @@ |
|
|
|
:online-count="onlineCount" |
|
|
|
:combat-time="combatTime" |
|
|
|
:k-time-display="kTimeDisplay" |
|
|
|
:child-room-k-times="childRoomKTimes" |
|
|
|
:selected-child-k-time-index="selectedChildKTimeIndex" |
|
|
|
@select-child-k-time="selectedChildKTimeIndex = $event" |
|
|
|
:astro-time="astroTime" |
|
|
|
:room-detail="roomDetail" |
|
|
|
:can-set-k-time="canSetKTime" |
|
|
|
@ -110,6 +119,7 @@ |
|
|
|
@import-ato="importATO" |
|
|
|
@import-layer="importLayer" |
|
|
|
@import-route="importRoute" |
|
|
|
@export-routes="openExportRoutesDialog" |
|
|
|
@export-plan="exportPlan" |
|
|
|
@route-edit="routeEdit" |
|
|
|
@military-marking="militaryMarking" |
|
|
|
@ -210,8 +220,8 @@ |
|
|
|
@delete-platform="handleDeletePlatform" |
|
|
|
@open-import-dialog="showImportDialog = true" |
|
|
|
/> |
|
|
|
<!-- 左下角工具面板 --> |
|
|
|
<bottom-left-panel v-show="!screenshotMode" @bottom-panel-visible="handleBottomPanelVisible" @six-steps-overlay-visible="sixStepsOverlayVisible = $event" :room-id="currentRoomId" /> |
|
|
|
<!-- 左下角工具面板(白板模式下隐藏,避免遮挡白板) --> |
|
|
|
<bottom-left-panel v-show="!screenshotMode && !showWhiteboardPanel" @bottom-panel-visible="handleBottomPanelVisible" @six-steps-overlay-visible="sixStepsOverlayVisible = $event" @open-whiteboard="toggleWhiteboardMode" :room-id="currentRoomId" :is-parent-room="!!(roomDetail && roomDetail.parentId == null)" /> |
|
|
|
<!-- 底部时间轴(最初版本的样式)- 蓝色主题 --> |
|
|
|
<div |
|
|
|
v-show="!screenshotMode" |
|
|
|
@ -369,6 +379,23 @@ |
|
|
|
@confirm="handleImportConfirm" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 导出航线弹窗 --> |
|
|
|
<export-routes-dialog |
|
|
|
v-model="showExportRoutesDialog" |
|
|
|
:routes="routes" |
|
|
|
:plans="plans" |
|
|
|
@export="handleExportRoutes" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 导入航线弹窗 --> |
|
|
|
<import-routes-dialog |
|
|
|
ref="importRoutesDialog" |
|
|
|
v-model="showImportRoutesDialog" |
|
|
|
:plans="plans" |
|
|
|
:all-platforms="allPlatformsForImport" |
|
|
|
@import="handleImportRoutes" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 4T悬浮窗(THREAT/TASK/TARGET/TACTIC)- 仅点击4T图标时打开 --> |
|
|
|
<four-t-panel |
|
|
|
v-if="show4TPanel && !screenshotMode" |
|
|
|
@ -376,6 +403,28 @@ |
|
|
|
:room-id="currentRoomId" |
|
|
|
/> |
|
|
|
|
|
|
|
<!-- 白板面板(底部) --> |
|
|
|
<whiteboard-panel |
|
|
|
v-show="showWhiteboardPanel && !screenshotMode" |
|
|
|
:visible="showWhiteboardPanel" |
|
|
|
:room-id="currentRoomId" |
|
|
|
:whiteboards="whiteboards" |
|
|
|
:current-whiteboard="currentWhiteboard" |
|
|
|
:air-platforms="airPlatforms" |
|
|
|
:sea-platforms="seaPlatforms" |
|
|
|
:ground-platforms="groundPlatforms" |
|
|
|
@select-whiteboard="handleWhiteboardSelect" |
|
|
|
@create-whiteboard="handleWhiteboardCreate" |
|
|
|
@rename-whiteboard="handleWhiteboardRename" |
|
|
|
@delete-whiteboard="handleWhiteboardDelete" |
|
|
|
@exit-whiteboard="handleWhiteboardExit" |
|
|
|
@select-time-block="handleWhiteboardTimeBlockSelect" |
|
|
|
@add-time-block="handleWhiteboardAddTimeBlock" |
|
|
|
@rename-time-block="handleWhiteboardRenameTimeBlock" |
|
|
|
@delete-time-block="handleWhiteboardDeleteTimeBlock" |
|
|
|
@draw-mode-change="handleWhiteboardDrawModeChange" |
|
|
|
/> |
|
|
|
|
|
|
|
<el-dialog |
|
|
|
title="新建方案" |
|
|
|
:visible.sync="showPlanNameDialog" |
|
|
|
@ -435,19 +484,25 @@ import RightPanel from './RightPanel' |
|
|
|
import BottomLeftPanel from './BottomLeftPanel' |
|
|
|
import TopHeader from './TopHeader' |
|
|
|
import FourTPanel from './FourTPanel' |
|
|
|
import WhiteboardPanel from './WhiteboardPanel' |
|
|
|
import { createRoomWebSocket } from '@/utils/websocket'; |
|
|
|
import { listScenario, addScenario, delScenario } from "@/api/system/scenario"; |
|
|
|
import { listRoutes, getRoutes, addRoutes, updateRoutes, delRoutes, getPlatformStyle, getMissileParams, updateMissilePositions } from "@/api/system/routes"; |
|
|
|
import { updateWaypoints, addWaypoints, delWaypoints } from "@/api/system/waypoints"; |
|
|
|
import { listLib,addLib,delLib} from "@/api/system/lib"; |
|
|
|
import { getRooms, updateRooms } from "@/api/system/rooms"; |
|
|
|
import { getRooms, updateRooms, listRooms } from "@/api/system/rooms"; |
|
|
|
import { getMenuConfig, saveMenuConfig } from "@/api/system/userMenuConfig"; |
|
|
|
import { listByRoomId as listRoomPlatformIcons, addRoomPlatformIcon, updateRoomPlatformIcon, delRoomPlatformIcon } from "@/api/system/roomPlatformIcon"; |
|
|
|
import { listWhiteboards, getWhiteboard, createWhiteboard, updateWhiteboard, deleteWhiteboard } from "@/api/system/whiteboard"; |
|
|
|
import PlatformImportDialog from "@/views/dialogs/PlatformImportDialog.vue"; |
|
|
|
import ExportRoutesDialog from "@/views/dialogs/ExportRoutesDialog.vue"; |
|
|
|
import ImportRoutesDialog from "@/views/dialogs/ImportRoutesDialog.vue"; |
|
|
|
export default { |
|
|
|
name: 'MissionPlanningView', |
|
|
|
components: { |
|
|
|
PlatformImportDialog, |
|
|
|
ExportRoutesDialog, |
|
|
|
ImportRoutesDialog, |
|
|
|
cesiumMap, |
|
|
|
OnlineMembersDialog, |
|
|
|
PlatformEditDialog, |
|
|
|
@ -461,7 +516,8 @@ export default { |
|
|
|
RightPanel, |
|
|
|
BottomLeftPanel, |
|
|
|
TopHeader, |
|
|
|
FourTPanel |
|
|
|
FourTPanel, |
|
|
|
WhiteboardPanel |
|
|
|
}, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
@ -504,6 +560,9 @@ export default { |
|
|
|
platformIconSaveTimer: null, |
|
|
|
//导入平台弹窗 |
|
|
|
showImportDialog: false, |
|
|
|
// 导出/导入航线弹窗 |
|
|
|
showExportRoutesDialog: false, |
|
|
|
showImportRoutesDialog: false, |
|
|
|
// 底部面板可见状态(时间轴/六步法) |
|
|
|
bottomPanelVisible: false, |
|
|
|
// 六步法弹窗可见时隐藏地图右下角坐标和比例尺 |
|
|
|
@ -529,6 +588,10 @@ export default { |
|
|
|
combatTime: 'K+00:00:00', // 进入房间时固定作战时间,不随真实时间走 |
|
|
|
astroTime: '', |
|
|
|
roomDetail: null, |
|
|
|
/** 大房间时子房间的 K 时列表 [{ name, kAnchorTime }] */ |
|
|
|
childRoomKTimes: [], |
|
|
|
/** 大房间时当前选中的子房间 K 时索引,用于切换展示 */ |
|
|
|
selectedChildKTimeIndex: 0, |
|
|
|
showKTimeSetDialog: false, |
|
|
|
kTimeForm: { dateTime: null }, |
|
|
|
saveRoomDrawingsTimer: null, |
|
|
|
@ -609,6 +672,13 @@ export default { |
|
|
|
// 4T悬浮窗显示控制(仅点击4T图标时打开/关闭) |
|
|
|
show4TPanel: false, |
|
|
|
|
|
|
|
// 白板模式 |
|
|
|
showWhiteboardPanel: false, |
|
|
|
whiteboards: [], |
|
|
|
currentWhiteboard: null, |
|
|
|
currentWhiteboardTimeBlock: null, |
|
|
|
whiteboardAirspaceDraw: false, |
|
|
|
|
|
|
|
// 显示/隐藏控制 |
|
|
|
showAirport: true, |
|
|
|
showLandmark: true, |
|
|
|
@ -673,7 +743,7 @@ export default { |
|
|
|
if (newRoomId != null && String(newRoomId) !== String(this.currentRoomId)) { |
|
|
|
this.currentRoomId = newRoomId; |
|
|
|
this.connectRoomWebSocket(); |
|
|
|
if (newRoomId) this.getRoomDetail(); |
|
|
|
if (newRoomId) this.getRoomDetail(() => this.getList()); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
@ -725,6 +795,7 @@ export default { |
|
|
|
); |
|
|
|
}, |
|
|
|
canSetKTime() { |
|
|
|
if (this.roomDetail && this.roomDetail.parentId == null) return false; |
|
|
|
return this.isRoomOwner || this.isAdmin; |
|
|
|
}, |
|
|
|
/** 格式化的 K 时(基准时刻),供右上角显示 */ |
|
|
|
@ -745,6 +816,31 @@ export default { |
|
|
|
if (this.addHoldContext.mode === 'drawing') return '在最后两航点之间插入盘旋,保存航线时生效。'; |
|
|
|
return `在 ${this.addHoldContext.fromName} 与 ${this.addHoldContext.toName} 之间添加盘旋,到计划时间后沿切线飞往下一航点(原「下一格」航点将被移除)。`; |
|
|
|
}, |
|
|
|
/** 白板模式下当前时间块应显示的实体(继承逻辑:当前时刻 = 上一时刻 + 本时刻差异) */ |
|
|
|
whiteboardDisplayEntities() { |
|
|
|
if (!this.showWhiteboardPanel || !this.currentWhiteboard || !this.currentWhiteboardTimeBlock) return [] |
|
|
|
const wb = this.currentWhiteboard |
|
|
|
const contentByTime = wb.contentByTime || {} |
|
|
|
const timeBlocks = (wb.timeBlocks || []).slice().sort((a, b) => this.compareWhiteboardTimeBlock(a, b)) |
|
|
|
const idx = timeBlocks.indexOf(this.currentWhiteboardTimeBlock) |
|
|
|
if (idx < 0) return [] |
|
|
|
const merged = {} |
|
|
|
for (let i = 0; i <= idx; i++) { |
|
|
|
const tb = timeBlocks[i] |
|
|
|
const ents = (contentByTime[tb] && contentByTime[tb].entities) || [] |
|
|
|
ents.forEach(e => { |
|
|
|
const id = e.id || (e.data && e.data.id) |
|
|
|
if (id) merged[id] = e |
|
|
|
else merged['_noid_' + Math.random()] = e |
|
|
|
}) |
|
|
|
} |
|
|
|
return Object.values(merged) |
|
|
|
}, |
|
|
|
|
|
|
|
/** 所有平台(用于导入航线时选择默认平台) */ |
|
|
|
allPlatformsForImport() { |
|
|
|
return [...(this.airPlatforms || []), ...(this.seaPlatforms || []), ...(this.groundPlatforms || [])]; |
|
|
|
}, |
|
|
|
/** 被其他成员编辑锁定的航线 ID 列表,供地图禁止拖拽等 */ |
|
|
|
routeLockedByOtherRouteIds() { |
|
|
|
const myId = this.currentUserId; |
|
|
|
@ -785,9 +881,12 @@ export default { |
|
|
|
created() { |
|
|
|
this.currentRoomId = this.$route.query.roomId; |
|
|
|
console.log("从路由接收到的真实房间 ID:", this.currentRoomId); |
|
|
|
this.getList(); |
|
|
|
this.getPlatformList(); |
|
|
|
if (this.currentRoomId) this.getRoomDetail(); |
|
|
|
if (this.currentRoomId) { |
|
|
|
this.getRoomDetail(() => this.getList()); |
|
|
|
} else { |
|
|
|
this.getList(); |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
handleBottomPanelVisible(visible) { |
|
|
|
@ -1504,9 +1603,8 @@ export default { |
|
|
|
const newer = existing.filter(m => (m.timestamp || 0) > maxTs); |
|
|
|
this.$set(this.privateChatMessages, targetUserId, [...history, ...newer]); |
|
|
|
}, |
|
|
|
onSyncRouteVisibility: (routeId, visible, senderSessionId) => { |
|
|
|
if (this.isMySyncSession(senderSessionId)) return; |
|
|
|
this.applySyncRouteVisibility(routeId, visible); |
|
|
|
onSyncRouteVisibility: () => { |
|
|
|
// 小房间内每人展示各自内容,不应用他人的航线显隐变更 |
|
|
|
}, |
|
|
|
onSyncWaypoints: (routeId, senderSessionId) => { |
|
|
|
if (this.isMySyncSession(senderSessionId)) return; |
|
|
|
@ -1524,8 +1622,8 @@ export default { |
|
|
|
if (this.isMySyncSession(senderSessionId)) return; |
|
|
|
this.applySyncPlatformStyles(); |
|
|
|
}, |
|
|
|
onRoomState: (visibleRouteIds) => { |
|
|
|
this.applyRoomStateVisibleRoutes(visibleRouteIds); |
|
|
|
onRoomState: () => { |
|
|
|
// 小房间内每人展示各自内容,新加入时不从他人同步可见航线 |
|
|
|
}, |
|
|
|
onConnected: () => {}, |
|
|
|
onDisconnected: () => { |
|
|
|
@ -1678,9 +1776,13 @@ export default { |
|
|
|
if (res.code === 200 && res.data) this.$refs.cesiumMap.loadRoomPlatformIcons(rId, res.data); |
|
|
|
}).catch(() => {}); |
|
|
|
}, |
|
|
|
/** 收到其他设备的空域图形变更同步:拉取最新房间数据并重绘 */ |
|
|
|
/** 收到其他设备的空域图形变更同步:拉取最新房间数据并重绘;大房间时重新合并子房间标绘 */ |
|
|
|
applySyncRoomDrawings() { |
|
|
|
if (!this.currentRoomId || !this.$refs.cesiumMap || typeof this.$refs.cesiumMap.loadFrontendDrawings !== 'function') return; |
|
|
|
if (this.roomDetail && this.roomDetail.parentId == null) { |
|
|
|
this.loadRoomDrawings(); |
|
|
|
return; |
|
|
|
} |
|
|
|
getRooms(this.currentRoomId).then(res => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.roomDetail = this.roomDetail ? { ...this.roomDetail, frontendDrawings: res.data.frontendDrawings } : { ...res.data }; |
|
|
|
@ -1996,19 +2098,33 @@ export default { |
|
|
|
const { skipRoomPlatformIcons = false } = opts; |
|
|
|
try { |
|
|
|
const roomId = this.$route.query.roomId || this.currentRoomId; |
|
|
|
const scenarioRes = await listScenario({ roomId: roomId }); |
|
|
|
const isParentRoom = this.roomDetail && this.roomDetail.parentId == null; |
|
|
|
const scenarioParams = isParentRoom |
|
|
|
? { parentRoomId: roomId, pageNum: 1, pageSize: 9999 } |
|
|
|
: { roomId: roomId, pageNum: 1, pageSize: 9999 }; |
|
|
|
const scenarioRes = await listScenario(scenarioParams); |
|
|
|
if (scenarioRes.code === 200) { |
|
|
|
this.plans = scenarioRes.rows.map(s => ({ |
|
|
|
id: s.id, |
|
|
|
name: s.name, |
|
|
|
roomId: s.roomId, |
|
|
|
frontendDrawings: s.frontendDrawings || null, |
|
|
|
routes: [] |
|
|
|
})); |
|
|
|
} |
|
|
|
// 获取所有航线 |
|
|
|
const routeRes = await listRoutes({ pageNum: 1, pageSize: 9999 }); |
|
|
|
// 获取航线:仅请求当前房间方案下的航线(大房间、小房间均按 planIds 限定) |
|
|
|
const planIds = this.plans.map(p => p.id); |
|
|
|
const routeParams = planIds.length > 0 |
|
|
|
? { scenarioIdsStr: planIds.join(','), pageNum: 1, pageSize: 9999 } |
|
|
|
: { pageNum: 1, pageSize: 9999 }; |
|
|
|
const routeRes = await listRoutes(routeParams); |
|
|
|
if (routeRes.code === 200) { |
|
|
|
const allRoutes = routeRes.rows.map(item => ({ |
|
|
|
let routeRows = routeRes.rows || []; |
|
|
|
// 大房间和小房间均只保留当前房间方案下的航线(小房间时 API 可能返回全部,需按 planIds 过滤) |
|
|
|
if (planIds.length > 0) { |
|
|
|
routeRows = routeRows.filter(r => planIds.includes(r.scenarioId)); |
|
|
|
} |
|
|
|
const allRoutes = routeRows.map(item => ({ |
|
|
|
id: item.id, |
|
|
|
name: item.callSign, |
|
|
|
platformId: item.platformId, |
|
|
|
@ -2025,15 +2141,17 @@ export default { |
|
|
|
}); |
|
|
|
|
|
|
|
this.routes = allRoutes; |
|
|
|
// 先预取所有展示中航线的平台样式,再渲染,避免平台图标先黑后变色 |
|
|
|
// 先预取所有展示中航线的平台样式,再渲染,避免平台图标先黑后变色(大房间时用方案所属子房间的 roomId) |
|
|
|
if (this.activeRouteIds.length > 0 && this.$refs.cesiumMap) { |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
await Promise.all(this.activeRouteIds.map(async (id) => { |
|
|
|
const route = this.routes.find(r => r.id === id); |
|
|
|
if (!route || !route.waypoints || route.waypoints.length === 0) return; |
|
|
|
if (roomId && route.platformId) { |
|
|
|
const plan = this.plans.find(p => p.id === route.scenarioId); |
|
|
|
const styleRoomId = (isParentRoom && plan && plan.roomId) ? plan.roomId : roomId; |
|
|
|
if (styleRoomId && route.platformId) { |
|
|
|
try { |
|
|
|
const res = await getPlatformStyle({ roomId, routeId: id, platformId: route.platformId }); |
|
|
|
const res = await getPlatformStyle({ roomId: styleRoomId, routeId: id, platformId: route.platformId }); |
|
|
|
if (res.data) this.$refs.cesiumMap.setPlatformStyle(id, res.data); |
|
|
|
} catch (_) {} |
|
|
|
} |
|
|
|
@ -2053,13 +2171,24 @@ export default { |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
// 加载该房间下保存的地图平台图标(删除航线等操作时跳过,避免清空再重画导致平台图标闪一下) |
|
|
|
// 加载该房间下保存的地图平台图标(大房间时加载所有子房间的图标) |
|
|
|
if (!skipRoomPlatformIcons) { |
|
|
|
const rId = roomId || this.currentRoomId; |
|
|
|
if (rId && this.$refs.cesiumMap && typeof this.$refs.cesiumMap.loadRoomPlatformIcons === 'function') { |
|
|
|
listRoomPlatformIcons(rId).then(res => { |
|
|
|
if (res.code === 200 && res.data) this.$refs.cesiumMap.loadRoomPlatformIcons(rId, res.data); |
|
|
|
}).catch(() => {}); |
|
|
|
if (isParentRoom) { |
|
|
|
listRooms({ pageNum: 1, pageSize: 999, parentId: rId }).then(roomsRes => { |
|
|
|
const childRooms = roomsRes.rows || roomsRes || []; |
|
|
|
childRooms.forEach(child => { |
|
|
|
listRoomPlatformIcons(child.id).then(res => { |
|
|
|
if (res.code === 200 && res.data) this.$refs.cesiumMap.loadRoomPlatformIcons(child.id, res.data); |
|
|
|
}).catch(() => {}); |
|
|
|
}); |
|
|
|
}).catch(() => {}); |
|
|
|
} else { |
|
|
|
listRoomPlatformIcons(rId).then(res => { |
|
|
|
if (res.code === 200 && res.data) this.$refs.cesiumMap.loadRoomPlatformIcons(rId, res.data); |
|
|
|
}).catch(() => {}); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
this.$nextTick(() => this.applyRoomStatePending()); |
|
|
|
@ -2427,22 +2556,68 @@ export default { |
|
|
|
const min = parseInt(m[3], 10); |
|
|
|
return sign * (h * 60 + min); |
|
|
|
}, |
|
|
|
getRoomDetail() { |
|
|
|
if (!this.currentRoomId) return; |
|
|
|
getRoomDetail(callback) { |
|
|
|
if (!this.currentRoomId) { callback && callback(); return; } |
|
|
|
getRooms(this.currentRoomId).then(res => { |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.roomDetail = res.data; |
|
|
|
this.$nextTick(() => this.loadRoomDrawings()); |
|
|
|
if (res.data.parentId == null) { |
|
|
|
listRooms({ pageNum: 1, pageSize: 999, parentId: this.currentRoomId }).then(roomsRes => { |
|
|
|
const rows = roomsRes.rows || roomsRes || []; |
|
|
|
this.childRoomKTimes = rows |
|
|
|
.filter(r => r.kAnchorTime) |
|
|
|
.map(r => ({ name: r.name || `房间${r.id}`, kAnchorTime: r.kAnchorTime })); |
|
|
|
this.selectedChildKTimeIndex = 0; |
|
|
|
}).catch(() => { this.childRoomKTimes = []; }); |
|
|
|
} else { |
|
|
|
this.childRoomKTimes = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}).catch(() => {}); |
|
|
|
callback && callback(); |
|
|
|
}).catch(() => { callback && callback(); }); |
|
|
|
}, |
|
|
|
/** 加载当前房间的空域/威力区图形(与房间 ID 绑定,进入该房间即显示) */ |
|
|
|
loadRoomDrawings() { |
|
|
|
/** 加载当前房间的空域/威力区图形(与房间 ID 绑定,进入该房间即显示);大房间时合并所有子房间的 frontend_drawings */ |
|
|
|
async loadRoomDrawings() { |
|
|
|
if (!this.roomDetail || !this.$refs.cesiumMap || typeof this.$refs.cesiumMap.loadFrontendDrawings !== 'function') return; |
|
|
|
if (this.roomDetail.frontendDrawings) { |
|
|
|
this.$refs.cesiumMap.loadFrontendDrawings(this.roomDetail.frontendDrawings); |
|
|
|
const isParentRoom = this.roomDetail.parentId == null; |
|
|
|
if (isParentRoom) { |
|
|
|
const roomsRes = await listRooms({ pageNum: 1, pageSize: 999, parentId: this.currentRoomId }).catch(() => ({ rows: [] })); |
|
|
|
const childRooms = roomsRes.rows || roomsRes || []; |
|
|
|
const allEntities = []; |
|
|
|
for (const child of childRooms) { |
|
|
|
const fd = child.frontendDrawings; |
|
|
|
if (!fd) continue; |
|
|
|
let payload = typeof fd === 'string' ? (() => { try { return JSON.parse(fd) } catch (_) { return null } })() : fd; |
|
|
|
if (!payload || !Array.isArray(payload.entities) || payload.entities.length === 0) continue; |
|
|
|
const prefix = `room_${child.id}_`; |
|
|
|
payload.entities.forEach(e => { |
|
|
|
const cloned = JSON.parse(JSON.stringify(e)); |
|
|
|
if (cloned.id) cloned.id = prefix + cloned.id; |
|
|
|
allEntities.push(cloned); |
|
|
|
}); |
|
|
|
} |
|
|
|
if (this.roomDetail.frontendDrawings) { |
|
|
|
let parentPayload = typeof this.roomDetail.frontendDrawings === 'string' |
|
|
|
? (() => { try { return JSON.parse(this.roomDetail.frontendDrawings) } catch (_) { return null } })() |
|
|
|
: this.roomDetail.frontendDrawings; |
|
|
|
if (parentPayload && Array.isArray(parentPayload.entities)) { |
|
|
|
parentPayload.entities.forEach(e => { |
|
|
|
if (e.id && !/^room_\d+_/.test(String(e.id))) allEntities.push(JSON.parse(JSON.stringify(e))); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
if (allEntities.length > 0) { |
|
|
|
this.$refs.cesiumMap.loadFrontendDrawings({ entities: allEntities }); |
|
|
|
} else { |
|
|
|
this.$refs.cesiumMap.clearDrawingEntities(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.$refs.cesiumMap.clearDrawingEntities(); |
|
|
|
if (this.roomDetail.frontendDrawings) { |
|
|
|
this.$refs.cesiumMap.loadFrontendDrawings(this.roomDetail.frontendDrawings); |
|
|
|
} else { |
|
|
|
this.$refs.cesiumMap.clearDrawingEntities(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 地图 viewer 就绪时加载当前房间图形(可能 getRoomDetail 尚未返回,此处再试一次) */ |
|
|
|
@ -2575,8 +2750,90 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
importRoute() { |
|
|
|
this.$message.success('导入航线'); |
|
|
|
// 这里可以添加导入航线的逻辑 |
|
|
|
this.showImportRoutesDialog = true; |
|
|
|
}, |
|
|
|
openExportRoutesDialog() { |
|
|
|
this.showExportRoutesDialog = true; |
|
|
|
}, |
|
|
|
/** 导出航线:获取完整数据并下载 JSON */ |
|
|
|
async handleExportRoutes(selectedIds) { |
|
|
|
if (!selectedIds || selectedIds.length === 0) return; |
|
|
|
try { |
|
|
|
const routeDataList = []; |
|
|
|
for (const id of selectedIds) { |
|
|
|
const res = await getRoutes(id); |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
const d = res.data; |
|
|
|
const waypoints = (d.waypoints || []).map(wp => { |
|
|
|
const { id: _id, routeId: _rid, ...rest } = wp; |
|
|
|
return rest; |
|
|
|
}); |
|
|
|
routeDataList.push({ |
|
|
|
route: { |
|
|
|
callSign: d.callSign, |
|
|
|
platformId: d.platformId, |
|
|
|
platform: d.platform, |
|
|
|
attributes: d.attributes |
|
|
|
}, |
|
|
|
waypoints |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
const exportData = { |
|
|
|
version: 1, |
|
|
|
exportTime: new Date().toISOString(), |
|
|
|
routes: routeDataList |
|
|
|
}; |
|
|
|
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' }); |
|
|
|
const url = URL.createObjectURL(blob); |
|
|
|
const a = document.createElement('a'); |
|
|
|
a.href = url; |
|
|
|
a.download = `航线导出_${new Date().toISOString().slice(0, 10)}.json`; |
|
|
|
a.click(); |
|
|
|
URL.revokeObjectURL(url); |
|
|
|
this.showExportRoutesDialog = false; |
|
|
|
this.$message.success(`已导出 ${selectedIds.length} 条航线`); |
|
|
|
} catch (err) { |
|
|
|
console.error('导出航线失败:', err); |
|
|
|
this.$message.error('导出失败,请重试'); |
|
|
|
} |
|
|
|
}, |
|
|
|
/** 导入航线:从 JSON 创建新航线 */ |
|
|
|
async handleImportRoutes({ routeItems, targetScenarioId, targetPlatformId }) { |
|
|
|
if (!routeItems || routeItems.length === 0 || !targetScenarioId) return; |
|
|
|
const importDialog = this.$refs.importRoutesDialog; |
|
|
|
if (importDialog && importDialog.setImporting) importDialog.setImporting(true); |
|
|
|
try { |
|
|
|
let successCount = 0; |
|
|
|
for (const item of routeItems) { |
|
|
|
const route = item.route || item; |
|
|
|
const waypoints = item.waypoints || route.waypoints || []; |
|
|
|
const cleanWaypoints = waypoints.map((wp, idx) => { |
|
|
|
const { id: _id, routeId: _rid, ...rest } = wp; |
|
|
|
return { |
|
|
|
...rest, |
|
|
|
seq: idx + 1 |
|
|
|
}; |
|
|
|
}); |
|
|
|
const payload = { |
|
|
|
callSign: route.callSign || route.name || `导入航线${successCount + 1}`, |
|
|
|
scenarioId: targetScenarioId, |
|
|
|
platformId: (targetPlatformId != null ? targetPlatformId : route.platformId) || 1, |
|
|
|
attributes: route.attributes || this.getDefaultRouteAttributes(), |
|
|
|
waypoints: cleanWaypoints |
|
|
|
}; |
|
|
|
const res = await addRoutes(payload); |
|
|
|
if (res.code === 200) successCount++; |
|
|
|
} |
|
|
|
this.showImportRoutesDialog = false; |
|
|
|
await this.getList(); |
|
|
|
this.$message.success(`成功导入 ${successCount} 条航线`); |
|
|
|
} catch (err) { |
|
|
|
console.error('导入航线失败:', err); |
|
|
|
this.$message.error('导入失败:' + (err.message || '请重试')); |
|
|
|
} finally { |
|
|
|
if (importDialog && importDialog.setImporting) importDialog.setImporting(false); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
exportPlan() { |
|
|
|
@ -2754,6 +3011,222 @@ export default { |
|
|
|
this.$message.success('已触发下载,请在浏览器保存对话框中选择保存位置') |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板:K+HH:MM:SS 时间块比较 */ |
|
|
|
compareWhiteboardTimeBlock(a, b) { |
|
|
|
const parse = (s) => { |
|
|
|
const m = /K\+(\d+):(\d+):(\d+)/.exec(s) |
|
|
|
if (!m) return 0 |
|
|
|
return parseInt(m[1], 10) * 3600 + parseInt(m[2], 10) * 60 + parseInt(m[3], 10) |
|
|
|
} |
|
|
|
return parse(a) - parse(b) |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板:进入/退出白板模式 */ |
|
|
|
async toggleWhiteboardMode() { |
|
|
|
if (this.showWhiteboardPanel) { |
|
|
|
this.handleWhiteboardExit() |
|
|
|
return |
|
|
|
} |
|
|
|
this.showWhiteboardPanel = true |
|
|
|
this.drawDom = false |
|
|
|
this.airspaceDrawDom = false |
|
|
|
this.whiteboardAirspaceDraw = false |
|
|
|
this.isRightPanelHidden = true |
|
|
|
this.show4TPanel = false |
|
|
|
await this.loadWhiteboards() |
|
|
|
if (this.whiteboards.length === 0) { |
|
|
|
await this.handleWhiteboardCreate() |
|
|
|
} else { |
|
|
|
this.currentWhiteboard = this.whiteboards[0] |
|
|
|
const blocks = this.currentWhiteboard.timeBlocks || [] |
|
|
|
this.currentWhiteboardTimeBlock = blocks.length > 0 ? blocks.sort((a, b) => this.compareWhiteboardTimeBlock(a, b))[0] : null |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async loadWhiteboards() { |
|
|
|
if (!this.currentRoomId) return |
|
|
|
try { |
|
|
|
const res = await listWhiteboards(this.currentRoomId) |
|
|
|
this.whiteboards = (res.data || res) || [] |
|
|
|
} catch (e) { |
|
|
|
this.whiteboards = [] |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
handleWhiteboardSelect(id) { |
|
|
|
const wb = this.whiteboards.find(w => w.id === id) |
|
|
|
if (wb) { |
|
|
|
this.currentWhiteboard = wb |
|
|
|
const blocks = (wb.timeBlocks || []).slice().sort((a, b) => this.compareWhiteboardTimeBlock(a, b)) |
|
|
|
this.currentWhiteboardTimeBlock = blocks.length > 0 ? blocks[0] : null |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async handleWhiteboardCreate(name) { |
|
|
|
if (!this.currentRoomId) { |
|
|
|
this.$message.warning('请先进入任务房间') |
|
|
|
return |
|
|
|
} |
|
|
|
try { |
|
|
|
const res = await createWhiteboard(this.currentRoomId, { name: name || '白板方案' }) |
|
|
|
const wb = res.data || res |
|
|
|
this.whiteboards.push(wb) |
|
|
|
this.currentWhiteboard = wb |
|
|
|
this.currentWhiteboardTimeBlock = null |
|
|
|
this.$message.success('已创建白板') |
|
|
|
} catch (e) { |
|
|
|
this.$message.error('创建白板失败') |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async handleWhiteboardRename(id, name) { |
|
|
|
if (!this.currentRoomId || !id || !name) return |
|
|
|
const wb = this.whiteboards.find(w => w.id === id) |
|
|
|
if (!wb) return |
|
|
|
try { |
|
|
|
const updated = { ...wb, name } |
|
|
|
await updateWhiteboard(this.currentRoomId, id, updated) |
|
|
|
wb.name = name |
|
|
|
if (this.currentWhiteboard && this.currentWhiteboard.id === id) this.currentWhiteboard = updated |
|
|
|
this.$message.success('已重命名') |
|
|
|
} catch (e) { |
|
|
|
this.$message.error('重命名失败') |
|
|
|
} |
|
|
|
}, |
|
|
|
async handleWhiteboardDelete(id) { |
|
|
|
if (!this.currentRoomId || !id) return |
|
|
|
try { |
|
|
|
await deleteWhiteboard(this.currentRoomId, id) |
|
|
|
this.whiteboards = this.whiteboards.filter(w => w.id !== id) |
|
|
|
if (this.currentWhiteboard && this.currentWhiteboard.id === id) { |
|
|
|
this.currentWhiteboard = this.whiteboards[0] || null |
|
|
|
this.currentWhiteboardTimeBlock = this.currentWhiteboard && (this.currentWhiteboard.timeBlocks || []).length > 0 |
|
|
|
? (this.currentWhiteboard.timeBlocks || []).sort((a, b) => this.compareWhiteboardTimeBlock(a, b))[0] |
|
|
|
: null |
|
|
|
} |
|
|
|
this.$message.success('已删除') |
|
|
|
} catch (e) { |
|
|
|
this.$message.error('删除失败') |
|
|
|
} |
|
|
|
}, |
|
|
|
handleWhiteboardExit() { |
|
|
|
this.showWhiteboardPanel = false |
|
|
|
this.whiteboardAirspaceDraw = false |
|
|
|
this.currentWhiteboard = null |
|
|
|
this.currentWhiteboardTimeBlock = null |
|
|
|
this.loadRoomDrawings() |
|
|
|
}, |
|
|
|
|
|
|
|
handleWhiteboardTimeBlockSelect(tb) { |
|
|
|
this.currentWhiteboardTimeBlock = tb |
|
|
|
}, |
|
|
|
|
|
|
|
async handleWhiteboardAddTimeBlock(tb) { |
|
|
|
if (!this.currentWhiteboard) return |
|
|
|
const blocks = [...(this.currentWhiteboard.timeBlocks || [])] |
|
|
|
if (blocks.includes(tb)) { |
|
|
|
this.$message.warning('该时间块已存在') |
|
|
|
return |
|
|
|
} |
|
|
|
blocks.push(tb) |
|
|
|
blocks.sort((a, b) => this.compareWhiteboardTimeBlock(a, b)) |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
contentByTime[tb] = { entities: [] } |
|
|
|
await this.saveCurrentWhiteboard({ timeBlocks: blocks, contentByTime }) |
|
|
|
this.currentWhiteboardTimeBlock = tb |
|
|
|
}, |
|
|
|
|
|
|
|
async handleWhiteboardRenameTimeBlock(oldTb, newTb) { |
|
|
|
if (!this.currentWhiteboard || oldTb === newTb) return |
|
|
|
const blocks = [...(this.currentWhiteboard.timeBlocks || [])] |
|
|
|
const idx = blocks.indexOf(oldTb) |
|
|
|
if (idx < 0) return |
|
|
|
blocks[idx] = newTb |
|
|
|
blocks.sort((a, b) => this.compareWhiteboardTimeBlock(a, b)) |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
if (contentByTime[oldTb]) { |
|
|
|
contentByTime[newTb] = contentByTime[oldTb] |
|
|
|
delete contentByTime[oldTb] |
|
|
|
} |
|
|
|
await this.saveCurrentWhiteboard({ timeBlocks: blocks, contentByTime }) |
|
|
|
this.currentWhiteboardTimeBlock = newTb |
|
|
|
}, |
|
|
|
|
|
|
|
async handleWhiteboardDeleteTimeBlock(tb) { |
|
|
|
if (!this.currentWhiteboard) return |
|
|
|
const blocks = (this.currentWhiteboard.timeBlocks || []).filter(t => t !== tb) |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
delete contentByTime[tb] |
|
|
|
await this.saveCurrentWhiteboard({ timeBlocks: blocks, contentByTime }) |
|
|
|
this.currentWhiteboardTimeBlock = blocks.length > 0 ? blocks.sort((a, b) => this.compareWhiteboardTimeBlock(a, b))[0] : null |
|
|
|
}, |
|
|
|
|
|
|
|
handleWhiteboardDrawModeChange(mode) { |
|
|
|
this.whiteboardAirspaceDraw = mode === 'airspace' |
|
|
|
}, |
|
|
|
|
|
|
|
handleWhiteboardDrawComplete(entityData) { |
|
|
|
if (!this.currentWhiteboard || !this.currentWhiteboardTimeBlock) return |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
const currentContent = contentByTime[this.currentWhiteboardTimeBlock] || { entities: [] } |
|
|
|
currentContent.entities = [...(currentContent.entities || []), entityData] |
|
|
|
contentByTime[this.currentWhiteboardTimeBlock] = currentContent |
|
|
|
this.saveCurrentWhiteboard({ contentByTime }) |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板平台拖拽/旋转后更新 contentByTime 并保存 */ |
|
|
|
handleWhiteboardPlatformUpdated(entityData) { |
|
|
|
if (!this.currentWhiteboard || !this.currentWhiteboardTimeBlock || !entityData || !entityData.id) return |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
const currentContent = contentByTime[this.currentWhiteboardTimeBlock] || { entities: [] } |
|
|
|
const ents = [...(currentContent.entities || [])] |
|
|
|
const idx = ents.findIndex(e => e.id === entityData.id) |
|
|
|
if (idx >= 0) { |
|
|
|
const updated = { ...ents[idx], lat: entityData.lat, lng: entityData.lng, heading: entityData.heading != null ? entityData.heading : 0 } |
|
|
|
if (entityData.iconScale != null) updated.iconScale = entityData.iconScale |
|
|
|
ents[idx] = updated |
|
|
|
contentByTime[this.currentWhiteboardTimeBlock] = { ...currentContent, entities: ents } |
|
|
|
this.saveCurrentWhiteboard({ contentByTime }) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板平台从右键菜单删除后,从 contentByTime 移除并保存 */ |
|
|
|
handleWhiteboardEntityDeleted(entityData) { |
|
|
|
if (!this.currentWhiteboard || !this.currentWhiteboardTimeBlock || !entityData || !entityData.id) return |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
const currentContent = contentByTime[this.currentWhiteboardTimeBlock] || { entities: [] } |
|
|
|
const ents = (currentContent.entities || []).filter(e => e.id !== entityData.id) |
|
|
|
contentByTime[this.currentWhiteboardTimeBlock] = { ...currentContent, entities: ents } |
|
|
|
this.saveCurrentWhiteboard({ contentByTime }) |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板空域/图形编辑后(调整位置、修改属性等)更新 contentByTime 并保存 */ |
|
|
|
handleWhiteboardDrawingUpdated(entityData) { |
|
|
|
if (!this.currentWhiteboard || !this.currentWhiteboardTimeBlock || !entityData || !entityData.id) return |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
const currentContent = contentByTime[this.currentWhiteboardTimeBlock] || { entities: [] } |
|
|
|
const ents = [...(currentContent.entities || [])] |
|
|
|
const idx = ents.findIndex(e => e.id === entityData.id) |
|
|
|
if (idx >= 0) { |
|
|
|
ents[idx] = entityData |
|
|
|
contentByTime[this.currentWhiteboardTimeBlock] = { ...currentContent, entities: ents } |
|
|
|
this.saveCurrentWhiteboard({ contentByTime }) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async saveCurrentWhiteboard(patch) { |
|
|
|
if (!this.currentRoomId || !this.currentWhiteboard) return |
|
|
|
const wb = { ...this.currentWhiteboard, ...patch } |
|
|
|
try { |
|
|
|
await updateWhiteboard(this.currentRoomId, wb.id, wb) |
|
|
|
this.currentWhiteboard = wb |
|
|
|
const i = this.whiteboards.findIndex(w => w.id === wb.id) |
|
|
|
if (i >= 0) this.whiteboards[i] = wb |
|
|
|
} catch (e) { |
|
|
|
this.$message.error('保存白板失败') |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
handleDeleteMenuItem(deletedItem) { |
|
|
|
const index = this.menuItems.findIndex(item => item.id === deletedItem.id) |
|
|
|
if (index > -1) { |
|
|
|
@ -2888,14 +3361,48 @@ export default { |
|
|
|
if (ev.dataTransfer) ev.dataTransfer.dropEffect = 'copy' |
|
|
|
}, |
|
|
|
|
|
|
|
/** 将平台图标放置到地图上,并保存到当前房间 */ |
|
|
|
/** 将平台图标放置到地图上,并保存到当前房间;白板模式下添加到白板当前时间块 */ |
|
|
|
async handleMapDrop(ev) { |
|
|
|
ev.preventDefault() |
|
|
|
const raw = ev.dataTransfer && ev.dataTransfer.getData('application/json') |
|
|
|
if (!raw) return |
|
|
|
try { |
|
|
|
const platform = JSON.parse(raw) |
|
|
|
const data = JSON.parse(raw) |
|
|
|
const platform = data.type === 'whiteboardPlatform' ? data.platform : data |
|
|
|
const map = this.$refs.cesiumMap |
|
|
|
|
|
|
|
if (this.showWhiteboardPanel && data.type === 'whiteboardPlatform') { |
|
|
|
if (!this.currentWhiteboard || !this.currentWhiteboardTimeBlock) { |
|
|
|
this.$message.warning('请先选择或添加时间块') |
|
|
|
return |
|
|
|
} |
|
|
|
const pos = map && typeof map.getLatLngFromScreen === 'function' ? map.getLatLngFromScreen(ev.clientX, ev.clientY) : null |
|
|
|
if (!pos) { |
|
|
|
this.$message.warning('请将图标放置到地图有效区域内') |
|
|
|
return |
|
|
|
} |
|
|
|
const entityId = 'wb_' + Date.now() + '_' + Math.random().toString(36).slice(2) |
|
|
|
const entity = { |
|
|
|
id: entityId, |
|
|
|
type: 'platformIcon', |
|
|
|
platformId: platform.id, |
|
|
|
platform, |
|
|
|
platformName: platform.name || '', |
|
|
|
lat: pos.lat, |
|
|
|
lng: pos.lng, |
|
|
|
heading: 0, |
|
|
|
label: platform.name || '平台', |
|
|
|
color: platform.color || '#008aff' |
|
|
|
} |
|
|
|
const contentByTime = { ...(this.currentWhiteboard.contentByTime || {}) } |
|
|
|
const currentContent = contentByTime[this.currentWhiteboardTimeBlock] || { entities: [] } |
|
|
|
currentContent.entities = [...(currentContent.entities || []), entity] |
|
|
|
contentByTime[this.currentWhiteboardTimeBlock] = currentContent |
|
|
|
await this.saveCurrentWhiteboard({ contentByTime }) |
|
|
|
this.$message.success('已添加到白板') |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if (!map || typeof map.addPlatformIconFromDrag !== 'function') return |
|
|
|
const entityData = map.addPlatformIconFromDrag(platform, ev.clientX, ev.clientY) |
|
|
|
if (!entityData || !this.currentRoomId) return |
|
|
|
@ -3046,8 +3553,8 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
importRouteData(path) { |
|
|
|
console.log('导入航路:', path) |
|
|
|
this.$message.success('航路数据导入成功'); |
|
|
|
// 统一打开导入航线弹窗(文件菜单、外部参数等入口均走此流程;浏览器无法读取本地路径,需用户选择文件) |
|
|
|
this.showImportRoutesDialog = true; |
|
|
|
}, |
|
|
|
|
|
|
|
importLandmark(path) { |
|
|
|
@ -3120,6 +3627,9 @@ export default { |
|
|
|
} else if (item.id === '4t') { |
|
|
|
// 4T:切换4T悬浮窗显示 |
|
|
|
this.show4TPanel = !this.show4TPanel; |
|
|
|
} else if (item.id === 'whiteboard') { |
|
|
|
// 白板:进入/退出白板模式 |
|
|
|
this.toggleWhiteboardMode(); |
|
|
|
} else if (item.id === 'start') { |
|
|
|
// 如果当前已经是冲突标签页,则关闭右侧面板 |
|
|
|
if (this.activeRightTab === 'conflict' && !this.isRightPanelHidden) { |
|
|
|
@ -3242,16 +3752,21 @@ export default { |
|
|
|
// 右上角作战时间随时与推演时间轴同步(无论是否展示航线) |
|
|
|
this.combatTime = this.currentTime; |
|
|
|
this.updateDeductionPositions(); |
|
|
|
// 有房间且已选航线时,按房间+航线组合从 Redis 加载导弹(仅变化时加载一次) |
|
|
|
// 有房间且已选航线时,按房间+航线组合从 Redis 加载导弹(仅变化时加载一次);大房间时用 plan.roomId |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
const routeIds = (this.activeRouteIds || []).slice().sort((a, b) => (a - b)); |
|
|
|
const isParentRoom = this.roomDetail && this.roomDetail.parentId == null; |
|
|
|
if (roomId != null && routeIds.length > 0 && this.$refs.cesiumMap && typeof this.$refs.cesiumMap.loadMissilesFromRedis === 'function') { |
|
|
|
const loadKey = roomId + '-' + routeIds.join(','); |
|
|
|
const routePlatforms = this.routes |
|
|
|
.filter(r => routeIds.includes(r.id)) |
|
|
|
.map(r => { |
|
|
|
const plan = this.plans.find(p => p.id === r.scenarioId); |
|
|
|
const rId = (isParentRoom && plan && plan.roomId) ? plan.roomId : roomId; |
|
|
|
return { routeId: r.id, platformId: r.platformId != null ? r.platformId : 0, roomId: rId }; |
|
|
|
}); |
|
|
|
const loadKey = routePlatforms.map(r => `${r.roomId}-${r.routeId}`).sort().join(','); |
|
|
|
if (this._missilesLoadKey !== loadKey) { |
|
|
|
this._missilesLoadKey = loadKey; |
|
|
|
const routePlatforms = this.routes |
|
|
|
.filter(r => routeIds.includes(r.id)) |
|
|
|
.map(r => ({ routeId: r.id, platformId: r.platformId != null ? r.platformId : 0 })); |
|
|
|
if (routePlatforms.length > 0) { |
|
|
|
this.$refs.cesiumMap.loadMissilesFromRedis(roomId, routePlatforms); |
|
|
|
} |
|
|
|
@ -3300,14 +3815,20 @@ export default { |
|
|
|
this.reloadMissilesAfterRouteChange(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** 根据当前选中的航线重新加载导弹(删除航线后需调用以清除地图上的导弹) */ |
|
|
|
/** 根据当前选中的航线重新加载导弹(删除航线后需调用以清除地图上的导弹);大房间时用 plan.roomId */ |
|
|
|
reloadMissilesAfterRouteChange() { |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
if (roomId == null || !this.$refs.cesiumMap || typeof this.$refs.cesiumMap.loadMissilesFromRedis !== 'function') return; |
|
|
|
const isParentRoom = this.roomDetail && this.roomDetail.parentId == null; |
|
|
|
const routeIds = (this.activeRouteIds || []).slice().sort((a, b) => (a - b)); |
|
|
|
const routePlatforms = this.routes |
|
|
|
.filter(r => routeIds.includes(r.id)) |
|
|
|
.map(r => ({ routeId: r.id, platformId: r.platformId != null ? r.platformId : 0 })); |
|
|
|
.map(r => { |
|
|
|
const plan = this.plans.find(p => p.id === r.scenarioId); |
|
|
|
const rId = (isParentRoom && plan && plan.roomId) ? plan.roomId : roomId; |
|
|
|
return { routeId: r.id, platformId: r.platformId != null ? r.platformId : 0, roomId: rId }; |
|
|
|
}); |
|
|
|
this._missilesLoadKey = routePlatforms.map(r => `${r.roomId}-${r.routeId}`).sort().join(','); |
|
|
|
this.$refs.cesiumMap.loadMissilesFromRedis(roomId, routePlatforms); |
|
|
|
}, |
|
|
|
|
|
|
|
@ -4191,7 +4712,7 @@ export default { |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
} |
|
|
|
} |
|
|
|
this.wsConnection?.sendSyncRouteVisibility?.(route.id, false); |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
this.$message.info(`已取消航线: ${route.name}`); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -4243,7 +4764,7 @@ export default { |
|
|
|
} else { |
|
|
|
this.$message.warning('该航线暂无坐标数据,无法在地图展示'); |
|
|
|
} |
|
|
|
this.wsConnection?.sendSyncRouteVisibility?.(route.id, true); |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("获取航线详情失败:", error); |
|
|
|
@ -4368,7 +4889,7 @@ export default { |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.removeRouteById(route.id); |
|
|
|
} |
|
|
|
if (!fromPlanSwitch) this.wsConnection?.sendSyncRouteVisibility?.(route.id, false); |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) { |
|
|
|
if (this.activeRouteIds.length > 0) { |
|
|
|
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1]; |
|
|
|
@ -4396,7 +4917,7 @@ export default { |
|
|
|
} else { |
|
|
|
// 航线已隐藏,显示它 |
|
|
|
await this.selectRoute(route); |
|
|
|
if (!fromPlanSwitch) this.wsConnection?.sendSyncRouteVisibility?.(route.id, true); |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|