Browse Source

小的优化

ctw
cuitw 5 days ago
parent
commit
4c41c3a555
  1. 4
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java
  2. 50
      ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
  3. 475
      ruoyi-ui/src/views/cesiumMap/index.vue
  4. 37
      ruoyi-ui/src/views/childRoom/SixStepsOverlay.vue
  5. 545
      ruoyi-ui/src/views/childRoom/index.vue
  6. 4
      ruoyi-ui/src/views/dialogs/RouteEditDialog.vue
  7. 13
      ruoyi-ui/src/views/dialogs/WaypointEditDialog.vue

4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/RoomPlatformIconController.java

@ -108,7 +108,7 @@ public class RoomPlatformIconController extends BaseController {
@PutMapping
public AjaxResult edit(@RequestBody RoomPlatformIcon roomPlatformIcon) {
RoomPlatformIcon before = roomPlatformIcon.getId() != null
? roomPlatformIconService.selectById(roomPlatformIcon.getId()) : null;
? roomPlatformIconService.selectById(roomPlatformIcon.getId()) : null;
int rows = roomPlatformIconService.update(roomPlatformIcon);
if (rows > 0 && before != null) {
RoomPlatformIcon after = roomPlatformIconService.selectById(roomPlatformIcon.getId());
@ -171,5 +171,5 @@ public class RoomPlatformIconController extends BaseController {
}
}
return toAjax(rows);
}
}
}

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

