|
|
@ -40,6 +40,8 @@ |
|
|
@show-transform-box="showPlatformIconTransformBox" |
|
|
@show-transform-box="showPlatformIconTransformBox" |
|
|
@toggle-route-label="toggleRouteLabelVisibility" |
|
|
@toggle-route-label="toggleRouteLabelVisibility" |
|
|
@toggle-route-lock="toggleRouteLock" |
|
|
@toggle-route-lock="toggleRouteLock" |
|
|
|
|
|
@edit-platform="openEditPlatformDialog" |
|
|
|
|
|
@power-zone="openPowerZoneDialog" |
|
|
/> |
|
|
/> |
|
|
|
|
|
|
|
|
<!-- 定位弹窗 --> |
|
|
<!-- 定位弹窗 --> |
|
|
@ -62,6 +64,75 @@ |
|
|
@confirm="handleRadiusConfirm" |
|
|
@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="map-info-panel"> |
|
|
<div class="scale-bar" @click="handleScaleClick"> |
|
|
<div class="scale-bar" @click="handleScaleClick"> |
|
|
@ -171,8 +242,30 @@ export default { |
|
|
position: { x: 0, y: 0 }, |
|
|
position: { x: 0, y: 0 }, |
|
|
entityData: null |
|
|
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 隐藏,不设则默认显示 |
|
|
// 航线飞机标牌显示状态:routeId -> true 显示 / false 隐藏,不设则默认显示 |
|
|
routeLabelVisible: {}, |
|
|
routeLabelVisible: {}, |
|
|
|
|
|
// 航线飞机标牌样式:routeId -> { fontSize, fontColor } |
|
|
|
|
|
routeLabelStyles: {}, |
|
|
// 航线上锁状态:routeId -> true 上锁(不可编辑)/ false 或未设 可编辑 |
|
|
// 航线上锁状态:routeId -> true 上锁(不可编辑)/ false 或未设 可编辑 |
|
|
routeLocked: {}, |
|
|
routeLocked: {}, |
|
|
// 默认样式 |
|
|
// 默认样式 |
|
|
@ -587,10 +680,191 @@ export default { |
|
|
|
|
|
|
|
|
/** 默认平台图标(无 imageUrl 时使用):简单飞机剪影 SVG */ |
|
|
/** 默认平台图标(无 imageUrl 时使用):简单飞机剪影 SVG */ |
|
|
getDefaultPlatformIconDataUrl() { |
|
|
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); |
|
|
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:蓝底、白边、白色弧形箭头 */ |
|
|
/** 伸缩框旋转手柄图标 SVG:蓝底、白边、白色弧形箭头 */ |
|
|
getRotationHandleIconDataUrl() { |
|
|
getRotationHandleIconDataUrl() { |
|
|
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">' + |
|
|
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">' + |
|
|
@ -1017,6 +1291,7 @@ export default { |
|
|
image: fullUrl, |
|
|
image: fullUrl, |
|
|
width: 144, |
|
|
width: 144, |
|
|
height: 144, |
|
|
height: 144, |
|
|
|
|
|
color: Cesium.Color.WHITE, // 初始颜色为白色(不染色) |
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, |
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER, |
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 2.0, 200000, 0.4), |
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 2.0, 200000, 0.4), |
|
|
@ -1024,6 +1299,23 @@ export default { |
|
|
...(initialRotation !== undefined && { rotation: initialRotation }) |
|
|
...(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 firstWp = waypoints[0]; |
|
|
const firstAlt = firstWp && (firstWp.alt != null) ? Number(firstWp.alt) : 0; |
|
|
const firstAlt = firstWp && (firstWp.alt != null) ? Number(firstWp.alt) : 0; |
|
|
@ -1038,22 +1330,31 @@ export default { |
|
|
headingDeg: initialHeadingDeg |
|
|
headingDeg: initialHeadingDeg |
|
|
}); |
|
|
}); |
|
|
const labelShow = this.routeLabelVisible[routeId] !== false |
|
|
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, |
|
|
id: platformLabelId, |
|
|
name: '平台标牌', |
|
|
name: '平台标牌', |
|
|
position: originalPositions[0], |
|
|
position: originalPositions[0], |
|
|
show: labelShow, |
|
|
show: labelShow, |
|
|
properties: { routeId: routeId }, |
|
|
properties: { routeId: routeId }, |
|
|
label: { |
|
|
billboard: { |
|
|
text: labelText, |
|
|
image: labelCanvas, // 使用 Canvas 作为图片 |
|
|
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), |
|
|
|
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, |
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, |
|
|
pixelOffset: new Cesium.Cartesian2(0, -42), |
|
|
pixelOffset: new Cesium.Cartesian2(0, -42), |
|
|
@ -1061,6 +1362,13 @@ export default { |
|
|
scaleByDistance: new Cesium.NearFarScalar(500, 1.0, 200000, 0.65) |
|
|
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) { |
|
|
if (waypoints.length > 1) { |
|
|
@ -1565,6 +1873,11 @@ export default { |
|
|
return { path, segmentEndIndices, holdArcRanges }; |
|
|
return { path, segmentEndIndices, holdArcRanges }; |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
removePowerZoneByRouteId(routeId) { |
|
|
|
|
|
const zoneId = `power-zone-${routeId}`; |
|
|
|
|
|
this.viewer.entities.removeById(zoneId); |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
removeRouteById(routeId) { |
|
|
removeRouteById(routeId) { |
|
|
// 从地图上移除所有属于该 routeId 的实体 |
|
|
// 从地图上移除所有属于该 routeId 的实体 |
|
|
const entityList = this.viewer.entities.values; |
|
|
const entityList = this.viewer.entities.values; |
|
|
@ -1572,7 +1885,7 @@ export default { |
|
|
const entity = entityList[i]; |
|
|
const entity = entityList[i]; |
|
|
let shouldRemove = false; |
|
|
let shouldRemove = false; |
|
|
// 平台图标与标牌实体:通过 id 匹配 |
|
|
// 平台图标与标牌实体:通过 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; |
|
|
shouldRemove = true; |
|
|
} else if (entity.properties && entity.properties.routeId) { |
|
|
} else if (entity.properties && entity.properties.routeId) { |
|
|
const id = entity.properties.routeId.getValue && entity.properties.routeId.getValue(); |
|
|
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}`); |
|
|
const labelEntity = this.viewer.entities.getById(`route-platform-label-${routeId}`); |
|
|
if (labelEntity && labelEntity.position) { |
|
|
if (labelEntity && labelEntity.position) { |
|
|
labelEntity.position = cartesian; |
|
|
labelEntity.position = cartesian; |
|
|
if (labelData && labelEntity.label) { |
|
|
if (labelData) { |
|
|
labelEntity.label.text = this.formatPlatformLabelText(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) |
|
|
}, 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() { |
|
|
initPlatformIconInteraction() { |
|
|
const canvas = this.viewer.scene.canvas; |
|
|
const canvas = this.viewer.scene.canvas; |
|
|
@ -4231,6 +4713,192 @@ export default { |
|
|
this.contextMenu.visible = false |
|
|
this.contextMenu.visible = false |
|
|
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender() |
|
|
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() { |
|
|
deleteEntityFromContextMenu() { |
|
|
if (this.contextMenu.entityData) { |
|
|
if (this.contextMenu.entityData) { |
|
|
@ -5338,4 +6006,38 @@ export default { |
|
|
min-width: 200px; |
|
|
min-width: 200px; |
|
|
text-align: right; |
|
|
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> |
|
|
</style> |
|
|
|