|
|
|
@ -4,7 +4,8 @@ |
|
|
|
<!-- 地图背景 --> |
|
|
|
<div id="gis-map-background" class="map-background"> |
|
|
|
<!-- cesiummap组件 --> |
|
|
|
<cesiumMap :drawDomClick="drawDom"/> |
|
|
|
<cesiumMap ref="cesiumMap" :drawDomClick="drawDom" @draw-complete="handleMapDrawComplete" |
|
|
|
@open-waypoint-dialog="handleOpenWaypointEdit" /> |
|
|
|
<div class="map-overlay-text"> |
|
|
|
<i class="el-icon-location-outline text-3xl mb-2 block"></i> |
|
|
|
<p>二维GIS地图区域</p> |
|
|
|
@ -21,6 +22,17 @@ |
|
|
|
<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"> |
|
|
|
<el-form label-width="80px"> |
|
|
|
<el-form-item label="航线名称"> |
|
|
|
<el-input v-model="newRouteName" placeholder="例如:航线一"></el-input> |
|
|
|
</el-form-item> |
|
|
|
</el-form> |
|
|
|
<div slot="footer" class="dialog-footer"> |
|
|
|
<el-button @click="showNameDialog = false">取 消</el-button> |
|
|
|
<el-button type="primary" @click="confirmSaveNewRoute">确 定</el-button> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 顶部导航栏 --> |
|
|
|
@ -30,7 +42,6 @@ |
|
|
|
:combat-time="combatTime" |
|
|
|
:astro-time="astroTime" |
|
|
|
:user-avatar="userAvatar" |
|
|
|
@select-nav="selectTopNav" |
|
|
|
@save-plan="savePlan" |
|
|
|
@import-plan-file="importPlanFile" |
|
|
|
@import-acd="importACD" |
|
|
|
@ -88,7 +99,7 @@ |
|
|
|
:is-hidden="isRightPanelHidden" |
|
|
|
:active-tab="activeRightTab" |
|
|
|
:routes="routes" |
|
|
|
:selected-route-id="selectedRouteId" |
|
|
|
:active-route-ids="activeRouteIds" |
|
|
|
:selected-route-details="selectedRouteDetails" |
|
|
|
:conflicts="conflicts" |
|
|
|
:conflict-count="conflictCount" |
|
|
|
@ -208,6 +219,8 @@ import LeftMenu from './LeftMenu' |
|
|
|
import RightPanel from './RightPanel' |
|
|
|
import BottomLeftPanel from './BottomLeftPanel' |
|
|
|
import TopHeader from './TopHeader' |
|
|
|
import { listRoutes, getRoutes, addRoutes } from "@/api/system/routes"; |
|
|
|
import { updateWaypoints } from "@/api/system/waypoints"; |
|
|
|
export default { |
|
|
|
name: 'MissionPlanningView', |
|
|
|
components: { |
|
|
|
@ -233,6 +246,9 @@ export default { |
|
|
|
selectedRoute: null, |
|
|
|
showWaypointDialog: false, |
|
|
|
selectedWaypoint: null, |
|
|
|
showNameDialog: false, |
|
|
|
newRouteName: '', |
|
|
|
tempMapPoints: [], |
|
|
|
|
|
|
|
// 作战信息 |
|
|
|
roomCode: 'JTF-7-ALPHA', |
|
|
|
@ -270,7 +286,7 @@ export default { |
|
|
|
|
|
|
|
// 右侧面板 |
|
|
|
activeRightTab: 'plan', |
|
|
|
selectedRouteId: 101, |
|
|
|
activeRouteIds: [], // 存储当前所有选中的航线ID |
|
|
|
selectedRouteDetails: null, |
|
|
|
|
|
|
|
// 冲突数据 |
|
|
|
@ -314,11 +330,7 @@ export default { |
|
|
|
], |
|
|
|
|
|
|
|
// 航线数据 |
|
|
|
routes: [ |
|
|
|
{ id: 101, name: 'Alpha进场航线', points: 8, conflict: true }, |
|
|
|
{ id: 102, name: 'Beta巡逻航线', points: 6, conflict: false }, |
|
|
|
{ id: 103, name: '侦察覆盖区', points: 4, conflict: false }, |
|
|
|
], |
|
|
|
routes: [], |
|
|
|
|
|
|
|
// 时间控制 |
|
|
|
timeProgress: 45, |
|
|
|
@ -332,11 +344,11 @@ export default { |
|
|
|
}; |
|
|
|
}, |
|
|
|
mounted() { |
|
|
|
this.getList(); |
|
|
|
// 初始化时左侧菜单隐藏 |
|
|
|
this.isMenuHidden = true; |
|
|
|
// 初始化时右侧面板隐藏 |
|
|
|
this.isRightPanelHidden = true; |
|
|
|
|
|
|
|
// 更新时间 |
|
|
|
this.updateTime(); |
|
|
|
setInterval(this.updateTime, 1000); |
|
|
|
@ -351,6 +363,25 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// 处理从地图点击传来的编辑请求 |
|
|
|
handleOpenWaypointEdit(wpName) { |
|
|
|
// 1. 确保当前有选中的航线详情 |
|
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) { |
|
|
|
this.$message.warning('请先在右侧列表选择一条航线'); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 2. 根据点击的航点名称在数据列表中查找 |
|
|
|
const wpData = this.selectedRouteDetails.waypoints.find(item => item.name === wpName); |
|
|
|
if (wpData) { |
|
|
|
// 3. 深拷贝给编辑弹窗绑定的变量,防止直接修改原始数组 |
|
|
|
this.selectedWaypoint = JSON.parse(JSON.stringify(wpData)); |
|
|
|
// 4. 打开弹窗 |
|
|
|
this.showWaypointDialog = true; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.$message.info('未找到该航点的业务数据'); |
|
|
|
} |
|
|
|
}, |
|
|
|
// 显示在线成员弹窗 |
|
|
|
showOnlineMembersDialog() { |
|
|
|
this.showOnlineMembers = true; |
|
|
|
@ -385,41 +416,114 @@ export default { |
|
|
|
this.$message.success('航线名称更新成功'); |
|
|
|
} |
|
|
|
}, |
|
|
|
// 新建航线(占位) |
|
|
|
// 新建航线 |
|
|
|
createRoute() { |
|
|
|
this.$message.info('新建航线功能开发中...'); |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.startMissionRouteDrawing(); |
|
|
|
this.$message.success('进入航线规划模式'); |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
/** 从数据库拉取最新的航线列表数据 */ |
|
|
|
async getList() { |
|
|
|
const query = { scenarioId: 1 }; |
|
|
|
try { |
|
|
|
const response = await listRoutes(query); |
|
|
|
if (response.code === 200) { |
|
|
|
this.routes = response.rows.map(item => ({ |
|
|
|
id: item.id, |
|
|
|
name: item.callSign, |
|
|
|
points: item.waypoints ? item.waypoints.length : 0, |
|
|
|
conflict: false |
|
|
|
})); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
this.$message.error("获取航线列表失败"); |
|
|
|
} |
|
|
|
}, |
|
|
|
handleMapDrawComplete(points) { |
|
|
|
if (!points || points.length < 2) { |
|
|
|
this.$message.error('航点太少,无法生成航线'); |
|
|
|
this.drawDom = false; |
|
|
|
return; |
|
|
|
} |
|
|
|
this.tempMapPoints = points; // 暂存坐标点 |
|
|
|
this.showNameDialog = true; // 弹出对话框起名 |
|
|
|
}, |
|
|
|
/** 弹窗点击“确定”:正式将数据保存到后端数据库 */ |
|
|
|
async confirmSaveNewRoute() { |
|
|
|
// 严格校验起名逻辑 |
|
|
|
if (!this.newRouteName || this.newRouteName.trim() === '') { |
|
|
|
this.$message.error('新增航线未命名,请输入名称后保存!'); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 构造符合后端 Routes 实体类的数据结构 |
|
|
|
const routeData = { |
|
|
|
callSign: this.newRouteName, |
|
|
|
scenarioId: 1, // 示例:当前归属方案ID(实际应从全局状态获取) |
|
|
|
platformId: 1, |
|
|
|
attributes: "{}", |
|
|
|
waypoints: this.tempMapPoints.map((p, index) => ({ |
|
|
|
name: `WP${index + 1}`, |
|
|
|
lat: p.lat, |
|
|
|
lng: p.lng, |
|
|
|
alt: 5000.0, |
|
|
|
speed: 800.0, |
|
|
|
startTime: 'K+00:00:00', |
|
|
|
turnAngle: 0.0 |
|
|
|
})) |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
// 调用后端 API 保存到数据库 |
|
|
|
const response = await addRoutes(routeData); |
|
|
|
if (response.code === 200) { |
|
|
|
this.$message.success('航线及其航点已成功保存至数据库'); |
|
|
|
// 重置 UI 状态 |
|
|
|
this.showNameDialog = false; |
|
|
|
this.drawDom = false; |
|
|
|
this.newRouteName = ''; |
|
|
|
this.tempMapPoints = []; |
|
|
|
// 重新拉取右侧航线列表以保持同步 |
|
|
|
await this.getList(); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("保存航线失败:", error); |
|
|
|
this.$message.error('保存失败,请检查后端服务'); |
|
|
|
} |
|
|
|
}, |
|
|
|
// 航点编辑弹窗相关方法 |
|
|
|
openWaypointDialog(waypoint) { |
|
|
|
this.selectedWaypoint = waypoint; |
|
|
|
this.showWaypointDialog = true; |
|
|
|
}, |
|
|
|
updateWaypoint(updatedWaypoint) { |
|
|
|
// 1. 检查是否有正在编辑的航线详情 |
|
|
|
if (this.selectedRouteDetails && this.selectedRouteDetails.waypoints) { |
|
|
|
|
|
|
|
// 2. 找到当前被编辑的这个航点在数组中的位置 |
|
|
|
const index = this.selectedRouteDetails.waypoints.indexOf(this.selectedWaypoint); |
|
|
|
|
|
|
|
if (index !== -1) { |
|
|
|
// 3. 使用 splice 方法替换数据 |
|
|
|
this.selectedRouteDetails.waypoints.splice(index, 1, updatedWaypoint); |
|
|
|
|
|
|
|
// 4. 更新当前的选中引用,以便连续编辑 |
|
|
|
this.selectedWaypoint = updatedWaypoint; |
|
|
|
|
|
|
|
this.$message.success('航点更新成功'); |
|
|
|
} else { |
|
|
|
// 如果用 indexOf 没找到,尝试用名字匹配兜底 |
|
|
|
const nameIndex = this.selectedRouteDetails.waypoints.findIndex(p => p.name === this.selectedWaypoint.name); |
|
|
|
if (nameIndex !== -1) { |
|
|
|
this.selectedRouteDetails.waypoints.splice(nameIndex, 1, updatedWaypoint); |
|
|
|
this.selectedWaypoint = updatedWaypoint; |
|
|
|
this.$message.success('航点更新成功'); |
|
|
|
} else { |
|
|
|
this.$message.error('更新失败:未找到对应航点'); |
|
|
|
/** 航点编辑保存:更新数据库并同步地图显示 */ |
|
|
|
async updateWaypoint(updatedWaypoint) { |
|
|
|
// 确保有选中的航线详情 |
|
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return; |
|
|
|
try { |
|
|
|
const response = await updateWaypoints(updatedWaypoint); |
|
|
|
if (response.code === 200) { |
|
|
|
// 找到当前编辑点在本地数组中的位置 |
|
|
|
const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id); |
|
|
|
if (index !== -1) { |
|
|
|
// 记录旧名称,用于地图组件定位 Entity |
|
|
|
const oldName = this.selectedRouteDetails.waypoints[index].name; |
|
|
|
const newName = updatedWaypoint.name; |
|
|
|
// 使用 splice 触发 Vue 响应式更新右侧面板 UI |
|
|
|
this.selectedRouteDetails.waypoints.splice(index, 1, { ...updatedWaypoint }); |
|
|
|
// 5. 同步更新地图上的图形 |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.updateWaypointGraphic(oldName, newName); |
|
|
|
} |
|
|
|
|
|
|
|
this.showWaypointDialog = false; |
|
|
|
this.$message.success('航点信息已持久化至数据库'); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("更新航点失败:", error); |
|
|
|
this.$message.error('数据库更新失败,请重试'); |
|
|
|
} |
|
|
|
}, |
|
|
|
updateTime() { |
|
|
|
@ -677,9 +781,9 @@ export default { |
|
|
|
this.isRightPanelHidden = false; |
|
|
|
} |
|
|
|
} else if(item.id === 'modify'){ |
|
|
|
this.drawDom = !this.drawDom |
|
|
|
console.log(this.drawDom,999999) |
|
|
|
} |
|
|
|
this.drawDom = !this.drawDom |
|
|
|
console.log(this.drawDom,999999) |
|
|
|
} |
|
|
|
if (item.id === 'deduction') { |
|
|
|
// 点击推演按钮,显示/隐藏K时弹出框 |
|
|
|
this.showKTimePopup = !this.showKTimePopup; |
|
|
|
@ -774,23 +878,64 @@ export default { |
|
|
|
const minutes = (val % 4) * 15; |
|
|
|
return `K+${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`; |
|
|
|
}, |
|
|
|
/** 切换航线:实现多选/开关逻辑 */ |
|
|
|
async selectRoute(route) { |
|
|
|
const index = this.activeRouteIds.indexOf(route.id); |
|
|
|
|
|
|
|
// 航线操作 |
|
|
|
selectRoute(route) { |
|
|
|
this.selectedRouteId = route.id; |
|
|
|
// 模拟获取航点数据 |
|
|
|
this.selectedRouteDetails = { |
|
|
|
name: route.name, |
|
|
|
waypoints: [ |
|
|
|
{ name: 'WP1', altitude: 5000, speed: '800km/h', eta: 'K+00:40:00' }, |
|
|
|
{ name: 'WP2', altitude: 6000, speed: '850km/h', eta: 'K+00:55:00' }, |
|
|
|
{ name: 'WP3', altitude: 5500, speed: '820km/h', eta: 'K+01:10:00' }, |
|
|
|
{ name: 'WP4', altitude: 5800, speed: '830km/h', eta: 'K+01:25:00' }, |
|
|
|
] |
|
|
|
}; |
|
|
|
// 移除原有的 this.openRouteDialog(route); |
|
|
|
// 航线已在选中列表中 |
|
|
|
if (index > -1) { |
|
|
|
this.activeRouteIds.splice(index, 1); |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.removeRouteById(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.selectedRouteDetails = { |
|
|
|
id: res.data.id, |
|
|
|
name: res.data.callSign, |
|
|
|
waypoints: res.data.waypoints || [] |
|
|
|
}; |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
console.error("回显剩余航线失败", e); |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
} |
|
|
|
} |
|
|
|
this.$message.info(`已移除航线: ${route.name}`); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 航线未被选中 |
|
|
|
try { |
|
|
|
const response = await getRoutes(route.id); |
|
|
|
if (response.code === 200 && response.data) { |
|
|
|
const fullRouteData = response.data; |
|
|
|
const waypoints = fullRouteData.waypoints || []; |
|
|
|
this.activeRouteIds.push(route.id); |
|
|
|
this.selectedRouteDetails = { |
|
|
|
id: fullRouteData.id, |
|
|
|
name: fullRouteData.callSign, |
|
|
|
waypoints: waypoints |
|
|
|
}; |
|
|
|
if (waypoints.length > 0) { |
|
|
|
// 通知地图渲染 |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.$refs.cesiumMap.renderRouteWaypoints(waypoints, route.id); |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.$message.warning('该航线暂无坐标数据,无法在地图展示'); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error("获取航线详情失败:", error); |
|
|
|
this.$message.error('无法加载该航线的详细航点数据'); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
addWaypoint() { |
|
|
|
if (this.selectedRouteDetails) { |
|
|
|
const count = this.selectedRouteDetails.waypoints.length + 1; |
|
|
|
@ -805,9 +950,15 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
cancelRoute() { |
|
|
|
this.selectedRouteId = null; |
|
|
|
// 清空所有选中的航线 |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
this.activeRouteIds.forEach(id => { |
|
|
|
this.$refs.cesiumMap.removeRouteById(id); |
|
|
|
}); |
|
|
|
} |
|
|
|
this.activeRouteIds = []; |
|
|
|
this.selectedRouteDetails = null; |
|
|
|
this.$message.info('已取消选中'); |
|
|
|
this.$message.info('已清空所有选中航线'); |
|
|
|
}, |
|
|
|
|
|
|
|
// 冲突操作 |
|
|
|
@ -857,7 +1008,7 @@ export default { |
|
|
|
height: 100%; |
|
|
|
background: linear-gradient(135deg, #1a2f4b 0%, #2c3e50 100%); |
|
|
|
/* 正确的写法,直接复制这行替换 */ |
|
|
|
background: url('~@/assets/map-background.png'); |
|
|
|
background: url('~@/assets/map-background.png'); |
|
|
|
background-size: cover; |
|
|
|
background-position: center; |
|
|
|
z-index: 1; |
|
|
|
|