|
|
@ -34,7 +34,6 @@ |
|
|
</div> |
|
|
</div> |
|
|
</el-dialog> |
|
|
</el-dialog> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 顶部导航栏 --> |
|
|
<!-- 顶部导航栏 --> |
|
|
<top-header |
|
|
<top-header |
|
|
:room-code="roomCode" |
|
|
:room-code="roomCode" |
|
|
@ -86,7 +85,6 @@ |
|
|
@route-favorites="routeFavorites" |
|
|
@route-favorites="routeFavorites" |
|
|
@show-online-members="showOnlineMembersDialog" |
|
|
@show-online-members="showOnlineMembersDialog" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
<!-- 左侧折叠菜单栏 - 蓝色主题 --> |
|
|
<!-- 左侧折叠菜单栏 - 蓝色主题 --> |
|
|
<left-menu |
|
|
<left-menu |
|
|
:is-hidden="isMenuHidden" |
|
|
:is-hidden="isMenuHidden" |
|
|
@ -103,7 +101,6 @@ |
|
|
@add-items="handleAddMenuItems" |
|
|
@add-items="handleAddMenuItems" |
|
|
@delete="handleDeleteMenuItem" |
|
|
@delete="handleDeleteMenuItem" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
<!-- 右侧实体列表(浮动)- 蓝色主题 --> |
|
|
<!-- 右侧实体列表(浮动)- 蓝色主题 --> |
|
|
<right-panel |
|
|
<right-panel |
|
|
ref="rightPanel" |
|
|
ref="rightPanel" |
|
|
@ -122,11 +119,12 @@ |
|
|
:sea-platforms="seaPlatforms" |
|
|
:sea-platforms="seaPlatforms" |
|
|
:ground-platforms="groundPlatforms" |
|
|
:ground-platforms="groundPlatforms" |
|
|
@hide="hideRightPanel" |
|
|
@hide="hideRightPanel" |
|
|
@select-plan="selectPlan" |
|
|
|
|
|
@select-route="selectRoute" |
|
|
@select-route="selectRoute" |
|
|
@create-route="createRoute" |
|
|
@create-route="createRoute" |
|
|
@delete-route="handleDeleteRoute" |
|
|
@delete-route="handleDeleteRoute" |
|
|
|
|
|
@select-plan="selectPlan" |
|
|
@create-plan="createPlan" |
|
|
@create-plan="createPlan" |
|
|
|
|
|
@delete-plan="executeDeletePlan" |
|
|
@open-plan-dialog="openPlanDialog" |
|
|
@open-plan-dialog="openPlanDialog" |
|
|
@open-route-dialog="openRouteDialog" |
|
|
@open-route-dialog="openRouteDialog" |
|
|
@open-waypoint-dialog="openWaypointDialog" |
|
|
@open-waypoint-dialog="openWaypointDialog" |
|
|
@ -138,10 +136,8 @@ |
|
|
@run-conflict-check="runConflictCheck" |
|
|
@run-conflict-check="runConflictCheck" |
|
|
@open-platform-dialog="openPlatformDialog" |
|
|
@open-platform-dialog="openPlatformDialog" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
<!-- 左下角工具面板 --> |
|
|
<!-- 左下角工具面板 --> |
|
|
<bottom-left-panel /> |
|
|
<bottom-left-panel /> |
|
|
|
|
|
|
|
|
<!-- 底部时间轴(最初版本的样式)- 蓝色主题 --> |
|
|
<!-- 底部时间轴(最初版本的样式)- 蓝色主题 --> |
|
|
<div |
|
|
<div |
|
|
class="floating-timeline blue-theme" |
|
|
class="floating-timeline blue-theme" |
|
|
@ -151,13 +147,11 @@ |
|
|
<div class="popup-hide-btn" @click="hideKTimePopup" title="隐藏K时"> |
|
|
<div class="popup-hide-btn" @click="hideKTimePopup" title="隐藏K时"> |
|
|
<i class="el-icon-arrow-down"></i> |
|
|
<i class="el-icon-arrow-down"></i> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="timeline-controls"> |
|
|
<div class="timeline-controls"> |
|
|
<div class="current-time blue-time"> |
|
|
<div class="current-time blue-time"> |
|
|
<i class="el-icon-time"></i> |
|
|
<i class="el-icon-time"></i> |
|
|
<span class="time-text">{{ currentTime }}</span> |
|
|
<span class="time-text">{{ currentTime }}</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="timeline-slider"> |
|
|
<div class="timeline-slider"> |
|
|
<el-slider |
|
|
<el-slider |
|
|
v-model="timeProgress" |
|
|
v-model="timeProgress" |
|
|
@ -166,7 +160,6 @@ |
|
|
class="compact-slider blue-slider" |
|
|
class="compact-slider blue-slider" |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="playback-controls"> |
|
|
<div class="playback-controls"> |
|
|
<button |
|
|
<button |
|
|
class="control-btn blue-control-btn" |
|
|
class="control-btn blue-control-btn" |
|
|
@ -175,7 +168,6 @@ |
|
|
> |
|
|
> |
|
|
<i :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i> |
|
|
<i :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i> |
|
|
</button> |
|
|
</button> |
|
|
|
|
|
|
|
|
<div class="speed-control"> |
|
|
<div class="speed-control"> |
|
|
<button |
|
|
<button |
|
|
class="control-btn blue-control-btn" |
|
|
class="control-btn blue-control-btn" |
|
|
@ -256,6 +248,23 @@ |
|
|
v-model="showPageLayoutDialog" |
|
|
v-model="showPageLayoutDialog" |
|
|
@save="savePageLayout" |
|
|
@save="savePageLayout" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
<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> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
|
|
|
|
|
|
@ -273,7 +282,7 @@ import LeftMenu from './LeftMenu' |
|
|
import RightPanel from './RightPanel' |
|
|
import RightPanel from './RightPanel' |
|
|
import BottomLeftPanel from './BottomLeftPanel' |
|
|
import BottomLeftPanel from './BottomLeftPanel' |
|
|
import TopHeader from './TopHeader' |
|
|
import TopHeader from './TopHeader' |
|
|
import { listScenario} from "@/api/system/scenario"; |
|
|
import { listScenario,addScenario,delScenario} from "@/api/system/scenario"; |
|
|
import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes"; |
|
|
import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes"; |
|
|
import { updateWaypoints } from "@/api/system/waypoints"; |
|
|
import { updateWaypoints } from "@/api/system/waypoints"; |
|
|
export default { |
|
|
export default { |
|
|
@ -328,13 +337,20 @@ export default { |
|
|
isMenuHidden: true, // 是否完全隐藏左侧菜单 |
|
|
isMenuHidden: true, // 是否完全隐藏左侧菜单 |
|
|
activeMenu: 'file', |
|
|
activeMenu: 'file', |
|
|
isIconEditMode: false, // 图标编辑模式 |
|
|
isIconEditMode: false, // 图标编辑模式 |
|
|
|
|
|
|
|
|
// 方案-航线-航点数据 |
|
|
// 方案-航线-航点数据 |
|
|
selectedPlanId: null, |
|
|
selectedPlanId: null, |
|
|
selectedPlanDetails: null, |
|
|
selectedPlanDetails: null, |
|
|
|
|
|
routes: [], |
|
|
selectedRouteId: null, |
|
|
selectedRouteId: null, |
|
|
selectedRouteDetails: null, |
|
|
selectedRouteDetails: null, |
|
|
|
|
|
|
|
|
|
|
|
showPlanNameDialog: false, // 控制新建方案弹窗 |
|
|
|
|
|
newPlanForm: { |
|
|
|
|
|
name: '' |
|
|
|
|
|
}, |
|
|
|
|
|
planRules: { |
|
|
|
|
|
name: [{ required: true, message: '请输入方案名称', trigger: 'blur' }] |
|
|
|
|
|
}, |
|
|
// 顶部导航菜单项(用于图标选择)- 只显示指定的菜单项 |
|
|
// 顶部导航菜单项(用于图标选择)- 只显示指定的菜单项 |
|
|
topNavItems: [ |
|
|
topNavItems: [ |
|
|
{ id: 'routeEdit', name: '航线编辑', icon: 'el-icon-edit-outline' }, |
|
|
{ id: 'routeEdit', name: '航线编辑', icon: 'el-icon-edit-outline' }, |
|
|
@ -370,7 +386,6 @@ export default { |
|
|
|
|
|
|
|
|
// 右侧面板控制 |
|
|
// 右侧面板控制 |
|
|
isRightPanelHidden: true, // 是否完全隐藏右侧面板 |
|
|
isRightPanelHidden: true, // 是否完全隐藏右侧面板 |
|
|
|
|
|
|
|
|
// K时弹出框控制 |
|
|
// K时弹出框控制 |
|
|
showKTimePopup: false, |
|
|
showKTimePopup: false, |
|
|
|
|
|
|
|
|
@ -394,9 +409,10 @@ export default { |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// 右侧面板 |
|
|
// 右侧面板 |
|
|
|
|
|
currentRoomId: null, |
|
|
|
|
|
plans: [], |
|
|
activeRightTab: 'plan', |
|
|
activeRightTab: 'plan', |
|
|
activeRouteIds: [], // 存储当前所有选中的航线ID |
|
|
activeRouteIds: [], // 存储当前所有选中的航线ID |
|
|
|
|
|
|
|
|
// 冲突数据 |
|
|
// 冲突数据 |
|
|
conflictCount: 2, |
|
|
conflictCount: 2, |
|
|
conflicts: [ |
|
|
conflicts: [ |
|
|
@ -437,9 +453,6 @@ export default { |
|
|
{ id: 10, name: '指挥控制车', type: '指挥车', icon: 'el-icon-monitor', color: '#1890ff', status: 'operating' }, |
|
|
{ id: 10, name: '指挥控制车', type: '指挥车', icon: 'el-icon-monitor', color: '#1890ff', status: 'operating' }, |
|
|
], |
|
|
], |
|
|
|
|
|
|
|
|
// 航线数据 - 改为方案-航线-航点三级结构 |
|
|
|
|
|
plans: [], |
|
|
|
|
|
|
|
|
|
|
|
// 时间控制 |
|
|
// 时间控制 |
|
|
timeProgress: 45, |
|
|
timeProgress: 45, |
|
|
currentTime: 'K+01:15:30', |
|
|
currentTime: 'K+01:15:30', |
|
|
@ -471,24 +484,44 @@ export default { |
|
|
this.playbackInterval = null; |
|
|
this.playbackInterval = null; |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
created() { |
|
|
|
|
|
this.currentRoomId = this.$route.query.roomId; |
|
|
|
|
|
console.log("从路由接收到的真实房间 ID:", this.currentRoomId); |
|
|
|
|
|
this.getList(); |
|
|
|
|
|
}, |
|
|
methods: { |
|
|
methods: { |
|
|
// 处理从地图点击传来的编辑请求 |
|
|
// 处理从地图点击传来的编辑请求 |
|
|
handleOpenWaypointEdit(wpName) { |
|
|
async handleOpenWaypointEdit(wpId, routeId) { |
|
|
// 1. 确保当前有选中的航线详情 |
|
|
console.log(`>>> [父组件接收] 航点 ID: ${wpId}, 所属航线 ID: ${routeId}`); |
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) { |
|
|
// 如果点击的点不属于当前选中的航线 |
|
|
this.$message.warning('请先在右侧列表选择一条航线'); |
|
|
if (this.selectedRouteId != routeId) { |
|
|
return; |
|
|
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; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
// 2. 根据点击的航点名称在数据列表中查找 |
|
|
// 此时账本已确保是最新且正确的,开始找点 |
|
|
const wpData = this.selectedRouteDetails.waypoints.find(item => item.name === wpName); |
|
|
const wpData = this.selectedRouteDetails.waypoints.find(item => item.id == wpId); |
|
|
if (wpData) { |
|
|
if (wpData) { |
|
|
// 3. 深拷贝给编辑弹窗绑定的变量,防止直接修改原始数组 |
|
|
// 成功找到业务数据,深拷贝后打开弹窗 |
|
|
this.selectedWaypoint = JSON.parse(JSON.stringify(wpData)); |
|
|
this.selectedWaypoint = JSON.parse(JSON.stringify(wpData)); |
|
|
// 4. 打开弹窗 |
|
|
|
|
|
this.showWaypointDialog = true; |
|
|
this.showWaypointDialog = true; |
|
|
} |
|
|
} else { |
|
|
else { |
|
|
|
|
|
this.$message.info('未找到该航点的业务数据'); |
|
|
this.$message.info('未找到该航点的业务数据'); |
|
|
|
|
|
console.error("查找失败!账本内IDs:", this.selectedRouteDetails.waypoints.map(w => w.id)); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
// 显示在线成员弹窗 |
|
|
// 显示在线成员弹窗 |
|
|
@ -514,7 +547,7 @@ export default { |
|
|
const index = this.routes.findIndex(r => r.id === updatedRoute.id); |
|
|
const index = this.routes.findIndex(r => r.id === updatedRoute.id); |
|
|
if (index !== -1) { |
|
|
if (index !== -1) { |
|
|
// 使用 splice 触发响应式更新 |
|
|
// 使用 splice 触发响应式更新 |
|
|
const newRouteData = { ...this.routes[index], ...updatedRoute }; |
|
|
const newRouteData = {...this.routes[index], ...updatedRoute}; |
|
|
this.routes.splice(index, 1, newRouteData); |
|
|
this.routes.splice(index, 1, newRouteData); |
|
|
|
|
|
|
|
|
// 如果当前选中的是这条航线,同步更新详情中的名称 |
|
|
// 如果当前选中的是这条航线,同步更新详情中的名称 |
|
|
@ -527,15 +560,19 @@ export default { |
|
|
}, |
|
|
}, |
|
|
// 新建航线 |
|
|
// 新建航线 |
|
|
createRoute(plan) { |
|
|
createRoute(plan) { |
|
|
// 保存当前选中的方案,用于后续新建航线时关联 |
|
|
if (!plan || !plan.id) return; |
|
|
if (plan) { |
|
|
console.log(`>>> [新建航线触发] 目标方案: ${plan.name}`); |
|
|
this.selectedPlanId = plan.id; |
|
|
// 只有当目标方案和当前方案不同时,才执行“大扫除” |
|
|
this.selectedPlanDetails = plan; |
|
|
if (this.selectedPlanId !== plan.id) { |
|
|
|
|
|
console.log(">>> [执行跨方案切换] 正在清理旧方案数据..."); |
|
|
|
|
|
this.selectPlan(plan); |
|
|
|
|
|
} else { |
|
|
|
|
|
console.log(">>> [同方案内操作] 保持当前航线显示,直接进入规划模式"); |
|
|
} |
|
|
} |
|
|
|
|
|
// 进入 Cesium 绘图模式 |
|
|
if (this.$refs.cesiumMap) { |
|
|
if (this.$refs.cesiumMap) { |
|
|
this.$refs.cesiumMap.startMissionRouteDrawing(); |
|
|
this.$refs.cesiumMap.startMissionRouteDrawing(); |
|
|
this.$message.success('进入航线规划模式'); |
|
|
this.$message.success(`${plan.name}开启航线规划`); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
async handleDeleteRoute(route) { |
|
|
async handleDeleteRoute(route) { |
|
|
@ -571,8 +608,8 @@ export default { |
|
|
/** 从数据库拉取最新的航线列表数据 */ |
|
|
/** 从数据库拉取最新的航线列表数据 */ |
|
|
async getList() { |
|
|
async getList() { |
|
|
try { |
|
|
try { |
|
|
// 1. 获取所有方案 |
|
|
const roomId = this.$route.query.roomId || this.currentRoomId; |
|
|
const scenarioRes = await listScenario({}); |
|
|
const scenarioRes = await listScenario({ roomId: roomId }); |
|
|
if (scenarioRes.code === 200) { |
|
|
if (scenarioRes.code === 200) { |
|
|
this.plans = scenarioRes.rows.map(s => ({ |
|
|
this.plans = scenarioRes.rows.map(s => ({ |
|
|
id: s.id, |
|
|
id: s.id, |
|
|
@ -580,8 +617,7 @@ export default { |
|
|
routes: [] |
|
|
routes: [] |
|
|
})); |
|
|
})); |
|
|
} |
|
|
} |
|
|
|
|
|
//获取所有航线 |
|
|
// 2. 获取所有航线 |
|
|
|
|
|
const routeRes = await listRoutes({}); |
|
|
const routeRes = await listRoutes({}); |
|
|
if (routeRes.code === 200) { |
|
|
if (routeRes.code === 200) { |
|
|
const allRoutes = routeRes.rows.map(item => ({ |
|
|
const allRoutes = routeRes.rows.map(item => ({ |
|
|
@ -592,14 +628,27 @@ export default { |
|
|
conflict: false, |
|
|
conflict: false, |
|
|
scenarioId: item.scenarioId |
|
|
scenarioId: item.scenarioId |
|
|
})); |
|
|
})); |
|
|
|
|
|
//分配航线到方案 |
|
|
// 3. 将航线分配到对应的方案中 |
|
|
|
|
|
this.plans.forEach(plan => { |
|
|
this.plans.forEach(plan => { |
|
|
plan.routes = allRoutes.filter(r => r.scenarioId === plan.id); |
|
|
plan.routes = allRoutes.filter(r => r.scenarioId === plan.id); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 保存一份扁平的 routes 供搜索或其他组件使用 |
|
|
|
|
|
this.routes = allRoutes; |
|
|
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) { |
|
|
} catch (error) { |
|
|
console.error("数据加载失败:", error); |
|
|
console.error("数据加载失败:", error); |
|
|
@ -617,70 +666,90 @@ export default { |
|
|
}, |
|
|
}, |
|
|
/** 弹窗点击“确定”:正式将数据保存到后端数据库 */ |
|
|
/** 弹窗点击“确定”:正式将数据保存到后端数据库 */ |
|
|
async confirmSaveNewRoute() { |
|
|
async confirmSaveNewRoute() { |
|
|
// 严格校验起名逻辑 |
|
|
// 1. 严格校验 |
|
|
if (!this.newRouteName || this.newRouteName.trim() === '') { |
|
|
if (!this.newRouteName || this.newRouteName.trim() === '') { |
|
|
this.$message.error('新增航线未命名,请输入名称后保存!'); |
|
|
this.$message.error('新增航线未命名,请输入名称后保存!'); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
// 获取当前选中的方案 ID |
|
|
|
|
|
const currentScenarioId = this.selectedPlanId; |
|
|
const currentScenarioId = this.selectedPlanId; |
|
|
if (!currentScenarioId) { |
|
|
if (!currentScenarioId) { |
|
|
this.$message.warning('请先在左侧选择一个方案,再保存航线!'); |
|
|
this.$message.warning('请先在左侧选择一个方案,再保存航线!'); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
// 构造符合后端 Routes 实体类的数据结构 |
|
|
|
|
|
|
|
|
// 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 = { |
|
|
const routeData = { |
|
|
callSign: this.newRouteName, |
|
|
callSign: this.newRouteName, |
|
|
scenarioId: currentScenarioId || 1, // 使用当前选中的方案ID,默认1 |
|
|
scenarioId: currentScenarioId, |
|
|
platformId: 1, |
|
|
platformId: 1, |
|
|
attributes: "{}", |
|
|
attributes: "{}", |
|
|
waypoints: this.tempMapPoints.map((p, index) => ({ |
|
|
waypoints: finalWaypoints |
|
|
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 { |
|
|
try { |
|
|
// 调用后端 API 保存到数据库 |
|
|
|
|
|
const response = await addRoutes(routeData); |
|
|
const response = await addRoutes(routeData); |
|
|
if (response.code === 200) { |
|
|
if (response.code === 200) { |
|
|
this.$message.success('航线及其航点已成功保存至当前方案'); |
|
|
|
|
|
// 获取后端生成的正式 ID |
|
|
|
|
|
const savedRoute = response.data; |
|
|
const savedRoute = response.data; |
|
|
const newRouteId = savedRoute ? savedRoute.id : null; |
|
|
const newRouteId = savedRoute?.id; |
|
|
if (this.$refs.cesiumMap) { |
|
|
|
|
|
this.$refs.cesiumMap.clearRoute(); |
|
|
if (!newRouteId || !savedRoute.waypoints) { |
|
|
// 如果拿到了正式 ID,则按照“正式航线”的规则渲染一次 |
|
|
this.$message.error('保存成功但返回数据结构异常,请手动刷新'); |
|
|
if (newRouteId) { |
|
|
return; |
|
|
if (!this.activeRouteIds.includes(savedRoute.id)) { |
|
|
|
|
|
this.activeRouteIds.push(savedRoute.id); |
|
|
|
|
|
} |
|
|
|
|
|
// 更新当前详情,确保右侧下方航点列表立刻显示 |
|
|
|
|
|
this.selectedRouteDetails = { |
|
|
|
|
|
id: newRouteId, |
|
|
|
|
|
name: this.newRouteName, |
|
|
|
|
|
waypoints: routeData.waypoints |
|
|
|
|
|
}; |
|
|
|
|
|
//使用正式 ID 渲染点和线 |
|
|
|
|
|
this.$refs.cesiumMap.renderRouteWaypoints(routeData.waypoints, newRouteId); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
// 重置 UI 状态 |
|
|
|
|
|
this.showNameDialog = false; |
|
|
// 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.drawDom = false; |
|
|
|
|
|
this.showNameDialog = false; |
|
|
this.newRouteName = ''; |
|
|
this.newRouteName = ''; |
|
|
this.tempMapPoints = []; |
|
|
this.tempMapPoints = []; |
|
|
// 重新拉取右侧航线列表以保持同步 |
|
|
|
|
|
await this.getList(); |
|
|
// 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) { |
|
|
} catch (error) { |
|
|
console.error("保存航线失败:", error); |
|
|
console.error(">>> [保存异常]:", error); |
|
|
this.$message.error('保存失败,请检查后端服务'); |
|
|
this.$message.error('保存失败,请检查网络或后端日志'); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
// 航点编辑弹窗相关方法 |
|
|
// 航点编辑弹窗相关方法 |
|
|
@ -690,22 +759,19 @@ export default { |
|
|
}, |
|
|
}, |
|
|
/** 航点编辑保存:更新数据库并同步地图显示 */ |
|
|
/** 航点编辑保存:更新数据库并同步地图显示 */ |
|
|
async updateWaypoint(updatedWaypoint) { |
|
|
async updateWaypoint(updatedWaypoint) { |
|
|
// 确保有选中的航线详情 |
|
|
|
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return; |
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return; |
|
|
try { |
|
|
try { |
|
|
const response = await updateWaypoints(updatedWaypoint); |
|
|
const response = await updateWaypoints(updatedWaypoint); // 更新数据库 |
|
|
if (response.code === 200) { |
|
|
if (response.code === 200) { |
|
|
// 找到当前编辑点在本地数组中的位置 |
|
|
|
|
|
const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id); |
|
|
const index = this.selectedRouteDetails.waypoints.findIndex(p => p.id === updatedWaypoint.id); |
|
|
if (index !== -1) { |
|
|
if (index !== -1) { |
|
|
// 记录旧名称,用于地图组件定位 Entity |
|
|
// 1. 更新本地 Vue 响应式数据,同步右侧面板 UI |
|
|
const oldName = this.selectedRouteDetails.waypoints[index].name; |
|
|
this.selectedRouteDetails.waypoints.splice(index, 1, {...updatedWaypoint}); |
|
|
const newName = updatedWaypoint.name; |
|
|
|
|
|
// 使用 splice 触发 Vue 响应式更新右侧面板 UI |
|
|
// 2. 【核心修改】通过 ID 通知地图组件更新图形 |
|
|
this.selectedRouteDetails.waypoints.splice(index, 1, { ...updatedWaypoint }); |
|
|
|
|
|
// 5. 同步更新地图上的图形 |
|
|
|
|
|
if (this.$refs.cesiumMap) { |
|
|
if (this.$refs.cesiumMap) { |
|
|
this.$refs.cesiumMap.updateWaypointGraphic(oldName, newName); |
|
|
// 直接传 ID 和新的名字 |
|
|
|
|
|
this.$refs.cesiumMap.updateWaypointGraphicById(updatedWaypoint.id, updatedWaypoint.name); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.showWaypointDialog = false; |
|
|
this.showWaypointDialog = false; |
|
|
@ -753,7 +819,6 @@ export default { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 右侧面板操作 |
|
|
// 右侧面板操作 |
|
|
showRightPanel() { |
|
|
showRightPanel() { |
|
|
this.isRightPanelHidden = false; |
|
|
this.isRightPanelHidden = false; |
|
|
@ -1103,11 +1168,11 @@ export default { |
|
|
this.activeRightTab = 'platform'; |
|
|
this.activeRightTab = 'platform'; |
|
|
this.isRightPanelHidden = false; |
|
|
this.isRightPanelHidden = false; |
|
|
} |
|
|
} |
|
|
} else if(item.id === 'modify'){ |
|
|
} else if (item.id === 'modify') { |
|
|
this.drawDom = !this.drawDom |
|
|
this.drawDom = !this.drawDom |
|
|
// 点击修改图标进行地图绘制时,自动收起右侧面板 |
|
|
// 点击修改图标进行地图绘制时,自动收起右侧面板 |
|
|
this.isRightPanelHidden = true; |
|
|
this.isRightPanelHidden = true; |
|
|
console.log(this.drawDom,999999) |
|
|
console.log(this.drawDom, 999999) |
|
|
} else if (item.id === 'deduction') { |
|
|
} else if (item.id === 'deduction') { |
|
|
// 点击推演按钮,显示/隐藏K时弹出框 |
|
|
// 点击推演按钮,显示/隐藏K时弹出框 |
|
|
this.showKTimePopup = !this.showKTimePopup; |
|
|
this.showKTimePopup = !this.showKTimePopup; |
|
|
@ -1209,7 +1274,6 @@ export default { |
|
|
const minutes = (val % 4) * 15; |
|
|
const minutes = (val % 4) * 15; |
|
|
return `K+${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`; |
|
|
return `K+${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00`; |
|
|
}, |
|
|
}, |
|
|
// 航线操作 |
|
|
|
|
|
selectPlan(plan) { |
|
|
selectPlan(plan) { |
|
|
if (plan && plan.id) { |
|
|
if (plan && plan.id) { |
|
|
this.selectedPlanId = plan.id; |
|
|
this.selectedPlanId = plan.id; |
|
|
@ -1218,8 +1282,17 @@ export default { |
|
|
this.selectedPlanId = null; |
|
|
this.selectedPlanId = null; |
|
|
this.selectedPlanDetails = null; |
|
|
this.selectedPlanDetails = null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 重置状态 |
|
|
this.selectedRouteId = null; |
|
|
this.selectedRouteId = null; |
|
|
this.selectedRouteDetails = 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) { |
|
|
async selectRoute(route) { |
|
|
@ -1228,8 +1301,6 @@ export default { |
|
|
// 航线已在选中列表中 |
|
|
// 航线已在选中列表中 |
|
|
if (index > -1) { |
|
|
if (index > -1) { |
|
|
if (isRouteExpanded) { |
|
|
if (isRouteExpanded) { |
|
|
// 航线已展开,点击则收回航点(不取消选中) |
|
|
|
|
|
// 这个逻辑在 RightPanel 的 toggleRoute 中处理 |
|
|
|
|
|
return; |
|
|
return; |
|
|
} else { |
|
|
} else { |
|
|
// 航线未展开,点击则取消选中(从地图移除) |
|
|
// 航线未展开,点击则取消选中(从地图移除) |
|
|
@ -1302,23 +1373,69 @@ export default { |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
createPlan() { |
|
|
createPlan() { |
|
|
const newId = Date.now(); |
|
|
this.newPlanForm.name = ''; // 重置名称 |
|
|
const newPlan = { |
|
|
this.showPlanNameDialog = true; // 打开对话框 |
|
|
id: newId, |
|
|
}, |
|
|
name: `新方案${this.plans.length + 1}`, |
|
|
async confirmCreatePlan() { |
|
|
routes: [] |
|
|
this.$refs.newPlanForm.validate(async (valid) => { |
|
|
}; |
|
|
if (!valid) return; |
|
|
this.plans.push(newPlan); |
|
|
try { |
|
|
this.selectPlan(newPlan); |
|
|
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) { |
|
|
openPlanDialog(plan) { |
|
|
this.$prompt('请输入方案名称', '编辑方案', { |
|
|
this.$prompt('请输入方案名称', '编辑方案', { |
|
|
confirmButtonText: '确定', |
|
|
confirmButtonText: '确定', |
|
|
cancelButtonText: '取消', |
|
|
cancelButtonText: '取消', |
|
|
inputValue: plan.name |
|
|
inputValue: plan.name |
|
|
}).then(({ value }) => { |
|
|
}).then(({value}) => { |
|
|
plan.name = value; |
|
|
plan.name = value; |
|
|
}).catch(() => {}); |
|
|
}).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() { |
|
|
addWaypoint() { |
|
|
if (this.selectedRouteDetails) { |
|
|
if (this.selectedRouteDetails) { |
|
|
@ -1337,7 +1454,6 @@ export default { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
cancelRoute() { |
|
|
cancelRoute() { |
|
|
// 清空所有选中的航线 |
|
|
// 清空所有选中的航线 |
|
|
if (this.$refs.cesiumMap) { |
|
|
if (this.$refs.cesiumMap) { |
|
|
@ -1381,7 +1497,6 @@ export default { |
|
|
this.selectedRouteDetails = null; |
|
|
this.selectedRouteDetails = null; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
this.$message.info(`已隐藏航线: ${route.name}`); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// 航线已隐藏,显示它 |
|
|
// 航线已隐藏,显示它 |
|
|
this.selectRoute(route); |
|
|
this.selectRoute(route); |
|
|
@ -1440,6 +1555,7 @@ export default { |
|
|
background-position: center; |
|
|
background-position: center; |
|
|
z-index: 1; |
|
|
z-index: 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* ...其余样式省略,保持不变... */ |
|
|
/* ...其余样式省略,保持不变... */ |
|
|
.map-overlay-text { |
|
|
.map-overlay-text { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
|