|
|
|
@ -507,7 +507,7 @@ |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- 截图保存弹窗:选择文件名后保存到本地(浏览器会弹出保存位置对话框) --> |
|
|
|
<!-- 截图保存弹窗:选择文件名后保存到本地 --> |
|
|
|
<el-dialog |
|
|
|
title="保存地图截图" |
|
|
|
:visible.sync="showScreenshotDialog" |
|
|
|
@ -522,7 +522,7 @@ |
|
|
|
<el-form-item label="文件名"> |
|
|
|
<el-input v-model="screenshotFileName" placeholder="例如:地图截图.png" /> |
|
|
|
</el-form-item> |
|
|
|
<p class="screenshot-tip">点击「保存」后,浏览器将弹出保存对话框,您可选择保存路径。</p> |
|
|
|
<p class="screenshot-tip">点击「保存」后会优先弹出保存位置对话框;若当前浏览器不支持,则会下载到浏览器默认目录。</p> |
|
|
|
</el-form> |
|
|
|
<div slot="footer" class="dialog-footer"> |
|
|
|
<el-button @click="showScreenshotDialog = false">取 消</el-button> |
|
|
|
@ -1332,6 +1332,38 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
/** 插入航点命名:按插入位置给“锚点-子序号”,不改动既有航点名称 */ |
|
|
|
getInsertedWaypointName(waypoints = [], insertIndex = 0) { |
|
|
|
const list = Array.isArray(waypoints) ? waypoints : [] |
|
|
|
const used = new Set( |
|
|
|
list |
|
|
|
.map(w => (w && w.name ? String(w.name).trim().toUpperCase() : '')) |
|
|
|
.filter(Boolean) |
|
|
|
) |
|
|
|
const parseBase = (name) => { |
|
|
|
const m = /^WP(\d+)(?:-(\d+))?$/i.exec(String(name || '').trim()) |
|
|
|
return m ? Number(m[1]) : NaN |
|
|
|
} |
|
|
|
const prev = insertIndex > 0 ? list[insertIndex - 1] : null |
|
|
|
const next = insertIndex < list.length ? list[insertIndex] : null |
|
|
|
const prevBase = prev && prev.name ? parseBase(prev.name) : NaN |
|
|
|
const nextBase = next && next.name ? parseBase(next.name) : NaN |
|
|
|
|
|
|
|
let anchor = Number.isFinite(prevBase) ? prevBase : NaN |
|
|
|
if (!Number.isFinite(anchor)) { |
|
|
|
if (Number.isFinite(nextBase)) anchor = Math.max(0, nextBase - 1) |
|
|
|
else anchor = 0 |
|
|
|
} |
|
|
|
|
|
|
|
let suffix = 1 |
|
|
|
let candidate = `WP${anchor}-${suffix}` |
|
|
|
while (used.has(candidate.toUpperCase())) { |
|
|
|
suffix += 1 |
|
|
|
candidate = `WP${anchor}-${suffix}` |
|
|
|
} |
|
|
|
return candidate |
|
|
|
}, |
|
|
|
|
|
|
|
/** 右键航点“向前/向后增加航点”:进入放置模式,传入 waypoints 给地图预览 */ |
|
|
|
handleAddWaypointAt({ routeId, waypointIndex, mode, segmentMode, segmentTargetMinutes, segmentTargetSpeed }) { |
|
|
|
if (this.isRouteLockedByOther(routeId)) { |
|
|
|
@ -1386,7 +1418,7 @@ export default { |
|
|
|
let segmentTargetSpeed = null; |
|
|
|
let plannedPrevSpeed = null; |
|
|
|
const count = waypoints.length + 1; |
|
|
|
const newName = `WP${insertIndex + 1}`; |
|
|
|
const newName = this.getInsertedWaypointName(waypoints, insertIndex); |
|
|
|
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); |
|
|
|
@ -1487,7 +1519,9 @@ export default { |
|
|
|
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}`)); |
|
|
|
const nameToUse = isNewlyInserted |
|
|
|
? (wp.name || newName) |
|
|
|
: (wp.name || (isHold(wp) ? `HOLD${newSeq}` : `WP${newSeq}`)); |
|
|
|
// 插入任意航点后:上一航点变为中间航点时,将上一航点转弯坡度设为 45° |
|
|
|
const prevTurnAngle = (isPrevToNew && insertIndex > 1) ? 45 : wp.turnAngle; |
|
|
|
const updatePayload = { |
|
|
|
@ -3839,19 +3873,58 @@ export default { |
|
|
|
viewer.scene.requestRender() |
|
|
|
}, |
|
|
|
|
|
|
|
/** 确认保存截图:触发浏览器下载(用户可在保存对话框中选择路径) */ |
|
|
|
confirmSaveScreenshot() { |
|
|
|
/** 确认保存截图:优先弹出系统保存位置选择框,不支持时回退浏览器下载 */ |
|
|
|
async confirmSaveScreenshot() { |
|
|
|
if (!this.screenshotDataUrl) return |
|
|
|
let name = (this.screenshotFileName || '地图截图.png').trim() |
|
|
|
if (!name) name = '地图截图.png' |
|
|
|
if (!/\.(png|jpg|jpeg)$/i.test(name)) name = name + '.png' |
|
|
|
const blob = await fetch(this.screenshotDataUrl).then((res) => res.blob()) |
|
|
|
|
|
|
|
try { |
|
|
|
if (window.showSaveFilePicker && window.isSecureContext) { |
|
|
|
const fileHandle = await window.showSaveFilePicker({ |
|
|
|
suggestedName: name, |
|
|
|
types: [ |
|
|
|
{ |
|
|
|
description: '图片文件', |
|
|
|
accept: { |
|
|
|
'image/png': ['.png'], |
|
|
|
'image/jpeg': ['.jpg', '.jpeg'] |
|
|
|
} |
|
|
|
} |
|
|
|
] |
|
|
|
}) |
|
|
|
const writable = await fileHandle.createWritable() |
|
|
|
await writable.write(blob) |
|
|
|
await writable.close() |
|
|
|
this.$message.success('截图已保存') |
|
|
|
} else { |
|
|
|
this.downloadScreenshotFallback(blob, name) |
|
|
|
this.$message.success('当前浏览器不支持选择保存路径,已下载到默认目录') |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
if (e && e.name === 'AbortError') { |
|
|
|
this.$message.info('已取消保存') |
|
|
|
return |
|
|
|
} |
|
|
|
console.warn('showSaveFilePicker 保存失败,已回退浏览器下载', e) |
|
|
|
this.downloadScreenshotFallback(blob, name) |
|
|
|
this.$message.success('保存位置选择失败,已回退为默认下载') |
|
|
|
} |
|
|
|
|
|
|
|
this.showScreenshotDialog = false |
|
|
|
this.screenshotDataUrl = '' |
|
|
|
}, |
|
|
|
|
|
|
|
/** 截图保存降级:使用浏览器默认下载流程 */ |
|
|
|
downloadScreenshotFallback(blob, name) { |
|
|
|
const url = URL.createObjectURL(blob) |
|
|
|
const a = document.createElement('a') |
|
|
|
a.href = this.screenshotDataUrl |
|
|
|
a.href = url |
|
|
|
a.download = name |
|
|
|
a.click() |
|
|
|
this.showScreenshotDialog = false |
|
|
|
this.screenshotDataUrl = '' |
|
|
|
this.$message.success('已触发下载,请在浏览器保存对话框中选择保存位置') |
|
|
|
setTimeout(() => URL.revokeObjectURL(url), 1000) |
|
|
|
}, |
|
|
|
|
|
|
|
/** 白板:K+HH:MM:SS 时间块比较 */ |
|
|
|
|