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 --- 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)