Browse Source

feat: Tasks 航线编辑改为 Mapbox 图层与点击交互

保留 replace/restore 搬迁语义,容器刷新改 resize。
main
xiaosi 3 weeks ago
parent
commit
d18a2caf33
  1. 185
      src/views/TasksView/TasksView.vue

185
src/views/TasksView/TasksView.vue

@ -229,7 +229,6 @@
<script setup>
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import * as maptalks from 'maptalks'
import { useUiStore } from '@/stores/modules/uiStore'
import request from '@/utils/http'
import { useDeviceStore } from '@/stores/modules/devicesStore'
@ -237,8 +236,11 @@ import * as urls from '@/config/urls'
import commonRefs from '@/utils/commonRefs'
import mapHelper from '@/core/mapHelper'
const ROUTE_LAYER_ID = 'tasks-route-editor'
const ROUTE_SOURCE = 'task-route'
const ROUTE_LINE = 'task-route-line'
const WP_SOURCE = 'task-waypoints'
const WP_CIRCLE = 'task-waypoints-circle'
const WP_LABEL = 'task-waypoints-label'
const route = useRoute()
const router = useRouter()
@ -259,7 +261,6 @@ const routeCursor = ref('--')
const routeMapEl = ref(null)
const routeMapReady = ref(false)
let routeMapInstance = null
let routeLayer = null
let routeMapAttached = false
const routeForm = reactive({ name: '', altitude: 50, speed: 10, turnMode: 'auto' })
@ -466,89 +467,128 @@ function resetRouteEditor() {
routeCursor.value = '--'
}
function syncRouteLayer() {
if (!routeLayer) return
routeLayer.clear()
function clearTaskLayers(map) {
if (!map) return
for (const id of [WP_LABEL, WP_CIRCLE, ROUTE_LINE]) {
if (map.getLayer(id)) map.removeLayer(id)
}
for (const id of [WP_SOURCE, ROUTE_SOURCE]) {
if (map.getSource(id)) map.removeSource(id)
}
}
function ensureTaskLayers(map) {
if (!map.getSource(ROUTE_SOURCE)) {
map.addSource(ROUTE_SOURCE, {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] },
})
}
if (!map.getSource(WP_SOURCE)) {
map.addSource(WP_SOURCE, {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] },
})
}
if (!map.getLayer(ROUTE_LINE)) {
map.addLayer({
id: ROUTE_LINE,
type: 'line',
source: ROUTE_SOURCE,
paint: {
'line-color': '#2d82da',
'line-width': 3,
'line-opacity': 0.95,
},
})
}
if (!map.getLayer(WP_CIRCLE)) {
map.addLayer({
id: WP_CIRCLE,
type: 'circle',
source: WP_SOURCE,
paint: {
'circle-radius': 9,
'circle-color': ['get', 'color'],
'circle-stroke-color': '#fff',
'circle-stroke-width': 2,
},
})
}
if (!map.getLayer(WP_LABEL)) {
map.addLayer({
id: WP_LABEL,
type: 'symbol',
source: WP_SOURCE,
layout: {
'text-field': ['get', 'indexLabel'],
'text-size': 11,
'text-allow-overlap': true,
},
paint: { 'text-color': '#fff' },
})
}
}
function syncRouteLayer() {
if (!routeMapInstance) return
ensureTaskLayers(routeMapInstance)
const coords = routePoints.value
.map((p) => [Number(p.longitude), Number(p.latitude)])
.filter(([lon, lat]) => Number.isFinite(lon) && Number.isFinite(lat))
if (coords.length >= 2) {
new maptalks.LineString(coords, {
symbol: {
lineColor: '#2d82da',
lineWidth: 3,
lineOpacity: 0.95,
},
}).addTo(routeLayer)
const lineData = {
type: 'FeatureCollection',
features: coords.length >= 2
? [{ type: 'Feature', geometry: { type: 'LineString', coordinates: coords }, properties: {} }]
: [],
}
coords.forEach((c, i) => {
const wpData = {
type: 'FeatureCollection',
features: coords.map((c, i) => {
const isStart = i === 0
const isEnd = i === coords.length - 1 && coords.length > 1
const color = isStart ? '#35a66a' : isEnd ? '#c56c28' : '#2d82da'
const marker = new maptalks.Marker(c, {
cursor: 'pointer',
properties: { index: i },
symbol: [
{
markerType: 'ellipse',
markerFill: color,
markerLineColor: '#fff',
markerLineWidth: 2,
markerWidth: 18,
markerHeight: 18,
},
{
textName: String(i + 1),
textFill: '#fff',
textSize: 11,
textWeight: 'bold',
textDy: 1,
return {
type: 'Feature',
properties: {
index: i,
indexLabel: String(i + 1),
color: isStart ? '#35a66a' : isEnd ? '#c56c28' : '#2d82da',
},
],
})
marker.on('click', (e) => {
if (e?.domEvent) {
e.domEvent.stopPropagation?.()
e.domEvent.preventDefault?.()
geometry: { type: 'Point', coordinates: c },
}
openWaypointEditor(i)
})
marker.addTo(routeLayer)
})
}),
}
routeMapInstance.getSource(ROUTE_SOURCE)?.setData(lineData)
routeMapInstance.getSource(WP_SOURCE)?.setData(wpData)
}
function onRouteMapClick(e) {
if (!routeMapAttached) return
// marker
if (e?.target && e.target !== routeMapInstance) return
if (!routeMapAttached || !routeMapInstance) return
const hit = routeMapInstance.queryRenderedFeatures(e.point, {
layers: [WP_CIRCLE, WP_LABEL].filter((id) => routeMapInstance.getLayer(id)),
})
if (hit[0]?.properties?.index != null) {
openWaypointEditor(Number(hit[0].properties.index))
return
}
if (!validRouteDefaults()) return
const coord = e?.coordinate
if (!coord) return
const longitude = Number(coord.x)
const latitude = Number(coord.y)
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return
const { lng, lat } = e.lngLat
routePoints.value.push({
longitude,
latitude,
longitude: lng,
latitude: lat,
altitude: Number(routeForm.altitude),
speed: Number(routeForm.speed),
turnMode: routeForm.turnMode,
})
routeCursor.value = formatCoord(longitude, latitude)
routeCursor.value = formatCoord(lng, lat)
routeFormError.value = ''
syncRouteLayer()
}
function onRouteMapMove(e) {
if (!routeMapAttached) return
const coord = e?.coordinate
if (!coord) return
routeCursor.value = formatCoord(Number(coord.x), Number(coord.y))
routeCursor.value = formatCoord(e.lngLat.lng, e.lngLat.lat)
}
async function attachRouteMap() {
@ -559,7 +599,7 @@ async function attachRouteMap() {
const map = await commonRefs.getRef('map')
if (!map) {
routeMapReady.value = false
ui.toast('地图未初始化(缺少天地图 token)')
ui.toast('地图未初始化(缺少 Mapbox token)')
return
}
@ -569,20 +609,17 @@ async function attachRouteMap() {
routeMapAttached = true
routeMapReady.value = true
const existing = map.getLayer(ROUTE_LAYER_ID)
if (existing) existing.remove()
routeLayer = new maptalks.VectorLayer(ROUTE_LAYER_ID, [], { zIndex: 140, forceRenderOnMoving: true })
routeLayer.addTo(map)
clearTaskLayers(map)
ensureTaskLayers(map)
map.on('click', onRouteMapClick)
map.on('mousemove', onRouteMapMove)
const center = map.getCenter()
if (center) routeCursor.value = formatCoord(center.x, center.y)
const c = map.getCenter()
routeCursor.value = formatCoord(c.lng, c.lat)
//
requestAnimationFrame(() => {
map.checkSize?.()
map.resize()
syncRouteLayer()
})
}
@ -591,16 +628,12 @@ function teardownRouteMap() {
if (routeMapInstance) {
routeMapInstance.off('click', onRouteMapClick)
routeMapInstance.off('mousemove', onRouteMapMove)
const layer = routeMapInstance.getLayer(ROUTE_LAYER_ID)
if (layer) layer.remove()
clearTaskLayers(routeMapInstance)
}
routeLayer = null
if (routeMapAttached) {
mapHelper.restoreMapContainer()
routeMapAttached = false
}
routeMapInstance = null
routeMapReady.value = false
}

Loading…
Cancel
Save