Browse Source

feat(routes): edit existing route in library editor

main
xiaosi 3 weeks ago
parent
commit
51c941b74f
  1. 129
      src/views/TasksView/TasksView.vue

129
src/views/TasksView/TasksView.vue

@ -127,6 +127,7 @@
:distance="r.distance"
:collected-at="r.collectedAt"
:map-class="r.mapClass"
@edit="openRouteEditorForEdit(r)"
@delete="deleteRoute(r)"
/>
<div v-if="!filteredRoutes.length" class="table-empty" style="grid-column:1/-1">暂无航线</div>
@ -136,8 +137,8 @@
<div class="route-editor" :hidden="!routeEditorOpen">
<header class="route-editor-header">
<t-button class="back-command" variant="text" type="button" @click="closeRouteEditor"><svg><use href="#i-chevron" /></svg>返回航线库</t-button>
<div><strong>新建航线</strong><span>{{ routeSummary }}</span></div>
<div><t-button variant="outline" type="button" :disabled="!routePoints.length" @click="undoRoutePoint"><svg><use href="#i-refresh" /></svg>撤销</t-button><t-button variant="outline" type="button" :disabled="!routePoints.length" @click="clearRoutePoints"><svg><use href="#i-x" /></svg>清空</t-button><t-button theme="primary" type="button" :disabled="routeSaving" @click="submitRoute">{{ routeSaving ? '' : '线' }}</t-button></div>
<div><strong>{{ editingRouteId ? '编辑航线' : '新建航线' }}</strong><span>{{ routeSummary }}</span></div>
<div><t-button variant="outline" type="button" :disabled="!routePoints.length" @click="undoRoutePoint"><svg><use href="#i-refresh" /></svg>撤销</t-button><t-button variant="outline" type="button" :disabled="!routePoints.length" @click="clearRoutePoints"><svg><use href="#i-x" /></svg>清空</t-button><t-button theme="primary" type="button" :disabled="routeSaving" @click="submitRoute">{{ routeSaving ? '' : (editingRouteId ? '' : '线') }}</t-button></div>
</header>
<div class="route-editor-body">
<aside class="route-config-panel">
@ -276,6 +277,7 @@ const execResultFilterOptions = [
]
const routeSearch = ref('')
const routeEditorOpen = ref(false)
const editingRouteId = ref(null)
const routeSaving = ref(false)
const routeFormError = ref('')
const routeCursor = ref('--')
@ -542,11 +544,33 @@ function formatCoord(lon, lat) {
return `${lon.toFixed(6)}, ${lat.toFixed(6)}`
}
function mapApiWaypointToPoint(wp) {
return {
longitude: Number(wp.longitude),
latitude: Number(wp.latitude),
altitude: Number(wp.altitude),
speed: Number(wp.speed),
turnMode: Number(wp.holdSec) > 0 ? 'stop' : 'auto',
}
}
function mapPointToApiWaypoint(point) {
return {
longitude: point.longitude,
latitude: point.latitude,
altitude: point.altitude,
speed: point.speed,
yaw: 0,
holdSec: point.turnMode === 'stop' ? 1 : 0,
}
}
function resetRouteEditor() {
Object.assign(routeForm, { name: '', altitude: 50, speed: 10, turnMode: 'auto' })
routePoints.value = []
routeFormError.value = ''
routeCursor.value = '--'
editingRouteId.value = null
}
function clearTaskLayers(map) {
@ -745,6 +769,31 @@ function closeRouteEditor() {
teardownRouteMap()
routeEditorOpen.value = false
waypointEditorOpen.value = false
editingRouteId.value = null
}
async function openRouteEditorForEdit(route) {
routeFormError.value = ''
try {
const detail = await request.get(urls.ROUTE(route.id))
resetRouteEditor()
editingRouteId.value = String(detail?.id ?? route.id)
const list = Array.isArray(detail?.waypoints) ? detail.waypoints : []
routePoints.value = list.map(mapApiWaypointToPoint)
const first = routePoints.value[0]
Object.assign(routeForm, {
name: detail?.name || route.name || '',
altitude: first ? first.altitude : 50,
speed: first ? first.speed : 10,
turnMode: first ? first.turnMode : 'auto',
})
routeEditorOpen.value = true
await attachRouteMap()
// boot already syncs; optional extra if ready:
if (routeMapReady.value) syncRouteLayer()
} catch (e) {
ui.toast(e.message || '加载航线详情失败')
}
}
function routeDistance(points) {
@ -858,17 +907,23 @@ async function submitRoute() {
routeFormError.value = '至少需要设置 2 个航点。'
return
}
const payload = {
name,
waypoints: routePoints.value.map(mapPointToApiWaypoint),
}
routeSaving.value = true
try {
await request.post(urls.ROUTES, {
name,
waypoints: routePoints.value.map((point) => ({ longitude: point.longitude, latitude: point.latitude, altitude: point.altitude, speed: point.speed, yaw: 0, holdSec: point.turnMode === 'stop' ? 1 : 0 }))
})
closeRouteEditor()
if (editingRouteId.value) {
await request.put(urls.ROUTE(editingRouteId.value), payload)
ui.toast('航线已更新')
} else {
await request.post(urls.ROUTES, payload)
ui.toast('航线已保存')
}
closeRouteEditor()
await load()
} catch (e) {
routeFormError.value = e.message || '保存航线失败'
routeFormError.value = e.message || (editingRouteId.value ? '更新航线失败' : '保存航线失败')
} finally {
routeSaving.value = false
}
@ -1058,27 +1113,73 @@ async function downloadRawVideo(record) {
}
.task-mode-radio {
width: 100%;
display: flex;
max-width: 100%;
flex-wrap: nowrap;
--td-bg-color-component: #edf1f4;
--td-bg-color-container-select: #fff;
--td-text-color-secondary: #64727e;
--td-text-color-primary: #245f9e;
--td-component-border: transparent;
--td-comp-size-m: 36px;
--td-comp-paddingTB-xxs: 3px;
--td-font-body-medium: 12px / 18px var(--td-font-family);
--td-radius-default: 4px;
--td-radius-small: 3px;
}
.task-mode-radio :deep(.t-radio-button) {
flex: 1;
flex: 1 1 0;
min-width: 0;
margin: 0;
gap: 0;
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.task-mode-radio :deep(.t-radio-group__bg-block) {
box-shadow: 0 1px 4px rgba(23, 39, 50, 0.12);
}
.task-schedule-radio :deep(.t-radio-button) {
min-width: 0;
padding: 0 4px;
white-space: nowrap;
padding-left: 4px;
padding-right: 4px;
}
.route-config-panel :deep(.t-input),
.route-config-panel :deep(.t-input-number) {
.route-config-panel :deep(.t-input-number),
.route-config-panel :deep(.t-input-number .t-input) {
width: 100%;
}
.route-config-panel :deep(.t-input) {
height: 35px;
min-height: 35px;
padding: 0 9px;
border: 1px solid var(--line);
border-radius: 4px;
color: #354550;
background: #fff;
font-size: 11px;
box-shadow: none;
}
.route-config-panel :deep(.t-input__inner) {
height: auto;
padding: 0;
border: 0;
background: transparent;
font-size: 11px;
box-shadow: none;
}
.number-input :deep(.t-input-number) {
width: 100%;
}
.number-input :deep(.t-input) {
padding-right: 34px;
}
.route-config-panel :deep(.apply-all-waypoints.t-button) {
width: 100%;
height: 33px;
min-height: 33px;
flex-shrink: 0;
overflow: visible;
}
.task-video-option :deep(.t-switch) {
justify-self: end;
}

Loading…
Cancel
Save