@@ -195,8 +266,32 @@ 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: {},
// 航线上锁状态由父组件通过 prop routeLocked 传入,与右侧列表锁图标同步
// 从平台右键进入的航线绘制:{ platformInfo: { platformId, platform }, mode: 'before'|'after' }
platformRouteDrawing: null,
@@ -902,10 +997,191 @@ export default {
/** 默认平台图标(无 imageUrl 时使用):简单飞机剪影 SVG */
getDefaultPlatformIconDataUrl() {
- const svg = '
';
+ // 修改默认图标颜色为白色,方便后续染色
+ const svg = '
';
return 'data:image/svg+xml,' + encodeURIComponent(svg);
},
+ /**
+ * 加载图片并转为白色(保留透明度),确保黑色图标可以被 Cesium 正确染色
+ * @param {string} url 图片 URL
+ * @returns {Promise
} 返回处理后的 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 = '