Browse Source

平台编辑,标牌编辑,威力区显示

mh
menghao 1 month ago
parent
commit
6065cb1e0d
  1. 2
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomsController.java
  2. 18
      ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
  3. 732
      ruoyi-ui/src/views/cesiumMap/index.vue
  4. 4
      ruoyi-ui/src/views/childRoom/index.vue

2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomsController.java

@ -2,8 +2,6 @@ package com.ruoyi.web.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.SecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;

18
ruoyi-ui/src/views/cesiumMap/ContextMenu.vue

@ -16,13 +16,21 @@
</div>
</div>
<!-- 航线上飞机显示/隐藏标牌 -->
<!-- 航线上飞机显示/隐藏/编辑标牌编辑平台 -->
<div class="menu-section" v-if="entityData && entityData.type === 'routePlatform'">
<div class="menu-title">飞机标牌</div>
<div class="menu-item" @click="handleToggleRouteLabel">
<span class="menu-icon">🏷</span>
<span>{{ entityData.labelVisible ? '隐藏标牌' : '显示标牌' }}</span>
</div>
<div class="menu-item" @click="handleEditPlatform">
<span class="menu-icon">📝</span>
<span>编辑</span>
</div>
<div class="menu-item" @click="handlePowerZone">
<span class="menu-icon"></span>
<span>威力区</span>
</div>
</div>
<!-- 线段特有选项 -->
@ -388,6 +396,14 @@ export default {
this.$emit('toggle-route-lock')
},
handleEditPlatform() {
this.$emit('edit-platform')
},
handlePowerZone() {
this.$emit('power-zone')
},
toggleColorPicker(property) {
if (this.showColorPickerFor === property) {
this.showColorPickerFor = null

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

@ -40,6 +40,8 @@
@show-transform-box="showPlatformIconTransformBox"
@toggle-route-label="toggleRouteLabelVisibility"
@toggle-route-lock="toggleRouteLock"
@edit-platform="openEditPlatformDialog"
@power-zone="openPowerZoneDialog"
/>
<!-- 定位弹窗 -->
@ -62,6 +64,75 @@
@confirm="handleRadiusConfirm"
/>
<!-- 编辑平台属性标牌+平台 -->
<el-dialog
title="编辑平台属性"
:visible.sync="editPlatformDialogVisible"
:modal="false"
width="360px"
>
<el-form :model="editPlatformForm" label-width="80px" size="small">
<el-divider content-position="left">标牌设置</el-divider>
<el-form-item label="字体大小">
<el-input-number
v-model="editPlatformForm.fontSize"
:min="10"
:max="32"
controls-position="right"
style="width: 100%;"
/>
</el-form-item>
<el-form-item label="字体颜色">
<el-color-picker v-model="editPlatformForm.fontColor" size="small" />
</el-form-item>
<el-divider content-position="left">平台设置</el-divider>
<el-form-item label="平台大小">
<el-input-number
v-model="editPlatformForm.iconSize"
:min="48"
:max="256"
controls-position="right"
style="width: 100%;"
/>
</el-form-item>
<el-form-item label="平台颜色">
<el-color-picker v-model="editPlatformForm.iconColor" size="small" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editPlatformDialogVisible = false"> </el-button>
<el-button type="primary" @click="applyEditPlatform"> </el-button>
</span>
</el-dialog>
<!-- 威力区半径输入弹窗 -->
<el-dialog
title="设置威力区半径"
:visible.sync="powerZoneDialogVisible"
:modal="false"
width="300px"
>
<el-form :model="powerZoneForm" label-width="80px" size="small">
<el-form-item label="半径(米)">
<el-input-number
v-model="powerZoneForm.radius"
:min="100"
:step="100"
controls-position="right"
style="width: 100%;"
/>
</el-form-item>
<el-form-item label="填充颜色">
<el-color-picker v-model="powerZoneForm.color" show-alpha size="small" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="powerZoneDialogVisible = false"> </el-button>
<el-button type="primary" @click="applyPowerZone"> </el-button>
</span>
</el-dialog>
<!-- 地图右下角比例尺 + 经纬度 -->
<div class="map-info-panel">
<div class="scale-bar" @click="handleScaleClick">
@ -171,8 +242,30 @@ export default {
position: { x: 0, y: 0 },
entityData: null
},
editPlatformLabelDialogVisible: false,
editPlatformLabelForm: {
routeId: null,
fontSize: 16,
color: '#333333'
},
editPlatformDialogVisible: false,
editPlatformForm: {
routeId: null,
fontSize: 16,
fontColor: '#333333',
iconSize: 144,
iconColor: '#ffffff'
},
powerZoneDialogVisible: false,
powerZoneForm: {
routeId: null,
radius: 1000,
color: 'rgba(255, 0, 0, 0.3)'
},
// 线routeId -> true / false
routeLabelVisible: {},
// 线routeId -> { fontSize, fontColor }
routeLabelStyles: {},
// 线routeId -> true / false
routeLocked: {},
//
@ -587,10 +680,191 @@ export default {
/** 默认平台图标(无 imageUrl 时使用):简单飞机剪影 SVG */
getDefaultPlatformIconDataUrl() {
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="%23666" d="M28 14L18 8l-4 2v4L8 10 4 14l4 2 2 4-4 2v4l4 2 4-2 2 4 2-2v-6l8-4 4-2v-4l-4 2-8-4z"/></svg>';
// 便
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path fill="white" d="M28 14L18 8l-4 2v4L8 10 4 14l4 2 2 4-4 2v4l4 2 4-2 2 4 2-2v-6l8-4 4-2v-4l-4 2-8-4z"/></svg>';
return 'data:image/svg+xml,' + encodeURIComponent(svg);
},
/**
* 加载图片并转为白色保留透明度确保黑色图标可以被 Cesium 正确染色
* @param {string} url 图片 URL
* @returns {Promise<string|HTMLCanvasElement>} 返回处理后的 Canvas 或原 URL
*/
loadAndWhitenImage(url) {
return new Promise((resolve) => {
// Data URL SVG
if (url && url.startsWith('data:image/svg+xml') && url.includes('fill="white"')) {
resolve(url);
return;
}
const img = new Image();
img.crossOrigin = "Anonymous";
img.src = url;
img.onload = () => {
try {
const canvas = document.createElement('canvas');
canvas.width = img.width;
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
if(data[i+3] > 0) {
data[i] = 255; // R
data[i+1] = 255; // G
data[i+2] = 255; // B
// Alpha
}
}
ctx.putImageData(imageData, 0, 0);
resolve(canvas); // Cesium 使 Canvas
} catch (e) {
console.warn("图片转白处理失败 (可能是跨域问题),使用原图:", e);
resolve(url);
}
};
img.onerror = () => {
console.warn("图片加载失败,使用原图:", url);
resolve(url);
};
});
},
/**
* 生成圆角矩形 + 多色文本的 Canvas 图像
* @param {Object} options 配置项
* @param {string} options.name 平台名称蓝色
* @param {string} options.altitude 高度值
* @param {string} options.speed 速度值
* @param {string} options.heading 航向值
* @param {number} options.fontSize 字号默认 16
* @param {string} options.fontColor 属性值颜色默认黑色
* @returns {HTMLCanvasElement}
*/
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 = ' 速度: ';
const labelHeading = ' 航向: ';
const textAlt = altitude + 'm';
const textSpeed = speed + 'km/h';
const textHeading = Math.round(heading) + '°';
//
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;
//
//
// + +
const line1Width = wName;
const line2Width = wLabelAlt + wValAlt + wLabelSpeed + wValSpeed + wLabelHeading + wValHeading;
const paddingX = 12;
const paddingY = 8;
const lineHeight = fontSize * 1.4;
const totalWidth = Math.max(line1Width, line2Width) + paddingX * 2;
const totalHeight = lineHeight * 2 + paddingY * 2;
// Canvas (Cesium Billboard )
canvas.width = totalWidth;
canvas.height = totalHeight;
// width context
ctx.font = font;
ctx.textBaseline = 'top';
// 1.
const r = 8; //
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'; //
ctx.beginPath();
ctx.moveTo(r, 0);
ctx.lineTo(totalWidth - r, 0);
ctx.quadraticCurveTo(totalWidth, 0, totalWidth, r);
ctx.lineTo(totalWidth, totalHeight - r);
ctx.quadraticCurveTo(totalWidth, totalHeight, totalWidth - r, totalHeight);
ctx.lineTo(r, totalHeight);
ctx.quadraticCurveTo(0, totalHeight, 0, totalHeight - r);
ctx.lineTo(0, r);
ctx.quadraticCurveTo(0, 0, r, 0);
ctx.closePath();
ctx.fill();
//
ctx.strokeStyle = '#e0e0e0';
ctx.lineWidth = 1;
ctx.stroke();
// 2.
const line1X = (totalWidth - wName) / 2;
const line1Y = paddingY;
ctx.fillStyle = colorName;
ctx.fillText(name, line1X, line1Y);
// 3.
const line2X = (totalWidth - line2Width) / 2;
const line2Y = paddingY + lineHeight;
let currentX = line2X;
//
ctx.fillStyle = colorLabel;
ctx.fillText(labelAlt, currentX, line2Y);
currentX += wLabelAlt;
ctx.fillStyle = colorValue;
ctx.fillText(textAlt, currentX, line2Y);
currentX += wValAlt;
//
ctx.fillStyle = colorLabel;
ctx.fillText(labelSpeed, currentX, line2Y);
currentX += wLabelSpeed;
ctx.fillStyle = colorValue;
ctx.fillText(textSpeed, currentX, line2Y);
currentX += wValSpeed;
//
ctx.fillStyle = colorLabel;
ctx.fillText(labelHeading, currentX, line2Y);
currentX += wLabelHeading;
ctx.fillStyle = colorValue;
ctx.fillText(textHeading, currentX, line2Y);
return canvas;
},
/** 伸缩框旋转手柄图标 SVG:蓝底、白边、白色弧形箭头 */
getRotationHandleIconDataUrl() {
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">' +
@ -1017,6 +1291,7 @@ export default {
image: fullUrl,
width: 144,
height: 144,
color: Cesium.Color.WHITE, //
verticalOrigin: Cesium.VerticalOrigin.CENTER,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
scaleByDistance: new Cesium.NearFarScalar(500, 2.0, 200000, 0.4),
@ -1024,6 +1299,23 @@ export default {
...(initialRotation !== undefined && { rotation: initialRotation })
}
});
// 便
this.loadAndWhitenImage(fullUrl).then(whiteImage => {
const target = this.viewer.entities.getById(platformBillboardId);
if (target && target.billboard) {
target.billboard.image = whiteImage;
// target.billboard.color WHITE
// ""
// ""
//
if (this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender();
}
}
});
//
const firstWp = waypoints[0];
const firstAlt = firstWp && (firstWp.alt != null) ? Number(firstWp.alt) : 0;
@ -1038,22 +1330,31 @@ export default {
headingDeg: initialHeadingDeg
});
const labelShow = this.routeLabelVisible[routeId] !== false
this.viewer.entities.add({
//
if (!this.routeLabelStyles[routeId]) {
this.$set(this.routeLabelStyles, routeId, { fontSize: 16, fontColor: '#000000' });
}
const currentStyle = this.routeLabelStyles[routeId];
// 使 Canvas
const labelCanvas = this.createRoundedLabelCanvas({
name: (platform && platform.name) || '平台',
altitude: firstAlt,
speed: firstSpeed,
heading: Math.round(initialHeadingDeg),
fontSize: currentStyle.fontSize,
fontColor: currentStyle.fontColor
});
const labelEntity = this.viewer.entities.add({
id: platformLabelId,
name: '平台标牌',
position: originalPositions[0],
show: labelShow,
properties: { routeId: routeId },
label: {
text: labelText,
font: '16px Microsoft YaHei',
fillColor: Cesium.Color.fromCssColorString('#333333'),
outlineColor: Cesium.Color.fromCssColorString('#e0e0e0'),
outlineWidth: 1,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
showBackground: true,
backgroundColor: Cesium.Color.fromCssColorString('rgba(255, 255, 255, 0.95)'),
backgroundPadding: new Cesium.Cartesian2(10, 6),
billboard: {
image: labelCanvas, // 使 Canvas
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
pixelOffset: new Cesium.Cartesian2(0, -42),
@ -1061,6 +1362,13 @@ export default {
scaleByDistance: new Cesium.NearFarScalar(500, 1.0, 200000, 0.65)
}
});
// 使
labelEntity.labelDataCache = {
name: (platform && platform.name) || '平台',
altitude: firstAlt,
speed: firstSpeed,
headingDeg: initialHeadingDeg
};
}
// 线
if (waypoints.length > 1) {
@ -1565,6 +1873,11 @@ export default {
return { path, segmentEndIndices, holdArcRanges };
},
removePowerZoneByRouteId(routeId) {
const zoneId = `power-zone-${routeId}`;
this.viewer.entities.removeById(zoneId);
},
removeRouteById(routeId) {
// routeId
const entityList = this.viewer.entities.values;
@ -1572,7 +1885,7 @@ export default {
const entity = entityList[i];
let shouldRemove = false;
// id
if (entity.id === `route-platform-${routeId}` || entity.id === `route-platform-label-${routeId}`) {
if (entity.id === `route-platform-${routeId}` || entity.id === `route-platform-label-${routeId}` || entity.id === `power-zone-${routeId}`) {
shouldRemove = true;
} else if (entity.properties && entity.properties.routeId) {
const id = entity.properties.routeId.getValue && entity.properties.routeId.getValue();
@ -1641,8 +1954,69 @@ export default {
const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`);
if (labelEntity && labelEntity.position) {
labelEntity.position = cartesian;
if (labelData && labelEntity.label) {
labelEntity.label.text = this.formatPlatformLabelText(labelData);
if (labelData) {
//
if (!labelEntity.labelDataCache) labelEntity.labelDataCache = {};
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();
}
} else if (labelEntity.label) {
labelEntity.label.text = this.formatPlatformLabelText(labelData);
}
}
}
},
@ -1904,6 +2278,114 @@ export default {
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
},
openEditPlatformLabelDialog() {
const ed = this.contextMenu.entityData
if (!ed || ed.type !== 'routePlatform' || ed.routeId == null) {
this.contextMenu.visible = false
return
}
const routeId = ed.routeId
const labelEntity = this.viewer && this.viewer.entities && this.viewer.entities.getById(`route-platform-label-${routeId}`)
let fontSize = 16
let color = '#333333'
if (labelEntity && labelEntity.label) {
const now = Cesium.JulianDate.now()
const fontValue = labelEntity.label.font && labelEntity.label.font.getValue
? labelEntity.label.font.getValue(now)
: labelEntity.label.font
if (fontValue && typeof fontValue === 'string') {
const match = fontValue.match(/(\d+)px/)
if (match) fontSize = parseInt(match[1], 10)
}
const fill = labelEntity.label.fillColor
if (fill) {
const c = fill.getValue ? fill.getValue(now) : fill
if (c && c.toCssColorString) {
color = c.toCssColorString()
}
}
}
this.editPlatformLabelForm = {
routeId,
fontSize,
color
}
this.contextMenu.visible = false
this.editPlatformLabelDialogVisible = true
},
applyEditPlatformLabel() {
const routeId = this.editPlatformLabelForm.routeId
if (!routeId || !this.viewer || !this.viewer.entities) {
this.editPlatformLabelDialogVisible = false
return
}
const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`)
if (labelEntity && labelEntity.label) {
const size = Math.max(10, Math.min(32, Number(this.editPlatformLabelForm.fontSize) || 16))
const color = this.editPlatformLabelForm.color || '#333333'
labelEntity.label.font = `${size}px Microsoft YaHei`
labelEntity.label.fillColor = Cesium.Color.fromCssColorString(color)
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
}
this.editPlatformLabelDialogVisible = false
},
openEditPlatformStyleDialog() {
const ed = this.contextMenu.entityData
if (!ed || ed.type !== 'routePlatform' || ed.routeId == null) {
this.contextMenu.visible = false
return
}
const routeId = ed.routeId
const platformEntity = this.viewer && this.viewer.entities && this.viewer.entities.getById(`route-platform-${routeId}`)
let size = 144
let color = '#ffffff'
if (platformEntity && platformEntity.billboard) {
const now = Cesium.JulianDate.now()
const widthValue = platformEntity.billboard.width && platformEntity.billboard.width.getValue
? platformEntity.billboard.width.getValue(now)
: platformEntity.billboard.width
if (widthValue != null) size = Number(widthValue)
const col = platformEntity.billboard.color
if (col) {
const c = col.getValue ? col.getValue(now) : col
if (c && c.toCssColorString) {
color = c.toCssColorString()
}
}
}
this.editPlatformStyleForm = {
routeId,
size,
color
}
this.contextMenu.visible = false
this.editPlatformStyleDialogVisible = true
},
applyEditPlatformStyle() {
const routeId = this.editPlatformStyleForm.routeId
if (!routeId || !this.viewer || !this.viewer.entities) {
this.editPlatformStyleDialogVisible = false
return
}
const entity = this.viewer.entities.getById(`route-platform-${routeId}`)
if (entity && entity.billboard) {
const size = Math.max(48, Math.min(256, Number(this.editPlatformStyleForm.size) || 144))
const color = this.editPlatformStyleForm.color || '#ffffff'
entity.billboard.width = size
entity.billboard.height = size
entity.billboard.color = Cesium.Color.fromCssColorString(color)
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
}
this.editPlatformStyleDialogVisible = false
},
/** 平台图标图形化操作:伸缩框(旋转手柄 + 四角缩放)、拖拽移动、单击选中 */
initPlatformIconInteraction() {
const canvas = this.viewer.scene.canvas;
@ -4231,6 +4713,192 @@ export default {
this.contextMenu.visible = false
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender()
},
/** 右键飞机:打开编辑平台对话框(合并了标牌和平台) */
openEditPlatformDialog() {
const ed = this.contextMenu.entityData
if (!ed || ed.type !== 'routePlatform' || ed.routeId == null) {
this.contextMenu.visible = false
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;
} else if (labelEntity && labelEntity.label) {
const now = Cesium.JulianDate.now()
const fontValue = labelEntity.label.font && labelEntity.label.font.getValue
? labelEntity.label.font.getValue(now)
: labelEntity.label.font
if (fontValue && typeof fontValue === 'string') {
const match = fontValue.match(/(\d+)px/)
if (match) fontSize = parseInt(match[1], 10)
}
const fill = labelEntity.label.fillColor
if (fill) {
const c = fill.getValue ? fill.getValue(now) : fill
if (c && c.toCssColorString) {
fontColor = c.toCssColorString()
}
}
}
//
const platformEntity = this.viewer.entities.getById(`route-platform-${routeId}`)
let iconSize = 144
let iconColor = '#ffffff'
if (platformEntity && platformEntity.billboard) {
const now = Cesium.JulianDate.now()
const widthValue = platformEntity.billboard.width && platformEntity.billboard.width.getValue
? platformEntity.billboard.width.getValue(now)
: platformEntity.billboard.width
if (widthValue != null) iconSize = Number(widthValue)
const col = platformEntity.billboard.color
if (col) {
const c = col.getValue ? col.getValue(now) : col
if (c && c.toCssColorString) {
iconColor = c.toCssColorString()
}
}
}
this.editPlatformForm.routeId = routeId
this.editPlatformForm.fontSize = fontSize
this.editPlatformForm.fontColor = fontColor
this.editPlatformForm.iconSize = iconSize
this.editPlatformForm.iconColor = iconColor
this.contextMenu.visible = false
this.editPlatformDialogVisible = true
},
/** 应用编辑:同时更新标牌和平台 */
applyEditPlatform() {
// el-input-number v-model
if (document.activeElement) {
document.activeElement.blur();
}
// 使 setTimeout v-model
setTimeout(() => {
const routeId = this.editPlatformForm.routeId
if (!routeId || !this.viewer || !this.viewer.entities) {
this.editPlatformDialogVisible = false
return
}
//
const fontSize = Math.max(10, Math.min(32, Number(this.editPlatformForm.fontSize) || 16));
const fontColor = this.editPlatformForm.fontColor || '#333333';
this.$set(this.routeLabelStyles, routeId, { fontSize, fontColor });
//
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);
} 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)')
}
}
//
const platformEntity = this.viewer.entities.getById(`route-platform-${routeId}`)
if (platformEntity && platformEntity.billboard) {
const size = Math.max(48, Math.min(256, Number(this.editPlatformForm.iconSize) || 144))
const color = this.editPlatformForm.iconColor || '#ffffff'
platformEntity.billboard.width = size
platformEntity.billboard.height = size
platformEntity.billboard.color = Cesium.Color.fromCssColorString(color)
}
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
//
setTimeout(() => this.viewer.scene.requestRender(), 50)
}
this.editPlatformDialogVisible = false
}, 0);
},
/** 右键飞机:打开威力区设置弹窗 */
openPowerZoneDialog() {
const ed = this.contextMenu.entityData
if (!ed || ed.type !== 'routePlatform' || ed.routeId == null) {
this.contextMenu.visible = false
return
}
// routeId
this.powerZoneForm = {
routeId: ed.routeId,
radius: 1000,
color: 'rgba(255, 0, 0, 0.3)'
}
this.contextMenu.visible = false
this.powerZoneDialogVisible = true
},
/** 应用威力区:以当前平台位置为中心画圆 */
applyPowerZone() {
const routeId = this.powerZoneForm.routeId
if (!routeId || !this.viewer) {
this.powerZoneDialogVisible = false
return
}
const platformEntity = this.viewer.entities.getById(`route-platform-${routeId}`)
if (!platformEntity) {
this.$message.error('未找到对应平台,无法添加威力区')
this.powerZoneDialogVisible = false
return
}
//
const oldZoneId = `power-zone-${routeId}`
this.viewer.entities.removeById(oldZoneId)
//
// 使 CallbackProperty 使
this.viewer.entities.add({
id: oldZoneId,
position: new Cesium.CallbackProperty(() => {
const now = Cesium.JulianDate.now()
return platformEntity.position.getValue(now)
}, false),
ellipse: {
semiMinorAxis: this.powerZoneForm.radius,
semiMajorAxis: this.powerZoneForm.radius,
material: Cesium.Color.fromCssColorString(this.powerZoneForm.color),
outline: true,
outlineColor: Cesium.Color.fromCssColorString(this.powerZoneForm.color).withAlpha(1.0),
outlineWidth: 2
}
})
if (this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
this.powerZoneDialogVisible = false
this.$message.success(`已添加半径 ${this.powerZoneForm.radius} 米的威力区`)
},
//
deleteEntityFromContextMenu() {
if (this.contextMenu.entityData) {
@ -5338,4 +6006,38 @@ export default {
min-width: 200px;
text-align: right;
}
/* 优化:居中且带圆角 */
::v-deep .el-dialog {
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
margin: 0 !important;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
::v-deep .el-dialog .el-dialog__header {
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
padding: 15px 20px;
}
::v-deep .el-dialog .el-dialog__title {
font-weight: 600;
font-size: 16px;
}
::v-deep .el-dialog .el-dialog__body {
padding: 20px 25px;
}
::v-deep .el-dialog .el-dialog__footer {
border-top: 1px solid #ebeef5;
padding: 10px 20px;
background-color: #fff;
}
</style>

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

@ -990,6 +990,8 @@ export default {
// 线
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(route.id);
// 线
this.$refs.cesiumMap.removePowerZoneByRouteId(route.id);
}
// ID
const idx = this.activeRouteIds.indexOf(route.id);
@ -2566,6 +2568,8 @@ export default {
this.activeRouteIds.splice(index, 1);
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.removeRouteById(route.id);
// 线
this.$refs.cesiumMap.removePowerZoneByRouteId(route.id);
}
if (this.selectedRouteDetails && this.selectedRouteDetails.id === route.id) {
if (this.activeRouteIds.length > 0) {

Loading…
Cancel
Save