@ -256,21 +256,22 @@
<span>磁方位</span>
</div>
</div>
<template v-if="toolMode === 'ranging'">
<div class="menu-item" @click="toggleRangingUnitMenu">
<MenuGlyph name="ruler" />
<span class="menu-label">距离单位</span>
<span class="menu-value menu-value-text">{{ rangingDistanceUnit === 'nm' ? '海里' : '公里' }}</span>
<div class="menu-item" @click="toggleRangingUnitMenu">
<MenuGlyph name="ruler" />
<span class="menu-label">距离单位</span>
<span class="menu-value menu-value-text">{{ rangingDistanceUnitLabel }}</span>
</div>
<div class="sub-menu" v-if="showRangingUnitMenu">
<div class="sub-menu-item" @click="selectRangingUnit('m')" :class="{ active: rangingDistanceUnit === 'm' }">
<span>m</span>
</div>
<div class="sub-menu" v-if="showRangingUnitMenu">
<div class="sub-menu-item" @click="selectRangingUnit('km')" :class="{ active: rangingDistanceUnit === 'km' }">
<span>公里km</span>
</div>
<div class="sub-menu-item" @click="selectRangingUnit('nm')" :class="{ active: rangingDistanceUnit === 'nm' }">
<span>海里1 海里 = 1852 </span>
</div>
<div class="sub-menu-item" @click="selectRangingUnit('km')" :class="{ active: rangingDistanceUnit === 'km' }">
<span>公里km</span>
</div>
</template>
<div class="sub-menu-item" @click="selectRangingUnit('nm')" :class="{ active: rangingDistanceUnit === 'nm' }">
<span>海里NM</span>
</div>
</div>
</div>
<!-- 点特有选项 -->
@ -746,7 +747,7 @@ export default {
/** 根据视口边界修正菜单位置,避免菜单在屏幕底部或右侧被截断 */
adjustedPosition() {
const padding = 12
const menuMaxWidth = 264
const menuMaxWidth = 228
const menuMaxHeight = 640
const winW = typeof window !== 'undefined' ? window.innerWidth : 1920
const winH = typeof window !== 'undefined' ? window.innerHeight : 1080
@ -786,6 +787,12 @@ export default {
const pt = ed.pointType || ed.point_type || ''
return pt === 'hold_circle' || pt === 'hold_ellipse'
},
rangingDistanceUnitLabel() {
const u = this.rangingDistanceUnit
if (u === 'nm') return '海里 NM'
if (u === 'km') return '公里 km'
return '米 m'
},
addWaypointDialogTitle() {
if (!this.addWaypointDialogMode) return '设置参数'
const dir = this.addWaypointDialogMode === 'before' ? '向前' : '向后'
@ -1221,7 +1228,7 @@ export default {
},
selectRangingUnit(unit) {
if (unit === 'km' || unit === 'nm') {
if (unit === 'm' || unit === 'km' || unit === 'nm') {
this.$emit('ranging-distance-unit', unit)
}
this.showRangingUnitMenu = false
@ -1239,8 +1246,10 @@ export default {
--ctx-surface: rgba(255, 255, 255, 0.72);
position: fixed;
z-index: 9999;
min-width: 188px;
max-width: 264px;
box-sizing: border-box;
width: 228px;
min-width: 228px;
max-width: 228px;
padding: 12px 0;
color: var(--ctx-text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Microsoft YaHei', Arial, sans-serif;
@ -1559,14 +1568,19 @@ export default {
backdrop-filter: blur(10px);
border: 1px solid rgba(22, 93, 255, 0.1);
border-radius: 10px;
max-width: 100%;
box-sizing: border-box;
}
.sub-menu-item {
padding: 8px 16px 8px 28px;
padding: 8px 12px 8px 20px;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
font-size: 12px;
color: var(--ctx-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sub-menu-item:hover {

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

@ -620,7 +620,7 @@ export default {
},
/** 辅助线:水平/竖直约束,'none' | 'horizontal' | 'vertical' */
auxiliaryLineConstraint: 'none',
/** 测距模式距离显示:'km' 公里 | 'nm' 海里(1 海里 = 1852 m) */
/** 线段累计距离显示:'m' 米 | 'km' 公里 | 'nm' 海里(1 海里 = 1852 m) */
rangingDistanceUnit: 'km',
//
coordinatesText: '经度: --, 纬度: --',
@ -834,10 +834,21 @@ export default {
}
},
rangingDistanceUnit() {
if (this.toolMode !== 'ranging' || !this.allEntities) return
this.allEntities.forEach((ed) => {
if (ed && ed.type === 'line') this.updateLineSegmentLabels(ed)
})
const seen = new Set()
const refresh = (ed) => {
if (!ed || ed.type !== 'line' || ed.routeId != null) return
const id = ed.id
if (id != null && seen.has(String(id))) return
if (id != null) seen.add(String(id))
this.updateLineSegmentLabels(ed)
}
if (this.allEntities) this.allEntities.forEach(refresh)
if (this.whiteboardEntityDataMap) Object.values(this.whiteboardEntityDataMap).forEach(refresh)
// requestRenderMode postRender DOM
this.syncMapScreenDomLabels()
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
}
},
computed: {
@ -2620,7 +2631,7 @@ export default {
this._routeWaypointsByRoute[routeId] = waypoints;
if (!this._routeHoldRadiiByRoute) this._routeHoldRadiiByRoute = {};
if (!this._routeHoldEllipseParamsByRoute) this._routeHoldEllipseParamsByRoute = {};
// i 线 45°
// i 线 45°线
const isTurnWaypointWithArc = (i) => {
if (i < 1 || i >= waypoints.length - 1) return false;
const wp = waypoints[i];
@ -2868,8 +2879,114 @@ export default {
}
// 线
if (waypoints.length > 1) {
const finalPathPositions = [originalPositions[0]];
let lastPos = originalPositions[0];
let finalPathPositions;
let lastPos;
if (this.isHoldWaypoint(waypoints[0])) {
finalPathPositions = [];
const wpFirstHold = waypoints[0];
const currPosFh = originalPositions[0];
const nextPosFh = originalPositions[1];
lastPos = this.getSyntheticInboundBeforeFirstHold(currPosFh, nextPosFh);
const paramsFh = this.parseHoldParams(wpFirstHold);
const legIndexHoldFh = 0;
const ptFh = wpFirstHold.pointType || wpFirstHold.point_type;
const useCircleFh = ptFh === 'hold_circle';
const effectiveTurnAngleFh = this.getEffectiveTurnAngle(wpFirstHold, 0, waypoints.length);
const prevWpForHoldFh = wpFirstHold;
const turnRadiusFh = this.getHoldRadiusFromPrevSpeed(wpFirstHold, prevWpForHoldFh, effectiveTurnAngleFh) || 500;
const radiusFh = turnRadiusFh;
const turnRadiusForHoldFh = turnRadiusFh;
const defaultSemiMajorFh = paramsFh && (paramsFh.semiMajor != null || paramsFh.semiMajorAxis != null) ? (paramsFh.semiMajor ?? paramsFh.semiMajorAxis) : 500;
const defaultSemiMinorFh = paramsFh && (paramsFh.semiMinor != null || paramsFh.semiMinorAxis != null) ? (paramsFh.semiMinor ?? paramsFh.semiMinorAxis) : 300;
const defaultHeadingRadFh = ((paramsFh && paramsFh.headingDeg != null ? paramsFh.headingDeg : 0) * Math.PI) / 180;
const edgeLengthMFh = Math.max(1000, paramsFh && paramsFh.edgeLength != null ? paramsFh.edgeLength : this.DEFAULT_RACE_TRACK_EDGE_LENGTH_M);
const arcRadiusMFh = turnRadiusFh;
const clockwiseFh = paramsFh && paramsFh.clockwise !== false;
const currPosClonedFh = Cesium.Cartesian3.clone(currPosFh);
const lastPosClonedFh = Cesium.Cartesian3.clone(lastPos);
const nextPosClonedFh = Cesium.Cartesian3.clone(nextPosFh);
const routeIdHoldFh = routeId;
const thatFh = this;
const buildHoldPositionsFh = (radiusOrEllipse, centerOverride) => {
const isCircleArg = typeof radiusOrEllipse === 'number';
const R = isCircleArg ? radiusOrEllipse : 0;
const smj = isCircleArg ? defaultSemiMajorFh : (radiusOrEllipse.semiMajor ?? defaultSemiMajorFh);
const smn = isCircleArg ? defaultSemiMinorFh : (radiusOrEllipse.semiMinor ?? defaultSemiMinorFh);
const hd = isCircleArg ? defaultHeadingRadFh : ((radiusOrEllipse.headingDeg != null ? radiusOrEllipse.headingDeg * Math.PI / 180 : defaultHeadingRadFh));
const centerPos = centerOverride || currPosClonedFh;
let entry; let exit; let centerForCircle;
if (useCircleFh) {
centerForCircle = thatFh.getHoldCenterFromPrevNext(lastPosClonedFh, centerPos, R, clockwiseFh);
entry = thatFh.getCircleTangentEntryPoint(centerForCircle, lastPosClonedFh, R, clockwiseFh);
exit = thatFh.getCircleTangentExitPoint(centerForCircle, nextPosClonedFh || centerPos, R, clockwiseFh);
} else {
entry = thatFh.getEllipseTangentEntryPoint(centerPos, lastPosClonedFh, smj, smn, hd, clockwiseFh);
exit = thatFh.getEllipseTangentExitPoint(centerPos, nextPosClonedFh || centerPos, smj, smn, hd, clockwiseFh);
}
let arcPoints;
if (useCircleFh) {
const center = centerForCircle;
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(center);
const eastVec = Cesium.Matrix4.getColumn(enu, 0, new Cesium.Cartesian3());
const northVec = Cesium.Matrix4.getColumn(enu, 1, new Cesium.Cartesian3());
const toEntry = Cesium.Cartesian3.subtract(entry, center, new Cesium.Cartesian3());
const startAngle = Math.atan2(Cesium.Cartesian3.dot(toEntry, eastVec), Cesium.Cartesian3.dot(toEntry, northVec));
const fullCirclePoints = thatFh.getCircleFullCircle(center, R, startAngle, clockwiseFh, 64);
const arcToExit = thatFh.getCircleArcEntryToExit(center, R, entry, exit, clockwiseFh, 32);
arcPoints = [entry, ...(fullCirclePoints || []).slice(1), ...(arcToExit || []).slice(1)];
if (!arcPoints || arcPoints.length < 2) arcPoints = [Cesium.Cartesian3.clone(entry), Cesium.Cartesian3.clone(exit)];
return arcPoints;
}
const tEntry = thatFh.cartesianToEllipseParam(centerPos, smj, smn, hd, entry);
const entryLocalAngle = Math.atan2(smn * Math.sin(tEntry), smj * Math.cos(tEntry));
const fullCirclePoints = thatFh.getEllipseFullCircle(centerPos, smj, smn, hd, entryLocalAngle, clockwiseFh, 128);
arcPoints = thatFh.buildEllipseHoldArc(centerPos, smj, smn, hd, entry, exit, clockwiseFh, 120);
return [entry, ...(fullCirclePoints || []).slice(1), ...(arcPoints || []).slice(1)];
};
const raceTrackDirectionRadFh = this.getRaceTrackDirectionRad(
currPosClonedFh,
lastPosClonedFh,
nextPosClonedFh,
paramsFh && paramsFh.headingDeg != null ? paramsFh.headingDeg : 0
);
const buildRaceTrackPositionsFh = (centerOverride) => thatFh.buildRaceTrackWithEntryExit(centerOverride || currPosClonedFh, lastPosClonedFh, nextPosClonedFh, raceTrackDirectionRadFh, edgeLengthMFh, arcRadiusMFh, clockwiseFh, 24);
const holdPositionsFh = useCircleFh ? buildHoldPositionsFh(radiusFh) : buildRaceTrackPositionsFh();
for (let k = 0; k < holdPositionsFh.length; k++) finalPathPositions.push(holdPositionsFh[k]);
const wpIdHoldFh = wpFirstHold.id;
const getHoldPositionsFh = () => {
let centerOverride = null;
if (thatFh.waypointDragging && thatFh.waypointDragging.routeId === routeIdHoldFh && thatFh.waypointDragging.dbId === wpIdHoldFh) {
const wpEnt = thatFh.viewer.entities.getById(`wp_${routeIdHoldFh}_${wpIdHoldFh}`);
if (wpEnt && wpEnt.position) {
const p = wpEnt.position.getValue(Cesium.JulianDate.now());
if (p) centerOverride = p;
}
}
if (useCircleFh) {
const R = (thatFh._routeHoldRadiiByRoute && thatFh._routeHoldRadiiByRoute[routeIdHoldFh] && thatFh._routeHoldRadiiByRoute[routeIdHoldFh][legIndexHoldFh] != null)
? thatFh._routeHoldRadiiByRoute[routeIdHoldFh][legIndexHoldFh]
: turnRadiusForHoldFh;
return buildHoldPositionsFh(R, centerOverride);
}
return buildRaceTrackPositionsFh(centerOverride);
};
this.viewer.entities.add({
id: `hold-line-${routeId}-0`,
show: false,
polyline: {
positions: new Cesium.CallbackProperty(getHoldPositionsFh, false),
width: lineWidth,
material: lineMaterial,
arcType: Cesium.ArcType.NONE,
zIndex: 20
},
properties: { routeId: routeId }
});
lastPos = holdPositionsFh[holdPositionsFh.length - 1];
} else {
finalPathPositions = [originalPositions[0]];
lastPos = originalPositions[0];
}
for (let i = 1; i < waypoints.length; i++) {
const currPos = originalPositions[i];
const wp = waypoints[i];
@ -3197,9 +3314,8 @@ export default {
return null;
},
/** 渲染/路径用:非首尾航点默认转弯坡度 45°,首尾为 0 */
/** 渲染/路径用:未设置转弯坡度时默认 45°(首尾可存盘并在盘旋等场景参与计算) */
getEffectiveTurnAngle(wp, index, waypointsLength) {
if (index === 0 || index === waypointsLength - 1) return wp.turnAngle != null ? wp.turnAngle : 0;
return wp.turnAngle != null ? wp.turnAngle : 45;
},
/** 转弯半径 R = v²/(g·tanθ),v 为速度(m/s),g=9.8,θ 为转弯坡度/坡度角(弧度) */
@ -3229,6 +3345,31 @@ export default {
return this.getHoldRadiusFromPrevSpeed(null, prevWp, turnAngle);
},
/**
* 首点为盘旋时没有上一航点 hold下一航点 的反方向取一参考点
* getHoldCenterFromPrevNext / 切点 几何与中间航点盘旋一致
*/
getSyntheticInboundBeforeFirstHold(holdCartesian, nextCartesian) {
const toNext = Cesium.Cartesian3.subtract(nextCartesian, holdCartesian, new Cesium.Cartesian3());
let dist = Cesium.Cartesian3.magnitude(toNext);
if (dist < 1e-3) {
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(holdCartesian);
const east = Cesium.Matrix4.getColumn(enu, 0, new Cesium.Cartesian3());
return Cesium.Cartesian3.add(
holdCartesian,
Cesium.Cartesian3.multiplyByScalar(east, -50000, new Cesium.Cartesian3()),
new Cesium.Cartesian3()
);
}
Cesium.Cartesian3.normalize(toNext, toNext);
const back = Math.max(dist, 5000);
return Cesium.Cartesian3.add(
holdCartesian,
Cesium.Cartesian3.multiplyByScalar(toNext, -back, new Cesium.Cartesian3()),
new Cesium.Cartesian3()
);
},
isHoldWaypoint(wp) {
const t = (wp && wp.pointType) || (wp && wp.point_type) || 'normal';
return t === 'hold_circle' || t === 'hold_ellipse';
@ -4584,11 +4725,69 @@ export default {
const originalPositions = waypoints.map(wp =>
Cesium.Cartesian3.fromDegrees(parseFloat(wp.lng), parseFloat(wp.lat), Number(wp.alt) || 0)
);
const path = [toLngLatAlt(originalPositions[0])];
const path = [];
const segmentEndIndices = [];
const holdArcRanges = {};
let lastPos = originalPositions[0];
let lastPos;
let prevWasHold = false;
if (waypoints.length > 1 && this.isHoldWaypoint(waypoints[0])) {
const wp = waypoints[0];
const currPos = originalPositions[0];
const nextPos = originalPositions[1];
lastPos = this.getSyntheticInboundBeforeFirstHold(currPos, nextPos);
const params = this.parseHoldParams(wp);
const legIndexFirst = 0;
const pt = wp.pointType || wp.point_type;
const useCircle = pt === 'hold_circle';
const clockwise = params && params.clockwise !== false;
const arcStartIdx = path.length;
let holdPositions;
if (useCircle) {
const prevWpForSpeed = wp;
const holdTurnAngle = this.getEffectiveTurnAngle(wp, 0, waypoints.length);
const turnRadius = this.getHoldRadiusFromPrevSpeed(wp, prevWpForSpeed, holdTurnAngle) || 500;
const radius = (holdRadiusByLegIndex[legIndexFirst] != null && Number.isFinite(holdRadiusByLegIndex[legIndexFirst]))
? holdRadiusByLegIndex[legIndexFirst]
: turnRadius;
const center = this.getHoldCenterFromPrevNext(lastPos, currPos, radius, clockwise);
const entry = this.getCircleTangentEntryPoint(center, lastPos, radius, clockwise);
const exit = this.getCircleTangentExitPoint(center, nextPos || currPos, radius, clockwise);
const enuPath = Cesium.Transforms.eastNorthUpToFixedFrame(center);
const eastPath = Cesium.Matrix4.getColumn(enuPath, 0, new Cesium.Cartesian3());
const northPath = Cesium.Matrix4.getColumn(enuPath, 1, new Cesium.Cartesian3());
const toEntryPath = Cesium.Cartesian3.subtract(entry, center, new Cesium.Cartesian3());
const startAnglePath = Math.atan2(Cesium.Cartesian3.dot(toEntryPath, eastPath), Cesium.Cartesian3.dot(toEntryPath, northPath));
const fullCirclePath = this.getCircleFullCircle(center, radius, startAnglePath, clockwise, 64);
const arcToExitPath = this.getCircleArcEntryToExit(center, radius, entry, exit, clockwise, 32);
holdPositions = [entry, ...(fullCirclePath || []).slice(1), ...(arcToExitPath || []).slice(1)];
if (!holdPositions || holdPositions.length < 2) holdPositions = [Cesium.Cartesian3.clone(entry), Cesium.Cartesian3.clone(exit)];
lastPos = exit;
} else {
const edgeLengthM = Math.max(1000, params && params.edgeLength != null ? params.edgeLength : this.DEFAULT_RACE_TRACK_EDGE_LENGTH_M);
const prevWpForSpeed = wp;
const holdTurnAngle = this.getEffectiveTurnAngle(wp, 0, waypoints.length);
const arcRadiusM = this.getHoldRadiusFromPrevSpeed(wp, prevWpForSpeed, holdTurnAngle) || 500;
const directionRad = this.getRaceTrackDirectionRad(
currPos,
lastPos,
nextPos,
params && params.headingDeg != null ? params.headingDeg : 0
);
holdPositions = this.buildRaceTrackWithEntryExit(currPos, lastPos, nextPos, directionRad, edgeLengthM, arcRadiusM, clockwise, 24);
lastPos = holdPositions.length ? holdPositions[holdPositions.length - 1] : currPos;
}
const holdLoopEndOffset = holdPositions._loopEndIndex != null ? holdPositions._loopEndIndex : null;
for (let k = 0; k < holdPositions.length; k++) path.push(toLngLatAlt(holdPositions[k]));
holdArcRanges[-1] = {
start: arcStartIdx,
end: path.length - 1,
loopEndIndex: holdLoopEndOffset != null ? arcStartIdx + holdLoopEndOffset : null
};
prevWasHold = true;
} else {
path.push(toLngLatAlt(originalPositions[0]));
lastPos = originalPositions[0];
}
for (let i = 1; i < waypoints.length; i++) {
const currPos = originalPositions[i];
const wp = waypoints[i];
@ -5390,7 +5589,7 @@ export default {
alt: wp.alt != null ? Number(wp.alt) : 5000,
speed: wp.speed != null ? wp.speed : 800,
startTime: wp.startTime || 'K+00:00:00',
turnAngle: wp.turnAngle != null ? wp.turnAngle : (idx === 0 || idx === this.copyPreviewWaypoints.length - 1 ? 0 : 45),
turnAngle: wp.turnAngle != null ? wp.turnAngle : 45,
labelFontSize: wp.labelFontSize != null ? wp.labelFontSize : 14,
labelColor: wp.labelColor || '#333333',
...(wp.pointType && { pointType: wp.pointType }),
@ -6981,26 +7180,13 @@ export default {
const length = this.calculateLineLength(tempPositions);
// bearingType
const bearing = this.calculateTrueBearing(tempPositions);
//
if (this.toolMode === 'ranging') {
this.hoverTooltip = {
visible: true,
content: `${this.formatRangingLengthText(length)}${bearing.toFixed(1)}°`,
position: {
x: movement.endPosition.x + 10,
y: movement.endPosition.y - 10
}
};
} else {
// 使
this.hoverTooltip = {
visible: true,
content: `长度:${length.toFixed(2)}\n真方位:${bearing.toFixed(2)}°`,
position: {
x: movement.endPosition.x + 10,
y: movement.endPosition.y - 10
}
};
this.hoverTooltip = {
visible: true,
content: `${this.formatRangingLengthText(length)}${bearing.toFixed(2)}°`,
position: {
x: movement.endPosition.x + 10,
y: movement.endPosition.y - 10
}
}
} else {
//
@ -7041,7 +7227,7 @@ export default {
kind: 'drawPoint',
entityId: pointId,
pixelOffset: { x: 15, y: 0 },
text: isStartPoint ? '起点' : `${(cumulativeDistance / 1000).toFixed(2)}km`
text: isStartPoint ? '起点' : this.formatRangingLengthText(cumulativeDistance)
}
}
this.drawingPointEntities.push(pointEntity);
@ -7124,7 +7310,20 @@ export default {
type: 'line',
label: '测距',
color: this.defaultStyles.line ? this.defaultStyles.line.color : '#165dff',
data: { points, width: (this.defaultStyles.line && this.defaultStyles.line.width) || 2 }
data: {
points,
width: (this.defaultStyles.line && this.defaultStyles.line.width) || 2,
bearingType: 'true'
}
}
// addLineEntity 线 entity线
this.unregisterDrawingPointDomLabelsForEntities(this.drawingPointEntities)
if (this.drawingPointEntities && this.drawingPointEntities.length) {
this.drawingPointEntities.forEach((pe) => {
try {
this.viewer.entities.remove(pe)
} catch (_) {}
})
}
this.$emit('whiteboard-draw-complete', entityData)
this.drawingPoints = []
@ -8711,17 +8910,21 @@ export default {
}
return totalLength
},
/** 测距:米 → 当前单位文案(方位角另拼);isTotal 时最后一段显示「共…」 */
formatRangingLengthText(meters, opts = {}) {
/** 线段距离:米 → 当前单位后缀 m / km / NM(与角度拼成「距离,角度°」) */
formatRangingLengthText(meters) {
const m = Number(meters)
if (!Number.isFinite(m)) return this.rangingDistanceUnit === 'nm' ? '0.00海里' : '0.0km'
const isTotal = opts.isTotal === true
if (!Number.isFinite(m)) {
if (this.rangingDistanceUnit === 'nm') return '0.00NM'
if (this.rangingDistanceUnit === 'km') return '0.00km'
return '0.00m'
}
if (this.rangingDistanceUnit === 'nm') {
const v = (m / 1852).toFixed(2)
return isTotal ? `${v}海里` : `${v}海里`
return `${(m / 1852).toFixed(2)}NM`
}
if (this.rangingDistanceUnit === 'km') {
return `${(m / 1000).toFixed(2)}km`
}
const v = (m / 1000).toFixed(1)
return isTotal ? `${v}km` : `${v}km`
return `${m.toFixed(2)}m`
},
/** 更新测距/空域线段每段终点的标签:屏幕 DOM,与 HoverTooltip 同类渲染 */
updateLineSegmentLabels(entityData) {
@ -8763,17 +8966,7 @@ export default {
const segLen = Cesium.Cartesian3.distance(positions[i], positions[i + 1])
cumulativeLength += segLen
const bearing = bearingFn([positions[i], positions[i + 1]])
const text =
this.toolMode === 'ranging'
? `${this.formatRangingLengthText(cumulativeLength)}${bearing.toFixed(1)}°`
: `累计长度:${cumulativeLength.toFixed(2)}\n${
bearingType === 'magnetic' ? '磁方位' : '真方位'
}${bearing.toFixed(2)}°`
const isLast = i === positions.length - 2
const displayText =
isLast && this.toolMode === 'ranging'
? `${this.formatRangingLengthText(cumulativeLength, { isTotal: true })} ,${bearing.toFixed(1)}°`
: text
const displayText = `${this.formatRangingLengthText(cumulativeLength)}${bearing.toFixed(2)}°`
const id = `${pref}${i}`
this._mapScreenDomLabelRegistry[id] = {
kind: 'segment',
@ -8781,7 +8974,7 @@ export default {
segmentVertexIndex: i + 1,
pixelOffset: { x: 15, y: 0 },
text: displayText,
multiline: displayText.indexOf('\n') >= 0
multiline: false
}
}
},
@ -11806,8 +11999,25 @@ export default {
}
if (entityData.isWhiteboard) {
this.$emit('whiteboard-entity-deleted', entityData)
if (entityData.type === 'line') {
this.unregisterLineSegmentDomLabels(entityData.id)
if (entityData.pointEntities && entityData.pointEntities.length) {
entityData.pointEntities.forEach((pe) => {
try {
this.viewer.entities.remove(pe)
} catch (_) {}
})
}
}
if (entityData.type === 'text' && entityData.entity && entityData.entity.id) {
this.unregisterMapTextDomLabel(`map-dom-maptext-text-${entityData.entity.id}`)
}
if (entityData.entity) this.viewer.entities.remove(entityData.entity)
if (this.whiteboardEntityDataMap && entityData.id) delete this.whiteboardEntityDataMap[entityData.id]
this.syncMapScreenDomLabels()
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
} else if (entityData.type === 'detectionZone' || entityData.type === 'powerZone') {
const type = entityData.type
const zoneId = entityData.zoneId
@ -12193,6 +12403,13 @@ export default {
}
//
this.updateEntityStyle(entityData)
if (property === 'bearingType' && entityData.type === 'line' && entityData.routeId == null) {
this.updateLineSegmentLabels(entityData)
this.syncMapScreenDomLabels()
if (this.viewer && this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
}
// contentByTime
if (entityData.isWhiteboard && this.getDrawingEntityTypes().includes(entityData.type)) {
this.$emit('whiteboard-drawing-updated', this.serializeWhiteboardDrawingEntity(entityData))
@ -12699,6 +12916,9 @@ export default {
data.points = entityData.points
data.width = entityData.width != null ? entityData.width : 2
data.color = entityData.color || entityData.data?.color
if (entityData.type === 'line') {
data.bearingType = entityData.bearingType || entityData.data?.bearingType || 'true'
}
}
break
case 'arrow':
@ -12932,7 +13152,30 @@ export default {
this.whiteboardHiddenEntityShows[id] = origShow
entity.show = false
})
// /线 DOM Cesium entity.show
if (this.allEntities && this.allEntities.length) {
this.allEntities.forEach((ed) => {
if (!ed || ed.type !== 'line' || ed.routeId != null || ed.isWhiteboard) return
const lid = ed.id
if (lid != null && !String(lid).startsWith('wb_')) {
this.unregisterLineSegmentDomLabels(lid)
}
})
}
this.ensureMapScreenDomLabelRegistry()
if (this._mapScreenDomLabelRegistry) {
Object.keys(this._mapScreenDomLabelRegistry).forEach((k) => {
if (k.startsWith('map-dom-drawlpt-')) delete this._mapScreenDomLabelRegistry[k]
})
}
this.hoverTooltip = this.hoverTooltip || {}
this.hoverTooltip.visible = false
this.measurementResult = null
this.renderWhiteboardEntities(this.whiteboardEntities || [])
this.syncMapScreenDomLabels()
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
} else {
Object.keys(this.whiteboardHiddenEntityShows || {}).forEach(id => {
const entity = this.viewer.entities.getById(id)
@ -12940,6 +13183,20 @@ export default {
})
this.whiteboardHiddenEntityShows = {}
this.clearWhiteboardEntities()
// 线
if (this.allEntities && this.allEntities.length) {
this.allEntities.forEach((ed) => {
if (!ed || ed.type !== 'line' || ed.routeId != null || ed.isWhiteboard) return
const lid = ed.id
if (lid != null && !String(lid).startsWith('wb_')) {
this.updateLineSegmentLabels(ed)
}
})
}
this.syncMapScreenDomLabels()
if (this.viewer.scene && this.viewer.scene.requestRenderMode) {
this.viewer.scene.requestRender()
}
}
},
/** 清除白板实体(id 以 wb_ 开头) */
@ -12970,7 +13227,15 @@ export default {
if (id) wantIds.add(id)
} else {
const id = (ed.id || '').startsWith('wb_') ? ed.id : ('wb_' + (ed.id || ''))
if (id) wantIds.add(id)
if (id) {
wantIds.add(id)
// 线 entity id `${lineId}_vtx_${i}` wantIds
if (ed.type === 'line' && ed.data && Array.isArray(ed.data.points)) {
for (let vi = 0; vi < ed.data.points.length; vi++) {
wantIds.add(`${id}_vtx_${vi}`)
}
}
}
}
})
@ -12986,6 +13251,16 @@ export default {
if (ed && ed.type === 'text' && ed.entity && ed.entity.id) {
this.unregisterMapTextDomLabel(`map-dom-maptext-text-${ed.entity.id}`)
}
if (ed && ed.type === 'line') {
this.unregisterLineSegmentDomLabels(ed.id)
if (ed.pointEntities && ed.pointEntities.length) {
ed.pointEntities.forEach((pe) => {
try {
this.viewer.entities.remove(pe)
} catch (_) {}
})
}
}
if (ed && ed.entity) this.viewer.entities.remove(ed.entity)
delete this.whiteboardEntityDataMap[id]
}
@ -13105,6 +13380,16 @@ export default {
if (existing.type === 'text' && existing.entity.id) {
this.unregisterMapTextDomLabel(`map-dom-maptext-text-${existing.entity.id}`)
}
if (existing.type === 'line') {
this.unregisterLineSegmentDomLabels(existing.id)
if (existing.pointEntities && existing.pointEntities.length) {
existing.pointEntities.forEach((pe) => {
try {
this.viewer.entities.remove(pe)
} catch (_) {}
})
}
}
this.viewer.entities.remove(existing.entity)
delete this.whiteboardEntityDataMap[id]
}
@ -13276,7 +13561,51 @@ export default {
entityData.borderColor = sectorBorderColor
break
}
case 'line':
case 'line': {
const pts = (data.points || []).map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat))
if (pts.length < 2) return
const lineWidth = data.width != null ? data.width : 2
const lineColor = data.color || color
const lineId = entityData.id
const pointEntities = []
for (let i = 0; i < pts.length; i++) {
const ptId = `${lineId}_vtx_${i}`
const pe = this.viewer.entities.add({
id: ptId,
position: pts[i],
point: {
pixelSize: this.defaultStyles.point.size,
color: Cesium.Color.fromCssColorString(this.defaultStyles.point.color),
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
pointEntities.push(pe)
}
const positionsProp = new Cesium.CallbackProperty(() => {
return pointEntities.map((pEnt) => {
const pos = pEnt.position.getValue(Cesium.JulianDate.now())
return pos || Cesium.Cartesian3.ZERO
})
}, false)
entity = this.viewer.entities.add({
id: lineId,
polyline: {
positions: positionsProp,
width: lineWidth,
material: Cesium.Color.fromCssColorString(lineColor),
arcType: Cesium.ArcType.NONE
}
})
entityData.positions = pts
entityData.points = data.points || pts.map(p => this.cartesianToLatLng(p))
entityData.width = lineWidth
entityData.color = lineColor
entityData.pointEntities = pointEntities
entityData.bearingType = data.bearingType || 'true'
break
}
case 'auxiliaryLine': {
const pts = (data.points || []).map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat))
if (pts.length < 2) return
@ -13347,6 +13676,9 @@ export default {
if (entityData.type === 'text') {
this.registerMapTextForInsertedText(entityData)
}
if (entityData.type === 'line') {
this.updateLineSegmentLabels(entityData)
}
}
},
/** 从房间/方案加载的 frontend_drawings JSON 恢复空域图形(先清空当前图形再导入;加载期间不触发自动保存) */
@ -14597,18 +14929,23 @@ export default {
'0 0 2px #fff, 0 0 2px #fff, 0 0 2px #fff, 0 1px 2px rgba(0,0,0,0.35)'
}
} else if (r.kind === 'segment') {
const positions = this.getLineEntityPositionsForDomLabels(
this.allEntities.find((e) => e.id === r.lineId && e.type === 'line'),
time
)
world =
positions && positions.length > r.segmentVertexIndex
? positions[r.segmentVertexIndex]
: undefined
text = r.text || ''
multiline = !!r.multiline
transform = 'translate(0, -50%)'
themeClass = 'map-screen-dom-label--tooltip'
// 线 DOM
if (this.whiteboardMode && r.lineId != null && !String(r.lineId).startsWith('wb_')) {
visible = false
} else {
const lineEd =
(this.allEntities && this.allEntities.find((e) => e.id === r.lineId && e.type === 'line')) ||
(this.whiteboardEntityDataMap && this.whiteboardEntityDataMap[r.lineId])
const positions = this.getLineEntityPositionsForDomLabels(lineEd, time)
world =
positions && positions.length > r.segmentVertexIndex
? positions[r.segmentVertexIndex]
: undefined
text = r.text || ''
multiline = !!r.multiline
transform = 'translate(0, -50%)'
themeClass = 'map-screen-dom-label--tooltip'
}
} else if (r.kind === 'drawPoint') {
const e = this.viewer.entities.getById(r.entityId)
world = e && e.position && e.position.getValue(time)

37
ruoyi-ui/src/views/childRoom/SixStepsOverlay.vue

@ -113,8 +113,18 @@
</el-dropdown-menu>
</el-dropdown>
</template>
</div>
<div class="header-right">
<button
type="button"
class="overlay-exit-btn"
title="退出六步法"
@click="close"
>
退出
</button>
</div>
</div>
</div>
<div class="overlay-body" :style="overlayBodyStyle">
<div class="overlay-content" :class="{
'task-page': overrideTitle === '任务',
@ -876,6 +886,31 @@ export default {
align-items: center;
gap: 16px;
margin-left: 2px;
min-width: 0;
flex: 1;
}
.header-right {
flex-shrink: 0;
margin-left: 12px;
}
.overlay-exit-btn {
padding: 6px 14px;
font-size: 13px;
font-weight: 500;
color: #64748b;
background: rgba(255, 255, 255, 0.75);
border: 1px solid rgba(226, 232, 240, 0.95);
border-radius: 8px;
cursor: pointer;
transition: color 0.2s, background 0.2s, border-color 0.2s;
}
.overlay-exit-btn:hover {
color: #165dff;
background: #fff;
border-color: rgba(22, 93, 255, 0.35);
}
.header-insert {

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

@ -1420,7 +1420,7 @@ export default {
const isNewlyInserted = wp.id === newWp.id;
const isPrevToNew = prevWp && wp.id === prevWp.id;
const nameToUse = isNewlyInserted ? (isHold(wp) ? `HOLD${newSeq}` : `WP${newSeq}`) : (wp.name || (isHold(wp) ? `HOLD${newSeq}` : `WP${newSeq}`));
// 450
// 45°
const prevTurnAngle = (isPrevToNew && insertIndex > 1) ? 45 : wp.turnAngle;
const updatePayload = {
id: wp.id,
@ -1466,24 +1466,27 @@ export default {
updated.waypoints = sortedWaypoints;
const routeInList = this.routes.find(r => r.id === routeId);
if (routeInList) routeInList.waypoints = sortedWaypoints;
if (this.selectedRouteId === routeId) {
this.selectedRouteDetails = { ...this.selectedRouteDetails, waypoints: sortedWaypoints };
}
await this.recalculateAndPersistRouteKTimes(routeId);
const routeAfterK = this.routes.find(r => r.id === routeId) || updated;
const wptsForMap = routeAfterK.waypoints || sortedWaypoints;
if (this.activeRouteIds.includes(routeId) && this.$refs.cesiumMap) {
const roomId = this.currentRoomId;
if (roomId && updated.platformId) {
if (roomId && routeAfterK.platformId) {
try {
const styleRes = await getPlatformStyle({ roomId, routeId, platformId: updated.platformId });
const styleRes = await getPlatformStyle({ roomId, routeId, platformId: routeAfterK.platformId });
if (styleRes.data) this.$refs.cesiumMap.setPlatformStyle(routeId, styleRes.data);
} catch (_) {}
}
this.$refs.cesiumMap.removeRouteById(routeId);
this.$refs.cesiumMap.renderRouteWaypoints(sortedWaypoints, routeId, updated.platformId, updated.platform, this.parseRouteStyle(updated.attributes));
this.$refs.cesiumMap.renderRouteWaypoints(wptsForMap, routeId, routeAfterK.platformId, routeAfterK.platform, this.parseRouteStyle(routeAfterK.attributes || updated.attributes));
this.$nextTick(() => this.updateDeductionPositions());
if (roomId && sortedWaypoints.length > 0) {
this.updateMissilePositionsAfterRouteEdit(roomId, routeId, updated.platformId != null ? updated.platformId : 0, sortedWaypoints);
if (roomId && wptsForMap.length > 0) {
this.updateMissilePositionsAfterRouteEdit(roomId, routeId, routeAfterK.platformId != null ? routeAfterK.platformId : 0, wptsForMap);
}
}
if (this.selectedRouteId === routeId) {
this.selectedRouteDetails = { ...this.selectedRouteDetails, waypoints: sortedWaypoints };
}
const modeLabel = segmentMode === 'fixed_time' ? '定时点' : (segmentMode === 'fixed_speed' ? '定速点' : '航点');
this.$message.success(`已添加${modeLabel},该航段已按所选方式规划`);
this.wsConnection?.sendSyncWaypoints?.(routeId);
@ -1528,12 +1531,11 @@ export default {
}
const index = waypoints.indexOf(wp);
const total = waypoints.length;
const isFirstOrLast = index === 0 || index === total - 1;
const isHold = this.isHoldWaypoint(wp);
let pointType;
let holdParams;
let turnAngle;
const preserveTurnAngle = () => isFirstOrLast ? 0 : (wp.turnAngle != null && wp.turnAngle !== '' ? Number(wp.turnAngle) : 0);
const preserveTurnAngle = () => (wp.turnAngle != null && wp.turnAngle !== '' ? Number(wp.turnAngle) : 45);
if (isHold) {
pointType = 'normal';
holdParams = null;
@ -1581,6 +1583,7 @@ export default {
const idxS = this.selectedRouteDetails.waypoints.findIndex(p => p.id === wp.id);
if (idxS !== -1) this.selectedRouteDetails.waypoints.splice(idxS, 1, merged);
}
await this.recalculateAndPersistRouteKTimes(routeId);
if (this.activeRouteIds.includes(routeId) && this.$refs.cesiumMap) {
const r = this.routes.find(rr => rr.id === routeId);
if (r && r.waypoints) {
@ -1592,7 +1595,7 @@ export default {
} catch (_) {}
}
const { minMinutes, maxMinutes } = this.getDeductionTimeRange();
if (r.waypoints.some(wp => this.isHoldWaypoint(wp))) {
if (r.waypoints.some(wp2 => this.isHoldWaypoint(wp2))) {
this.getPositionAtMinutesFromK(r.waypoints, minMinutes, minMinutes, maxMinutes, routeId);
}
this.$refs.cesiumMap.removeRouteById(routeId);
@ -1668,7 +1671,7 @@ export default {
lng: wp.lng,
alt: wp.alt,
speed: wp.speed != null ? wp.speed : 800,
turnAngle: wp.turnAngle != null && wp.turnAngle !== '' ? Number(wp.turnAngle) : 0,
turnAngle: wp.turnAngle != null && wp.turnAngle !== '' ? Number(wp.turnAngle) : 45,
pointType: (wp.pointType || wp.point_type || 'hold_circle'),
...(wp.startTime != null && wp.startTime !== '' ? { startTime: wp.startTime } : {})
};
@ -1692,6 +1695,7 @@ export default {
const idxS = this.selectedRouteDetails.waypoints.findIndex(p => p.id === wp.id);
if (idxS !== -1) this.selectedRouteDetails.waypoints.splice(idxS, 1, merged);
}
await this.recalculateAndPersistRouteKTimes(routeId);
if (this.activeRouteIds.includes(routeId) && this.$refs.cesiumMap) {
const r = this.routes.find(rr => rr.id === routeId);
if (r && r.waypoints) {
@ -1925,109 +1929,12 @@ export default {
const i = this.selectedRouteDetails.waypoints.findIndex(p => p.id === dbId);
if (i !== -1) this.selectedRouteDetails.waypoints.splice(i, 1, merged);
}
// /K
const routeForPlatform = this.routes.find(r => r.id === routeId) || route;
if (idx > 0) {
const prev = waypoints[idx - 1];
const distM = this.segmentDistance(
{ lat: prev.lat, lng: prev.lng, alt: prev.alt },
{ lat: merged.lat, lng: merged.lng, alt: merged.alt }
);
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prev.startTime);
const prevSegMode = getSegMode(prev);
const prevSegTargetSpeed = getSegTargetSpeed(prev);
const mergedSegMode = getSegMode(merged);
const mergedSegTargetMinutes = getSegTargetMinutes(merged);
if (prevSegMode === 'fixed_speed') {
const speedKmh = Number(prevSegTargetSpeed ?? prev.speed) || 800;
const newMinutesFromK = prevMinutes + (distM / 1000) / speedKmh * 60;
const newStartTime = this.minutesToStartTimeWithSeconds(newMinutesFromK);
const startPayload = { ...merged, startTime: newStartTime };
if (mergedSegMode != null) startPayload.segmentMode = mergedSegMode;
try {
const r2 = await updateWaypoints(startPayload, roomIdParam);
if (r2.code === 200) {
Object.assign(merged, { startTime: newStartTime });
if (idx !== -1) waypoints.splice(idx, 1, merged);
if (this.selectedRouteDetails && this.selectedRouteDetails.id === routeId) {
const i = this.selectedRouteDetails.waypoints.findIndex(p => p.id === dbId);
if (i !== -1) this.selectedRouteDetails.waypoints.splice(i, 1, merged);
}
}
} catch (e) {
console.warn('定速重算相对K时失败', e);
}
} else if (mergedSegMode === 'fixed_time') {
const currMinutes = (mergedSegTargetMinutes != null && mergedSegTargetMinutes !== '')
? Number(mergedSegTargetMinutes)
: this.waypointStartTimeToMinutesDecimal(merged.startTime);
const deltaMin = currMinutes - prevMinutes;
if (deltaMin > 0.001) {
const newSpeedKmh = (distM / 1000) / (deltaMin / 60);
const speedVal = Math.round(newSpeedKmh * 10) / 10;
const speedPayload = { ...prev, speed: speedVal };
if (prevSegMode != null) speedPayload.segmentMode = prevSegMode;
try {
const r2 = await updateWaypoints(speedPayload, roomIdParam);
if (r2.code === 200) {
Object.assign(prev, { speed: speedVal });
const prevIdx = idx - 1;
waypoints.splice(prevIdx, 1, prev);
if (this.selectedRouteDetails && this.selectedRouteDetails.id === routeId) {
const i = this.selectedRouteDetails.waypoints.findIndex(p => p.id === prev.id);
if (i !== -1) this.selectedRouteDetails.waypoints.splice(i, 1, prev);
}
}
} catch (e) {
console.warn('定时重算上一航点速度失败', e);
}
}
}
}
// 使 K
if (idx >= 0 && idx < waypoints.length - 1) {
const next = waypoints[idx + 1];
const nextSegMode = getSegMode(next);
const nextSegTargetMinutes = getSegTargetMinutes(next);
if (nextSegMode === 'fixed_time') {
const distToNextM = this.segmentDistance(
{ lat: merged.lat, lng: merged.lng, alt: merged.alt },
{ lat: next.lat, lng: next.lng, alt: next.alt }
);
const mergedSegTargetMinutes = getSegTargetMinutes(merged);
const currMinutes = (mergedSegTargetMinutes != null && mergedSegTargetMinutes !== '')
? Number(mergedSegTargetMinutes)
: this.waypointStartTimeToMinutesDecimal(merged.startTime);
const nextMinutes = (nextSegTargetMinutes != null && nextSegTargetMinutes !== '')
? Number(nextSegTargetMinutes)
: this.waypointStartTimeToMinutesDecimal(next.startTime);
const deltaMin = nextMinutes - currMinutes;
if (deltaMin > 0.001) {
const newSpeedKmh = (distToNextM / 1000) / (deltaMin / 60);
const speedVal = Math.round(newSpeedKmh * 10) / 10;
const currPayload = { ...merged, speed: speedVal };
if (wpSegMode != null) currPayload.segmentMode = wpSegMode;
if (merged.labelFontSize != null) currPayload.labelFontSize = merged.labelFontSize;
if (merged.labelColor != null) currPayload.labelColor = merged.labelColor;
if (merged.color != null) currPayload.color = merged.color;
if (merged.pixelSize != null) currPayload.pixelSize = merged.pixelSize;
if (merged.outlineColor != null) currPayload.outlineColor = merged.outlineColor;
try {
const r2 = await updateWaypoints(currPayload, roomIdParam);
if (r2.code === 200) {
Object.assign(merged, { speed: speedVal });
waypoints.splice(idx, 1, merged);
if (this.selectedRouteDetails && this.selectedRouteDetails.id === routeId) {
const i = this.selectedRouteDetails.waypoints.findIndex(p => p.id === dbId);
if (i !== -1) this.selectedRouteDetails.waypoints.splice(i, 1, merged);
}
}
} catch (e) {
console.warn('下一航点为定时点重算本航点速度失败', e);
}
}
}
const routeInListSync = this.routes.find(r => r.id === routeId);
if (routeInListSync && routeInListSync.waypoints !== waypoints) {
this.$set(routeInListSync, 'waypoints', waypoints);
}
await this.recalculateAndPersistRouteKTimes(routeId);
if (this.$refs.cesiumMap) {
const roomId = this.currentRoomId;
if (roomId && routeForPlatform.platformId) {
@ -2762,6 +2669,9 @@ export default {
this.selectedRouteDetails.platform = updatedRoute.platform;
this.selectedRouteDetails.attributes = updatedRoute.attributes;
}
if (updatedRoute.waypoints && updatedRoute.waypoints.length > 0) {
await this.recalculateAndPersistRouteKTimes(updatedRoute.id);
}
this.$message.success(updatedRoute.waypoints && updatedRoute.waypoints.length > 0 ? '航线与航点已保存' : '航线更新成功');
this.wsConnection?.sendSyncWaypoints?.(updatedRoute.id);
const routeStyle = updatedRoute.routeStyle || this.parseRouteStyle(updatedRoute.attributes);
@ -3044,10 +2954,8 @@ export default {
}
return { ...p, startTime };
});
// 线 0 45°
// 线 45°
const finalWaypoints = pointsWithStartTime.map((p, index) => {
const isFirstOrLast = index === 0 || index === wpCount - 1;
const defaultTurnAngle = isFirstOrLast ? 0.0 : 45.0;
return {
name: p.name || `WP${index + 1}`,
lat: p.lat,
@ -3055,7 +2963,7 @@ export default {
alt: p.alt != null ? p.alt : 5000.0,
speed: p.speed != null ? p.speed : 800.0,
startTime: p.startTime,
turnAngle: p.turnAngle != null ? p.turnAngle : defaultTurnAngle,
turnAngle: p.turnAngle != null ? p.turnAngle : 45.0,
labelFontSize: p.labelFontSize != null ? p.labelFontSize : 14,
labelColor: p.labelColor || '#333333',
...(p.pointType && { pointType: p.pointType }),
@ -3163,7 +3071,7 @@ export default {
this.selectedWaypoint = data;
this.showWaypointDialog = true;
},
/** 航点编辑保存:更新数据库并同步地图显示。定时/定速点变更会级联重算整条航线后续航点的 K 时。 */
/** 航点编辑保存:更新数据库并同步地图显示;保存后按航段类型整条重算 K 时并写库。 */
async updateWaypoint(updatedWaypoint) {
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return;
const routeId = updatedWaypoint.routeId != null ? updatedWaypoint.routeId : this.selectedRouteDetails.id;
@ -3172,7 +3080,6 @@ export default {
return;
}
const sd = this.selectedRouteDetails;
const waypointsBefore = JSON.parse(JSON.stringify(sd.waypoints));
try {
const idxForRadius = sd.waypoints.findIndex(p => p.id === updatedWaypoint.id);
if (this.$refs.cesiumMap && updatedWaypoint.turnAngle > 0 && idxForRadius >= 0) {
@ -3181,23 +3088,10 @@ export default {
} else {
updatedWaypoint.turnRadius = 0;
}
// startTimeK
let startTimeToUse = (updatedWaypoint.startTime != null && updatedWaypoint.startTime !== '')
// 线 K recalculateAndPersistRouteKTimes
const startTimeToUse = (updatedWaypoint.startTime != null && updatedWaypoint.startTime !== '')
? updatedWaypoint.startTime
: 'K+00:00:00';
// KK
const indexForSpeed = sd.waypoints.findIndex(p => p.id === updatedWaypoint.id);
if (updatedWaypoint.segmentMode === 'fixed_speed' && (updatedWaypoint.segmentTargetSpeed != null && updatedWaypoint.segmentTargetSpeed !== '') && indexForSpeed > 0) {
const prev = sd.waypoints[indexForSpeed - 1];
const distM = this.segmentDistance(
{ lat: prev.lat, lng: prev.lng, alt: prev.alt },
{ lat: updatedWaypoint.lat, lng: updatedWaypoint.lng, alt: updatedWaypoint.alt }
);
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prev.startTime);
const speedKmh = Number(updatedWaypoint.segmentTargetSpeed) || 800;
const newMinutesFromK = prevMinutes + (distM / 1000) / speedKmh * 60;
startTimeToUse = this.minutesToStartTimeWithSeconds(newMinutesFromK);
}
const payload = {
id: updatedWaypoint.id,
routeId: updatedWaypoint.routeId,
@ -3234,126 +3128,7 @@ export default {
const idxInList = routeInList.waypoints.findIndex(p => p.id === updatedWaypoint.id);
if (idxInList !== -1) routeInList.waypoints.splice(idxInList, 1, merged);
}
// K使 K+
if (merged.segmentMode === 'fixed_time' && index > 0) {
const prev = sd.waypoints[index - 1];
const distM = this.segmentDistance(
{ lat: prev.lat, lng: prev.lng, alt: prev.alt },
{ lat: merged.lat, lng: merged.lng, alt: merged.alt }
);
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prev.startTime);
const currMinutes = (merged.segmentTargetMinutes != null && merged.segmentTargetMinutes !== '') ? Number(merged.segmentTargetMinutes) : this.waypointStartTimeToMinutesDecimal(merged.startTime);
const deltaMin = currMinutes - prevMinutes;
if (deltaMin > 0.001) {
const newSpeedKmh = (distM / 1000) / (deltaMin / 60);
const speedVal = Math.round(newSpeedKmh * 10) / 10;
const prevPayload = { ...prev, speed: speedVal };
if (prev.segmentMode != null) prevPayload.segmentMode = prev.segmentMode;
if (prev.labelFontSize != null) prevPayload.labelFontSize = prev.labelFontSize;
if (prev.labelColor != null) prevPayload.labelColor = prev.labelColor;
if (prev.color != null) prevPayload.color = prev.color;
if (prev.pixelSize != null) prevPayload.pixelSize = prev.pixelSize;
if (prev.outlineColor != null) prevPayload.outlineColor = prev.outlineColor;
try {
const r2 = await updateWaypoints(prevPayload, roomIdParam);
if (r2.code === 200) {
Object.assign(prev, { speed: speedVal });
sd.waypoints.splice(index - 1, 1, prev);
if (routeInList && routeInList.waypoints) {
const prevIdxInList = routeInList.waypoints.findIndex(p => p.id === prev.id);
if (prevIdxInList !== -1) routeInList.waypoints.splice(prevIdxInList, 1, prev);
}
}
} catch (e) {
console.warn('定时点重算上一航点速度失败', e);
}
}
}
// 使 K
if (index < sd.waypoints.length - 1) {
const next = sd.waypoints[index + 1];
if (next.segmentMode === 'fixed_time') {
const distToNextM = this.segmentDistance(
{ lat: merged.lat, lng: merged.lng, alt: merged.alt },
{ lat: next.lat, lng: next.lng, alt: next.alt }
);
const currMinutes = this.waypointStartTimeToMinutesDecimal(merged.startTime);
const nextMinutes = (next.segmentTargetMinutes != null && next.segmentTargetMinutes !== '') ? Number(next.segmentTargetMinutes) : this.waypointStartTimeToMinutesDecimal(next.startTime);
const deltaMin = nextMinutes - currMinutes;
if (deltaMin > 0.001) {
const newSpeedKmh = (distToNextM / 1000) / (deltaMin / 60);
const speedVal = Math.round(newSpeedKmh * 10) / 10;
const currPayload = { ...merged, speed: speedVal };
if (merged.segmentMode != null) currPayload.segmentMode = merged.segmentMode;
if (merged.labelFontSize != null) currPayload.labelFontSize = merged.labelFontSize;
if (merged.labelColor != null) currPayload.labelColor = merged.labelColor;
if (merged.pixelSize != null) currPayload.pixelSize = merged.pixelSize;
if (merged.color != null) currPayload.color = merged.color;
if (merged.outlineColor != null) currPayload.outlineColor = merged.outlineColor;
try {
const r2 = await updateWaypoints(currPayload, roomIdParam);
if (r2.code === 200) {
Object.assign(merged, { speed: speedVal });
sd.waypoints.splice(index, 1, merged);
if (routeInList && routeInList.waypoints) {
const idxInList = routeInList.waypoints.findIndex(p => p.id === merged.id);
if (idxInList !== -1) routeInList.waypoints.splice(idxInList, 1, merged);
}
}
} catch (e) {
console.warn('下一航点为定时点重算本航点速度失败', e);
}
}
}
}
// / K 使线
const oldMinutesAtIdx = this.waypointStartTimeToMinutesDecimal(waypointsBefore[index]?.startTime);
const newMinutesAtIdx = this.waypointStartTimeToMinutesDecimal(sd.waypoints[index]?.startTime);
const deltaMin = newMinutesAtIdx - oldMinutesAtIdx;
if (Math.abs(deltaMin) > 0.001 && index < sd.waypoints.length - 1) {
for (let i = index + 1; i < sd.waypoints.length; i++) {
const wp = sd.waypoints[i];
const oldArrival = (wp.segmentTargetMinutes != null && wp.segmentTargetMinutes !== '') ? Number(wp.segmentTargetMinutes) : this.waypointStartTimeToMinutesDecimal(wp.startTime);
const newArrival = oldArrival + deltaMin;
const newStartTime = this.minutesToStartTimeWithSeconds(newArrival);
const cascadePayload = {
id: wp.id,
routeId,
name: wp.name,
seq: wp.seq,
lat: wp.lat,
lng: wp.lng,
alt: wp.alt,
speed: wp.speed,
startTime: newStartTime,
turnAngle: wp.turnAngle
};
if (wp.pointType != null) cascadePayload.pointType = wp.pointType;
if (wp.holdParams != null) cascadePayload.holdParams = wp.holdParams;
if (wp.segmentMode != null) cascadePayload.segmentMode = wp.segmentMode;
if (wp.segmentMode === 'fixed_time') cascadePayload.segmentTargetMinutes = newArrival;
else if (wp.segmentTargetMinutes != null) cascadePayload.segmentTargetMinutes = wp.segmentTargetMinutes;
if (wp.segmentTargetSpeed != null) cascadePayload.segmentTargetSpeed = wp.segmentTargetSpeed;
if (wp.labelFontSize != null) cascadePayload.labelFontSize = wp.labelFontSize;
if (wp.labelColor != null) cascadePayload.labelColor = wp.labelColor;
if (wp.color != null) cascadePayload.color = wp.color;
if (wp.pixelSize != null) cascadePayload.pixelSize = wp.pixelSize;
if (wp.outlineColor != null) cascadePayload.outlineColor = wp.outlineColor;
try {
const rCascade = await updateWaypoints(cascadePayload, roomIdParam);
if (rCascade.code === 200) {
Object.assign(wp, { startTime: newStartTime, ...(wp.segmentMode === 'fixed_time' && { segmentTargetMinutes: newArrival }) });
sd.waypoints.splice(i, 1, wp);
if (routeInList && routeInList.waypoints) {
const idxInList = routeInList.waypoints.findIndex(p => p.id === wp.id);
if (idxInList !== -1) routeInList.waypoints.splice(idxInList, 1, wp);
}
}
} catch (e) {
console.warn(`级联更新航点 ${i + 1} K时失败`, e);
}
}
}
await this.recalculateAndPersistRouteKTimes(routeId);
if (this.$refs.cesiumMap) {
if (roomId && sd.platformId) {
try {
@ -5613,6 +5388,265 @@ export default {
return `K${sign}${String(h).padStart(2, '0')}:${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
},
/** 与推演一致:按 seq 排序航点 */
sortWaypointsBySeqForK(waypoints) {
if (!waypoints || !waypoints.length) return [];
return waypoints.slice().sort((a, b) => {
const saRaw = a.seq != null ? a.seq : a.Seq;
const sbRaw = b.seq != null ? b.seq : b.Seq;
const saNum = Number(saRaw);
const sbNum = Number(sbRaw);
const sa = Number.isFinite(saNum) ? saNum : Number.POSITIVE_INFINITY;
const sb = Number.isFinite(sbNum) ? sbNum : Number.POSITIVE_INFINITY;
if (sa !== sb) return sa - sb;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
},
/**
* 按几何与航段类型默认/定速/定时盘旋正向推算整条航线各航点相对 K
* 首点锚定在 K+0定时点保持 segmentTargetMinutes 为计划到达盘旋点为该时刻为进入盘旋必要时将进入时刻与几何到达取 max
*/
computeRecalculatedKTimeMap(sorted) {
const result = new Map();
const getMode = (w) => (w?.segmentMode ?? w?.displayStyle?.segmentMode ?? null);
const getTargetMin = (w) => {
const v = w?.segmentTargetMinutes ?? w?.displayStyle?.segmentTargetMinutes;
if (v == null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
};
const getTargetSpeed = (w) => {
const v = w?.segmentTargetSpeed ?? w?.displayStyle?.segmentTargetSpeed;
if (v == null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) && n > 0 ? n : null;
};
const legSpeedKmh = (A, B) => {
if (getMode(B) === 'fixed_speed') {
const sp = getTargetSpeed(B);
if (sp != null) return sp;
}
return Number(A.speed) || 800;
};
const snapshotHoldDurMin = (B) => {
if (!this.isHoldWaypoint(B)) return 0;
const exitM = this.waypointStartTimeToMinutesDecimal(B.startTime);
const mode = getMode(B);
const entryStored = getTargetMin(B);
if (mode === 'fixed_time' && entryStored != null && exitM > entryStored + 1e-6) {
return exitM - entryStored;
}
return 5;
};
const n = sorted.length;
if (n === 0) return result;
const t0 = this.minutesToStartTimeWithSeconds(0);
result.set(sorted[0].id, { startTime: t0 });
let tCurrent = 0;
if (n === 1) return result;
for (let i = 0; i < n - 1; i++) {
const A = sorted[i];
const B = sorted[i + 1];
const distM = this.segmentDistance(
{ lat: A.lat, lng: A.lng, alt: A.alt },
{ lat: B.lat, lng: B.lng, alt: B.alt }
);
const tDepart = tCurrent;
const speedKmh = legSpeedKmh(A, B);
const flyMin = distM > 0 && speedKmh > 0 ? (distM / 1000) / speedKmh * 60 : 0;
const tFly = tDepart + flyMin;
const modeB = getMode(B);
const targetB = getTargetMin(B);
if (modeB === 'fixed_time' && targetB != null) {
if (this.isHoldWaypoint(B)) {
const hd = snapshotHoldDurMin(B);
const tEntry = Math.max(tFly, targetB);
const tExit = tEntry + hd;
result.set(B.id, {
startTime: this.minutesToStartTimeWithSeconds(tExit),
segmentTargetMinutes: tEntry
});
tCurrent = tExit;
} else {
result.set(B.id, {
startTime: this.minutesToStartTimeWithSeconds(targetB),
segmentTargetMinutes: targetB
});
tCurrent = targetB;
}
} else if (this.isHoldWaypoint(B)) {
const hd = snapshotHoldDurMin(B);
const tExit = tFly + hd;
result.set(B.id, { startTime: this.minutesToStartTimeWithSeconds(tExit) });
tCurrent = tExit;
} else {
result.set(B.id, { startTime: this.minutesToStartTimeWithSeconds(tFly) });
tCurrent = tFly;
}
}
return result;
},
getWaypointSegTargetMinutesForCompare(wp) {
const v = wp?.segmentTargetMinutes ?? wp?.displayStyle?.segmentTargetMinutes;
if (v == null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
},
/** 根据当前几何重算并持久化整条航线 K 时(不写甘特图拉伸等纯时间变换场景) */
async recalculateAndPersistRouteKTimes(routeId) {
if (routeId == null || this.isRouteLockedByOther(routeId)) return;
const routeInList = this.routes.find(r => r.id === routeId);
if (!routeInList || !routeInList.waypoints || routeInList.waypoints.length === 0) return;
const sorted = this.sortWaypointsBySeqForK(routeInList.waypoints);
const plan = this.computeRecalculatedKTimeMap(sorted);
const roomIdParam = this.currentRoomId != null ? { roomId: this.currentRoomId } : {};
for (const wp of sorted) {
const p = plan.get(wp.id);
if (!p) continue;
const oldM = this.waypointStartTimeToMinutesDecimal(wp.startTime);
const newM = this.waypointStartTimeToMinutesDecimal(p.startTime);
const oldTm = this.getWaypointSegTargetMinutesForCompare(wp);
const newTm = p.segmentTargetMinutes != null && Number.isFinite(p.segmentTargetMinutes) ? p.segmentTargetMinutes : null;
const mode = wp.segmentMode ?? wp.displayStyle?.segmentMode ?? null;
let tmChanged = false;
if (mode === 'fixed_time' && newTm != null) {
tmChanged = oldTm == null || Math.abs(oldTm - newTm) > 1e-4;
}
if (Math.abs(oldM - newM) < 1e-4 && !tmChanged) continue;
const nextSegTarget = mode === 'fixed_time' && newTm != null ? newTm : undefined;
const payload = this.buildWaypointTimeUpdatePayload(wp, routeId, p.startTime, nextSegTarget);
const idx = sorted.findIndex(x => x.id === wp.id);
const prevWp = idx > 0 ? sorted[idx - 1] : null;
if (wp.turnAngle > 0 && this.$refs.cesiumMap) {
payload.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWp, wp.turnAngle);
} else {
payload.turnRadius = 0;
}
try {
const res = await updateWaypoints(payload, roomIdParam);
if (res.code === 200) {
wp.startTime = p.startTime;
if (nextSegTarget != null && Number.isFinite(nextSegTarget)) {
wp.segmentTargetMinutes = Number(nextSegTarget.toFixed(6));
wp.displayStyle = { ...(wp.displayStyle || {}), segmentMode: wp.segmentMode, segmentTargetMinutes: wp.segmentTargetMinutes };
}
}
} catch (e) {
console.warn('重算航线 K 时持久化失败', wp.id, e);
}
}
await this.persistLegSpeedsForFixedTimeAnchors(routeId, sorted);
if (this.selectedRouteDetails && this.selectedRouteDetails.id === routeId) {
if (this.selectedRouteDetails.waypoints === routeInList.waypoints) {
/* 同一引用,已在上面原地更新 */
} else {
this.selectedRouteDetails.waypoints.forEach((w) => {
const u = routeInList.waypoints.find(x => x.id === w.id);
if (u) {
Object.assign(w, {
startTime: u.startTime,
segmentTargetMinutes: u.segmentTargetMinutes,
displayStyle: u.displayStyle,
speed: u.speed
});
}
});
}
}
},
/**
* 航段终点为定时点时上一航点(或盘旋切出)K 本点计划到达/盘旋进入与距离反算该段应飞的地速并写回上一航点的 speed与原先航点编辑保存行为一致
* 须在 K 时已重算并写回 startTime/segmentTargetMinutes 之后调用
*/
async persistLegSpeedsForFixedTimeAnchors(routeId, sorted) {
if (routeId == null || !sorted || sorted.length < 2) return;
const roomIdParam = this.currentRoomId != null ? { roomId: this.currentRoomId } : {};
const getMode = (w) => (w?.segmentMode ?? w?.displayStyle?.segmentMode ?? null);
const getTargetMin = (w) => {
const v = w?.segmentTargetMinutes ?? w?.displayStyle?.segmentTargetMinutes;
if (v == null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
};
for (let i = 0; i < sorted.length - 1; i++) {
const A = sorted[i];
const B = sorted[i + 1];
if (getMode(B) !== 'fixed_time') continue;
const distM = this.segmentDistance(
{ lat: A.lat, lng: A.lng, alt: A.alt },
{ lat: B.lat, lng: B.lng, alt: B.alt }
);
const tDepart = this.waypointStartTimeToMinutesDecimal(A.startTime);
let tArr;
if (this.isHoldWaypoint(B)) {
tArr = getTargetMin(B);
if (tArr == null) continue;
} else {
tArr = getTargetMin(B);
if (tArr == null) {
tArr = this.waypointStartTimeToMinutesDecimal(B.startTime);
}
}
const deltaMin = tArr - tDepart;
if (deltaMin <= 0.001 || distM <= 0) continue;
const newSpeedKmh = Math.round(((distM / 1000) / (deltaMin / 60)) * 10) / 10;
if (!Number.isFinite(newSpeedKmh) || newSpeedKmh <= 0) continue;
if (Math.abs(Number(A.speed) - newSpeedKmh) < 0.05) continue;
const idxA = sorted.findIndex(x => x.id === A.id);
const prevWpForA = idxA > 0 ? sorted[idxA - 1] : null;
const payload = {
id: A.id,
routeId: A.routeId != null ? A.routeId : routeId,
name: A.name,
seq: A.seq,
lat: A.lat,
lng: A.lng,
alt: A.alt,
speed: newSpeedKmh,
startTime: A.startTime != null && A.startTime !== '' ? A.startTime : 'K+00:00:00',
turnAngle: A.turnAngle
};
if (A.pointType != null) payload.pointType = A.pointType;
if (A.holdParams != null) payload.holdParams = A.holdParams;
if (A.labelFontSize != null) payload.labelFontSize = A.labelFontSize;
if (A.labelColor != null) payload.labelColor = A.labelColor;
if (A.segmentMode != null) payload.segmentMode = A.segmentMode;
if (A.segmentTargetMinutes != null && A.segmentTargetMinutes !== '') payload.segmentTargetMinutes = A.segmentTargetMinutes;
if (A.segmentTargetSpeed != null && A.segmentTargetSpeed !== '') payload.segmentTargetSpeed = A.segmentTargetSpeed;
if (A.pixelSize != null) payload.pixelSize = A.pixelSize;
if (A.color != null) payload.color = A.color;
if (A.outlineColor != null) payload.outlineColor = A.outlineColor;
if (A.turnAngle > 0 && this.$refs.cesiumMap) {
payload.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWpForA, A.turnAngle);
} else {
payload.turnRadius = 0;
}
try {
const res = await updateWaypoints(payload, roomIdParam);
if (res.code === 200) {
A.speed = newSpeedKmh;
}
} catch (e) {
console.warn('定时航段反算上一航点速度失败', A.id, e);
}
}
},
isHoldWaypoint(wp) {
const t = (wp && wp.pointType) || (wp && wp.point_type) || 'normal';
return t === 'hold_circle' || t === 'hold_ellipse';
@ -6408,6 +6442,7 @@ export default {
this.showAddHoldDialog = false;
this.addHoldContext = null;
await this.getList();
await this.recalculateAndPersistRouteKTimes(routeId);
const updated = this.routes.find(r => r.id === routeId);
if (updated && updated.waypoints && this.activeRouteIds.includes(routeId) && this.$refs.cesiumMap) {
const roomId = this.currentRoomId;

4
ruoyi-ui/src/views/dialogs/RouteEditDialog.vue

@ -260,7 +260,7 @@
<el-table-column label="转弯坡度(°)" min-width="100">
<template slot-scope="scope">
<span v-if="!waypointsEditMode">{{ formatNum(scope.row.turnAngle) }}</span>
<el-input v-else v-model.number="scope.row.turnAngle" size="mini" placeholder="0" />
<el-input v-else v-model.number="scope.row.turnAngle" size="mini" placeholder="45" />
</template>
</el-table-column>
<el-table-column label="盘旋" width="72" align="center">
@ -775,7 +775,7 @@ export default {
alt: row.alt,
speed: speedVal,
startTime: this.minutesToStartTime(row.minutesFromK),
turnAngle: row.turnAngle != null ? row.turnAngle : 0,
turnAngle: row.turnAngle != null ? row.turnAngle : 45,
turnRadius,
pointType: row.pointType || null,
holdParams: row.holdParams || null,

13
ruoyi-ui/src/views/dialogs/WaypointEditDialog.vue

@ -116,13 +116,9 @@
<el-input-number
v-model="formData.turnAngle"
controls-position="right"
placeholder="请输入转弯坡度"
placeholder="默认 45°"
class="full-width-input"
:disabled="formData.isBankDisabled"
></el-input-number>
<div v-if="formData.isBankDisabled" class="form-tip form-warn">
首尾航点坡度已锁定为 0不可编辑
</div>
</el-form-item>
<template v-if="isHoldWaypoint">
<el-form-item label="盘旋类型">
@ -217,7 +213,6 @@ export default {
segmentTargetSpeed: null,
currentIndex: -1,
totalPoints: 0,
isBankDisabled: false,
pointType: 'normal',
holdClockwise: true,
holdEdgeLengthKm: 20,
@ -268,7 +263,6 @@ export default {
initFormData() {
const index = this.waypoint.currentIndex !== undefined ? this.waypoint.currentIndex : -1;
const total = this.waypoint.totalPoints || 0;
const locked = (index === 0) || (total > 0 && index === total - 1);
const pt = (this.waypoint.pointType || this.waypoint.point_type) || 'normal';
let holdClockwise = true;
@ -305,8 +299,9 @@ export default {
segmentTargetSpeed: (disp && disp.segmentTargetSpeed != null) ? Number(disp.segmentTargetSpeed) : (this.waypoint.segmentTargetSpeed != null ? Number(this.waypoint.segmentTargetSpeed) : (this.waypoint.speed != null ? Number(this.waypoint.speed) : null)),
currentIndex: index,
totalPoints: total,
isBankDisabled: locked,
turnAngle: locked ? 0 : (this.waypoint.turnAngle != null ? Number(this.waypoint.turnAngle) : 45),
turnAngle: this.waypoint.turnAngle != null && this.waypoint.turnAngle !== ''
? Number(this.waypoint.turnAngle)
: 45,
pointType: pt,
holdClockwise,
holdEdgeLengthKm,

Loading…
Cancel
Save