diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml index 037db5c..c40f2aa 100644 --- a/ruoyi-admin/src/main/resources/application-druid.yml +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -8,7 +8,7 @@ spring: master: url: jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root - password: 123456 + password: A20040303ctw! # 从库数据源 slave: # 从数据源开关/默认关闭 diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/Routes.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/Routes.java index d1055b2..b12ba07 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/Routes.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/Routes.java @@ -47,6 +47,9 @@ public class Routes extends BaseEntity { private List waypoints; + /** 关联的平台信息(仅用于 API 返回,非数据库字段) */ + private java.util.Map platform; + public void setId(Long id) { this.id = id; } @@ -95,6 +98,14 @@ public class Routes extends BaseEntity { this.waypoints = waypoints; } + public java.util.Map getPlatform() { + return platform; + } + + public void setPlatform(java.util.Map platform) { + this.platform = platform; + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java index cf781a4..bf8ec6c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RoutesServiceImpl.java @@ -1,8 +1,12 @@ package com.ruoyi.system.service.impl; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import com.ruoyi.system.domain.PlatformLib; import com.ruoyi.system.domain.RouteWaypoints; +import com.ruoyi.system.service.IPlatformLibService; import com.ruoyi.system.service.IRouteWaypointsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -26,6 +30,9 @@ public class RoutesServiceImpl implements IRoutesService @Autowired private IRouteWaypointsService routeWaypointsService; + @Autowired + private IPlatformLibService platformLibService; + /** * 查询实体部署与航线 * @@ -45,6 +52,17 @@ public class RoutesServiceImpl implements IRoutesService // 把查出来的航点列表塞进 routes 对象的 waypoints 属性里 routes.setWaypoints(wpList); + // 如有 platformId,查询平台信息并填充 + if (routes.getPlatformId() != null) { + PlatformLib lib = platformLibService.selectPlatformLibById(routes.getPlatformId()); + if (lib != null) { + Map platform = new HashMap<>(); + platform.put("id", lib.getId()); + platform.put("name", lib.getName()); + platform.put("iconUrl", lib.getIconUrl()); + routes.setPlatform(platform); + } + } } return routes; } @@ -66,6 +84,16 @@ public class RoutesServiceImpl implements IRoutesService queryWp.setRouteId(r.getId()); List wpList = routeWaypointsService.selectRouteWaypointsList(queryWp); r.setWaypoints(wpList); + if (r.getPlatformId() != null) { + PlatformLib lib = platformLibService.selectPlatformLibById(r.getPlatformId()); + if (lib != null) { + Map platform = new HashMap<>(); + platform.put("id", lib.getId()); + platform.put("name", lib.getName()); + platform.put("iconUrl", lib.getIconUrl()); + r.setPlatform(platform); + } + } } return list; } diff --git a/ruoyi-ui/.env.development b/ruoyi-ui/.env.development index ebae234..94cd40e 100644 --- a/ruoyi-ui/.env.development +++ b/ruoyi-ui/.env.development @@ -8,7 +8,7 @@ ENV = 'development' VUE_APP_BASE_API = '/dev-api' # 访问地址(绕过 /dev-api 代理,用于解决静态资源/图片访问 401 认证问题) -VUE_APP_BACKEND_URL = 'http://192.168.50.145:8080' +VUE_APP_BACKEND_URL = 'http://192.168.50.30:8080' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/ruoyi-ui/src/views/cesiumMap/index.vue b/ruoyi-ui/src/views/cesiumMap/index.vue index 0eefc48..721a567 100644 --- a/ruoyi-ui/src/views/cesiumMap/index.vue +++ b/ruoyi-ui/src/views/cesiumMap/index.vue @@ -338,8 +338,15 @@ export default { }, 200); }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); }, - //正式航线渲染函数 - renderRouteWaypoints(waypoints, routeId = 'default') { + // 格式化平台图标 URL(与 RouteEditDialog / RightPanel 一致) + formatPlatformIconUrl(url) { + if (!url) return ''; + const cleanPath = (url + '').replace(/\/+/g, '/'); + const backendUrl = process.env.VUE_APP_BACKEND_URL || ''; + return backendUrl + cleanPath; + }, + //正式航线渲染函数(style 可选:waypoint { pixelSize, color, outlineColor, outlineWidth },line { style, width, color, gapColor, dashLength }) + renderRouteWaypoints(waypoints, routeId = 'default', platformId, platform, style) { if (!waypoints || waypoints.length < 1) return; // 清理旧线 const lineId = `route-line-${routeId}`; @@ -360,6 +367,12 @@ export default { this.viewer.entities.remove(existingArc); } }); + const wpStyle = (style && style.waypoint) ? style.waypoint : {}; + const lineStyle = (style && style.line) ? style.line : {}; + const pixelSize = wpStyle.pixelSize != null ? wpStyle.pixelSize : 7; + const wpColor = wpStyle.color || '#ffffff'; + const wpOutline = wpStyle.outlineColor || '#0078FF'; + const wpOutlineW = wpStyle.outlineWidth != null ? wpStyle.outlineWidth : 2; // 遍历并绘制航点标记 const originalPositions = []; waypoints.forEach((wp, index) => { @@ -379,16 +392,16 @@ export default { dbId: wp.id, }, point: { - pixelSize: 10, - color: Cesium.Color.WHITE, - outlineColor: Cesium.Color.fromCssColorString('#0078FF'), - outlineWidth: 3, + pixelSize: pixelSize, + color: Cesium.Color.fromCssColorString(wpColor), + outlineColor: Cesium.Color.fromCssColorString(wpOutline), + outlineWidth: wpOutlineW, disableDepthTestDistance: Number.POSITIVE_INFINITY }, label: { text: wp.name || `WP${index + 1}`, - font: '12px MicroSoft YaHei', - pixelOffset: new Cesium.Cartesian2(0, -20), + font: `${Math.max(9, pixelSize + 2)}px MicroSoft YaHei`, + pixelOffset: new Cesium.Cartesian2(0, -Math.max(14, pixelSize + 8)), fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, @@ -396,6 +409,27 @@ export default { } }); }); + // 在起点渲染平台图标(当航线有关联平台且平台有图标时) + const iconUrl = (platform && platform.imageUrl) || (platform && platform.iconUrl); + if (iconUrl && originalPositions.length > 0) { + const platformBillboardId = `route-platform-${routeId}`; + const fullUrl = this.formatPlatformIconUrl(iconUrl); + this.viewer.entities.add({ + id: platformBillboardId, + name: (platform && platform.name) || '平台', + position: originalPositions[0], + properties: { routeId: routeId }, + billboard: { + image: fullUrl, + width: 144, + height: 144, + verticalOrigin: Cesium.VerticalOrigin.CENTER, + horizontalOrigin: Cesium.HorizontalOrigin.CENTER, + scaleByDistance: new Cesium.NearFarScalar(500, 2.0, 200000, 0.4), + translucencyByDistance: new Cesium.NearFarScalar(1000, 1.0, 500000, 0.6) + } + }); + } // 绘制连线 if (waypoints.length > 1) { let finalPathPositions = []; @@ -431,17 +465,25 @@ export default { finalPathPositions.push(...arcPoints); } } + const lineWidth = lineStyle.width != null ? lineStyle.width : 4; + const lineColor = lineStyle.color || '#ffffff'; + const gapColor = lineStyle.gapColor != null ? lineStyle.gapColor : '#000000'; + const dashLen = lineStyle.dashLength != null ? lineStyle.dashLength : 20; + const useDash = (lineStyle.style || 'dash') === 'dash'; + const lineMaterial = useDash + ? new Cesium.PolylineDashMaterialProperty({ + color: Cesium.Color.fromCssColorString(lineColor), + gapColor: Cesium.Color.fromCssColorString(gapColor), + dashLength: dashLen + }) + : Cesium.Color.fromCssColorString(lineColor); // 绘制包含弧线的 Polyline const routeEntity = this.viewer.entities.add({ id: lineId, polyline: { positions: finalPathPositions, - width: 4, - material: new Cesium.PolylineDashMaterialProperty({ - color: Cesium.Color.WHITE, - gapColor: Cesium.Color.BLACK, - dashLength: 20.0 - }), + width: lineWidth, + material: lineMaterial, clampToGround: true, zIndex: 1 }, @@ -495,17 +537,17 @@ export default { const entityList = this.viewer.entities.values; for (let i = entityList.length - 1; i >= 0; i--) { const entity = entityList[i]; - // 获取 entity 身上绑定的 routeId 属性 - if (entity.properties && entity.properties.routeId) { - // Cesium 的属性系统比较特殊,需要 getValue() 拿原始值 - const id = entity.properties.routeId.getValue(); - if (id === routeId) { - this.viewer.entities.remove(entity); - } + let shouldRemove = false; + // 平台图标实体:通过 id 匹配 + if (entity.id === `route-platform-${routeId}`) { + shouldRemove = true; + } else if (entity.properties && entity.properties.routeId) { + const id = entity.properties.routeId.getValue && entity.properties.routeId.getValue(); + if (id === routeId) shouldRemove = true; } + if (shouldRemove) this.viewer.entities.remove(entity); } - // 同时清理你本地维护的 allEntities 数组 - this.allEntities = this.allEntities.filter(item => item.id !== routeId); + this.allEntities = this.allEntities.filter(item => item.id !== routeId && item.id !== `route-platform-${routeId}`); }, checkCesiumLoaded() { if (typeof Cesium === 'undefined') { diff --git a/ruoyi-ui/src/views/childRoom/index.vue b/ruoyi-ui/src/views/childRoom/index.vue index a8fb150..f4bbca5 100644 --- a/ruoyi-ui/src/views/childRoom/index.vue +++ b/ruoyi-ui/src/views/childRoom/index.vue @@ -297,7 +297,7 @@ import RightPanel from './RightPanel' import BottomLeftPanel from './BottomLeftPanel' import TopHeader from './TopHeader' import { listScenario,addScenario,delScenario} from "@/api/system/scenario"; -import { listRoutes, getRoutes, addRoutes,delRoutes } from "@/api/system/routes"; +import { listRoutes, getRoutes, addRoutes, updateRoutes, delRoutes } from "@/api/system/routes"; import { updateWaypoints } from "@/api/system/waypoints"; import { listLib,addLib } from "@/api/system/lib"; import PlatformImportDialog from "@/views/dialogs/PlatformImportDialog.vue"; @@ -569,6 +569,9 @@ export default { id: routeData.id, name: routeData.callSign, scenarioId: routeData.scenarioId, + platformId: routeData.platformId, + platform: routeData.platform, + attributes: routeData.attributes, points: routeData.waypoints ? routeData.waypoints.length : 0, waypoints: routeData.waypoints || [] }; @@ -649,25 +652,80 @@ export default { // 这里可以添加实际的更新逻辑 this.$message.success('平台更新成功'); }, + /** 从航线 attributes JSON 解析出地图渲染用的样式对象 */ + parseRouteStyle(attributes) { + if (attributes == null || attributes === '') return null; + try { + const attrs = typeof attributes === 'string' ? JSON.parse(attributes) : attributes; + const wp = attrs.waypointStyle || {}; + const ln = attrs.lineStyle || {}; + if (!wp.pixelSize && !wp.color && !ln.width && !ln.color) return null; + return { + waypoint: { + pixelSize: wp.pixelSize, + color: wp.color, + outlineColor: wp.outlineColor, + outlineWidth: wp.outlineWidth + }, + line: { + style: ln.style, + width: ln.width, + color: ln.color, + gapColor: ln.gapColor, + dashLength: ln.dashLength + } + }; + } catch (_) { + return null; + } + }, // 航线编辑弹窗相关方法 openRouteDialog(route) { this.selectedRoute = route; this.showRouteDialog = true; }, - // 更新航线数据 - updateRoute(updatedRoute) { + // 更新航线数据(名称、平台等,并持久化到数据库) + async 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; + if (index === -1) return; + try { + const apiData = { + id: updatedRoute.id, + callSign: updatedRoute.name, + platformId: updatedRoute.platformId || null, + attributes: updatedRoute.attributes != null ? updatedRoute.attributes : undefined + }; + const res = await updateRoutes(apiData); + if (res.code === 200) { + const newRouteData = { + ...this.routes[index], + name: updatedRoute.name, + platformId: updatedRoute.platformId, + platform: updatedRoute.platform, + attributes: updatedRoute.attributes + }; + this.routes.splice(index, 1, newRouteData); + if (this.selectedRouteDetails && this.selectedRouteId === updatedRoute.id) { + this.selectedRouteDetails.name = updatedRoute.name; + this.selectedRouteDetails.platformId = updatedRoute.platformId; + this.selectedRouteDetails.platform = updatedRoute.platform; + this.selectedRouteDetails.attributes = updatedRoute.attributes; + } + this.$message.success('航线更新成功'); + const routeStyle = updatedRoute.routeStyle || this.parseRouteStyle(updatedRoute.attributes); + if (this.$refs.cesiumMap && this.activeRouteIds.includes(updatedRoute.id)) { + const route = this.routes.find(r => r.id === updatedRoute.id); + if (route && route.waypoints && route.waypoints.length > 0) { + this.$refs.cesiumMap.removeRouteById(updatedRoute.id); + this.$refs.cesiumMap.renderRouteWaypoints(route.waypoints, updatedRoute.id, route.platformId, route.platform, routeStyle); + } + } + } else { + this.$message.error(res.msg || '航线更新失败'); } - - this.$message.success('航线名称更新成功'); + } catch (error) { + this.$message.error('航线更新失败'); + console.error('航线更新失败:', error); } }, // 新建航线 @@ -727,12 +785,15 @@ export default { routes: [] })); } - //获取所有航线 - const routeRes = await listRoutes({}); + // 获取所有航线(传大 pageSize 避免分页只返回第一页,新建的航线才能出现在列表中) + const routeRes = await listRoutes({ pageNum: 1, pageSize: 9999 }); if (routeRes.code === 200) { const allRoutes = routeRes.rows.map(item => ({ id: item.id, name: item.callSign, + platformId: item.platformId, + platform: item.platform, + attributes: item.attributes, points: item.waypoints ? item.waypoints.length : 0, waypoints: item.waypoints || [], conflict: false, @@ -753,7 +814,7 @@ export default { if (route && route.waypoints && route.waypoints.length > 0) { if (this.$refs.cesiumMap) { this.$refs.cesiumMap.removeRouteById(id); - this.$refs.cesiumMap.renderRouteWaypoints(route.waypoints, id); + this.$refs.cesiumMap.renderRouteWaypoints(route.waypoints, id, route.platformId, route.platform, this.parseRouteStyle(route.attributes)); } } }); @@ -846,16 +907,13 @@ export default { // 如果 clearAllWaypoints 会清空所有,那这里就不能调用它 } - // 5. 渲染新航线 - this.$nextTick(() => { - if (this.$refs.cesiumMap) { - // 只画这一条新线,旧线因为没被清除,依然会留在地图上 - this.$refs.cesiumMap.renderRouteWaypoints(savedRoute.waypoints, newRouteId); - } - this.$message.success('航线及其航点已成功保存并同步'); - }); - - this.getList(); + // 5. 刷新右侧列表(拉取最新数据,含新建航线);getList 内部会根据 activeRouteIds 自动渲染新航线 + await this.getList(); + const routeFromList = this.routes.find(r => r.id === newRouteId); + if (routeFromList) { + this.selectedRouteDetails = { ...routeFromList }; + } + this.$message.success('航线及其航点已成功保存并同步'); } } catch (error) { console.error(">>> [保存异常]:", error); @@ -891,7 +949,9 @@ export default { // 重新触发航线渲染 this.$refs.cesiumMap.renderRouteWaypoints( this.selectedRouteDetails.waypoints, - this.selectedRouteDetails.id + this.selectedRouteDetails.id, + this.selectedRouteDetails.platformId, + this.selectedRouteDetails.platform ); } this.showWaypointDialog = false; @@ -1507,7 +1567,7 @@ export default { if (waypoints.length > 0) { // 通知地图渲染 if (this.$refs.cesiumMap) { - this.$refs.cesiumMap.renderRouteWaypoints(waypoints, route.id); + this.$refs.cesiumMap.renderRouteWaypoints(waypoints, route.id, route.platformId, route.platform, this.parseRouteStyle(route.attributes)); } } else { this.$message.warning('该航线暂无坐标数据,无法在地图展示'); diff --git a/ruoyi-ui/src/views/dialogs/RouteEditDialog.vue b/ruoyi-ui/src/views/dialogs/RouteEditDialog.vue index f62fd46..7c928d4 100644 --- a/ruoyi-ui/src/views/dialogs/RouteEditDialog.vue +++ b/ruoyi-ui/src/views/dialogs/RouteEditDialog.vue @@ -2,16 +2,161 @@ - - - - - + + +
+ + + + + 航点样式 +
+ + + + + + +
+
+ +
+ + {{ styleForm.waypoint.color }} +
+
+ +
+ + {{ styleForm.waypoint.outlineColor }} +
+
+
+ 航线样式 +
+ + + + + + + + + +
+
+ +
+ + {{ styleForm.line.color }} +
+
+ +
+ + {{ styleForm.line.gapColor }} +
+
+
+
+
+
+ +
+
加载中...
+ +
+
+
取 消 @@ -21,57 +166,166 @@