You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2016 lines
62 KiB

<template>
<!-- 以地图为绝对定位背景所有组件浮动其上 -->
<div class="mission-planning-container">
<!-- 地图背景 -->
<div id="gis-map-background" class="map-background">
<!-- cesiummap组件 -->
<cesiumMap ref="cesiumMap" :drawDomClick="drawDom || airspaceDrawDom"
:tool-mode="drawDom ? 'ranging' : (airspaceDrawDom ? 'airspace' : 'airspace')"
@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>
<p class="text-sm mt-1">支持标绘/航线/空域/实时态势</p>
</div>
<!-- 地图中间的浮动红点触发左侧菜单 -->
<div
class="floating-red-dot left-red-dot"
:class="{ hidden: !isMenuHidden }"
@click="showMenu"
title="显示左侧菜单"
>
<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>
2 months ago
<!-- 顶部导航栏 -->
<top-header
:room-code="roomCode"
:online-count="onlineCount"
:combat-time="combatTime"
:astro-time="astroTime"
:user-avatar="userAvatar"
@select-nav="selectTopNav"
@save-plan="savePlan"
@import-plan-file="importPlanFile"
@import-acd="importACD"
@import-ato="importATO"
@import-layer="importLayer"
@import-route="importRoute"
@export-plan="exportPlan"
@route-edit="routeEdit"
@military-marking="militaryMarking"
@icon-edit="iconEdit"
@toggle-icon-edit="toggleIconEditMode"
2 months ago
@attribute-edit="attributeEdit"
@time-settings="timeSettings"
@aircraft-settings="aircraftSettings"
@key-event-edit="keyEventEdit"
@missile-launch="missileLaunch"
@toggle-2d-3d="toggle2D3D"
@toggle-ruler="toggleRuler"
@toggle-grid="toggleGrid"
@save-scale="saveScale"
2 months ago
@load-terrain="loadTerrain"
@change-projection="changeProjection"
@load-aero-chart="loadAeroChart"
@save-power-zone="savePowerZone"
2 months ago
@threat-zone="threatZone"
@route-calculation="routeCalculation"
@conflict-display="conflictDisplay"
@data-materials="dataMaterials"
@coordinate-conversion="coordinateConversion"
@page-layout="pageLayout"
@data-storage-path="dataStoragePath"
@save-external-params="saveExternalParams"
@import-airport="importAirport"
@import-route-data="importRouteData"
@import-landmark="importLandmark"
2 months ago
@toggle-airport="toggleAirport"
@toggle-landmark="toggleLandmark"
@toggle-route="toggleRoute"
@system-description="systemDescription"
@layer-favorites="layerFavorites"
@route-favorites="routeFavorites"
@show-online-members="showOnlineMembersDialog"
/>
<!-- 左侧折叠菜单栏 - 蓝色主题 -->
3 months ago
<left-menu
:is-hidden="isMenuHidden"
:menu-items="menuItems"
:active-menu="activeMenu"
:is-edit-mode="isIconEditMode"
:available-icons="topNavItems"
:position="menuPosition"
3 months ago
@hide="hideMenu"
@select="selectMenu"
@menu-action="handleMenuAction"
@update:menu-items="updateMenuItems"
@drag-end="handleMenuDragEnd"
@add-items="handleAddMenuItems"
@delete="handleDeleteMenuItem"
3 months ago
/>
<!-- 右侧实体列表浮动- 蓝色主题 -->
3 months ago
<right-panel
ref="rightPanel"
3 months ago
:is-hidden="isRightPanelHidden"
:active-tab="activeRightTab"
:plans="plans"
:selected-plan-id="selectedPlanId"
:selected-plan-details="selectedPlanDetails"
3 months ago
:selected-route-id="selectedRouteId"
:routes="routes"
:active-route-ids="activeRouteIds"
3 months ago
:selected-route-details="selectedRouteDetails"
:conflicts="conflicts"
:conflict-count="conflictCount"
:air-platforms="airPlatforms"
:sea-platforms="seaPlatforms"
:ground-platforms="groundPlatforms"
@hide="hideRightPanel"
@select-route="selectRoute"
@create-route="createRoute"
2 months ago
@delete-route="handleDeleteRoute"
@select-plan="selectPlan"
@create-plan="createPlan"
@delete-plan="executeDeletePlan"
@open-plan-dialog="openPlanDialog"
3 months ago
@open-route-dialog="openRouteDialog"
@open-waypoint-dialog="openWaypointDialog"
@add-waypoint="addWaypoint"
@cancel-route="cancelRoute"
@toggle-route-visibility="toggleRouteVisibility"
3 months ago
@view-conflict="viewConflict"
@resolve-conflict="resolveConflict"
@run-conflict-check="runConflictCheck"
@open-platform-dialog="openPlatformDialog"
2 months ago
@open-import-dialog="showImportDialog = true"
3 months ago
/>
3 months ago
<!-- 左下角工具面板 -->
<bottom-left-panel />
<!-- 底部时间轴最初版本的样式- 蓝色主题 -->
<div
class="floating-timeline blue-theme"
:class="{ 'show': showKTimePopup }"
>
<!-- 隐藏按钮向下箭头 -->
<div class="popup-hide-btn" @click="hideKTimePopup" title="隐藏K时">
<i class="el-icon-arrow-down"></i>
</div>
<div class="timeline-controls">
<div class="current-time blue-time">
<i class="el-icon-time"></i>
<span class="time-text">{{ currentTime }}</span>
</div>
<div class="timeline-slider">
<el-slider
v-model="timeProgress"
:max="100"
:format-tooltip="formatTimeTooltip"
class="compact-slider blue-slider"
/>
</div>
3 months ago
<div class="playback-controls">
<button
class="control-btn blue-control-btn"
3 months ago
@click="togglePlay"
:title="isPlaying ? '暂停' : '播放'"
>
<i :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
</button>
<div class="speed-control">
<button
class="control-btn blue-control-btn"
3 months ago
@click="decreaseSpeed"
:disabled="playbackSpeed <= 1"
title="减速"
>
<i class="el-icon-arrow-down"></i>
</button>
<span class="speed-text">{{ playbackSpeed }}x</span>
<button
class="control-btn blue-control-btn"
3 months ago
@click="increaseSpeed"
:disabled="playbackSpeed >= 25"
title="加速"
>
<i class="el-icon-arrow-up"></i>
</button>
</div>
</div>
</div>
3 months ago
</div>
3 months ago
<!-- 在线成员弹窗 -->
<online-members-dialog
v-model="showOnlineMembers"
/>
3 months ago
<!-- 平台编辑弹窗 -->
<platform-edit-dialog
v-model="showPlatformDialog"
:platform="selectedPlatform"
@save="updatePlatform"
/>
3 months ago
<!-- 航线编辑弹窗 -->
<route-edit-dialog
v-model="showRouteDialog"
:route="selectedRoute"
@save="updateRoute"
/>
3 months ago
<!-- 航点编辑弹窗 -->
<waypoint-edit-dialog
v-model="showWaypointDialog"
:waypoint="selectedWaypoint"
@save="updateWaypoint"
/>
<!-- 威力区弹窗 -->
<power-zone-dialog
v-model="showPowerZoneDialog"
:power-zone="currentPowerZone"
@save="savePowerZone"
/>
<!-- 比例尺弹窗 -->
<scale-dialog
v-model="showScaleDialog"
:scale="currentScale"
@save="saveScale"
/>
<!-- 外部参数弹窗 -->
<external-params-dialog
v-model="showExternalParamsDialog"
:external-params="currentExternalParams"
@save="saveExternalParams"
@import-airport="importAirport"
@import-route-data="importRoute"
@import-landmark="importLandmark"
/>
<!-- 页面布局弹窗 -->
<page-layout-dialog
v-model="showPageLayoutDialog"
@save="savePageLayout"
/>
2 months ago
<!-- 导入平台弹窗 -->
<PlatformImportDialog
:visible.sync="showImportDialog"
@confirm="handleImportConfirm"
/>
<el-dialog
title="新建方案"
:visible.sync="showPlanNameDialog"
width="400px"
append-to-body
>
<el-form :model="newPlanForm" ref="newPlanForm" :rules="planRules" label-width="80px">
<el-form-item label="方案名称" prop="name">
<el-input v-model="newPlanForm.name" placeholder="请输入方案名称"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showPlanNameDialog = false"> </el-button>
<el-button type="primary" @click="confirmCreatePlan"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import cesiumMap from '@/views/cesiumMap'
3 months ago
import OnlineMembersDialog from '@/views/dialogs/OnlineMembersDialog'
import PlatformEditDialog from '@/views/dialogs/PlatformEditDialog'
import RouteEditDialog from '@/views/dialogs/RouteEditDialog'
import WaypointEditDialog from '@/views/dialogs/WaypointEditDialog'
import PowerZoneDialog from '@/views/dialogs/PowerZoneDialog'
import ScaleDialog from '@/views/dialogs/ScaleDialog'
import ExternalParamsDialog from '@/views/dialogs/ExternalParamsDialog'
import PageLayoutDialog from '@/views/dialogs/PageLayoutDialog'
3 months ago
import LeftMenu from './LeftMenu'
import RightPanel from './RightPanel'
3 months ago
import BottomLeftPanel from './BottomLeftPanel'
2 months ago
import TopHeader from './TopHeader'
import { listScenario,addScenario,delScenario} from "@/api/system/scenario";
import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes";
import { updateWaypoints } from "@/api/system/waypoints";
2 months ago
import { listLib,addLib } from "@/api/system/lib";
import PlatformImportDialog from "@/views/dialogs/PlatformImportDialog.vue";
export default {
name: 'MissionPlanningView',
components: {
2 months ago
PlatformImportDialog,
3 months ago
cesiumMap,
OnlineMembersDialog,
PlatformEditDialog,
RouteEditDialog,
WaypointEditDialog,
PowerZoneDialog,
ScaleDialog,
ExternalParamsDialog,
PageLayoutDialog,
3 months ago
LeftMenu,
3 months ago
RightPanel,
2 months ago
BottomLeftPanel,
TopHeader
},
data() {
return {
3 months ago
drawDom:false,
airspaceDrawDom:false,
3 months ago
// 在线成员弹窗
showOnlineMembers: false,
// 编辑弹窗控制
showPlatformDialog: false,
selectedPlatform: null,
showRouteDialog: false,
selectedRoute: null,
showWaypointDialog: false,
selectedWaypoint: null,
// 威力区、比例尺、外部参数弹窗
showPowerZoneDialog: false,
currentPowerZone: {},
showScaleDialog: false,
currentScale: {},
showExternalParamsDialog: false,
currentExternalParams: {},
showPageLayoutDialog: false,
menuPosition: 'left',
showNameDialog: false,
newRouteName: '',
tempMapPoints: [],
2 months ago
//导入平台弹窗
showImportDialog: false,
// 作战信息
3 months ago
roomCode: 'JTF-7-ALPHA',
onlineCount: 30,
combatTime: 'K+01:30:45',
astroTime: '',
// 左侧菜单栏
isMenuHidden: true, // 是否完全隐藏左侧菜单
activeMenu: 'file',
isIconEditMode: false, // 图标编辑模式
// 方案-航线-航点数据
selectedPlanId: null,
selectedPlanDetails: null,
routes: [],
selectedRouteId: null,
selectedRouteDetails: null,
showPlanNameDialog: false, // 控制新建方案弹窗
newPlanForm: {
name: ''
},
planRules: {
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }]
},
// 顶部导航菜单项(用于图标选择)- 只显示指定的菜单项
topNavItems: [
{ id: 'routeEdit', name: '航线编辑', icon: 'el-icon-edit-outline' },
{ id: 'militaryMarking', name: '军事标绘', icon: 'el-icon-crop' },
{ id: 'attributeEdit', name: '属性修改', icon: 'el-icon-setting' },
{ id: 'timeSettings', name: '时间设置', icon: 'el-icon-timer' },
{ id: 'aircraftSettings', name: '机型设置', icon: 'el-icon-s-custom' },
{ id: 'keyEventEdit', name: '关键事件编辑', icon: 'el-icon-edit' },
{ id: 'missileLaunch', name: '导弹发射', icon: 'el-icon-s-promotion' },
{ id: 'toggle2D3D', name: '2D/3D切换', icon: 'el-icon-picture-outline' },
{ id: 'toggleRuler', name: '显示/隐藏标尺', icon: 'el-icon-set-up' },
{ id: 'toggleGrid', name: '网格', icon: 'el-icon-s-grid' },
{ id: 'toggleScale', name: '比例尺', icon: 'el-icon-data-line' },
{ id: 'loadTerrain', name: '加载/切换地形', icon: 'el-icon-picture-outline-round' },
{ id: 'changeProjection', name: '投影', icon: 'el-icon-aim' },
{ id: 'loadAeroChart', name: '航空图', icon: 'el-icon-map-location' },
{ id: 'powerZone', name: '威力区', icon: 'el-icon-warning-outline' },
{ id: 'threatZone', name: '威胁区', icon: 'el-icon-warning' },
{ id: 'routeCalculation', name: '航线计算', icon: 'el-icon-s-data' },
{ id: 'conflictDisplay', name: '冲突显示', icon: 'el-icon-error' },
{ id: 'dataMaterials', name: '数据资料', icon: 'el-icon-folder' },
{ id: 'coordinateConversion', name: '坐标换算', icon: 'el-icon-coordinate' },
{ id: 'pageLayout', name: '页面布局', icon: 'el-icon-menu' },
{ id: 'dataStoragePath', name: '数据存储路径', icon: 'el-icon-folder-opened' },
{ id: 'externalParams', name: '外部参数', icon: 'el-icon-s-tools' },
{ id: 'toggleAirport', name: '显示/隐藏机场', icon: 'el-icon-s-flag' },
{ id: 'toggleLandmark', name: '显示/隐藏地标', icon: 'el-icon-location-outline' },
{ id: 'toggleRoute', name: '显示/隐藏航线', icon: 'el-icon-position' },
{ id: 'systemDescription', name: '系统说明', icon: 'el-icon-info' },
{ id: 'layerFavorites', name: '图层收藏', icon: 'el-icon-star-off' },
{ id: 'routeFavorites', name: '航线收藏', icon: 'el-icon-collection' }
],
// 右侧面板控制
isRightPanelHidden: true, // 是否完全隐藏右侧面板
// K时弹出框控制
showKTimePopup: false,
2 months ago
// 显示/隐藏控制
showAirport: true,
showLandmark: true,
showRoute: true,
menuItems: [
{ id: 'file', name: '方案', icon: 'el-icon-folder-opened' },
{ id: 'start', name: '冲突', icon: 'el-icon-error' },
{ id: 'insert', name: '平台', icon: 'el-icon-s-platform' },
{ id: 'pattern', name: '空域', icon: 'el-icon-picture-outline-round' },
{ id: 'deduction', name: '推演', icon: 'el-icon-video-play' },
{ id: 'modify', name: '测距', icon: 'el-icon-edit-outline' },
{ id: 'refresh', name: '刷新', icon: 'el-icon-refresh' },
{ id: 'basemap', name: '底图', icon: 'el-icon-picture' },
{ id: 'save', name: '保存', icon: 'el-icon-document-checked' },
{ id: 'import', name: '导入', icon: 'el-icon-upload2' },
{ id: 'export', name: '导出', icon: 'el-icon-download' }
],
// 右侧面板
currentRoomId: null,
plans: [],
activeRightTab: 'plan',
activeRouteIds: [], // 存储当前所有选中的航线ID
// 冲突数据
conflictCount: 2,
conflicts: [
{
id: 1,
title: '航线空间冲突',
routes: ['Alpha进场航线', 'Beta巡逻航线'],
time: 'K+01:20:00',
position: 'E116° N39°',
severity: 'high'
},
{
id: 2,
title: '时间窗口重叠',
routes: ['侦察覆盖区', '无人机巡逻'],
time: 'K+02:15:00',
position: 'E117° N38°',
severity: 'medium'
}
],
// 平台数据
activePlatformTab: 'air',
2 months ago
airPlatforms: [],
seaPlatforms: [],
groundPlatforms: [],
// 时间控制
timeProgress: 45,
currentTime: 'K+01:15:30',
3 months ago
isPlaying: false,
playbackSpeed: 1,
playbackInterval: null,
// 用户
userAvatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
};
},
mounted() {
this.getList();
// 初始化时左侧菜单隐藏
this.isMenuHidden = true;
// 初始化时右侧面板隐藏
this.isRightPanelHidden = true;
3 months ago
// 更新时间
this.updateTime();
setInterval(this.updateTime, 1000);
// 作战时间也需要实时更新
setInterval(this.updateCombatTime, 1000);
},
3 months ago
beforeDestroy() {
// 清除播放定时器
if (this.playbackInterval) {
clearInterval(this.playbackInterval);
this.playbackInterval = null;
}
},
created() {
this.currentRoomId = this.$route.query.roomId;
console.log("从路由接收到的真实房间 ID:", this.currentRoomId);
this.getList();
2 months ago
this.getPlatformList();
},
methods: {
// 处理从地图点击传来的编辑请求
async handleOpenWaypointEdit(wpId, routeId) {
console.log(`>>> [父组件接收] 航点 ID: ${wpId}, 所属航线 ID: ${routeId}`);
// 如果点击的点不属于当前选中的航线
if (this.selectedRouteId != routeId) {
console.log(">>> [自动切换] 正在获取新航线详情...");
try {
const response = await getRoutes(routeId);
if (response.code === 200 && response.data) {
const fullRouteData = response.data;
// 同步更新父组件状态,保持和 selectRoute 方法一致的结构
this.selectedRouteId = fullRouteData.id;
this.selectedRouteDetails = {
id: fullRouteData.id,
name: fullRouteData.callSign,
waypoints: fullRouteData.waypoints || []
};
}
} catch (error) {
this.$message.error('切换航线数据失败');
return;
}
}
// 此时账本已确保是最新且正确的,开始找点
const wpData = this.selectedRouteDetails.waypoints.find(item => item.id == wpId);
if (wpData) {
// 成功找到业务数据,深拷贝后打开弹窗
this.selectedWaypoint = JSON.parse(JSON.stringify(wpData));
this.showWaypointDialog = true;
} else {
this.$message.info('未找到该航点的业务数据');
console.error("查找失败!账本内IDs:", this.selectedRouteDetails.waypoints.map(w => w.id));
}
2 months ago
// 获取当前最新的航点列表
const waypointsList = this.selectedRouteDetails.waypoints;
// 找到点在数组里的索引
const index = waypointsList.findIndex(item => item.id == wpId);
const total = waypointsList.length;
if (index !== -1) {
const wpData = waypointsList[index];
this.selectedWaypoint = {
...JSON.parse(JSON.stringify(wpData)),
currentIndex: index,
totalPoints: total
};
console.log(`>>> [地图触发锁定] 序号: ${index}, 总数: ${total}, 数据已装载`);
this.showWaypointDialog = true;
} else {
this.$message.info('未找到该航点的业务数据');
console.error("查找失败!账本内IDs:", waypointsList.map(w => w.id));
}
},
3 months ago
// 显示在线成员弹窗
showOnlineMembersDialog() {
this.showOnlineMembers = true;
},
// 平台编辑弹窗相关方法
openPlatformDialog(platform) {
this.selectedPlatform = platform;
this.showPlatformDialog = true;
},
2 months ago
/** 从数据库查询并分拣平台库数据 */
getPlatformList() {
listLib().then(res => {
const allData = res.rows || [];
this.airPlatforms = [];
this.seaPlatforms = [];
this.groundPlatforms = [];
allData.forEach(item => {
const platform = {
id: item.id,
name: item.name,
type: item.type,
imageUrl: item.iconUrl || '',
icon: item.iconUrl ? '' : 'el-icon-picture-outline',
status: 'ready'
};
if (item.type === 'Air') {
this.airPlatforms.push(platform);
} else if (item.type === 'Sea') {
this.seaPlatforms.push(platform);
} else if (item.type === 'Ground') {
this.groundPlatforms.push(platform);
}
});
});
},
/** 导入确认:将弹窗填写的模版数据存入数据库 */
handleImportConfirm(formData) {
if (!formData.name || !formData.type) {
this.$modal.msgError("请填写完整的平台信息");
return;
}
// 创建 FormData 对象,这是传输二进制文件的标准格式
let data = new FormData();
// 把文件塞进去
if (formData.icon_file) {
data.append("file", formData.icon_file);
}
// 把其他文本字段也塞进去
data.append("name", formData.name);
data.append("type", formData.type);
data.append("specsJson", JSON.stringify({
scenarioId: this.selectedPlanId,
createTime: new Date().getTime()
}));
addLib(data).then(response => {
this.$modal.msgSuccess("导入成功");
this.showImportDialog = false;
this.getPlatformList();
}).catch(err => {
// 增加错误提示,防止请求失败时没有任何反应
console.error("上传出错:", err);
});
},
3 months ago
updatePlatform(updatedPlatform) {
// 这里可以添加实际的更新逻辑
this.$message.success('平台更新成功');
},
// 航线编辑弹窗相关方法
openRouteDialog(route) {
this.selectedRoute = route;
this.showRouteDialog = true;
},
2 months ago
// 更新航线数据
3 months ago
updateRoute(updatedRoute) {
const index = this.routes.findIndex(r => r.id === updatedRoute.id);
if (index !== -1) {
// 使用 splice 触发响应式更新
const newRouteData = {...this.routes[index], ...updatedRoute};
this.routes.splice(index, 1, newRouteData);
// 如果当前选中的是这条航线,同步更新详情中的名称
if (this.selectedRouteDetails && this.selectedRouteId === updatedRoute.id) {
this.selectedRouteDetails.name = updatedRoute.name;
}
this.$message.success('航线名称更新成功');
}
},
// 新建航线
createRoute(plan) {
if (!plan || !plan.id) return;
// 只有当目标方案和当前方案不同时,才执行“大扫除”
if (this.selectedPlanId !== plan.id) {
this.selectPlan(plan);
} else {
2 months ago
console.log(">>> 保持当前航线显示,直接进入规划模式");
}
// 进入 Cesium 绘图模式
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.startMissionRouteDrawing();
this.$message.success(`${plan.name}开启航线规划`);
}
},
async handleDeleteRoute(route) {
try {
// 二次确认,防止误删
await this.$confirm(`确定要彻底删除航线 "${route.name}" 吗?`, '提示', {
type: 'warning'
});
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) {
this.selectedRouteDetails = null;
}
const res = await delRoutes(route.id);
if (res.code === 200) {
this.$message.success('删除成功');
// 同步地图:如果该航线正在显示,立即清除
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(route.id);
}
// 同步状态:从选中列表中移除该 ID
const idx = this.activeRouteIds.indexOf(route.id);
if (idx > -1) {
this.activeRouteIds.splice(idx, 1);
}
await this.getList();
}
} catch (e) {
if (e !== 'cancel') {
console.error("删除航线失败:", e);
this.$message.error('删除操作失败');
}
}
},
/** 从数据库拉取最新的航线列表数据 */
async getList() {
try {
const roomId = this.$route.query.roomId || this.currentRoomId;
const scenarioRes = await listScenario({ roomId: roomId });
2 months ago
if (scenarioRes.code === 200) {
this.plans = scenarioRes.rows.map(s => ({
id: s.id,
name: s.name,
routes: []
}));
}
//获取所有航线
2 months ago
const routeRes = await listRoutes({});
if (routeRes.code === 200) {
const allRoutes = routeRes.rows.map(item => ({
id: item.id,
name: item.callSign,
points: item.waypoints ? item.waypoints.length : 0,
waypoints: item.waypoints || [],
conflict: false,
scenarioId: item.scenarioId
}));
//分配航线到方案
2 months ago
this.plans.forEach(plan => {
plan.routes = allRoutes.filter(r => r.scenarioId === plan.id);
});
this.routes = allRoutes;
// 直接根据 activeRouteIds 同步地图即可
if (this.activeRouteIds.length > 0) {
this.$nextTick(() => {
this.activeRouteIds.forEach(id => {
const route = this.routes.find(r => r.id === id);
// 只要数据里有点位,直接指挥地图画线
if (route && route.waypoints && route.waypoints.length > 0) {
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(id);
this.$refs.cesiumMap.renderRouteWaypoints(route.waypoints, id);
}
}
});
});
}
}
} catch (error) {
2 months ago
console.error("数据加载失败:", 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() {
// 1. 严格校验
if (!this.newRouteName || this.newRouteName.trim() === '') {
this.$message.error('新增航线未命名,请输入名称后保存!');
return;
}
const currentScenarioId = this.selectedPlanId;
if (!currentScenarioId) {
this.$message.warning('请先在左侧选择一个方案,再保存航线!');
return;
}
// 2. 构造数据
const finalWaypoints = 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
}));
const routeData = {
callSign: this.newRouteName,
scenarioId: currentScenarioId,
platformId: 1,
attributes: "{}",
waypoints: finalWaypoints
};
2 months ago
try {
const response = await addRoutes(routeData);
if (response.code === 200) {
2 months ago
const savedRoute = response.data;
const newRouteId = savedRoute?.id;
if (!newRouteId || !savedRoute.waypoints) {
this.$message.error('保存成功但返回数据结构异常,请手动刷新');
return;
}
// 1. 更新当前选中详情
this.selectedRouteId = newRouteId;
this.selectedRouteDetails = JSON.parse(JSON.stringify(savedRoute));
// 2. 【修复关键 A】:使用 push 而不是赋值
// 确保新 ID 进去的同时,activeRouteIds 里原有的老 ID(如 112)还在
// 这样右侧列表里,老的航线依然会保持勾选状态
if (!this.activeRouteIds.includes(newRouteId)) {
this.activeRouteIds.push(newRouteId);
}
// 3. UI 重置
this.drawDom = false;
this.showNameDialog = false;
this.newRouteName = '';
this.tempMapPoints = [];
// 4. 【修复关键 B】:千万不要调用 clearAllWaypoints()!
// 改为只删除绘制过程中的临时预览实体,保留地图上已有的正式线
if (this.$refs.cesiumMap) {
// 只清理临时点(假如你的地图组件有这个更细的方法)
// 如果没有,就直接删掉 tempPreviewEntity
if (this.$refs.cesiumMap.tempPreviewEntity) {
this.viewer.entities.remove(this.$refs.cesiumMap.tempPreviewEntity);
this.$refs.cesiumMap.tempPreviewEntity = null;
}
// 如果 clearAllWaypoints 会清空所有,那这里就不能调用它
}
// 5. 渲染新航线
this.$nextTick(() => {
if (this.$refs.cesiumMap) {
// 只画这一条新线,旧线因为没被清除,依然会留在地图上
this.$refs.cesiumMap.renderRouteWaypoints(savedRoute.waypoints, newRouteId);
}
this.$message.success('航线及其航点已成功保存并同步');
});
this.getList();
}
} catch (error) {
console.error(">>> [保存异常]:", error);
this.$message.error('保存失败,请检查网络或后端日志');
}
},
3 months ago
// 航点编辑弹窗相关方法
2 months ago
openWaypointDialog(data) {
console.log(">>> [父组件接收] 编辑航点详情:", data);
// 将整个对象赋值给弹窗绑定的变量
this.selectedWaypoint = data;
3 months ago
this.showWaypointDialog = true;
},
/** 航点编辑保存:更新数据库并同步地图显示 */
async updateWaypoint(updatedWaypoint) {
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return;
try {
2 months ago
if (this.$refs.cesiumMap && updatedWaypoint.turnAngle > 0) {
updatedWaypoint.turnRadius = this.$refs.cesiumMap.getWaypointRadius(updatedWaypoint);
} else {
updatedWaypoint.turnRadius = 0;
}
const response = await updateWaypoints(updatedWaypoint);
if (response.code === 200) {
const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id);
if (index !== -1) {
2 months ago
// 更新本地数据
this.selectedRouteDetails.waypoints.splice(index, 1, { ...updatedWaypoint });
// 通知地图组件同步更新
if (this.$refs.cesiumMap) {
2 months ago
// 更新航点图标
this.$refs.cesiumMap.updateWaypointGraphicById(updatedWaypoint.id, updatedWaypoint.name);
2 months ago
// 重新触发航线渲染
this.$refs.cesiumMap.renderRouteWaypoints(
this.selectedRouteDetails.waypoints,
this.selectedRouteDetails.id
);
}
this.showWaypointDialog = false;
this.$message.success('航点信息已持久化至数据库');
}
2 months ago
} else {
// 如果 code 不是 200,手动抛出错误进入catch
throw new Error(response.msg || '后端业务逻辑校验失败');
}
} catch (error) {
console.error("更新航点失败:", error);
2 months ago
this.$message.error(error.message || '数据库更新失败,请重试');
}
3 months ago
},
3 months ago
updateTime() {
const now = new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
this.astroTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
3 months ago
updateCombatTime() {
// 模拟作战时间(K时)的更新
// 这里简单模拟,实际应该根据业务逻辑计算
const now = new Date();
const baseSeconds = 5400; // 1小时30分钟 = 5400秒
const currentSeconds = now.getSeconds() + now.getMinutes() * 60 + now.getHours() * 3600;
const combatSeconds = baseSeconds + (currentSeconds % 86400);
3 months ago
const hours = Math.floor(combatSeconds / 3600);
const minutes = Math.floor((combatSeconds % 3600) / 60);
const seconds = combatSeconds % 60;
3 months ago
this.combatTime = `K+${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
},
// 左侧菜单栏操作
showMenu() {
this.isMenuHidden = false;
},
hideMenu() {
this.isMenuHidden = true;
},
// 右侧面板操作
showRightPanel() {
this.isRightPanelHidden = false;
this.$message.info('显示右侧面板');
},
// 顶部导航栏操作
selectTopNav(item) {
this.activeMenu = item.id;
},
3 months ago
// 文件下拉菜单方法
savePlan() {
this.$message.success('保存计划');
},
3 months ago
importPlanFile() {
this.$message.success('导入计划');
// 这里可以添加导入计划文件的逻辑
3 months ago
},
3 months ago
importACD() {
this.$message.success('导入ACD');
// 这里可以添加导入ACD文件的逻辑
3 months ago
},
3 months ago
importATO() {
this.$message.success('导入ATO');
// 这里可以添加导入ATO文件的逻辑
3 months ago
},
3 months ago
importLayer() {
this.$message.success('导入图层');
// 这里可以添加导入图层的逻辑
3 months ago
},
3 months ago
importRoute() {
this.$message.success('导入航线');
// 这里可以添加导入航线的逻辑
3 months ago
},
3 months ago
exportPlan() {
this.$message.success('导出计划');
// 这里可以添加导出计划的逻辑
3 months ago
},
3 months ago
// 编辑下拉菜单方法
routeEdit() {
this.$message.success('航线编辑');
// 这里可以添加航线编辑的逻辑
3 months ago
},
3 months ago
militaryMarking() {
this.$message.success('军事标绘');
// 这里可以添加军事标绘的逻辑
3 months ago
},
3 months ago
iconEdit() {
// 这个方法现在由 TopHeader 组件处理状态切换
// 只需要在这里处理其他逻辑(如果有的话)
},
toggleIconEditMode(isEditMode) {
this.isIconEditMode = isEditMode
},
updateMenuItems(newItems) {
this.menuItems = newItems
},
handleMenuDragEnd(newItems) {
this.menuItems = newItems
},
handleAddMenuItems(selectedItems) {
selectedItems.forEach(item => {
const newId = Date.now().toString() + Math.random().toString(36).substr(2, 9)
const newMenuItem = {
id: newId,
name: item.name,
icon: item.icon,
action: item.id
}
this.menuItems.push(newMenuItem)
})
},
handleMenuAction(actionId) {
const actionMap = {
'savePlan': () => this.savePlan(),
'routeEdit': () => this.routeEdit(),
'militaryMarking': () => this.militaryMarking(),
'attributeEdit': () => this.attributeEdit(),
'timeSettings': () => this.timeSettings(),
'aircraftSettings': () => this.aircraftSettings(),
'keyEventEdit': () => this.keyEventEdit(),
'missileLaunch': () => this.missileLaunch(),
'toggle2D3D': () => this.toggle2D3D(),
'toggleRuler': () => this.toggleRuler(),
'toggleGrid': () => this.toggleGrid(),
'toggleScale': () => {
this.showScaleDialog = true
this.currentScale = {}
},
'loadTerrain': () => this.loadTerrain(),
'changeProjection': () => this.changeProjection(),
'loadAeroChart': () => this.loadAeroChart(),
'powerZone': () => {
this.showPowerZoneDialog = true
this.currentPowerZone = {}
},
'threatZone': () => this.threatZone(),
'routeCalculation': () => this.routeCalculation(),
'conflictDisplay': () => this.conflictDisplay(),
'dataMaterials': () => this.dataMaterials(),
'coordinateConversion': () => this.coordinateConversion(),
'pageLayout': () => this.pageLayout(),
'dataStoragePath': () => this.dataStoragePath(),
'externalParams': () => {
this.showExternalParamsDialog = true
this.currentExternalParams = {}
},
'toggleAirport': () => this.toggleAirport(),
'toggleLandmark': () => this.toggleLandmark(),
'toggleRoute': () => this.toggleRoute(),
'layerFavorites': () => this.layerFavorites(),
'routeFavorites': () => this.routeFavorites()
}
if (actionMap[actionId]) {
actionMap[actionId]()
}
},
handleDeleteMenuItem(deletedItem) {
const index = this.menuItems.findIndex(item => item.id === deletedItem.id)
if (index > -1) {
this.menuItems.splice(index, 1)
}
3 months ago
},
3 months ago
attributeEdit() {
this.$message.success('属性修改');
},
3 months ago
timeSettings() {
this.$message.success('时间设置');
// 这里可以添加时间设置的逻辑
3 months ago
},
3 months ago
aircraftSettings() {
this.$message.success('机型设置');
// 这里可以添加机型设置的逻辑
3 months ago
},
3 months ago
keyEventEdit() {
this.$message.success('关键事件编辑');
// 这里可以添加关键事件编辑的逻辑
3 months ago
},
3 months ago
missileLaunch() {
this.$message.success('导弹发射');
// 这里可以添加导弹发射的逻辑
3 months ago
},
3 months ago
// 视图下拉菜单方法
toggle2D3D() {
this.$message.success('2D/3D切换');
// 这里可以添加2D/3D切换的逻辑
3 months ago
},
3 months ago
toggleRuler() {
this.$message.success('显示/隐藏标尺');
// 这里可以添加标尺显示/隐藏的逻辑
3 months ago
},
3 months ago
toggleGrid() {
this.$message.success('显示/隐藏网格');
// 这里可以添加网格显示/隐藏的逻辑
3 months ago
},
saveScale(scale) {
console.log('保存比例尺:', scale)
const scaleText = `${scale.scaleNumerator}:${scale.scaleDenominator}`
this.$message.success(`比例尺 "${scaleText}" 保存成功`);
3 months ago
},
3 months ago
// 地图下拉菜单方法
loadTerrain() {
this.$message.success('加载/切换地形');
// 这里可以添加地形加载/切换的逻辑
3 months ago
},
3 months ago
changeProjection() {
this.$message.success('投影');
// 这里可以添加投影切换的逻辑
3 months ago
},
3 months ago
loadAeroChart() {
this.$message.success('航空图');
2 months ago
},
2 months ago
// 空域下拉菜单方法
savePowerZone(powerZone) {
console.log('保存威力区:', powerZone)
this.$message.success(`威力区 "${powerZone.name}" 保存成功`);
2 months ago
},
2 months ago
threatZone() {
this.$message.success('威胁区');
3 months ago
},
3 months ago
// 工具下拉菜单方法
routeCalculation() {
this.$message.success('航线计算');
// 这里可以添加航线计算的逻辑
3 months ago
},
3 months ago
conflictDisplay() {
this.$message.success('冲突显示');
// 这里可以添加冲突显示的逻辑
3 months ago
},
3 months ago
dataMaterials() {
this.$message.success('数据资料');
// 这里可以添加数据资料管理的逻辑
3 months ago
},
3 months ago
coordinateConversion() {
this.$message.success('坐标换算');
// 这里可以添加坐标换算的逻辑
3 months ago
},
3 months ago
// 选项下拉菜单方法
2 months ago
pageLayout() {
this.showPageLayoutDialog = true;
2 months ago
},
2 months ago
dataStoragePath() {
this.$message.success('数据存储路径');
},
saveExternalParams(externalParams) {
console.log('保存外部参数:', externalParams)
this.$message.success('外部参数保存成功');
},
savePageLayout(position) {
this.menuPosition = position;
this.$message.success(`菜单位置已设置为:${this.getPositionLabel(position)}`);
},
getPositionLabel(position) {
const labels = {
'top': '顶部',
'bottom': '底部',
'left': '左侧',
'right': '右侧'
};
return labels[position] || position;
},
importAirport(path) {
console.log('导入机场:', path)
this.$message.success('机场数据导入成功');
},
importRouteData(path) {
console.log('导入航路:', path)
this.$message.success('航路数据导入成功');
},
importLandmark(path) {
console.log('导入地标:', path)
this.$message.success('地标数据导入成功');
2 months ago
},
2 months ago
toggleAirport() {
this.showAirport = !this.showAirport;
this.$message.success(this.showAirport ? '显示机场' : '隐藏机场');
},
2 months ago
toggleLandmark() {
this.showLandmark = !this.showLandmark;
this.$message.success(this.showLandmark ? '显示地标' : '隐藏地标');
},
2 months ago
toggleRoute() {
this.showRoute = !this.showRoute;
this.$message.success(this.showRoute ? '显示航线' : '隐藏航线');
3 months ago
},
3 months ago
systemDescription() {
this.$message.success('系统说明');
// 这里可以添加系统说明的逻辑
3 months ago
},
3 months ago
// 收藏下拉菜单方法
layerFavorites() {
this.$message.success('图层收藏');
// 这里可以添加图层收藏的逻辑
3 months ago
},
3 months ago
routeFavorites() {
this.$message.success('航线收藏');
2 months ago
},
hideRightPanel() {
this.isRightPanelHidden = true;
this.$message.info('隐藏右侧面板');
},
selectMenu(item) {
this.activeMenu = item.id;
if (item.action) {
this.handleMenuAction(item.action)
}
// 点击方案、平台、冲突等菜单项时,停止地图绘制状态
if (item.id === 'file' || item.id === 'start' || item.id === 'insert') {
this.drawDom = false;
this.airspaceDrawDom = false;
}
3 months ago
// 点击左侧的方案、冲突、平台时,切换右侧面板内容
if (item.id === 'file') {
// 如果当前已经是方案标签页,则关闭右侧面板
if (this.activeRightTab === 'plan' && !this.isRightPanelHidden) {
this.isRightPanelHidden = true;
} else {
this.activeRightTab = 'plan';
this.isRightPanelHidden = false;
}
} else if (item.id === 'start') {
// 如果当前已经是冲突标签页,则关闭右侧面板
if (this.activeRightTab === 'conflict' && !this.isRightPanelHidden) {
this.isRightPanelHidden = true;
} else {
this.activeRightTab = 'conflict';
this.isRightPanelHidden = false;
}
} else if (item.id === 'insert') {
// 如果当前已经是平台标签页,则关闭右侧面板
if (this.activeRightTab === 'platform' && !this.isRightPanelHidden) {
this.isRightPanelHidden = true;
} else {
this.activeRightTab = 'platform';
this.isRightPanelHidden = false;
}
} else if (item.id === 'modify') {
// 点击测距时,启用测距绘制模式,关闭空域绘制模式
this.drawDom = !this.drawDom
this.airspaceDrawDom = false
// 点击测距图标进行地图绘制时,自动收起右侧面板
this.isRightPanelHidden = true;
console.log('测距绘制模式:', this.drawDom, 999999)
} else if (item.id === 'pattern') {
// 点击空域时,启用空域绘制模式,关闭测距绘制模式
this.airspaceDrawDom = !this.airspaceDrawDom
this.drawDom = false
// 点击空域图标进行地图绘制时,自动收起右侧面板
this.isRightPanelHidden = true;
console.log('空域绘制模式:', this.airspaceDrawDom, 999999)
} else if (item.id === 'deduction') {
// 点击推演按钮,显示/隐藏K时弹出框
this.showKTimePopup = !this.showKTimePopup;
// 点击推演时,也停止地图绘制状态
this.drawDom = false;
this.airspaceDrawDom = false;
} else {
// 点击其他菜单项时,也自动收起右侧面板
this.isRightPanelHidden = true;
// 点击其他菜单项时,也停止地图绘制状态
this.drawDom = false;
this.airspaceDrawDom = false;
}
},
// K时弹出框操作
hideKTimePopup() {
this.showKTimePopup = false;
this.$message.info('隐藏推演时钟控制');
},
3 months ago
// 播放控制
togglePlay() {
this.isPlaying = !this.isPlaying;
if (this.isPlaying) {
this.startPlayback();
this.$message.success('开始播放');
} else {
this.stopPlayback();
this.$message.info('暂停播放');
}
},
3 months ago
startPlayback() {
if (this.playbackInterval) {
clearInterval(this.playbackInterval);
}
this.playbackInterval = setInterval(() => {
this.timeProgress += this.playbackSpeed * 0.1;
if (this.timeProgress >= 100) {
this.timeProgress = 0;
}
this.updateTimeFromProgress();
}, 100);
},
3 months ago
stopPlayback() {
if (this.playbackInterval) {
clearInterval(this.playbackInterval);
this.playbackInterval = null;
}
},
3 months ago
increaseSpeed() {
if (this.playbackSpeed < 25) {
this.playbackSpeed++;
if (this.isPlaying) {
this.startPlayback();
}
}
},
3 months ago
decreaseSpeed() {
if (this.playbackSpeed > 1) {
this.playbackSpeed--;
if (this.isPlaying) {
this.startPlayback();
}
}
},
3 months ago
updateTimeFromProgress() {
const totalSeconds = Math.floor(this.timeProgress * 72);
const hours = Math.floor(totalSeconds / 3600) - 2;
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
3 months ago
const sign = hours >= 0 ? '+' : '-';
const absHours = Math.abs(hours);
3 months ago
this.currentTime = `K${sign}${String(absHours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
},
// 时间控制(保留用于底部时间轴)
play() {
this.$message.success('推演开始');
},
pause() {
this.$message.info('推演暂停');
},
reset() {
this.timeProgress = 0;
this.currentTime = 'K+00:00:00';
this.$message.info('推演已重置');
},
formatTimeTooltip(val) {
const hours = Math.floor(val / 4);
const minutes = (val % 4) * 15;
return `K+${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`;
},
selectPlan(plan) {
if (plan && plan.id) {
this.selectedPlanId = plan.id;
this.selectedPlanDetails = plan;
} else {
this.selectedPlanId = null;
this.selectedPlanDetails = null;
}
// 重置状态
this.selectedRouteId = null;
this.selectedRouteDetails = null;
this.activeRouteIds = [];
// 物理清场
if (this.$refs.cesiumMap && this.$refs.cesiumMap.clearAllWaypoints) {
this.$refs.cesiumMap.clearAllWaypoints();
}
console.log(`>>> [切换成功] 已进入方案: ${plan.name},地图已清空,列表已展开。`);
},
/** 切换航线:实现多选/开关逻辑 */
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);
}
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);
}
} else {
this.selectedRouteId = null;
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.selectedRouteId = fullRouteData.id;
this.selectedRouteDetails = {
id: fullRouteData.id,
name: fullRouteData.callSign,
waypoints: waypoints
};
// 更新 routes 数组中对应航线的 waypoints 字段
const routeIndex = this.routes.findIndex(r => r.id === route.id);
if (routeIndex > -1) {
this.$set(this.routes, routeIndex, {
...this.routes[routeIndex],
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('无法加载该航线的详细航点数据');
}
},
createPlan() {
this.newPlanForm.name = ''; // 重置名称
this.showPlanNameDialog = true; // 打开对话框
},
async confirmCreatePlan() {
this.$refs.newPlanForm.validate(async (valid) => {
if (!valid) return;
try {
const postData = {
roomId: this.currentRoomId,
name: this.newPlanForm.name,
};
const res = await addScenario(postData);
if (res.code === 200) {
this.$message.success('方案创建成功');
this.showPlanNameDialog = false;
// 刷新列表,确保新方案带上后端 ID
await this.getList();
// 自动选中最新创建的方案
if (res.data && res.data.id) {
const newPlan = this.plans.find(p => p.id === res.data.id);
if (newPlan) this.selectPlan(newPlan);
}
}
} catch (error) {
console.error("创建方案失败:", error);
this.$message.error('保存方案失败,请检查网络');
}
});
},
openPlanDialog(plan) {
this.$prompt('请输入方案名称', '编辑方案', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue: plan.name
}).then(({value}) => {
plan.name = value;
}).catch(() => {
});
},
async executeDeletePlan(plan) {
try {
const res = await delScenario(plan.id);
if (res.code === 200) {
this.$message.success('方案删除成功');
const relatedRoutes = this.routes.filter(r => r.scenarioId === plan.id);
relatedRoutes.forEach(route => {
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(route.id);
}
const idx = this.activeRouteIds.indexOf(route.id);
if (idx > -1) {
this.activeRouteIds.splice(idx, 1);
}
});
//重置方案选中状态
if (this.selectedPlanId === plan.id) {
this.selectedPlanId = null;
}
await this.getList(); // 刷新列表
}
} catch (e) {
if (e !== 'cancel') console.error("删除方案失败:", e);
}
},
addWaypoint() {
if (this.selectedRouteDetails) {
const count = this.selectedRouteDetails.waypoints.length + 1;
this.selectedRouteDetails.waypoints.push({
name: `WP${count}`,
2 months ago
alt: 5000,
speed: '800km/h',
eta: `K+01:${(count * 15).toString().padStart(2, '0')}:00`
});
if (this.selectedPlanDetails) {
const route = this.selectedPlanDetails.routes.find(r => r.id === this.selectedRouteId);
if (route) {
route.points = this.selectedRouteDetails.waypoints.length;
}
}
}
},
cancelRoute() {
// 清空所有选中的航线
if (this.$refs.cesiumMap) {
this.activeRouteIds.forEach(id => {
this.$refs.cesiumMap.removeRouteById(id);
});
}
this.activeRouteIds = [];
this.selectedRouteDetails = null;
this.$message.info('已清空所有选中航线');
},
// 切换航线显示/隐藏
toggleRouteVisibility(route) {
const index = this.activeRouteIds.indexOf(route.id);
if (index > -1) {
// 航线已显示,隐藏它
// 使用过滤创建新数组,确保 Vue 能够检测到变化
this.activeRouteIds = this.activeRouteIds.filter(id => id !== route.id);
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(route.id);
}
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) {
if (this.activeRouteIds.length > 0) {
2 months ago
const lastId = this.activeRouteIds[this.activeRouteIds.length - 1];
getRoutes(lastId).then(res => {
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 || []
};
}
2 months ago
}).catch(e => {
console.error("获取航线详情失败", e);
});
} else {
this.selectedRouteId = null;
this.selectedRouteDetails = null;
}
}
} else {
// 航线已隐藏,显示它
this.selectRoute(route);
}
},
// 冲突操作
runConflictCheck() {
this.conflictCount = 2;
this.$message.warning('检测到2处航线冲突');
},
viewConflict(conflict) {
this.$message.info(`查看冲突:${conflict.title}`);
},
resolveConflict(conflict) {
this.$message.success(`解决冲突:${conflict.title}`);
// 移除已解决的冲突
this.conflicts = this.conflicts.filter(c => c.id !== conflict.id);
this.conflictCount = this.conflicts.length;
},
// 系统功能
exportReport() {
this.$message.success('作战报表导出成功');
},
// 新增导入功能
importData() {
this.$message.success('导入数据成功');
}
}
};
</script>
<style scoped>
.mission-planning-container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
/* 地图背景 - 保持不变 */
.map-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1a2f4b 0%, #2c3e50 100%);
3 months ago
/* 正确的写法,直接复制这行替换 */
background: url('~@/assets/map-background.png');
background-size: cover;
background-position: center;
z-index: 1;
}
/* ...其余样式省略,保持不变... */
.map-overlay-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: rgba(255, 255, 255, 0.3);
text-align: center;
font-size: 18px;
pointer-events: none;
}
/* 地图中间的浮动红点 - 通用样式 */
.floating-red-dot {
position: absolute;
top: 50%;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(5px);
border: 2px solid rgba(255, 0, 0, 0.8);
box-shadow: 0 0 20px rgba(255, 0, 0, 0.6);
cursor: pointer;
z-index: 80;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
animation: pulse-red 2s infinite;
}
.floating-red-dot:hover {
transform: translateY(-50%) scale(1.1);
box-shadow: 0 0 25px rgba(255, 0, 0, 0.8);
}
.floating-red-dot.hidden {
display: none;
}
/* 左侧红点 */
.left-red-dot {
left: 20px;
transform: translateY(-50%);
}
.red-dot {
width: 20px;
height: 20px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ff4444, #cc0000);
position: relative;
}
.icon-inside {
position: absolute;
color: white;
font-size: 16px;
}
@keyframes pulse-red {
0% {
box-shadow: 0 0 20px rgba(255, 0, 0, 0.6);
}
50% {
box-shadow: 0 0 25px rgba(255, 0, 0, 0.8);
}
100% {
box-shadow: 0 0 20px rgba(255, 0, 0, 0.6);
}
}
/* 蓝色主题通用类 */
.blue-theme {
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 138, 255, 0.1);
box-shadow: 0 4px 12px rgba(0, 138, 255, 0.2);
}
.blue-btn {
background: rgba(0, 138, 255, 0.8);
border: 1px solid rgba(0, 138, 255, 0.9);
color: white;
transition: all 0.3s;
}
.blue-btn:hover {
background: rgba(0, 138, 255, 0.9);
border-color: rgba(0, 138, 255, 1);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 138, 255, 0.3);
}
.blue-text-btn {
color: #008aff;
}
.blue-text-btn:hover {
color: #0066cc;
}
.blue-badge {
background: rgba(0, 138, 255, 0.8);
color: white;
}
.blue-tag {
background: rgba(0, 138, 255, 0.2);
color: #008aff;
border: 1px solid rgba(0, 138, 255, 0.3);
}
.blue-time {
background: rgba(0, 138, 255, 0.1);
border: 1px solid rgba(0, 138, 255, 0.3);
color: #008aff;
}
.blue-success {
color: #008aff;
}
.blue-warning {
color: #ff9900;
}
.blue-mark {
color: #008aff;
}
.status-dot.operating {
background: #008aff;
animation: pulse 2s infinite;
box-shadow: 0 0 10px rgba(0, 138, 255, 0.8);
}
/* 蓝色主题标签页 */
.blue-tabs >>> .el-tabs__item {
color: #666;
transition: all 0.3s;
}
.blue-tabs >>> .el-tabs__item:hover {
color: #008aff;
}
.blue-tabs >>> .el-tabs__item.is-active {
color: #008aff;
font-weight: 600;
}
.blue-tabs >>> .el-tabs__active-bar {
background-color: #008aff;
box-shadow: 0 0 6px rgba(0, 138, 255, 0.5);
}
.blue-tabs >>> .el-tabs__nav-wrap::after {
background-color: rgba(0, 138, 255, 0.3);
}
/* 底部时间轴(最初版本的样式)- 蓝色主题 */
.floating-timeline {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%) translateY(100%);
width: 80%;
border-radius: 12px;
z-index: 95;
3 months ago
padding: 10px 20px;
color: #333;
transition: all 0.3s ease;
opacity: 0;
pointer-events: none;
backdrop-filter: blur(15px);
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 8px 32px rgba(0, 138, 255, 0.25);
border: 1px solid rgba(0, 138, 255, 0.3);
}
.floating-timeline.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
pointer-events: auto;
}
.popup-hide-btn {
position: absolute;
top: -28px;
left: 50%;
transform: translateX(-50%);
width: 56px;
height: 28px;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 138, 255, 0.4);
border-bottom: none;
border-radius: 12px 12px 0 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #008aff;
font-size: 18px;
transition: all 0.3s;
z-index: 10;
}
.popup-hide-btn:hover {
background: rgba(0, 138, 255, 0.2);
color: #0066cc;
height: 32px;
top: -32px;
}
.timeline-controls {
display: flex;
align-items: center;
justify-content: space-between;
3 months ago
margin-bottom: 5px;
}
.current-time {
display: flex;
align-items: center;
gap: 8px;
font-family: monospace;
font-size: 14px;
font-weight: 500;
padding: 4px 12px;
border-radius: 4px;
}
.timeline-slider {
flex: 1;
margin: 0 20px;
}
.compact-slider {
width: 100%;
}
.blue-slider >>> .el-slider__bar {
background-color: rgba(0, 138, 255, 0.8);
}
.blue-slider >>> .el-slider__button {
border-color: rgba(0, 138, 255, 0.8);
background-color: rgba(0, 138, 255, 0.8);
}
3 months ago
.playback-controls {
display: flex;
3 months ago
align-items: center;
gap: 10px;
}
3 months ago
.control-btn {
width: 26px;
height: 26px;
border: 1px solid rgba(0, 138, 255, 0.3);
background: rgba(255, 255, 255, 0.9);
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
color: #008aff;
}
.control-btn:hover:not(:disabled) {
background: rgba(0, 138, 255, 0.1);
border-color: rgba(0, 138, 255, 0.5);
}
.control-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.control-btn i {
font-size: 14px;
}
.speed-control {
display: flex;
align-items: center;
gap: 6px;
padding: 2px 8px;
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 138, 255, 0.3);
border-radius: 4px;
}
.speed-text {
font-size: 11px;
font-weight: 600;
color: #008aff;
min-width: 24px;
text-align: center;
}
.system-status {
display: flex;
justify-content: center;
gap: 30px;
font-size: 12px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid rgba(0, 138, 255, 0.4);
}
.status-item {
display: flex;
align-items: center;
gap: 5px;
}
.status-label {
color: #666;
}
.status-value.success {
font-weight: 500;
}
.status-value.warning {
font-weight: 500;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: rgba(0, 138, 255, 0.1);
border-radius: 3px;
backdrop-filter: blur(5px);
}
::-webkit-scrollbar-thumb {
background: linear-gradient(135deg, rgba(0, 138, 255, 0.5), rgba(0, 138, 255, 0.7));
border-radius: 3px;
box-shadow: 0 0 6px rgba(0, 138, 255, 0.4);
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(135deg, rgba(0, 138, 255, 0.7), rgba(0, 138, 255, 0.9));
}
.ml-3 {
margin-left: 10px;
}
</style>