|
|
|
@ -278,9 +278,7 @@ |
|
|
|
<el-radio label="hold_ellipse">椭圆</el-radio> |
|
|
|
</el-radio-group> |
|
|
|
</el-form-item> |
|
|
|
<el-form-item v-if="addHoldForm.holdType === 'hold_circle'" label="半径(米)"> |
|
|
|
<el-input-number v-model="addHoldForm.radius" :min="100" :max="50000" style="width:100%" /> |
|
|
|
</el-form-item> |
|
|
|
<!-- 圆形盘旋半径由速度与转弯坡度自动计算,无需填写 --> |
|
|
|
<template v-if="addHoldForm.holdType === 'hold_ellipse'"> |
|
|
|
<el-form-item label="跑道边长(km)"> |
|
|
|
<el-input-number v-model="addHoldForm.edgeLengthKm" :min="1" :max="200" :step="1" style="width:100%" /> |
|
|
|
@ -342,6 +340,7 @@ |
|
|
|
<waypoint-edit-dialog |
|
|
|
v-model="showWaypointDialog" |
|
|
|
:waypoint="selectedWaypoint" |
|
|
|
:prev-waypoint="prevWaypointForEdit" |
|
|
|
@save="updateWaypoint" |
|
|
|
/> |
|
|
|
|
|
|
|
@ -760,7 +759,7 @@ export default { |
|
|
|
deductionEarlyArrivalByRoute: {}, // routeId -> earlyArrivalLegs |
|
|
|
showAddHoldDialog: false, |
|
|
|
addHoldContext: null, // { routeId, routeName, legIndex, fromName, toName } |
|
|
|
addHoldForm: { holdType: 'hold_circle', radius: 15000, edgeLengthKm: 20, clockwise: true, startTime: '', startTimeMinutes: null }, |
|
|
|
addHoldForm: { holdType: 'hold_circle', edgeLengthKm: 20, clockwise: true, startTime: '', startTimeMinutes: null }, |
|
|
|
missionDrawingActive: false, |
|
|
|
missionDrawingPointsCount: 0, |
|
|
|
isPlaying: false, |
|
|
|
@ -848,6 +847,14 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
/** 航点编辑时:上一航点,用于定时点“设定的时间”与“到达K时”的转换 */ |
|
|
|
prevWaypointForEdit() { |
|
|
|
const wp = this.selectedWaypoint; |
|
|
|
const waypoints = this.selectedRouteDetails?.waypoints; |
|
|
|
if (!wp || !waypoints || !Array.isArray(waypoints)) return null; |
|
|
|
const idx = wp.currentIndex != null ? wp.currentIndex : waypoints.findIndex(w => w.id === wp.id); |
|
|
|
return idx > 0 ? waypoints[idx - 1] : null; |
|
|
|
}, |
|
|
|
currentUserId() { |
|
|
|
const id = this.$store.getters.id; |
|
|
|
return id != null ? Number(id) : null; |
|
|
|
@ -1171,7 +1178,7 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
/** 右键航点“向前/向后增加航点”:进入放置模式,传入 waypoints 给地图预览 */ |
|
|
|
handleAddWaypointAt({ routeId, waypointIndex, mode }) { |
|
|
|
handleAddWaypointAt({ routeId, waypointIndex, mode, segmentMode, segmentTargetMinutes, segmentTargetSpeed }) { |
|
|
|
if (this.isRouteLockedByOther(routeId)) { |
|
|
|
this.$message.warning('该航线正被其他成员编辑,无法添加航点'); |
|
|
|
return; |
|
|
|
@ -1185,37 +1192,104 @@ export default { |
|
|
|
this.$message.warning('航线无航点数据'); |
|
|
|
return; |
|
|
|
} |
|
|
|
// 定速点的下一航点不能设为定时点 |
|
|
|
if (segmentMode === 'fixed_time') { |
|
|
|
const prevIdx = mode === 'before' ? waypointIndex - 1 : waypointIndex; |
|
|
|
const prevWp = prevIdx >= 0 ? route.waypoints[prevIdx] : null; |
|
|
|
if (prevWp && prevWp.segmentMode === 'fixed_speed') { |
|
|
|
this.$message.warning('定速点的下一航点不能设置为定时点,请先将上一航点改为默认或定时'); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!this.$refs.cesiumMap || typeof this.$refs.cesiumMap.startAddWaypointAt !== 'function') return; |
|
|
|
this.$refs.cesiumMap.startAddWaypointAt(routeId, waypointIndex, mode, route.waypoints); |
|
|
|
this.$refs.cesiumMap.startAddWaypointAt(routeId, waypointIndex, mode, route.waypoints, segmentMode, segmentTargetMinutes, segmentTargetSpeed); |
|
|
|
}, |
|
|
|
|
|
|
|
/** 地图放置新航点后:调用 addWaypoints 插入,再按插入位置重排 seq 并重绘。向后添加时:当前第 K 个,新航点为第 K+1 个,原第 K+1 个及以后依次后移。 */ |
|
|
|
async handleAddWaypointPlaced({ routeId, waypointIndex, mode, position }) { |
|
|
|
/** 地图放置新航点后:调用 addWaypoints 插入,再按插入位置重排 seq 并重绘。segmentMode 为 null/fixed_time/fixed_speed 时按所选方式规划该航段。segmentTargetMinutes/segmentTargetSpeed 为用户在弹窗填写的固定值(可选)。 */ |
|
|
|
async handleAddWaypointPlaced({ routeId, waypointIndex, mode, position, segmentMode, segmentTargetMinutes: userSegmentTargetMinutes, segmentTargetSpeed: userSegmentTargetSpeed }) { |
|
|
|
const route = this.routes.find(r => r.id === routeId); |
|
|
|
if (!route || !route.waypoints) { |
|
|
|
this.$message.warning('航线不存在或无航点'); |
|
|
|
return; |
|
|
|
} |
|
|
|
const waypoints = route.waypoints; |
|
|
|
// 定速点的下一航点不能设为定时点 |
|
|
|
if (segmentMode === 'fixed_time') { |
|
|
|
const insertIndex = mode === 'before' ? waypointIndex : waypointIndex + 1; |
|
|
|
const prevWp = insertIndex > 0 ? waypoints[insertIndex - 1] : null; |
|
|
|
if (prevWp && prevWp.segmentMode === 'fixed_speed') { |
|
|
|
this.$message.warning('定速点的下一航点不能设置为定时点,请先将上一航点改为默认或定时'); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
const refWp = waypoints[waypointIndex]; |
|
|
|
// 向后添加:insertIndex = waypointIndex+1,新航点成为第 (waypointIndex+2) 个,原第 waypointIndex+2 个变为第 waypointIndex+3 个 |
|
|
|
const insertIndex = mode === 'before' ? waypointIndex : waypointIndex + 1; |
|
|
|
const prevWp = insertIndex > 0 ? waypoints[insertIndex - 1] : null; |
|
|
|
const startTime = prevWp && prevWp.startTime ? prevWp.startTime : 'K+00:00:00'; |
|
|
|
const nextWp = insertIndex < waypoints.length ? waypoints[insertIndex] : null; |
|
|
|
let startTime = prevWp && prevWp.startTime ? prevWp.startTime : 'K+00:00:00'; |
|
|
|
let segmentTargetMinutes = null; |
|
|
|
let segmentTargetSpeed = null; |
|
|
|
let plannedPrevSpeed = null; |
|
|
|
const count = waypoints.length + 1; |
|
|
|
const newName = `WP${insertIndex + 1}`; |
|
|
|
const pos = { lat: position.lat, lng: position.lng, alt: position.alt != null ? position.alt : (refWp && refWp.alt) || 5000 }; |
|
|
|
if (segmentMode === 'fixed_time' && prevWp) { |
|
|
|
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prevWp.startTime); |
|
|
|
const distPrevNew = this.segmentDistance({ lat: prevWp.lat, lng: prevWp.lng, alt: prevWp.alt }, pos); |
|
|
|
if (userSegmentTargetMinutes != null && userSegmentTargetMinutes !== '' && Number(userSegmentTargetMinutes) > 0) { |
|
|
|
// 用户填写的是“距上一航点”的飞行时间(分),如10表示飞10分钟到达 |
|
|
|
const deltaMin = Number(userSegmentTargetMinutes); |
|
|
|
segmentTargetMinutes = prevMinutes + deltaMin; // 新航点相对K时 = 上一航点 + 飞行时间 |
|
|
|
startTime = this.minutesToStartTimeWithSeconds(segmentTargetMinutes); |
|
|
|
if (deltaMin > 0.001) { |
|
|
|
plannedPrevSpeed = Math.round(((distPrevNew / 1000) / (deltaMin / 60)) * 10) / 10; |
|
|
|
} |
|
|
|
} else { |
|
|
|
const nextMinutes = nextWp ? this.waypointStartTimeToMinutesDecimal(nextWp.startTime) : prevMinutes + 10; |
|
|
|
const distPrevNext = nextWp ? this.segmentDistance({ lat: prevWp.lat, lng: prevWp.lng, alt: prevWp.alt }, { lat: nextWp.lat, lng: nextWp.lng, alt: nextWp.alt }) : distPrevNew; |
|
|
|
const ratio = distPrevNext > 0 ? Math.min(1, distPrevNew / distPrevNext) : 0.5; |
|
|
|
segmentTargetMinutes = prevMinutes + (nextMinutes - prevMinutes) * ratio; |
|
|
|
const deltaMin = segmentTargetMinutes - prevMinutes; |
|
|
|
if (deltaMin > 0.001) { |
|
|
|
plannedPrevSpeed = Math.round(((distPrevNew / 1000) / (deltaMin / 60)) * 10) / 10; |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (segmentMode === 'fixed_speed' && prevWp) { |
|
|
|
const speedKmh = (userSegmentTargetSpeed != null && userSegmentTargetSpeed !== '' && Number(userSegmentTargetSpeed) > 0) |
|
|
|
? Number(userSegmentTargetSpeed) |
|
|
|
: Number(refWp && refWp.speed != null ? refWp.speed : 800); |
|
|
|
segmentTargetSpeed = speedKmh; |
|
|
|
plannedPrevSpeed = speedKmh; // 上一航点到新航点使用定速,需更新上一航点速度 |
|
|
|
const distPrevNew = this.segmentDistance({ lat: prevWp.lat, lng: prevWp.lng, alt: prevWp.alt }, pos); |
|
|
|
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prevWp.startTime); |
|
|
|
const newMinutesFromK = prevMinutes + (distPrevNew / 1000) / speedKmh * 60; |
|
|
|
startTime = this.minutesToStartTimeWithSeconds(newMinutesFromK); |
|
|
|
} else if (!segmentMode && prevWp) { |
|
|
|
// 默认航点:上一航点速度改为默认800,新航点相对K时根据路程和默认速度计算 |
|
|
|
plannedPrevSpeed = 800; |
|
|
|
const distPrevNew = this.segmentDistance({ lat: prevWp.lat, lng: prevWp.lng, alt: prevWp.alt }, pos); |
|
|
|
const prevMinutes = this.waypointStartTimeToMinutesDecimal(prevWp.startTime); |
|
|
|
const newMinutesFromK = prevMinutes + (distPrevNew / 1000) / 800 * 60; |
|
|
|
startTime = this.minutesToStartTimeWithSeconds(newMinutesFromK); |
|
|
|
} |
|
|
|
try { |
|
|
|
const addRes = await addWaypoints({ |
|
|
|
const defaultSpeed = segmentTargetSpeed != null ? segmentTargetSpeed : ((refWp && refWp.speed != null) ? refWp.speed : 800); |
|
|
|
const defaultTurnAngle = insertIndex === 0 || insertIndex === count - 1 ? 0 : 45; |
|
|
|
const addPayload = { |
|
|
|
routeId, |
|
|
|
name: newName, |
|
|
|
seq: insertIndex + 1, |
|
|
|
lat: position.lat, |
|
|
|
lng: position.lng, |
|
|
|
alt: position.alt, |
|
|
|
speed: (refWp && refWp.speed != null) ? refWp.speed : 800, |
|
|
|
alt: pos.alt, |
|
|
|
speed: defaultSpeed, |
|
|
|
startTime, |
|
|
|
turnAngle: (refWp && refWp.turnAngle != null) ? refWp.turnAngle : (insertIndex === 0 || insertIndex === count - 1 ? 0 : 45) |
|
|
|
}); |
|
|
|
turnAngle: defaultTurnAngle |
|
|
|
}; |
|
|
|
if (segmentMode != null) addPayload.segmentMode = segmentMode; |
|
|
|
if (segmentTargetMinutes != null) addPayload.segmentTargetMinutes = segmentTargetMinutes; |
|
|
|
if (segmentTargetSpeed != null) addPayload.segmentTargetSpeed = segmentTargetSpeed; |
|
|
|
const addRes = await addWaypoints(addPayload); |
|
|
|
await this.getList(); |
|
|
|
let updated = this.routes.find(r => r.id === routeId); |
|
|
|
if (!updated || !updated.waypoints || updated.waypoints.length !== count) { |
|
|
|
@ -1247,8 +1321,11 @@ export default { |
|
|
|
const wp = reordered[i]; |
|
|
|
const newSeq = i + 1; |
|
|
|
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}`)); |
|
|
|
await updateWaypoints({ |
|
|
|
// 插入任意航点后:上一航点之后有航点了,应把上一航点默认转弯坡度设为45度(首点保持0) |
|
|
|
const prevTurnAngle = (isPrevToNew && insertIndex > 1) ? 45 : wp.turnAngle; |
|
|
|
const updatePayload = { |
|
|
|
id: wp.id, |
|
|
|
routeId, |
|
|
|
name: nameToUse, |
|
|
|
@ -1256,14 +1333,22 @@ export default { |
|
|
|
lat: wp.lat, |
|
|
|
lng: wp.lng, |
|
|
|
alt: wp.alt, |
|
|
|
speed: wp.speed, |
|
|
|
startTime: wp.startTime, |
|
|
|
turnAngle: wp.turnAngle, |
|
|
|
speed: isPrevToNew && plannedPrevSpeed != null ? plannedPrevSpeed : (isNewlyInserted && segmentTargetSpeed != null ? segmentTargetSpeed : wp.speed), |
|
|
|
startTime: isNewlyInserted && segmentMode === 'fixed_speed' ? startTime : wp.startTime, |
|
|
|
turnAngle: isNewlyInserted ? defaultTurnAngle : prevTurnAngle, |
|
|
|
...(wp.pointType != null && { pointType: wp.pointType }), |
|
|
|
...(wp.holdParams != null && { holdParams: wp.holdParams }), |
|
|
|
...(wp.labelFontSize != null && { labelFontSize: wp.labelFontSize }), |
|
|
|
...(wp.labelColor != null && { labelColor: wp.labelColor }) |
|
|
|
}); |
|
|
|
}; |
|
|
|
if (isNewlyInserted && segmentMode != null) { |
|
|
|
updatePayload.segmentMode = segmentMode; |
|
|
|
updatePayload.displayStyle = { ...(wp.displayStyle || {}), segmentMode, segmentTargetMinutes, segmentTargetSpeed }; |
|
|
|
} |
|
|
|
if (isNewlyInserted && segmentTargetMinutes != null) updatePayload.segmentTargetMinutes = segmentTargetMinutes; |
|
|
|
if (isNewlyInserted && segmentTargetSpeed != null) updatePayload.segmentTargetSpeed = segmentTargetSpeed; |
|
|
|
if (isNewlyInserted && segmentMode === 'fixed_speed') updatePayload.startTime = startTime; |
|
|
|
await updateWaypoints(updatePayload); |
|
|
|
} |
|
|
|
await this.getList(); |
|
|
|
updated = this.routes.find(r => r.id === routeId); |
|
|
|
@ -1293,7 +1378,8 @@ export default { |
|
|
|
if (this.selectedRouteId === routeId) { |
|
|
|
this.selectedRouteDetails = { ...this.selectedRouteDetails, waypoints: sortedWaypoints }; |
|
|
|
} |
|
|
|
this.$message.success('已添加航点'); |
|
|
|
const modeLabel = segmentMode === 'fixed_time' ? '定时点' : (segmentMode === 'fixed_speed' ? '定速点' : '航点'); |
|
|
|
this.$message.success(`已添加${modeLabel},该航段已按所选方式规划`); |
|
|
|
this.wsConnection?.sendSyncWaypoints?.(routeId); |
|
|
|
} catch (e) { |
|
|
|
this.$message.error(e.msg || e.message || '添加航点失败'); |
|
|
|
@ -1348,7 +1434,7 @@ export default { |
|
|
|
turnAngle = preserveTurnAngle(); |
|
|
|
} else { |
|
|
|
pointType = 'hold_circle'; |
|
|
|
holdParams = JSON.stringify({ radius: 15000, clockwise: true }); |
|
|
|
holdParams = JSON.stringify({ type: 'hold_circle', clockwise: true }); |
|
|
|
turnAngle = preserveTurnAngle(); |
|
|
|
} |
|
|
|
try { |
|
|
|
@ -1370,7 +1456,8 @@ export default { |
|
|
|
if (wp.labelFontSize != null) payload.labelFontSize = wp.labelFontSize; |
|
|
|
if (wp.labelColor != null) payload.labelColor = wp.labelColor; |
|
|
|
if (turnAngle > 0 && this.$refs.cesiumMap) { |
|
|
|
payload.turnRadius = this.$refs.cesiumMap.getWaypointRadius(payload); |
|
|
|
const prevWp = index > 0 ? waypoints[index - 1] : null; |
|
|
|
payload.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWp, turnAngle); |
|
|
|
} else { |
|
|
|
payload.turnRadius = 0; |
|
|
|
} |
|
|
|
@ -1494,11 +1581,15 @@ export default { |
|
|
|
if (wp.color != null) payload.color = wp.color; |
|
|
|
if (wp.pixelSize != null) payload.pixelSize = wp.pixelSize; |
|
|
|
if (wp.outlineColor != null) payload.outlineColor = wp.outlineColor; |
|
|
|
const idx = waypoints.findIndex(p => p.id === dbId); |
|
|
|
if (wp.turnAngle > 0 && this.$refs.cesiumMap && idx >= 0) { |
|
|
|
const prevWp = idx > 0 ? waypoints[idx - 1] : null; |
|
|
|
payload.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWp, wp.turnAngle); |
|
|
|
} |
|
|
|
try { |
|
|
|
const response = await updateWaypoints(payload); |
|
|
|
if (response.code === 200) { |
|
|
|
const merged = { ...wp, ...payload }; |
|
|
|
const idx = waypoints.findIndex(p => p.id === dbId); |
|
|
|
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); |
|
|
|
@ -2139,7 +2230,9 @@ export default { |
|
|
|
}; |
|
|
|
// 若编辑航线弹窗中提交了航点表数据,逐条持久化到数据库并合并到本地 |
|
|
|
if (updatedRoute.waypoints && updatedRoute.waypoints.length > 0) { |
|
|
|
for (const wp of updatedRoute.waypoints) { |
|
|
|
for (let idx = 0; idx < updatedRoute.waypoints.length; idx++) { |
|
|
|
const wp = updatedRoute.waypoints[idx]; |
|
|
|
const prevWp = idx > 0 ? updatedRoute.waypoints[idx - 1] : null; |
|
|
|
const payload = { |
|
|
|
id: wp.id, |
|
|
|
routeId: wp.routeId, |
|
|
|
@ -2160,7 +2253,7 @@ export default { |
|
|
|
if (wp.color != null) payload.color = wp.color; |
|
|
|
if (wp.outlineColor != null) payload.outlineColor = wp.outlineColor; |
|
|
|
if (payload.turnAngle > 0 && this.$refs.cesiumMap) { |
|
|
|
payload.turnRadius = this.$refs.cesiumMap.getWaypointRadius(payload); |
|
|
|
payload.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWp, wp.turnAngle); |
|
|
|
} else { |
|
|
|
payload.turnRadius = 0; |
|
|
|
} |
|
|
|
@ -2394,7 +2487,7 @@ export default { |
|
|
|
|
|
|
|
openAddHoldDuringDrawing() { |
|
|
|
this.addHoldContext = { mode: 'drawing' }; |
|
|
|
this.addHoldForm = { holdType: 'hold_circle', radius: 15000, edgeLengthKm: 20, clockwise: true, startTime: '', startTimeMinutes: 60 }; |
|
|
|
this.addHoldForm = { holdType: 'hold_circle', edgeLengthKm: 20, clockwise: true, startTime: '', startTimeMinutes: 60 }; |
|
|
|
this.showAddHoldDialog = true; |
|
|
|
}, |
|
|
|
|
|
|
|
@ -2532,7 +2625,7 @@ export default { |
|
|
|
this.selectedWaypoint = data; |
|
|
|
this.showWaypointDialog = true; |
|
|
|
}, |
|
|
|
/** 航点编辑保存:更新数据库并同步地图显示 */ |
|
|
|
/** 航点编辑保存:更新数据库并同步地图显示。定时/定速点变更会级联重算整条航线后续航点的 K 时。 */ |
|
|
|
async updateWaypoint(updatedWaypoint) { |
|
|
|
if (!this.selectedRouteDetails || !this.selectedRouteDetails.waypoints) return; |
|
|
|
const routeId = updatedWaypoint.routeId != null ? updatedWaypoint.routeId : this.selectedRouteDetails.id; |
|
|
|
@ -2540,13 +2633,33 @@ export default { |
|
|
|
this.$message.warning('该航线正被其他成员编辑,无法保存'); |
|
|
|
return; |
|
|
|
} |
|
|
|
const sd = this.selectedRouteDetails; |
|
|
|
const waypointsBefore = JSON.parse(JSON.stringify(sd.waypoints)); |
|
|
|
try { |
|
|
|
if (this.$refs.cesiumMap && updatedWaypoint.turnAngle > 0) { |
|
|
|
updatedWaypoint.turnRadius = this.$refs.cesiumMap.getWaypointRadius(updatedWaypoint); |
|
|
|
const idxForRadius = sd.waypoints.findIndex(p => p.id === updatedWaypoint.id); |
|
|
|
if (this.$refs.cesiumMap && updatedWaypoint.turnAngle > 0 && idxForRadius >= 0) { |
|
|
|
const prevWpForTurn = idxForRadius > 0 ? sd.waypoints[idxForRadius - 1] : null; |
|
|
|
updatedWaypoint.turnRadius = this.$refs.cesiumMap.getTurnRadiusFromPrevSpeed(prevWpForTurn, updatedWaypoint.turnAngle); |
|
|
|
} else { |
|
|
|
updatedWaypoint.turnRadius = 0; |
|
|
|
} |
|
|
|
// 明确构造后端需要的字段,确保 startTime(相对K时)一定会被提交并更新到数据库 |
|
|
|
let startTimeToUse = (updatedWaypoint.startTime != null && updatedWaypoint.startTime !== '') |
|
|
|
? updatedWaypoint.startTime |
|
|
|
: 'K+00:00:00'; |
|
|
|
// 定速点:根据上一航点K时、路程和定速重算本航点相对K时 |
|
|
|
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, |
|
|
|
@ -2556,9 +2669,7 @@ export default { |
|
|
|
lng: updatedWaypoint.lng, |
|
|
|
alt: updatedWaypoint.alt, |
|
|
|
speed: updatedWaypoint.speed, |
|
|
|
startTime: (updatedWaypoint.startTime != null && updatedWaypoint.startTime !== '') |
|
|
|
? updatedWaypoint.startTime |
|
|
|
: 'K+00:00:00', |
|
|
|
startTime: startTimeToUse, |
|
|
|
turnAngle: updatedWaypoint.turnAngle |
|
|
|
}; |
|
|
|
if (updatedWaypoint.pointType != null) payload.pointType = updatedWaypoint.pointType; |
|
|
|
@ -2574,7 +2685,6 @@ export default { |
|
|
|
const response = await updateWaypoints(payload); |
|
|
|
if (response.code === 200) { |
|
|
|
const roomId = this.currentRoomId; |
|
|
|
const sd = this.selectedRouteDetails; |
|
|
|
const index = sd.waypoints.findIndex(p => p.id === updatedWaypoint.id); |
|
|
|
if (index !== -1) { |
|
|
|
// 更新本地数据(用已提交的 payload 保证 startTime 等与数据库一致) |
|
|
|
@ -2657,6 +2767,54 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// 定时/定速点变更后:级联重算后续航点的 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); |
|
|
|
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); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (this.$refs.cesiumMap) { |
|
|
|
if (roomId && sd.platformId) { |
|
|
|
try { |
|
|
|
@ -4269,9 +4427,10 @@ export default { |
|
|
|
const posCur = { lng: points[i].lng, lat: points[i].lat, alt: points[i].alt }; |
|
|
|
const entryPos = toEntrySlice.length ? toEntrySlice[toEntrySlice.length - 1] : posCur; |
|
|
|
const holdWp = waypoints[i + 1]; |
|
|
|
const prevWpForHold = waypoints[i]; |
|
|
|
const holdParams = this.parseHoldParams(holdWp); |
|
|
|
const holdCenter = holdWp ? { lng: parseFloat(holdWp.lng), lat: parseFloat(holdWp.lat), alt: Number(holdWp.alt) || 0 } : null; |
|
|
|
const computedR = this.$refs.cesiumMap ? this.$refs.cesiumMap.getWaypointRadius(holdWp) : null; |
|
|
|
const computedR = this.$refs.cesiumMap?.getHoldRadiusFromPrevSpeed?.(holdWp, prevWpForHold) ?? (this.$refs.cesiumMap ? this.$refs.cesiumMap.getWaypointRadius(holdWp) : null); |
|
|
|
const holdRadius = (computedR != null && computedR > 0) ? computedR : 500; |
|
|
|
const holdClockwise = holdParams && holdParams.clockwise !== false; |
|
|
|
const holdCircumference = holdRadius != null ? 2 * Math.PI * holdRadius : null; |
|
|
|
@ -4624,7 +4783,7 @@ export default { |
|
|
|
if (!this.addHoldContext) return; |
|
|
|
if (this.addHoldContext.mode === 'drawing') { |
|
|
|
const holdParams = this.addHoldForm.holdType === 'hold_circle' |
|
|
|
? { radius: this.addHoldForm.radius, clockwise: this.addHoldForm.clockwise } |
|
|
|
? { type: 'hold_circle', clockwise: this.addHoldForm.clockwise } |
|
|
|
: { edgeLength: (this.addHoldForm.edgeLengthKm != null ? this.addHoldForm.edgeLengthKm : 20) * 1000, clockwise: this.addHoldForm.clockwise }; |
|
|
|
if (this.$refs.cesiumMap && this.$refs.cesiumMap.insertHoldBetweenLastTwo) { |
|
|
|
this.$refs.cesiumMap.insertHoldBetweenLastTwo(holdParams); |
|
|
|
@ -4646,7 +4805,7 @@ export default { |
|
|
|
const newSeq = (prevWp.seq != null ? Number(prevWp.seq) : legIndex + 1) + 1; |
|
|
|
const baseSeq = prevWp.seq != null ? Number(prevWp.seq) : legIndex + 1; |
|
|
|
const holdParams = this.addHoldForm.holdType === 'hold_circle' |
|
|
|
? { radius: this.addHoldForm.radius, clockwise: this.addHoldForm.clockwise } |
|
|
|
? { type: 'hold_circle', clockwise: this.addHoldForm.clockwise } |
|
|
|
: { edgeLength: (this.addHoldForm.edgeLengthKm != null ? this.addHoldForm.edgeLengthKm : 20) * 1000, clockwise: this.addHoldForm.clockwise }; |
|
|
|
const startTime = this.addHoldForm.startTimeMinutes !== '' && this.addHoldForm.startTimeMinutes != null && !Number.isNaN(Number(this.addHoldForm.startTimeMinutes)) |
|
|
|
? this.minutesToStartTime(Number(this.addHoldForm.startTimeMinutes)) |
|
|
|
|