From f63b8d16a6508c6b07b5a128c4669d14dd5eb566 Mon Sep 17 00:00:00 2001 From: menghao <1584479611@qq.com> Date: Wed, 25 Feb 2026 08:41:23 +0800 Subject: [PATCH 1/2] 1 --- ruoyi-ui/src/views/cesiumMap/index.vue | 212 ++++++++++++++++----------------- 1 file changed, 103 insertions(+), 109 deletions(-) diff --git a/ruoyi-ui/src/views/cesiumMap/index.vue b/ruoyi-ui/src/views/cesiumMap/index.vue index e34114c..d5b2077 100644 --- a/ruoyi-ui/src/views/cesiumMap/index.vue +++ b/ruoyi-ui/src/views/cesiumMap/index.vue @@ -40,14 +40,11 @@ @show-transform-box="showPlatformIconTransformBox" @toggle-route-label="toggleRouteLabelVisibility" @toggle-route-lock="toggleRouteLock" -<<<<<<< HEAD - @edit-platform="openEditPlatformDialog" - @power-zone="openPowerZoneDialog" -======= @start-route-before-platform="handleStartRouteBeforePlatform" @start-route-after-platform="handleStartRouteAfterPlatform" @copy-route="handleCopyRouteFromMenu" ->>>>>>> 9363256d79ba1d213479769fa0a97c737d179e32 + @edit-platform="openEditPlatformDialog" + @power-zone="openPowerZoneDialog" /> @@ -136,7 +133,7 @@ /> - + @@ -291,16 +288,13 @@ export default { }, // 航线飞机标牌显示状态:routeId -> true 显示 / false 隐藏,不设则默认显示 routeLabelVisible: {}, -<<<<<<< HEAD // 航线飞机标牌样式:routeId -> { fontSize, fontColor } routeLabelStyles: {}, // 航线上锁状态:routeId -> true 上锁(不可编辑)/ false 或未设 可编辑 routeLocked: {}, -======= // 航线上锁状态由父组件通过 prop routeLocked 传入,与右侧列表锁图标同步 // 从平台右键进入的航线绘制:{ platformInfo: { platformId, platform }, mode: 'before'|'after' } platformRouteDrawing: null, ->>>>>>> 9363256d79ba1d213479769fa0a97c737d179e32 // 默认样式 defaultStyles: { point: { color: '#FF0000', size: 12 }, @@ -1031,11 +1025,11 @@ export default { canvas.height = img.height; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); - + // 获取像素数据 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; - + // 遍历像素,将所有非透明像素转为白色 for(let i = 0; i < data.length; i += 4) { // Alpha > 0 则处理 @@ -1046,7 +1040,7 @@ export default { // Alpha 保持不变 } } - + ctx.putImageData(imageData, 0, 0); resolve(canvas); // Cesium 支持直接使用 Canvas } catch (e) { @@ -1074,19 +1068,19 @@ export default { */ createRoundedLabelCanvas(options) { const { name, altitude, speed, heading, fontSize = 16, fontColor = '#000000' } = options; - + const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); - + // 设置字体 const font = `${fontSize}px "Microsoft YaHei"`; ctx.font = font; - + // 颜色定义 const colorName = '#0078FF'; // 平台名蓝色 const colorLabel = '#888888'; // 属性名灰色 const colorValue = fontColor; // 属性值(默认黑,可配置) - + // 文本内容 const labelAlt = '高度: '; const labelSpeed = ' 速度: '; @@ -1097,13 +1091,13 @@ export default { // 计算各部分宽度 const wName = ctx.measureText(name).width; - + const wLabelAlt = ctx.measureText(labelAlt).width; const wValAlt = ctx.measureText(textAlt).width; - + const wLabelSpeed = ctx.measureText(labelSpeed).width; const wValSpeed = ctx.measureText(textSpeed).width; - + const wLabelHeading = ctx.measureText(labelHeading).width; const wValHeading = ctx.measureText(textHeading).width; @@ -1112,7 +1106,7 @@ export default { // 第二行:高度 + 速度 + 航向 const line1Width = wName; const line2Width = wLabelAlt + wValAlt + wLabelSpeed + wValSpeed + wLabelHeading + wValHeading; - + const paddingX = 12; const paddingY = 8; const lineHeight = fontSize * 1.4; @@ -1122,7 +1116,7 @@ export default { // 设置 Canvas 大小 (根据设备像素比优化清晰度,这里简单处理,Cesium Billboard 会自动缩放) canvas.width = totalWidth; canvas.height = totalHeight; - + // 重新设置字体(因为重设 width 会重置 context) ctx.font = font; ctx.textBaseline = 'top'; @@ -1142,7 +1136,7 @@ export default { ctx.quadraticCurveTo(0, 0, r, 0); ctx.closePath(); ctx.fill(); - + // 描边(可选,这里加个淡淡的边框) ctx.strokeStyle = '#e0e0e0'; ctx.lineWidth = 1; @@ -1163,7 +1157,7 @@ export default { ctx.fillStyle = colorLabel; ctx.fillText(labelAlt, currentX, line2Y); currentX += wLabelAlt; - + ctx.fillStyle = colorValue; ctx.fillText(textAlt, currentX, line2Y); currentX += wValAlt; @@ -1172,7 +1166,7 @@ export default { ctx.fillStyle = colorLabel; ctx.fillText(labelSpeed, currentX, line2Y); currentX += wLabelSpeed; - + ctx.fillStyle = colorValue; ctx.fillText(textSpeed, currentX, line2Y); currentX += wValSpeed; @@ -1181,7 +1175,7 @@ export default { ctx.fillStyle = colorLabel; ctx.fillText(labelHeading, currentX, line2Y); currentX += wLabelHeading; - + ctx.fillStyle = colorValue; ctx.fillText(textHeading, currentX, line2Y); @@ -1642,7 +1636,7 @@ export default { // 注意:此时如果用户没有修改过颜色,target.billboard.color 默认为 WHITE,显示白图 // 如果用户希望"未编辑时显示原图",这里可能会把黑图变成白图。 // 但根据用户需求"一开始加入平台时将平台的颜色固定为白色",这正是想要的效果。 - + // 触发一次渲染刷新 if (this.viewer.scene.requestRenderMode) { this.viewer.scene.requestRender(); @@ -1664,10 +1658,10 @@ export default { headingDeg: initialHeadingDeg }); const labelShow = this.routeLabelVisible[routeId] !== false - + // 初始化样式 if (!this.routeLabelStyles[routeId]) { - this.$set(this.routeLabelStyles, routeId, { fontSize: 16, fontColor: '#000000' }); + this.$set(this.routeLabelStyles, routeId, { fontSize: 16, fontColor: '#000000' }); } const currentStyle = this.routeLabelStyles[routeId]; @@ -1698,10 +1692,10 @@ export default { }); // 缓存初始数据,供样式更新使用 labelEntity.labelDataCache = { - name: (platform && platform.name) || '平台', - altitude: firstAlt, - speed: firstSpeed, - headingDeg: initialHeadingDeg + name: (platform && platform.name) || '平台', + altitude: firstAlt, + speed: firstSpeed, + headingDeg: initialHeadingDeg }; } // 绘制连线(含盘旋弧) @@ -2333,62 +2327,62 @@ export default { Object.assign(labelEntity.labelDataCache, labelData); if (labelEntity.billboard) { - const style = this.routeLabelStyles[routeId] || { fontSize: 16, fontColor: '#000000' }; - - // 预处理显示数据(取整),既解决了小数点过长问题,也用于差异检测 - const displayData = { - name: labelData.name || '平台', - altitude: Math.round(Number(labelData.altitude || 0)), - speed: Math.round(Number(labelData.speed || 0)), - heading: Math.round(Number(labelData.headingDeg || 0)) - }; - - // 差异检测:如果关键数据和样式都没变,就不重绘 - const last = labelEntity._lastRenderParams; - const now = Date.now(); - // 节流控制:只有当数据变化且距离上次更新超过 1000ms 时才更新(首次除外) - // 这样可以保证位置平滑移动(position每帧更新),但纹理贴图每秒只更新一次,彻底解决闪烁 - if (last && - last.name === displayData.name && - last.altitude === displayData.altitude && - last.speed === displayData.speed && - last.heading === displayData.heading && - last.fontSize === style.fontSize && - last.fontColor === style.fontColor) { - // 数据完全一致,无需更新 - return; - } - - // 如果数据变了,但还没到 1秒 间隔,且不是强制更新(比如刚修改了样式),则跳过 - if (last && (now - (labelEntity._lastUpdateTime || 0) < 1000)) { - return; - } - - const canvas = this.createRoundedLabelCanvas({ - name: displayData.name, - altitude: displayData.altitude, - speed: displayData.speed, - heading: displayData.heading, - fontSize: style.fontSize, - fontColor: style.fontColor - }); - // 直接赋值 canvas,避免 ConstantProperty 包装可能带来的额外开销(Cesium 会自动处理) - labelEntity.billboard.image = canvas; - - // 记录本次渲染参数和时间 - labelEntity._lastRenderParams = { - ...displayData, - fontSize: style.fontSize, - fontColor: style.fontColor - }; - labelEntity._lastUpdateTime = now; - - // 确保在 requestRenderMode 下能刷出来 - if (this.viewer.scene.requestRenderMode) { - this.viewer.scene.requestRender(); - } + const style = this.routeLabelStyles[routeId] || { fontSize: 16, fontColor: '#000000' }; + + // 预处理显示数据(取整),既解决了小数点过长问题,也用于差异检测 + const displayData = { + name: labelData.name || '平台', + altitude: Math.round(Number(labelData.altitude || 0)), + speed: Math.round(Number(labelData.speed || 0)), + heading: Math.round(Number(labelData.headingDeg || 0)) + }; + + // 差异检测:如果关键数据和样式都没变,就不重绘 + const last = labelEntity._lastRenderParams; + const now = Date.now(); + // 节流控制:只有当数据变化且距离上次更新超过 1000ms 时才更新(首次除外) + // 这样可以保证位置平滑移动(position每帧更新),但纹理贴图每秒只更新一次,彻底解决闪烁 + if (last && + last.name === displayData.name && + last.altitude === displayData.altitude && + last.speed === displayData.speed && + last.heading === displayData.heading && + last.fontSize === style.fontSize && + last.fontColor === style.fontColor) { + // 数据完全一致,无需更新 + return; + } + + // 如果数据变了,但还没到 1秒 间隔,且不是强制更新(比如刚修改了样式),则跳过 + if (last && (now - (labelEntity._lastUpdateTime || 0) < 1000)) { + return; + } + + const canvas = this.createRoundedLabelCanvas({ + name: displayData.name, + altitude: displayData.altitude, + speed: displayData.speed, + heading: displayData.heading, + fontSize: style.fontSize, + fontColor: style.fontColor + }); + // 直接赋值 canvas,避免 ConstantProperty 包装可能带来的额外开销(Cesium 会自动处理) + labelEntity.billboard.image = canvas; + + // 记录本次渲染参数和时间 + labelEntity._lastRenderParams = { + ...displayData, + fontSize: style.fontSize, + fontColor: style.fontColor + }; + labelEntity._lastUpdateTime = now; + + // 确保在 requestRenderMode 下能刷出来 + if (this.viewer.scene.requestRenderMode) { + this.viewer.scene.requestRender(); + } } else if (labelEntity.label) { - labelEntity.label.text = this.formatPlatformLabelText(labelData); + labelEntity.label.text = this.formatPlatformLabelText(labelData); } } } @@ -5245,15 +5239,15 @@ export default { return } const routeId = ed.routeId - + // 读取标牌设置 const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`) let fontSize = 16 let fontColor = '#333333' if (this.routeLabelStyles[routeId]) { - fontSize = this.routeLabelStyles[routeId].fontSize; - fontColor = this.routeLabelStyles[routeId].fontColor; + fontSize = this.routeLabelStyles[routeId].fontSize; + fontColor = this.routeLabelStyles[routeId].fontColor; } else if (labelEntity && labelEntity.label) { const now = Cesium.JulianDate.now() const fontValue = labelEntity.label.font && labelEntity.label.font.getValue @@ -5296,7 +5290,7 @@ export default { this.editPlatformForm.fontColor = fontColor this.editPlatformForm.iconSize = iconSize this.editPlatformForm.iconColor = iconColor - + this.contextMenu.visible = false this.editPlatformDialogVisible = true }, @@ -5315,7 +5309,7 @@ export default { this.editPlatformDialogVisible = false return } - + // 保存样式 const fontSize = Math.max(10, Math.min(32, Number(this.editPlatformForm.fontSize) || 16)); const fontColor = this.editPlatformForm.fontColor || '#333333'; @@ -5325,23 +5319,23 @@ export default { const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`) if (labelEntity) { if (labelEntity.billboard) { - const data = labelEntity.labelDataCache || { name: '平台', altitude: 0, speed: 0, headingDeg: 0 }; - const canvas = this.createRoundedLabelCanvas({ - name: data.name, - altitude: data.altitude, - speed: data.speed, - heading: data.headingDeg, - fontSize: fontSize, - fontColor: fontColor - }); - // 使用 ConstantProperty 确保更新 - labelEntity.billboard.image = new Cesium.ConstantProperty(canvas); + const data = labelEntity.labelDataCache || { name: '平台', altitude: 0, speed: 0, headingDeg: 0 }; + const canvas = this.createRoundedLabelCanvas({ + name: data.name, + altitude: data.altitude, + speed: data.speed, + heading: data.headingDeg, + fontSize: fontSize, + fontColor: fontColor + }); + // 使用 ConstantProperty 确保更新 + labelEntity.billboard.image = new Cesium.ConstantProperty(canvas); } else if (labelEntity.label) { - labelEntity.label.font = `${fontSize}px Microsoft YaHei` - labelEntity.label.fillColor = Cesium.Color.fromCssColorString(fontColor) - - // 优化透明度:背景颜色透明度调低,看起来更清爽 - labelEntity.label.backgroundColor = Cesium.Color.fromCssColorString('rgba(255, 255, 255, 0.6)') + labelEntity.label.font = `${fontSize}px Microsoft YaHei` + labelEntity.label.fillColor = Cesium.Color.fromCssColorString(fontColor) + + // 优化透明度:背景颜色透明度调低,看起来更清爽 + labelEntity.label.backgroundColor = Cesium.Color.fromCssColorString('rgba(255, 255, 255, 0.6)') } } @@ -5394,7 +5388,7 @@ export default { this.powerZoneDialogVisible = false return } - + // 移除旧的威力区(如果存在) const oldZoneId = `power-zone-${routeId}` this.viewer.entities.removeById(oldZoneId) From 46b77da38b3360063a40625bea7603430960a5ea Mon Sep 17 00:00:00 2001 From: menghao <1584479611@qq.com> Date: Wed, 25 Feb 2026 15:59:41 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E3=80=81=E6=A0=87=E7=89=8C=E6=A0=B7=E5=BC=8F=E5=AD=98redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/web/controller/RoutesController.java | 39 ++++ .../com/ruoyi/framework/config/RedisConfig.java | 26 ++- ruoyi-ui/src/api/system/routes.js | 18 ++ ruoyi-ui/src/views/cesiumMap/index.vue | 208 ++++++++++++++++++++- 4 files changed, 285 insertions(+), 6 deletions(-) diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java index 18a57c5..f9c29ef 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoutesController.java @@ -20,6 +20,9 @@ import com.ruoyi.system.domain.Routes; import com.ruoyi.system.service.IRoutesService; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.system.domain.dto.PlatformStyleDTO; +import com.alibaba.fastjson2.JSON; +import org.springframework.data.redis.core.RedisTemplate; /** * 实体部署与航线Controller @@ -34,6 +37,42 @@ public class RoutesController extends BaseController @Autowired private IRoutesService routesService; + @Autowired + private RedisTemplate redisTemplate; + + /** + * 保存平台样式到 Redis + */ + @PreAuthorize("@ss.hasPermi('system:routes:edit')") + @PostMapping("/savePlatformStyle") + public AjaxResult savePlatformStyle(@RequestBody PlatformStyleDTO dto) + { + if (dto.getRoomId() == null || dto.getRouteId() == null || dto.getPlatformId() == null) { + return AjaxResult.error("参数不完整"); + } + String key = "room:" + dto.getRoomId() + ":route:" + dto.getRouteId() + ":platforms"; + redisTemplate.opsForHash().put(key, String.valueOf(dto.getPlatformId()), JSON.toJSONString(dto)); + return success(); + } + + /** + * 从 Redis 获取平台样式 + */ + @PreAuthorize("@ss.hasPermi('system:routes:query')") + @GetMapping("/getPlatformStyle") + public AjaxResult getPlatformStyle(PlatformStyleDTO dto) + { + if (dto.getRoomId() == null || dto.getRouteId() == null || dto.getPlatformId() == null) { + return AjaxResult.error("参数不完整"); + } + String key = "room:" + dto.getRoomId() + ":route:" + dto.getRouteId() + ":platforms"; + Object val = redisTemplate.opsForHash().get(key, String.valueOf(dto.getPlatformId())); + if (val != null) { + return success(JSON.parseObject(val.toString(), PlatformStyleDTO.class)); + } + return success(); + } + /** * 查询实体部署与航线列表 */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index 3f4f485..113521b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -25,7 +25,31 @@ public class RedisConfig extends CachingConfigurerSupport RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); - FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer<>(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + /** + * 配置 RedisTemplate + * 解决 RoutesController 中注入 RedisTemplate 失败的问题 + */ + @Bean + public RedisTemplate stringObjectRedisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer<>(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); diff --git a/ruoyi-ui/src/api/system/routes.js b/ruoyi-ui/src/api/system/routes.js index 36b9db4..cf6ad48 100644 --- a/ruoyi-ui/src/api/system/routes.js +++ b/ruoyi-ui/src/api/system/routes.js @@ -42,3 +42,21 @@ export function delRoutes(id) { method: 'delete' }) } + +// 保存平台样式 +export function savePlatformStyle(data) { + return request({ + url: '/system/routes/savePlatformStyle', + method: 'post', + data: data + }) +} + +// 获取平台样式 +export function getPlatformStyle(query) { + return request({ + url: '/system/routes/getPlatformStyle', + method: 'get', + params: query + }) +} diff --git a/ruoyi-ui/src/views/cesiumMap/index.vue b/ruoyi-ui/src/views/cesiumMap/index.vue index bf79670..4fddb47 100644 --- a/ruoyi-ui/src/views/cesiumMap/index.vue +++ b/ruoyi-ui/src/views/cesiumMap/index.vue @@ -169,6 +169,8 @@ import RadiusDialog from '../dialogs/RadiusDialog.vue' import axios from 'axios' import request from '@/utils/request' import { getToken } from '@/utils/auth' +import { savePlatformStyle, getRoutes, getPlatformStyle } from '@/api/system/routes' + export default { name: 'CesiumMap', props: { @@ -295,6 +297,8 @@ export default { // 航线上锁状态由父组件通过 prop routeLocked 传入,与右侧列表锁图标同步 // 从平台右键进入的航线绘制:{ platformInfo: { platformId, platform }, mode: 'before'|'after' } platformRouteDrawing: null, + // 航线平台自定义样式缓存:routeId -> { labelFontSize, labelFontColor, platformSize, platformColor } + platformCustomStyles: {}, // 默认样式 defaultStyles: { point: { color: '#FF0000', size: 12 }, @@ -367,6 +371,27 @@ export default { LocateDialog, RadiusDialog }, + watch: { + // 监听 routeLabelVisible 的变化,当航线显示状态改变时,重新应用样式 + // 这样当从隐藏切换为显示时,可以确保样式被正确应用 + routeLabelVisible: { + handler(newVal) { + // 使用 debounce 或 setTimeout 避免频繁调用 + if (this.applyStylesTimer) clearTimeout(this.applyStylesTimer); + this.applyStylesTimer = setTimeout(() => { + this.applyRedisPlatformStyles(); + }, 300); + }, + deep: true + }, + // 监听 allEntities 变化,当有新实体添加时尝试应用样式(主要针对从隐藏到显示的场景) + allEntities(newVal) { + if (this.applyStylesTimer) clearTimeout(this.applyStylesTimer); + this.applyStylesTimer = setTimeout(() => { + this.applyRedisPlatformStyles(); + }, 500); + } + }, mounted() { console.log(this.drawDomClick,999999) // this.initMap() @@ -1616,7 +1641,10 @@ export default { id: platformBillboardId, name: (platform && platform.name) || '平台', position: originalPositions[0], - properties: { routeId: routeId }, + properties: { + routeId: routeId, + platformId: (platform && platform.id) || 0 + }, billboard: { image: fullUrl, width: 144, @@ -1636,6 +1664,15 @@ export default { if (target && target.billboard) { target.billboard.image = whiteImage; // 此时 target.billboard.color 已在创建时设为 BLACK(航线飞机默认黑色),染色后显示为黑色图标 + + // 如果在图片处理期间已经加载了自定义样式(Redis),需要重新应用一次颜色否则这里会保持默认黑色,覆盖掉 applyRedisPlatformStyles 的效果(如果它先执行了) + + if (this.platformCustomStyles && this.platformCustomStyles[routeId]) { + const style = this.platformCustomStyles[routeId]; + if (style.platformColor) { + target.billboard.color = Cesium.Color.fromCssColorString(style.platformColor); + } + } // 触发一次渲染刷新 if (this.viewer.scene.requestRenderMode) { @@ -2327,9 +2364,18 @@ export default { if (!labelEntity.labelDataCache) labelEntity.labelDataCache = {}; Object.assign(labelEntity.labelDataCache, labelData); - if (labelEntity.billboard) { - const style = this.routeLabelStyles[routeId] || { fontSize: 16, fontColor: '#000000' }; + // 优先使用 Redis 自定义样式,如果没有则使用 routeLabelStyles(可能为空或旧值) + const customStyle = this.platformCustomStyles[routeId]; + let style = { fontSize: 16, fontColor: '#000000' }; + + if (customStyle) { + style.fontSize = customStyle.labelFontSize || 16; + style.fontColor = customStyle.labelFontColor || '#000000'; + } else if (this.routeLabelStyles[routeId]) { + style = this.routeLabelStyles[routeId]; + } + if (labelEntity.billboard) { // 预处理显示数据(取整),既解决了小数点过长问题,也用于差异检测 const displayData = { name: labelData.name || '平台', @@ -2720,12 +2766,29 @@ export default { const idStr = typeof pickedEntity.id === 'string' ? pickedEntity.id : (pickedEntity.id || '') let entityData = null // 航线上的飞机(平台图标):右键可切换标牌显示/隐藏 - if (idStr.startsWith('route-platform-') && !idStr.startsWith('route-platform-label-')) { - const routeId = idStr.replace('route-platform-', '') + if (idStr.startsWith('route-platform-')) { + const routeId = idStr.replace('route-platform-', '').replace('route-platform-label-', '') + // 尝试获取该实体绑定的 platformId 和 platformName + // 通常这些信息绑定在 entity.properties 中 + let platformId = 0 + let platformName = '平台' + if (pickedEntity.properties) { + const now = Cesium.JulianDate.now(); + // 尝试直接获取 + if (pickedEntity.properties.platformId) { + platformId = pickedEntity.properties.platformId.getValue ? pickedEntity.properties.platformId.getValue(now) : pickedEntity.properties.platformId + } + if (pickedEntity.properties.platformName) { + platformName = pickedEntity.properties.platformName.getValue ? pickedEntity.properties.platformName.getValue(now) : pickedEntity.properties.platformName + } + } + entityData = { type: 'routePlatform', routeId, entity: pickedEntity, + platformId, + platformName, labelVisible: this.routeLabelVisible[routeId] !== false } } @@ -5274,6 +5337,17 @@ export default { const platformEntity = this.viewer.entities.getById(`route-platform-${routeId}`) let iconSize = 144 let iconColor = '#ffffff' + let platformName = '平台' // 默认名称 + + // 尝试获取平台名称 + if (labelEntity && labelEntity.label && labelEntity.label.text) { + const now = Cesium.JulianDate.now() + const text = labelEntity.label.text.getValue ? labelEntity.label.text.getValue(now) : labelEntity.label.text + if (text) platformName = text + } else if (ed.platformName) { + platformName = ed.platformName + } + if (platformEntity && platformEntity.billboard) { const now = Cesium.JulianDate.now() const widthValue = platformEntity.billboard.width && platformEntity.billboard.width.getValue @@ -5294,6 +5368,23 @@ export default { this.editPlatformForm.fontColor = fontColor this.editPlatformForm.iconSize = iconSize this.editPlatformForm.iconColor = iconColor + this.editPlatformForm.platformName = platformName + this.editPlatformForm.platformId = ed.platformId || 0 + + // 异步获取最新航线信息,更新 platformId 和 platformName + if (routeId) { + getRoutes(routeId).then(response => { + const route = response.data; + if (route && route.platformId) { + this.editPlatformForm.platformId = route.platformId; + if (route.platform && route.platform.name) { + this.editPlatformForm.platformName = route.platform.name; + } + } + }).catch(err => { + console.error("获取航线信息失败", err); + }); + } this.contextMenu.visible = false this.editPlatformDialogVisible = true @@ -5352,6 +5443,35 @@ export default { platformEntity.billboard.height = size platformEntity.billboard.color = Cesium.Color.fromCssColorString(color) } + + // 更新样式缓存 + this.$set(this.platformCustomStyles, routeId, { + labelFontSize: fontSize, + labelFontColor: fontColor, + platformSize: Math.max(48, Math.min(256, Number(this.editPlatformForm.iconSize) || 144)), + platformColor: this.editPlatformForm.iconColor || '#ffffff' + }); + + // 调用后端接口保存样式到Redis + const currentRoomId = this.$route.query.roomId || (this.$parent && this.$parent.currentRoomId); + + if (currentRoomId) { + const styleData = { + roomId: String(currentRoomId), + routeId: routeId, + platformId: this.editPlatformForm.platformId, // 需要确保 openEditPlatformDialog 中设置了 platformId + platformName: this.editPlatformForm.platformName, // 需要确保 openEditPlatformDialog 中设置了 platformName + labelFontSize: fontSize, + labelFontColor: fontColor, + platformSize: Math.max(48, Math.min(256, Number(this.editPlatformForm.iconSize) || 144)), + platformColor: this.editPlatformForm.iconColor || '#ffffff' + }; + savePlatformStyle(styleData).then(() => { + // console.log("样式保存成功"); + }).catch(err => { + console.error("样式保存失败", err); + }); + } if (this.viewer.scene && this.viewer.scene.requestRenderMode) { this.viewer.scene.requestRender() @@ -5434,6 +5554,84 @@ export default { this.contextMenu.visible = false } }, + + /** + * 批量获取并应用航线平台样式(Redis) + * 遍历 allEntities 中的所有航线平台,调用 getPlatformStyle + */ + applyRedisPlatformStyles() { + // 获取 roomId + const currentRoomId = this.$route.query.roomId || (this.$parent && this.$parent.currentRoomId); + if (!currentRoomId) return; + + // 遍历查找所有的航线平台实体 + const entities = this.viewer.entities.values; + for (const entity of entities) { + const idStr = entity.id || ''; + if (idStr.startsWith('route-platform-') && !idStr.startsWith('route-platform-label-')) { + const routeId = idStr.replace('route-platform-', ''); + + // 获取 platformId + let platformId = 0; + if (entity.properties && entity.properties.platformId) { + const now = Cesium.JulianDate.now(); + platformId = entity.properties.platformId.getValue ? entity.properties.platformId.getValue(now) : entity.properties.platformId; + } + + // 确保 platformId 存在(可能是字符串或数字,需转为数字判断) + if (Number(platformId) > 0) { + getPlatformStyle({ + roomId: currentRoomId, + routeId: routeId, + platformId: platformId + }).then(res => { + if (res.data) { + const style = res.data; + + // 缓存样式 + this.$set(this.platformCustomStyles, routeId, { + labelFontSize: style.labelFontSize, + labelFontColor: style.labelFontColor, + platformSize: style.platformSize, + platformColor: style.platformColor + }); + + // 应用标牌样式 + const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`); + if (labelEntity) { + // 触发重绘(借助 updatePlatformPosition 中的逻辑) + // 这里手动设置一下样式,updatePlatformPosition 会在下次更新时使用 + this.$set(this.routeLabelStyles, routeId, { + fontSize: style.labelFontSize || 16, + fontColor: style.labelFontColor || '#333333' + }); + // 强制更新标牌内容 + if (labelEntity.labelDataCache) { + // 清除最后更新时间,强制刷新 + labelEntity._lastUpdateTime = 0; + this.updatePlatformPosition(routeId, entity.position.getValue(Cesium.JulianDate.now()), null, labelEntity.labelDataCache); + } + } + + // 应用平台图标样式 + if (entity.billboard) { + const size = Math.max(48, Math.min(256, Number(style.platformSize) || 144)); + const color = style.platformColor || '#ffffff'; + entity.billboard.width = size; + entity.billboard.height = size; + entity.billboard.color = Cesium.Color.fromCssColorString(color); + } + + if (this.viewer.scene.requestRenderMode) { + this.viewer.scene.requestRender(); + } + } + }); + } + } + } + }, + // 更新实体属性 updateEntityProperty(property, value) { if (this.contextMenu.entityData) {