|
|
|
@ -22,6 +22,8 @@ |
|
|
|
:route-locked-by-other-ids="routeLockedByOtherRouteIds" |
|
|
|
:deduction-time-minutes="deductionMinutesFromK" |
|
|
|
:room-id="currentRoomId" |
|
|
|
:platform-box-select-mode="platformBoxSelectMode" |
|
|
|
@request-exit-platform-box-select="onRequestExitPlatformBoxSelect" |
|
|
|
@draw-complete="handleMapDrawComplete" |
|
|
|
@route-lock-changed="handleRouteLockChanged" |
|
|
|
@drawing-points-update="missionDrawingPointsCount = $event" |
|
|
|
@ -30,6 +32,8 @@ |
|
|
|
@open-waypoint-dialog="handleOpenWaypointEdit" |
|
|
|
@open-route-dialog="handleOpenRouteEdit" |
|
|
|
@copy-route="handleCopyRoute" |
|
|
|
@route-segment-pick="handleRouteSegmentPick" |
|
|
|
@route-segment-placed="handleRouteSegmentPlaced" |
|
|
|
@single-route-deduction="handleSingleRouteDeduction" |
|
|
|
@route-copy-placed="handleRouteCopyPlaced" |
|
|
|
@add-waypoint-at="handleAddWaypointAt" |
|
|
|
@ -40,6 +44,7 @@ |
|
|
|
@missile-deleted="handleMissileDeleted" |
|
|
|
@scale-click="handleScaleClick" |
|
|
|
@platform-icon-updated="onPlatformIconUpdated" |
|
|
|
@platform-icons-batch-updated="onPlatformIconsBatchUpdated" |
|
|
|
@platform-icon-removed="onPlatformIconRemoved" |
|
|
|
@viewer-ready="onViewerReady" |
|
|
|
@drawing-entities-changed="onDrawingEntitiesChanged" |
|
|
|
@ -69,7 +74,7 @@ |
|
|
|
<div class="red-dot"></div> |
|
|
|
<i class="el-icon-s-unfold icon-inside"></i> |
|
|
|
</div> |
|
|
|
<el-dialog title="保存新航线" :visible.sync="showNameDialog" width="30%" :append-to-body="true" @open="onSaveRouteDialogOpen" @close="tempMapPlatform = null; saveDialogScenarioId = null"> |
|
|
|
<el-dialog title="保存新航线" :visible.sync="showNameDialog" width="30%" :append-to-body="true" @open="onSaveRouteDialogOpen" @close="onSaveNewRouteDialogClose"> |
|
|
|
<el-form label-width="80px"> |
|
|
|
<el-form-item label="航线名称"> |
|
|
|
<el-input v-model="newRouteName" placeholder="例如:航线一"></el-input> |
|
|
|
@ -560,6 +565,8 @@ export default { |
|
|
|
return { |
|
|
|
drawDom:false, |
|
|
|
airspaceDrawDom:false, |
|
|
|
/** 左侧菜单「框选平台」:多选房间平台图标并整体平移 */ |
|
|
|
platformBoxSelectMode: false, |
|
|
|
/** 是否允许地图拖动(由顶部小手图标切换,默认关闭) */ |
|
|
|
mapDragEnabled: false, |
|
|
|
// 在线成员弹窗 |
|
|
|
@ -590,6 +597,8 @@ export default { |
|
|
|
showNameDialog: false, |
|
|
|
newRouteName: '', |
|
|
|
tempMapPoints: [], |
|
|
|
/** 拆分航段:保存新航线成功后从原航线删除这些航点 id */ |
|
|
|
pendingSegmentSplitAfterNewRoute: null, |
|
|
|
/** 从平台右键「在此之前/在此之后插入航线」完成时传入,保存航线时用作 platformId */ |
|
|
|
tempMapPlatform: null, |
|
|
|
/** 保存新航线弹窗内选择的方案 ID(弹窗内可直接选方案,无需先展开侧边) */ |
|
|
|
@ -644,6 +653,7 @@ export default { |
|
|
|
{ id: '4t', name: '4T', icon: 'T' }, |
|
|
|
{ id: 'start', name: '冲突', icon: 'chongtu' }, |
|
|
|
{ id: 'insert', name: '平台', icon: 'el-icon-s-platform' }, |
|
|
|
{ id: 'platformBoxSelect', name: '框选平台', icon: 'el-icon-crop', action: 'platformBoxSelect' }, |
|
|
|
{ id: 'pattern', name: '空域', icon: 'ky' }, |
|
|
|
{ id: 'deduction', name: '推演', icon: 'el-icon-video-play' }, |
|
|
|
{ id: 'modify', name: '测距', icon: 'cj' }, |
|
|
|
@ -671,6 +681,7 @@ export default { |
|
|
|
}, |
|
|
|
// 顶部导航菜单项(用于图标选择)- 只显示指定的菜单项 |
|
|
|
topNavItems: [ |
|
|
|
{ id: 'platformBoxSelect', name: '框选平台', icon: 'el-icon-crop' }, |
|
|
|
{ id: 'routeEdit', name: '航线编辑', icon: 'el-icon-edit-outline' }, |
|
|
|
{ id: 'militaryMarking', name: '军事标绘', icon: 'el-icon-crop' }, |
|
|
|
{ id: 'attributeEdit', name: '属性修改', icon: 'el-icon-setting' }, |
|
|
|
@ -1681,11 +1692,58 @@ export default { |
|
|
|
|
|
|
|
/** 复制航线已放置:用当前偏移后的航点打开「保存新航线」弹窗 */ |
|
|
|
handleRouteCopyPlaced(points) { |
|
|
|
this.pendingSegmentSplitAfterNewRoute = null; |
|
|
|
this.tempMapPoints = points || []; |
|
|
|
this.tempMapPlatform = null; |
|
|
|
this.showNameDialog = true; |
|
|
|
}, |
|
|
|
|
|
|
|
/** 右键拆分航段/拆分复制:拉取航点后进入地图两点选范围 */ |
|
|
|
async handleRouteSegmentPick({ routeId, mode }) { |
|
|
|
if (routeId == null) return; |
|
|
|
if (this.routeLocked[routeId]) { |
|
|
|
this.$message.info('该航线已上锁,请先解锁后再操作'); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.isRouteLockedByOther(routeId)) { |
|
|
|
this.$message.warning('该航线正被其他成员编辑,请稍后再试'); |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
const res = await getRoutes(routeId); |
|
|
|
if (res.code !== 200 || !res.data) { |
|
|
|
this.$message.error('获取航线数据失败'); |
|
|
|
return; |
|
|
|
} |
|
|
|
const waypoints = res.data.waypoints || []; |
|
|
|
if (waypoints.length < 2) { |
|
|
|
this.$message.warning('航线航点不足,无法选取航段'); |
|
|
|
return; |
|
|
|
} |
|
|
|
if (this.$refs.cesiumMap && typeof this.$refs.cesiumMap.startRouteSegmentPickMode === 'function') { |
|
|
|
const routeStyle = this.parseRouteStyle(res.data.attributes); |
|
|
|
this.$refs.cesiumMap.startRouteSegmentPickMode(routeId, waypoints, mode, routeStyle); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
this.$message.error('获取航线数据失败'); |
|
|
|
console.error(e); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 航段预览已放置:打开保存弹窗;拆分航段时记下待从原航线删除的航点 id */ |
|
|
|
handleRouteSegmentPlaced(payload) { |
|
|
|
const { mode, sourceRouteId, removedWaypointIds, points } = payload || {}; |
|
|
|
if (!points || points.length < 2) return; |
|
|
|
this.tempMapPoints = points; |
|
|
|
this.tempMapPlatform = null; |
|
|
|
if (mode === 'split' && sourceRouteId != null && removedWaypointIds && removedWaypointIds.length) { |
|
|
|
this.pendingSegmentSplitAfterNewRoute = { sourceRouteId, waypointIds: removedWaypointIds }; |
|
|
|
} else { |
|
|
|
this.pendingSegmentSplitAfterNewRoute = null; |
|
|
|
} |
|
|
|
this.showNameDialog = true; |
|
|
|
}, |
|
|
|
|
|
|
|
/** 地图上拖拽航点结束:将新位置写回数据库并刷新显示 */ |
|
|
|
async handleWaypointPositionChanged({ dbId, routeId, lat, lng, alt }) { |
|
|
|
if (this.isRouteLockedByOther(routeId)) { |
|
|
|
@ -2658,6 +2716,7 @@ export default { |
|
|
|
this.drawDom = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
this.pendingSegmentSplitAfterNewRoute = null; |
|
|
|
this.tempMapPoints = points; |
|
|
|
this.tempMapPlatform = (options && (options.platformId != null || options.platform)) ? options : null; |
|
|
|
this.showNameDialog = true; |
|
|
|
@ -2667,6 +2726,12 @@ export default { |
|
|
|
this.saveDialogScenarioId = this.selectedPlanId || (this.plans[0] && this.plans[0].id) || null; |
|
|
|
}, |
|
|
|
|
|
|
|
onSaveNewRouteDialogClose() { |
|
|
|
this.tempMapPlatform = null; |
|
|
|
this.saveDialogScenarioId = null; |
|
|
|
this.pendingSegmentSplitAfterNewRoute = null; |
|
|
|
}, |
|
|
|
|
|
|
|
openAddHoldDuringDrawing() { |
|
|
|
this.addHoldContext = { mode: 'drawing' }; |
|
|
|
this.addHoldForm = { holdType: 'hold_circle', edgeLengthKm: 20, clockwise: true, startTime: '', startTimeMinutes: 60 }; |
|
|
|
@ -2741,6 +2806,21 @@ export default { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const pendingSplit = this.pendingSegmentSplitAfterNewRoute; |
|
|
|
if (pendingSplit && pendingSplit.sourceRouteId != null && Array.isArray(pendingSplit.waypointIds) && pendingSplit.waypointIds.length > 0) { |
|
|
|
const roomIdParam = this.currentRoomId != null ? { roomId: this.currentRoomId } : {}; |
|
|
|
try { |
|
|
|
for (const wid of pendingSplit.waypointIds) { |
|
|
|
await delWaypoints(wid, roomIdParam); |
|
|
|
} |
|
|
|
this.wsConnection?.sendSyncWaypoints?.(pendingSplit.sourceRouteId); |
|
|
|
} catch (splitErr) { |
|
|
|
console.error(splitErr); |
|
|
|
this.$message.warning('新航线已保存,但从原航线切除所选航段失败,请手动删除原航线上的对应航点'); |
|
|
|
} |
|
|
|
} |
|
|
|
this.pendingSegmentSplitAfterNewRoute = null; |
|
|
|
|
|
|
|
// 1. 更新当前选中详情 |
|
|
|
this.selectedRouteId = newRouteId; |
|
|
|
this.selectedRouteDetails = JSON.parse(JSON.stringify(savedRoute)); |
|
|
|
@ -3456,7 +3536,8 @@ export default { |
|
|
|
'toggleRoute': () => this.toggleRoute(), |
|
|
|
'layerFavorites': () => this.layerFavorites(), |
|
|
|
'routeFavorites': () => this.routeFavorites(), |
|
|
|
'refresh': () => this.captureMapScreenshot() |
|
|
|
'refresh': () => this.captureMapScreenshot(), |
|
|
|
'platformBoxSelect': () => this.togglePlatformBoxSelectMenu() |
|
|
|
} |
|
|
|
|
|
|
|
if (actionMap[actionId]) { |
|
|
|
@ -3464,6 +3545,27 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 左侧「框选平台」:与菜单项 id 无关,统一用 action: platformBoxSelect 触发(含图标编辑添加的随机 id 项) */ |
|
|
|
togglePlatformBoxSelectMenu() { |
|
|
|
this.platformBoxSelectMode = !this.platformBoxSelectMode |
|
|
|
this.activeMenu = this.platformBoxSelectMode ? 'platformBoxSelect' : '' |
|
|
|
if (!this.platformBoxSelectMode) { |
|
|
|
if (this.$refs.cesiumMap && typeof this.$refs.cesiumMap.exitPlatformBoxSelectMode === 'function') { |
|
|
|
this.$refs.cesiumMap.exitPlatformBoxSelectMode() |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.drawDom = false |
|
|
|
this.airspaceDrawDom = false |
|
|
|
this.isRightPanelHidden = true |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 地图内右键退出框选(子组件 emit) */ |
|
|
|
onRequestExitPlatformBoxSelect() { |
|
|
|
this.platformBoxSelectMode = false |
|
|
|
this.activeMenu = '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** 截图:隐藏上下左右菜单只保留地图,用 postRender + readPixels 避免 WebGL 缓冲被清空导致黑屏 */ |
|
|
|
async captureMapScreenshot() { |
|
|
|
const cm = this.$refs.cesiumMap |
|
|
|
@ -4159,6 +4261,27 @@ export default { |
|
|
|
}).catch(() => {}) |
|
|
|
}, 500) |
|
|
|
}, |
|
|
|
/** 框选整体拖拽结束:批量写库(避免多次 emit 被防抖合并为只保存最后一个) */ |
|
|
|
onPlatformIconsBatchUpdated(list) { |
|
|
|
if (!list || !list.length) return |
|
|
|
const payloads = list.filter(e => e && e.serverId) |
|
|
|
if (!payloads.length) return |
|
|
|
Promise.all( |
|
|
|
payloads.map(ed => |
|
|
|
updateRoomPlatformIcon({ |
|
|
|
id: ed.serverId, |
|
|
|
lng: ed.lng, |
|
|
|
lat: ed.lat, |
|
|
|
heading: ed.heading != null ? ed.heading : 0, |
|
|
|
iconScale: ed.iconScale != null ? ed.iconScale : 1 |
|
|
|
}) |
|
|
|
) |
|
|
|
) |
|
|
|
.then(() => { |
|
|
|
this.wsConnection?.sendSyncPlatformIcons?.() |
|
|
|
}) |
|
|
|
.catch(() => {}) |
|
|
|
}, |
|
|
|
/** 平台图标从地图删除时同步删除服务端记录 */ |
|
|
|
onPlatformIconRemoved({ serverId }) { |
|
|
|
if (!serverId) return |
|
|
|
@ -4509,6 +4632,21 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
selectMenu(item) { |
|
|
|
// 带 action 的项会先触发 handleMenuAction 再进此处;框选已在 handleMenuAction 中切换,避免重复 |
|
|
|
if (item.action === 'platformBoxSelect') { |
|
|
|
return |
|
|
|
} |
|
|
|
// 旧版菜单数据可能仅有 id、无 action |
|
|
|
if (item.id === 'platformBoxSelect') { |
|
|
|
this.togglePlatformBoxSelectMenu() |
|
|
|
return |
|
|
|
} |
|
|
|
if (this.platformBoxSelectMode) { |
|
|
|
this.platformBoxSelectMode = false |
|
|
|
if (this.$refs.cesiumMap && typeof this.$refs.cesiumMap.exitPlatformBoxSelectMode === 'function') { |
|
|
|
this.$refs.cesiumMap.exitPlatformBoxSelectMode() |
|
|
|
} |
|
|
|
} |
|
|
|
this.activeMenu = item.id; |
|
|
|
if (item.action) { |
|
|
|
this.handleMenuAction(item.action) |
|
|
|
@ -5674,45 +5812,37 @@ export default { |
|
|
|
/** 切换航线:实现多选/开关逻辑 */ |
|
|
|
async selectRoute(route) { |
|
|
|
const index = this.activeRouteIds.indexOf(route.id); |
|
|
|
const isRouteExpanded = this.$refs.rightPanel ? this.$refs.rightPanel.expandedRoutes.includes(route.id) : false; |
|
|
|
// 航线已在选中列表中 |
|
|
|
// 航线已在地图上选中:再次点击右侧航线行则取消选中(右侧不再展开航点子列表) |
|
|
|
if (index > -1) { |
|
|
|
if (isRouteExpanded) { |
|
|
|
return; |
|
|
|
} else { |
|
|
|
// 航线未展开,点击则取消选中(从地图移除) |
|
|
|
this.activeRouteIds.splice(index, 1); |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.removeRouteById(route.id); |
|
|
|
// 隐藏航线时,同时移除关联的探测区、威力区 |
|
|
|
this.$refs.cesiumMap.removeDetectionZoneByRouteId(route.id); |
|
|
|
this.$refs.cesiumMap.removePowerZoneByRouteId(route.id); |
|
|
|
} |
|
|
|
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) { |
|
|
|
if (this.activeRouteIds.length > 0) { |
|
|
|
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1]; |
|
|
|
try { |
|
|
|
const res = await getRoutes(lastId); |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.selectedRouteId = res.data.id; |
|
|
|
this.selectedRouteDetails = { |
|
|
|
id: res.data.id, |
|
|
|
name: res.data.callSign, |
|
|
|
waypoints: res.data.waypoints || [] |
|
|
|
}; |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
console.error("回显剩余航线失败", e); |
|
|
|
this.activeRouteIds.splice(index, 1); |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.removeRouteById(route.id); |
|
|
|
this.$refs.cesiumMap.removeDetectionZoneByRouteId(route.id); |
|
|
|
this.$refs.cesiumMap.removePowerZoneByRouteId(route.id); |
|
|
|
} |
|
|
|
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) { |
|
|
|
if (this.activeRouteIds.length > 0) { |
|
|
|
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1]; |
|
|
|
try { |
|
|
|
const res = await getRoutes(lastId); |
|
|
|
if (res.code === 200 && res.data) { |
|
|
|
this.selectedRouteId = res.data.id; |
|
|
|
this.selectedRouteDetails = { |
|
|
|
id: res.data.id, |
|
|
|
name: res.data.callSign, |
|
|
|
waypoints: res.data.waypoints || [] |
|
|
|
}; |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.selectedRouteId = null; |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
} catch (e) { |
|
|
|
console.error("回显剩余航线失败", e); |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.selectedRouteId = null; |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
} |
|
|
|
// 航线显隐仅本地生效,不同步给他人 |
|
|
|
this.$message.info(`已取消航线: ${route.name}`); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.$message.info(`已取消航线: ${route.name}`); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 航线未被选中,点击则选中并显示航线和航点 |
|
|
|
|