Browse Source

方案-航线-航点

master
menghao 2 months ago
parent
commit
7b003f8ec0
  1. 7
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java
  2. 11
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java
  3. 422
      ruoyi-ui/src/views/cesiumMap/index.vue
  4. 59
      ruoyi-ui/src/views/childRoom/RightPanel.vue
  5. 312
      ruoyi-ui/src/views/childRoom/index.vue
  6. 5
      ruoyi-ui/src/views/selectRoom/index.vue

7
ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java

@ -77,7 +77,12 @@ public class RoutesController extends BaseController
@PostMapping
public AjaxResult add(@RequestBody Routes routes)
{
return toAjax(routesService.insertRoutes(routes));
// 1. 执行插入,MyBatis 会通过 useGeneratedKeys="true" 自动将新 ID 注入 routes 对象
int rows = routesService.insertRoutes(routes);
// 2. 不要用 toAjax,直接返回 success 并带上 routes 对象
// 这样前端 response.data 就会包含这个带有 ID 的完整对象
return rows > 0 ? AjaxResult.success(routes) : AjaxResult.error("新增航线失败");
}
/**

11
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java

@ -58,7 +58,16 @@ public class RoutesServiceImpl implements IRoutesService
@Override
public List<Routes> selectRoutesList(Routes routes)
{
return routesMapper.selectRoutesList(routes);
// 获取基础列表
List<Routes> list = routesMapper.selectRoutesList(routes);
// 遍历列表,为每一条航线补全它的航点信息
for (Routes r : list) {
RouteWaypoints queryWp = new RouteWaypoints();
queryWp.setRouteId(r.getId());
List<RouteWaypoints> wpList = routeWaypointsService.selectRouteWaypointsList(queryWp);
r.setWaypoints(wpList);
}
return list;
}
/**

422
ruoyi-ui/src/views/cesiumMap/index.vue

File diff suppressed because it is too large

59
ruoyi-ui/src/views/childRoom/RightPanel.vue

@ -285,6 +285,15 @@ export default {
}
},
watch: {
selectedPlanId(newId) {
if (newId) {
console.log('>>> [子组件同步] 检测到方案切换,自动展开 ID:', newId);
//
if (!this.expandedPlans.includes(newId)) {
this.expandedPlans.push(newId);
}
}
},
activeRouteIds: {
handler(newVal) {
console.log('activeRouteIds updated:', newVal);
@ -297,25 +306,33 @@ export default {
togglePlan(planId) {
const index = this.expandedPlans.indexOf(planId)
if (index > -1) {
// 线
this.expandedPlans.splice(index, 1)
const planRoutes = this.routes.filter(r => r.scenarioId === planId)
planRoutes.forEach(route => {
if (this.activeRouteIds.includes(route.id)) {
// 线
this.handleToggleRouteVisibility(route)
}
})
//
this.$emit('select-plan', { id: null })
} else {
//
// 线
// 线 activeRouteIds
this.activeRouteIds.forEach(activeId => {
const activeRoute = this.routes.find(r => r.id === activeId);
if (activeRoute) {
this.$emit('toggle-route-visibility', activeRoute);
}
});
//
this.expandedPlans = [];
this.expandedRoutes = [];
// --- ---
this.expandedPlans.push(planId)
const plan = this.plans.find(p => p.id === planId)
if (plan) {
this.$emit('select-plan', plan)
}
// 线
// 线
const planRoutes = this.routes.filter(r => r.scenarioId === planId)
planRoutes.forEach(route => {
if (!this.activeRouteIds.includes(route.id)) {
@ -329,25 +346,25 @@ export default {
toggleRoute(routeId) {
const route = this.routes.find(r => r.id === routeId)
if (!route) return
if (!route.waypoints) {
this.$set(route, 'waypoints', []); // 使 $set
}
const isRouteSelected = this.activeRouteIds.includes(routeId)
const isRouteExpanded = this.expandedRoutes.includes(routeId)
if (isRouteSelected) {
// 线
if (isRouteExpanded) {
// 线
const index = this.expandedRoutes.indexOf(routeId)
this.expandedRoutes.splice(index, 1)
} else {
// 线
this.expandedRoutes.push(routeId)
}
} else {
// 线线
this.handleSelectRoute(route)
//
this.$nextTick(() => {
// 线
if (!this.expandedPlans.includes(route.scenarioId)) {
this.expandedPlans.push(route.scenarioId);
}
if (!this.expandedRoutes.includes(routeId)) {
this.expandedRoutes.push(routeId)
}
@ -359,11 +376,6 @@ export default {
this.$emit('hide')
},
// 线
handleSelectPlan(plan) {
this.$emit('select-plan', plan)
},
handleSelectRoute(route) {
// 线
this.$emit('select-route', route)
@ -373,16 +385,19 @@ export default {
this.$emit('create-plan')
},
handleCreateRoute() {
this.$emit('create-route')
},
handleCreateRouteForPlan(plan) {
this.$emit('create-route', plan)
},
handleDeletePlan(plan) {
//
this.$confirm(`是否确认删除方案 "${plan.name}"?这将同时删除该方案下的所有航线。`, "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$emit('delete-plan', plan);
}).catch(() => {
});
},
handleOpenPlanDialog(plan) {

312
ruoyi-ui/src/views/childRoom/index.vue

@ -34,7 +34,6 @@
</div>
</el-dialog>
</div>
<!-- 顶部导航栏 -->
<top-header
:room-code="roomCode"
@ -86,7 +85,6 @@
@route-favorites="routeFavorites"
@show-online-members="showOnlineMembersDialog"
/>
<!-- 左侧折叠菜单栏 - 蓝色主题 -->
<left-menu
:is-hidden="isMenuHidden"
@ -103,7 +101,6 @@
@add-items="handleAddMenuItems"
@delete="handleDeleteMenuItem"
/>
<!-- 右侧实体列表浮动- 蓝色主题 -->
<right-panel
ref="rightPanel"
@ -122,11 +119,12 @@
:sea-platforms="seaPlatforms"
:ground-platforms="groundPlatforms"
@hide="hideRightPanel"
@select-plan="selectPlan"
@select-route="selectRoute"
@create-route="createRoute"
@delete-route="handleDeleteRoute"
@select-plan="selectPlan"
@create-plan="createPlan"
@delete-plan="executeDeletePlan"
@open-plan-dialog="openPlanDialog"
@open-route-dialog="openRouteDialog"
@open-waypoint-dialog="openWaypointDialog"
@ -138,10 +136,8 @@
@run-conflict-check="runConflictCheck"
@open-platform-dialog="openPlatformDialog"
/>
<!-- 左下角工具面板 -->
<bottom-left-panel />
<!-- 底部时间轴最初版本的样式- 蓝色主题 -->
<div
class="floating-timeline blue-theme"
@ -151,13 +147,11 @@
<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"
@ -166,7 +160,6 @@
class="compact-slider blue-slider"
/>
</div>
<div class="playback-controls">
<button
class="control-btn blue-control-btn"
@ -175,7 +168,6 @@
>
<i :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
</button>
<div class="speed-control">
<button
class="control-btn blue-control-btn"
@ -256,6 +248,23 @@
v-model="showPageLayoutDialog"
@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>
</template>
@ -273,7 +282,7 @@ import LeftMenu from './LeftMenu'
import RightPanel from './RightPanel'
import BottomLeftPanel from './BottomLeftPanel'
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 { updateWaypoints } from "@/api/system/waypoints";
export default {
@ -328,13 +337,20 @@ export default {
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' },
@ -370,7 +386,6 @@ export default {
//
isRightPanelHidden: true, //
// K
showKTimePopup: false,
@ -394,9 +409,10 @@ export default {
],
//
currentRoomId: null,
plans: [],
activeRightTab: 'plan',
activeRouteIds: [], // 线ID
//
conflictCount: 2,
conflicts: [
@ -437,9 +453,6 @@ export default {
{ id: 10, name: '指挥控制车', type: '指挥车', icon: 'el-icon-monitor', color: '#1890ff', status: 'operating' },
],
// 线 - -线-
plans: [],
//
timeProgress: 45,
currentTime: 'K+01:15:30',
@ -471,24 +484,44 @@ export default {
this.playbackInterval = null;
}
},
created() {
this.currentRoomId = this.$route.query.roomId;
console.log("从路由接收到的真实房间 ID:", this.currentRoomId);
this.getList();
},
methods: {
//
handleOpenWaypointEdit(wpName) {
// 1. 线
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) {
this.$message.warning('请先在右侧列表选择一条航线');
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;
}
// 2.
const wpData = this.selectedRouteDetails.waypoints.find(item => item.name === wpName);
}
//
const wpData = this.selectedRouteDetails.waypoints.find(item => item.id == wpId);
if (wpData) {
// 3.
//
this.selectedWaypoint = JSON.parse(JSON.stringify(wpData));
// 4.
this.showWaypointDialog = true;
}
else {
} else {
this.$message.info('未找到该航点的业务数据');
console.error("查找失败!账本内IDs:", this.selectedRouteDetails.waypoints.map(w => w.id));
}
},
// 线
@ -527,15 +560,19 @@ export default {
},
// 线
createRoute(plan) {
// 线
if (plan) {
this.selectedPlanId = plan.id;
this.selectedPlanDetails = plan;
if (!plan || !plan.id) return;
console.log(`>>> [新建航线触发] 目标方案: ${plan.name}`);
//
if (this.selectedPlanId !== plan.id) {
console.log(">>> [执行跨方案切换] 正在清理旧方案数据...");
this.selectPlan(plan);
} else {
console.log(">>> [同方案内操作] 保持当前航线显示,直接进入规划模式");
}
// Cesium
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.startMissionRouteDrawing();
this.$message.success('进入航线规划模式');
this.$message.success(`${plan.name}开启航线规划`);
}
},
async handleDeleteRoute(route) {
@ -571,8 +608,8 @@ export default {
/** 从数据库拉取最新的航线列表数据 */
async getList() {
try {
// 1.
const scenarioRes = await listScenario({});
const roomId = this.$route.query.roomId || this.currentRoomId;
const scenarioRes = await listScenario({ roomId: roomId });
if (scenarioRes.code === 200) {
this.plans = scenarioRes.rows.map(s => ({
id: s.id,
@ -580,8 +617,7 @@ export default {
routes: []
}));
}
// 2. 线
//线
const routeRes = await listRoutes({});
if (routeRes.code === 200) {
const allRoutes = routeRes.rows.map(item => ({
@ -592,14 +628,27 @@ export default {
conflict: false,
scenarioId: item.scenarioId
}));
// 3. 线
//线
this.plans.forEach(plan => {
plan.routes = allRoutes.filter(r => r.scenarioId === plan.id);
});
// routes 使
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) {
console.error("数据加载失败:", error);
@ -617,24 +666,19 @@ export default {
},
/** 弹窗点击“确定”:正式将数据保存到后端数据库 */
async confirmSaveNewRoute() {
//
// 1.
if (!this.newRouteName || this.newRouteName.trim() === '') {
this.$message.error('新增航线未命名,请输入名称后保存!');
return;
}
// ID
const currentScenarioId = this.selectedPlanId;
if (!currentScenarioId) {
this.$message.warning('请先在左侧选择一个方案,再保存航线!');
return;
}
// Routes
const routeData = {
callSign: this.newRouteName,
scenarioId: currentScenarioId || 1, // 使ID1
platformId: 1,
attributes: "{}",
waypoints: this.tempMapPoints.map((p, index) => ({
// 2.
const finalWaypoints = this.tempMapPoints.map((p, index) => ({
name: `WP${index + 1}`,
lat: p.lat,
lng: p.lng,
@ -642,45 +686,70 @@ export default {
speed: 800.0,
startTime: 'K+00:00:00',
turnAngle: 0.0
}))
}));
const routeData = {
callSign: this.newRouteName,
scenarioId: currentScenarioId,
platformId: 1,
attributes: "{}",
waypoints: finalWaypoints
};
try {
// API
const response = await addRoutes(routeData);
if (response.code === 200) {
this.$message.success('航线及其航点已成功保存至当前方案');
// ID
const savedRoute = response.data;
const newRouteId = savedRoute ? savedRoute.id : null;
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.clearRoute();
// ID线
if (newRouteId) {
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);
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);
}
// UI
this.showNameDialog = false;
// 3. UI
this.drawDom = false;
this.showNameDialog = false;
this.newRouteName = '';
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) {
console.error("保存航线失败:", error);
this.$message.error('保存失败,请检查后端服务');
console.error(">>> [保存异常]:", error);
this.$message.error('保存失败,请检查网络或后端日志');
}
},
//
@ -690,22 +759,19 @@ export default {
},
/** 航点编辑保存:更新数据库并同步地图显示 */
async updateWaypoint(updatedWaypoint) {
// 线
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return;
try {
const response = await updateWaypoints(updatedWaypoint);
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
// 1. Vue UI
this.selectedRouteDetails.waypoints.splice(index, 1, {...updatedWaypoint});
// 5.
// 2. ID
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.updateWaypointGraphic(oldName, newName);
// ID
this.$refs.cesiumMap.updateWaypointGraphicById(updatedWaypoint.id, updatedWaypoint.name);
}
this.showWaypointDialog = false;
@ -753,7 +819,6 @@ export default {
},
//
showRightPanel() {
this.isRightPanelHidden = false;
@ -1209,7 +1274,6 @@ export default {
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;
@ -1218,8 +1282,17 @@ export default {
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) {
@ -1228,8 +1301,6 @@ export default {
// 线
if (index > -1) {
if (isRouteExpanded) {
// 线
// RightPanel toggleRoute
return;
} else {
// 线
@ -1302,14 +1373,34 @@ export default {
},
createPlan() {
const newId = Date.now();
const newPlan = {
id: newId,
name: `新方案${this.plans.length + 1}`,
routes: []
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,
};
this.plans.push(newPlan);
this.selectPlan(newPlan);
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('请输入方案名称', '编辑方案', {
@ -1318,7 +1409,33 @@ export default {
inputValue: plan.name
}).then(({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() {
if (this.selectedRouteDetails) {
@ -1337,7 +1454,6 @@ export default {
}
}
},
cancelRoute() {
// 线
if (this.$refs.cesiumMap) {
@ -1381,7 +1497,6 @@ export default {
this.selectedRouteDetails = null;
}
}
this.$message.info(`已隐藏航线: ${route.name}`);
} else {
// 线
this.selectRoute(route);
@ -1440,6 +1555,7 @@ export default {
background-position: center;
z-index: 1;
}
/* ...其余样式省略,保持不变... */
.map-overlay-text {
position: absolute;

5
ruoyi-ui/src/views/selectRoom/index.vue

@ -192,7 +192,10 @@ export default {
},
enterRoom() {
if (this.selectedRoom) {
this.$router.push('/childRoom');
this.$router.push({
path: '/childRoom',
query: { roomId: this.selectedRoom }
});
}
},
showContextMenu(event, room) {

Loading…
Cancel
Save