支持图源切换
This commit is contained in:
@@ -12,10 +12,12 @@ const props = withDefaults(defineProps<{
|
||||
autoFit?: boolean
|
||||
loading?: boolean
|
||||
mapSource?: PublicMapTileSource
|
||||
mapSources?: PublicMapTileSource[]
|
||||
}>(), {
|
||||
autoFit: true,
|
||||
loading: false,
|
||||
mapSource: () => fallbackMapSource,
|
||||
mapSources: () => [fallbackMapSource],
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -24,6 +26,7 @@ const emit = defineEmits<{
|
||||
'delete-node': [nodeId: string]
|
||||
'delete-and-block-node': [payload: { nodeId: string; nodeNum: number | null }]
|
||||
'bounds-change': [payload: MapBoundsChangePayload]
|
||||
'map-source-change': [sourceId: number]
|
||||
}>()
|
||||
|
||||
const mapEl = ref<HTMLElement | null>(null)
|
||||
@@ -92,6 +95,10 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
function selectMapSource(sourceId: number) {
|
||||
emit('map-source-change', sourceId)
|
||||
}
|
||||
|
||||
function applyTileLayer() {
|
||||
if (!map) {
|
||||
return
|
||||
@@ -392,6 +399,43 @@ function escapeHTML(value: string): string {
|
||||
<template>
|
||||
<section class="map-panel panel">
|
||||
<div ref="mapEl" class="map-container"></div>
|
||||
<div
|
||||
class="map-source-control"
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
@dblclick.stop
|
||||
@wheel.stop
|
||||
>
|
||||
<button class="map-source-icon" type="button" aria-label="切换地图图源">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 18.5l-3 -1.5l-6 3v-13l6 -3l6 3l6 -3v7.5" />
|
||||
<path d="M9 4v13" />
|
||||
<path d="M15 7v5.5" />
|
||||
<path d="M21.121 20.121a3 3 0 1 0 -4.242 0c.418 .419 1.125 1.045 2.121 1.879c1.051 -.89 1.759 -1.516 2.121 -1.879" />
|
||||
<path d="M19 18v.01" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="map-source-popover">
|
||||
<div class="map-source-drawer-header">
|
||||
<span>地图图源</span>
|
||||
</div>
|
||||
<div v-if="mapSources.length > 1" class="map-source-options">
|
||||
<button
|
||||
v-for="source in mapSources"
|
||||
:key="source.id"
|
||||
class="map-source-option"
|
||||
:class="{ active: source.id === mapSource.id }"
|
||||
type="button"
|
||||
@click="selectMapSource(source.id)"
|
||||
>
|
||||
<span class="map-source-option-name">{{ source.name }}</span>
|
||||
<span v-if="source.id === mapSource.id" class="map-source-option-check">当前</span>
|
||||
</button>
|
||||
</div>
|
||||
<span v-else class="map-source-control-pill">{{ mapSource.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div v-if="loading" class="map-empty">正在加载当前区域坐标...</div>
|
||||
<div v-else-if="items.length === 0" class="map-empty">暂无可显示坐标的节点</div> -->
|
||||
<div
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { createNodeBlockingRule, deleteNode, deleteTextMessage, getMapReportById, getNodeInfoById, getPositions, getTelemetry, getTextMessages } from '../api'
|
||||
import type { MapReport, NodeInfo, PositionRecord, PublicMapTileSource, TelemetryRecord, TextMessage } from '../types'
|
||||
import { fallbackMapSource, loadDefaultMapSource } from '../mapSource'
|
||||
import { fallbackMapSource, loadEnabledMapSources } from '../mapSource'
|
||||
import ConfirmDeleteModal from './ConfirmDeleteModal.vue'
|
||||
import NodeTrajectoryMap from './NodeTrajectoryMap.vue'
|
||||
|
||||
@@ -16,6 +16,7 @@ const mapReport = ref<MapReport | null>(null)
|
||||
const messages = ref<TextMessage[]>([])
|
||||
const positions = ref<PositionRecord[]>([])
|
||||
const telemetry = ref<TelemetryRecord[]>([])
|
||||
const mapSources = ref<PublicMapTileSource[]>([fallbackMapSource])
|
||||
const mapSource = ref<PublicMapTileSource>(fallbackMapSource)
|
||||
const loading = ref(true)
|
||||
const chatLoadingOlder = ref(false)
|
||||
@@ -370,7 +371,16 @@ function handleChatScroll() {
|
||||
}
|
||||
|
||||
async function loadMapSource() {
|
||||
mapSource.value = await loadDefaultMapSource()
|
||||
const sources = await loadEnabledMapSources()
|
||||
mapSources.value = sources
|
||||
mapSource.value = sources[0] ?? fallbackMapSource
|
||||
}
|
||||
|
||||
function selectMapSource(sourceId: number) {
|
||||
const source = mapSources.value.find((item) => item.id === sourceId)
|
||||
if (source) {
|
||||
mapSource.value = source
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDetails() {
|
||||
@@ -499,7 +509,12 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
<span class="badge">{{ positions.length }}</span>
|
||||
</div>
|
||||
<NodeTrajectoryMap :positions="positions" :map-source="mapSource" />
|
||||
<NodeTrajectoryMap
|
||||
:positions="positions"
|
||||
:map-source="mapSource"
|
||||
:map-sources="mapSources"
|
||||
@map-source-change="selectMapSource"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,15 +8,25 @@ import type { PositionRecord, PublicMapTileSource } from '../types'
|
||||
const props = withDefaults(defineProps<{
|
||||
positions: PositionRecord[]
|
||||
mapSource?: PublicMapTileSource
|
||||
mapSources?: PublicMapTileSource[]
|
||||
}>(), {
|
||||
mapSource: () => fallbackMapSource,
|
||||
mapSources: () => [fallbackMapSource],
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
'map-source-change': [sourceId: number]
|
||||
}>()
|
||||
|
||||
const mapEl = ref<HTMLElement | null>(null)
|
||||
let map: L.Map | null = null
|
||||
let tileLayer: L.TileLayer | null = null
|
||||
let layer: L.LayerGroup | null = null
|
||||
|
||||
function selectMapSource(sourceId: number) {
|
||||
emit('map-source-change', sourceId)
|
||||
}
|
||||
|
||||
function applyTileLayer() {
|
||||
if (!map) {
|
||||
return
|
||||
@@ -93,5 +103,44 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="mapEl" class="trajectory-map"></div>
|
||||
<div class="trajectory-map-shell">
|
||||
<div ref="mapEl" class="trajectory-map"></div>
|
||||
<div
|
||||
class="map-source-control"
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
@dblclick.stop
|
||||
@wheel.stop
|
||||
>
|
||||
<button class="map-source-icon" type="button" aria-label="切换地图图源">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 18.5l-3 -1.5l-6 3v-13l6 -3l6 3l6 -3v7.5" />
|
||||
<path d="M9 4v13" />
|
||||
<path d="M15 7v5.5" />
|
||||
<path d="M21.121 20.121a3 3 0 1 0 -4.242 0c.418 .419 1.125 1.045 2.121 1.879c1.051 -.89 1.759 -1.516 2.121 -1.879" />
|
||||
<path d="M19 18v.01" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="map-source-popover">
|
||||
<div class="map-source-drawer-header">
|
||||
<span>地图图源</span>
|
||||
</div>
|
||||
<div v-if="mapSources.length > 1" class="map-source-options">
|
||||
<button
|
||||
v-for="source in mapSources"
|
||||
:key="source.id"
|
||||
class="map-source-option"
|
||||
:class="{ active: source.id === mapSource.id }"
|
||||
type="button"
|
||||
@click="selectMapSource(source.id)"
|
||||
>
|
||||
<span class="map-source-option-name">{{ source.name }}</span>
|
||||
<span v-if="source.id === mapSource.id" class="map-source-option-check">当前</span>
|
||||
</button>
|
||||
</div>
|
||||
<span v-else class="map-source-control-pill">{{ mapSource.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user